From 778a092257e9b078c719b287f43a774675b90c77 Mon Sep 17 00:00:00 2001 From: pigwantacat <79680042+pigwantacat@users.noreply.github.com> Date: Wed, 31 Jan 2024 09:57:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=94=9F=E6=88=90?= =?UTF-8?q?=E9=9A=8F=E6=9C=BA=E5=AD=97=E7=AC=A6=E4=B8=B2=20(#154)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit see issues #121 --- strutil/random.go | 115 +++++++++++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 42 deletions(-) diff --git a/strutil/random.go b/strutil/random.go index 7a00141ad..68914ea76 100644 --- a/strutil/random.go +++ b/strutil/random.go @@ -1,8 +1,10 @@ package strutil import ( - mRand "math/rand" + "math" + "math/rand" "time" + "unsafe" "github.com/gookit/goutil/byteutil" "github.com/gookit/goutil/encodes" @@ -10,8 +12,9 @@ import ( // some consts string chars const ( - Numbers = "0123456789" - HexChars = "0123456789abcdef" // base16 + MaximumCapacity = 1 << 31 + Numbers = "0123456789" + HexChars = "0123456789abcdef" // base16 AlphaBet = "abcdefghijklmnopqrstuvwxyz" AlphaBet1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" @@ -21,47 +24,84 @@ const ( 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)))) + // 最大的字母id掩码 + var letterIdMask int64 = 1<= 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 @@ -69,16 +109,7 @@ 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.