Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

优化生成随机字符串 #154

Merged
merged 1 commit into from
Jan 31, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 73 additions & 42 deletions strutil/random.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package strutil

import (
mRand "math/rand"
"math"
"math/rand"
"time"
"unsafe"

"github.com/gookit/goutil/byteutil"
"github.com/gookit/goutil/encodes"
)

// some consts string chars
const (
Numbers = "0123456789"
HexChars = "0123456789abcdef" // base16
MaximumCapacity = 1 << 31
Numbers = "0123456789"
HexChars = "0123456789abcdef" // base16

AlphaBet = "abcdefghijklmnopqrstuvwxyz"
AlphaBet1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
Expand All @@ -21,64 +24,92 @@
AlphaNum3 = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
)

func newRand() *mRand.Rand {
return mRand.New(mRand.NewSource(time.Now().UnixNano()))
var rn = rand.NewSource(time.Now().UnixNano())

// nearestPowerOfTwo 返回一个大于等于cap的最近的2的整数次幂,参考java8的hashmap的tableSizeFor函数
// cap 输入参数
// 返回一个大于等于cap的最近的2的整数次幂
func nearestPowerOfTwo(cap int) int {
n := cap - 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
if n < 0 {
return 1
} else if n >= MaximumCapacity {
return MaximumCapacity
}
return n + 1
}

// RandomChars generate give length random chars at `a-z`
func RandomChars(ln int) string {
cs := make([]byte, ln)
// buildRandomString 生成随机字符串
// letters 字符串模板
// length 生成长度
// 返回一个指定长度的随机字符串
func buildRandomString(letters string, length int) string {
// 仿照strings.Builder
// 创建一个长度为 length 的字节切片
bytes := make([]byte, length)
strLength := len(letters)
if strLength <= 0 {
return ""
} else if strLength == 1 {
for i := 0; i < length; i++ {
bytes[i] = letters[0]
}
return *(*string)(unsafe.Pointer(&bytes))
}
// letters的字符需要使用多少个比特位数才能表示完
// letterIdBits := int(math.Ceil(math.Log2(strLength))),下面比上面的代码快
letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(strLength))))

Check warning on line 66 in strutil/random.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

var letterIdBits should be letterIDBits
// 最大的字母id掩码
var letterIdMask int64 = 1<<letterIdBits - 1

Check warning on line 68 in strutil/random.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

var letterIdMask should be letterIDMask
// 可用次数的最大值
letterIdMax := 63 / letterIdBits

Check warning on line 70 in strutil/random.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

var letterIdMax should be letterIDMax
// UnixNano: 1607400451937462000
rn := newRand()

for i := 0; i < ln; i++ {
// rand in 0 - 25
cs[i] = AlphaBet[rn.Intn(25)]
// 循环生成随机字符串
for i, cache, remain := length-1, rn.Int63(), letterIdMax; i >= 0; {
// 检查随机数生成器是否用尽所有随机数
if remain == 0 {
cache, remain = rn.Int63(), letterIdMax
}
// 从可用字符的字符串中随机选择一个字符
if idx := int(cache & letterIdMask); idx < len(letters) {
bytes[i] = letters[idx]
i--
}
// 右移比特位数,为下次选择字符做准备
cache >>= letterIdBits
remain--
}
return string(cs)
// 仿照strings.Builder用unsafe包返回一个字符串,避免拷贝
// 将字节切片转换为字符串并返回
return *(*string)(unsafe.Pointer(&bytes))
}

// RandomChars generate give length random chars at `a-z`
func RandomChars(ln int) string {
return buildRandomString(AlphaBet, ln)
}

// RandomCharsV2 generate give length random chars in `0-9a-z`
func RandomCharsV2(ln int) string {
cs := make([]byte, ln)
// UnixNano: 1607400451937462000
rn := newRand()

for i := 0; i < ln; i++ {
// rand in 0 - 35
cs[i] = AlphaNum[rn.Intn(35)]
}
return string(cs)
return buildRandomString(AlphaNum, ln)
}

// RandomCharsV3 generate give length random chars in `0-9a-zA-Z`
func RandomCharsV3(ln int) string {
cs := make([]byte, ln)
// UnixNano: 1607400451937462000
rn := newRand()

for i := 0; i < ln; i++ {
// rand in 0 - 61
cs[i] = AlphaNum2[rn.Intn(61)]
}
return string(cs)
return buildRandomString(AlphaNum2, ln)
}

// RandWithTpl generate random string with give template
func RandWithTpl(n int, letters string) string {
if len(letters) == 0 {
letters = AlphaNum2
}

ln := len(letters)
cs := make([]byte, n)
rn := newRand()

for i := 0; i < n; i++ {
// rand in 0 - ln
cs[i] = letters[rn.Intn(ln)]
}
return byteutil.String(cs)
return buildRandomString(letters, n)
}

// RandomString generate.
Expand Down
Loading