1
1
package strutil
2
2
3
3
import (
4
- mRand "math/rand"
4
+ "math"
5
+ "math/rand"
5
6
"time"
7
+ "unsafe"
6
8
7
9
"github.com/gookit/goutil/byteutil"
8
10
"github.com/gookit/goutil/encodes"
9
11
)
10
12
11
13
// some consts string chars
12
14
const (
13
- Numbers = "0123456789"
14
- HexChars = "0123456789abcdef" // base16
15
+ MaximumCapacity = 1 << 31
16
+ Numbers = "0123456789"
17
+ HexChars = "0123456789abcdef" // base16
15
18
16
19
AlphaBet = "abcdefghijklmnopqrstuvwxyz"
17
20
AlphaBet1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
@@ -21,64 +24,92 @@ const (
21
24
AlphaNum3 = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
22
25
)
23
26
24
- func newRand () * mRand.Rand {
25
- return mRand .New (mRand .NewSource (time .Now ().UnixNano ()))
27
+ var rn = rand .NewSource (time .Now ().UnixNano ())
28
+
29
+ // nearestPowerOfTwo 返回一个大于等于cap的最近的2的整数次幂,参考java8的hashmap的tableSizeFor函数
30
+ // cap 输入参数
31
+ // 返回一个大于等于cap的最近的2的整数次幂
32
+ func nearestPowerOfTwo (cap int ) int {
33
+ n := cap - 1
34
+ n |= n >> 1
35
+ n |= n >> 2
36
+ n |= n >> 4
37
+ n |= n >> 8
38
+ n |= n >> 16
39
+ if n < 0 {
40
+ return 1
41
+ } else if n >= MaximumCapacity {
42
+ return MaximumCapacity
43
+ }
44
+ return n + 1
26
45
}
27
46
28
- // RandomChars generate give length random chars at `a-z`
29
- func RandomChars (ln int ) string {
30
- cs := make ([]byte , ln )
47
+ // buildRandomString 生成随机字符串
48
+ // letters 字符串模板
49
+ // length 生成长度
50
+ // 返回一个指定长度的随机字符串
51
+ func buildRandomString (letters string , length int ) string {
52
+ // 仿照strings.Builder
53
+ // 创建一个长度为 length 的字节切片
54
+ bytes := make ([]byte , length )
55
+ strLength := len (letters )
56
+ if strLength <= 0 {
57
+ return ""
58
+ } else if strLength == 1 {
59
+ for i := 0 ; i < length ; i ++ {
60
+ bytes [i ] = letters [0 ]
61
+ }
62
+ return * (* string )(unsafe .Pointer (& bytes ))
63
+ }
64
+ // letters的字符需要使用多少个比特位数才能表示完
65
+ // letterIdBits := int(math.Ceil(math.Log2(strLength))),下面比上面的代码快
66
+ letterIdBits := int (math .Log2 (float64 (nearestPowerOfTwo (strLength ))))
67
+ // 最大的字母id掩码
68
+ var letterIdMask int64 = 1 << letterIdBits - 1
69
+ // 可用次数的最大值
70
+ letterIdMax := 63 / letterIdBits
31
71
// UnixNano: 1607400451937462000
32
- rn := newRand ()
33
-
34
- for i := 0 ; i < ln ; i ++ {
35
- // rand in 0 - 25
36
- cs [i ] = AlphaBet [rn .Intn (25 )]
72
+ // 循环生成随机字符串
73
+ for i , cache , remain := length - 1 , rn .Int63 (), letterIdMax ; i >= 0 ; {
74
+ // 检查随机数生成器是否用尽所有随机数
75
+ if remain == 0 {
76
+ cache , remain = rn .Int63 (), letterIdMax
77
+ }
78
+ // 从可用字符的字符串中随机选择一个字符
79
+ if idx := int (cache & letterIdMask ); idx < len (letters ) {
80
+ bytes [i ] = letters [idx ]
81
+ i --
82
+ }
83
+ // 右移比特位数,为下次选择字符做准备
84
+ cache >>= letterIdBits
85
+ remain --
37
86
}
38
- return string (cs )
87
+ // 仿照strings.Builder用unsafe包返回一个字符串,避免拷贝
88
+ // 将字节切片转换为字符串并返回
89
+ return * (* string )(unsafe .Pointer (& bytes ))
90
+ }
91
+
92
+ // RandomChars generate give length random chars at `a-z`
93
+ func RandomChars (ln int ) string {
94
+ return buildRandomString (AlphaBet , ln )
39
95
}
40
96
41
97
// RandomCharsV2 generate give length random chars in `0-9a-z`
42
98
func RandomCharsV2 (ln int ) string {
43
- cs := make ([]byte , ln )
44
- // UnixNano: 1607400451937462000
45
- rn := newRand ()
46
-
47
- for i := 0 ; i < ln ; i ++ {
48
- // rand in 0 - 35
49
- cs [i ] = AlphaNum [rn .Intn (35 )]
50
- }
51
- return string (cs )
99
+ return buildRandomString (AlphaNum , ln )
52
100
}
53
101
54
102
// RandomCharsV3 generate give length random chars in `0-9a-zA-Z`
55
103
func RandomCharsV3 (ln int ) string {
56
- cs := make ([]byte , ln )
57
- // UnixNano: 1607400451937462000
58
- rn := newRand ()
59
-
60
- for i := 0 ; i < ln ; i ++ {
61
- // rand in 0 - 61
62
- cs [i ] = AlphaNum2 [rn .Intn (61 )]
63
- }
64
- return string (cs )
104
+ return buildRandomString (AlphaNum2 , ln )
65
105
}
66
106
67
107
// RandWithTpl generate random string with give template
68
108
func RandWithTpl (n int , letters string ) string {
69
109
if len (letters ) == 0 {
70
110
letters = AlphaNum2
71
111
}
72
-
73
- ln := len (letters )
74
- cs := make ([]byte , n )
75
- rn := newRand ()
76
-
77
- for i := 0 ; i < n ; i ++ {
78
- // rand in 0 - ln
79
- cs [i ] = letters [rn .Intn (ln )]
80
- }
81
- return byteutil .String (cs )
112
+ return buildRandomString (letters , n )
82
113
}
83
114
84
115
// RandomString generate.
0 commit comments