86 lines
2.1 KiB
Go
86 lines
2.1 KiB
Go
//go:build windows
|
|
|
|
package strutil
|
|
|
|
import (
|
|
"math"
|
|
"math/rand"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
const MaximumCapacity = math.MaxInt>>1 + 1
|
|
|
|
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
|
|
}
|
|
|
|
if n >= MaximumCapacity {
|
|
return MaximumCapacity
|
|
}
|
|
return n + 1
|
|
}
|
|
|
|
// buildRandomString 生成随机字符串
|
|
// - letters 字符串模板
|
|
// - length 生成长度
|
|
//
|
|
// 返回一个指定长度的随机字符串
|
|
func buildRandomString(letters string, length int) string {
|
|
// 仿照strings.Builder
|
|
// 创建一个长度为 length 的字节切片
|
|
bytes := make([]byte, length)
|
|
strLength := len(letters)
|
|
if strLength <= 0 {
|
|
return ""
|
|
}
|
|
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<<letterIdBits - 1
|
|
// 可用次数的最大值
|
|
letterIdMax := 63 / letterIdBits
|
|
|
|
// UnixNano: 1607400451937462000
|
|
// 循环生成随机字符串
|
|
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 < strLength {
|
|
bytes[i] = letters[idx]
|
|
i--
|
|
}
|
|
// 右移比特位数,为下次选择字符做准备
|
|
cache >>= letterIdBits
|
|
remain--
|
|
}
|
|
|
|
// 仿照strings.Builder用unsafe包返回一个字符串,避免拷贝
|
|
// 将字节切片转换为字符串并返回
|
|
return *(*string)(unsafe.Pointer(&bytes))
|
|
}
|