Skip to content

Commit

Permalink
👔 up: str - random string gen split non-win and windows implement
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Mar 24, 2024
1 parent b77a7f1 commit 5f16ce7
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 79 deletions.
81 changes: 6 additions & 75 deletions strutil/random.go
@@ -1,94 +1,25 @@
package strutil

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

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

// some consts string chars
// some constants string chars
const (
MaximumCapacity = math.MaxInt>>1 + 1
Numbers = "0123456789"
HexChars = "0123456789abcdef" // base16
Numbers = "0123456789"
HexChars = "0123456789abcdef" // base16

AlphaBet = "abcdefghijklmnopqrstuvwxyz"
AlphaBet1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"

AlphaNum = "abcdefghijklmnopqrstuvwxyz0123456789"
// AlphaNum chars, can use for base36 encode
AlphaNum = "abcdefghijklmnopqrstuvwxyz0123456789"
// AlphaNum2 chars, can use for base62 encode
AlphaNum2 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
AlphaNum3 = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
)

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
}

// 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<<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))
}

// RandomChars generate give length random chars at `a-z`
func RandomChars(ln int) string {
return buildRandomString(AlphaBet, ln)
Expand Down
27 changes: 27 additions & 0 deletions strutil/random_nonwin.go
@@ -0,0 +1,27 @@
//go:build !windows

package strutil

import (
"math/rand"
"time"
)

var rn = newRand()

func newRand() *rand.Rand {
return rand.New(rand.NewSource(time.Now().UnixNano()))
}

// buildRandomString 生成随机字符串
func buildRandomString(letters string, length int) string {
// rn := newRand()
cs := make([]byte, length)

lettersN := len(letters)
for i := 0; i < length; i++ {
cs[i] = letters[rn.Intn(lettersN)]
}

return Byte2str(cs)
}
8 changes: 4 additions & 4 deletions strutil/random_test.go
Expand Up @@ -26,7 +26,7 @@ func TestRandomCharsV2(t *testing.T) {
fmt.Println(str)
assert.Len(t, str, 6)
keyMp[str] = true
time.Sleep(time.Microsecond * 10)
// time.Sleep(time.Microsecond * 10)
}

assert.Len(t, keyMp, 10)
Expand All @@ -41,7 +41,7 @@ func TestRandomCharsV2_issues121(t *testing.T) {
fmt.Println(str)
assert.Len(t, str, 32)
keyMp[str] = true
time.Sleep(time.Microsecond * 10)
// time.Sleep(time.Microsecond * 10)
}

assert.Len(t, keyMp, 10)
Expand All @@ -56,7 +56,7 @@ func TestRandomCharsV3(t *testing.T) {
fmt.Println(str)
assert.Len(t, str, 4)
keyMp[str] = true
time.Sleep(time.Microsecond * 10)
// time.Sleep(time.Microsecond * 10)
}

assert.Len(t, keyMp, size)
Expand All @@ -71,7 +71,7 @@ func TestRandWithTpl(t *testing.T) {
fmt.Println(str)
assert.Len(t, str, 4)
keyMp[str] = true
time.Sleep(time.Microsecond * 10)
// time.Sleep(time.Microsecond * 10)
}

assert.NotEmpty(t, strutil.RandWithTpl(8, ""))
Expand Down
85 changes: 85 additions & 0 deletions strutil/random_windows.go
@@ -0,0 +1,85 @@
//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))
}

0 comments on commit 5f16ce7

Please sign in to comment.