1
1
package lo
2
2
3
3
import (
4
+ "github.com/samber/lo/internal/rand"
5
+ "math"
4
6
"regexp"
5
7
"strings"
6
8
"unicode"
7
9
"unicode/utf8"
8
10
9
- "github.com/samber/lo/internal/rand"
10
-
11
11
"golang.org/x/text/cases"
12
12
"golang.org/x/text/language"
13
13
)
25
25
splitWordReg = regexp .MustCompile (`([a-z])([A-Z0-9])|([a-zA-Z])([0-9])|([0-9])([a-zA-Z])|([A-Z])([A-Z])([a-z])` )
26
26
// bearer:disable go_lang_permissive_regex_validation
27
27
splitNumberLetterReg = regexp .MustCompile (`([0-9])([a-zA-Z])` )
28
+ maximumCapacity = math .MaxInt >> 1 + 1
28
29
)
29
30
30
31
// RandomString return a random string.
@@ -37,12 +38,53 @@ func RandomString(size int, charset []rune) string {
37
38
panic ("lo.RandomString: Charset parameter must not be empty" )
38
39
}
39
40
40
- b := make ([]rune , size )
41
- possibleCharactersCount := len (charset )
42
- for i := range b {
43
- b [i ] = charset [rand .IntN (possibleCharactersCount )]
41
+ // see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go
42
+ sb := strings.Builder {}
43
+ sb .Grow (size )
44
+ // Calculate the number of bits required to represent the charset,
45
+ // e.g., for 62 characters, it would need 6 bits (since 62 -> 64 = 2^6)
46
+ letterIdBits := int (math .Log2 (float64 (nearestPowerOfTwo (len (charset )))))
47
+ // Determine the corresponding bitmask,
48
+ // e.g., for 62 characters, the bitmask would be 111111.
49
+ var letterIdMask int64 = 1 << letterIdBits - 1
50
+ // Available count, since rand.Int64() returns a non-negative number, the first bit is fixed, so there are 63 random bits
51
+ // e.g., for 62 characters, this value is 10 (63 / 6).
52
+ letterIdMax := 63 / letterIdBits
53
+ // Generate the random string in a loop.
54
+ for i , cache , remain := size - 1 , rand .Int64 (), letterIdMax ; i >= 0 ; {
55
+ // Regenerate the random number if all available bits have been used
56
+ if remain == 0 {
57
+ cache , remain = rand .Int64 (), letterIdMax
58
+ }
59
+ // Select a character from the charset
60
+ if idx := int (cache & letterIdMask ); idx < len (charset ) {
61
+ sb .WriteRune (charset [idx ])
62
+ i --
63
+ }
64
+ // Shift the bits to the right to prepare for the next character selection,
65
+ // e.g., for 62 characters, shift by 6 bits.
66
+ cache >>= letterIdBits
67
+ // Decrease the remaining number of uses for the current random number.
68
+ remain --
44
69
}
45
- return string (b )
70
+ return sb .String ()
71
+ }
72
+
73
+ // nearestPowerOfTwo returns the nearest power of two.
74
+ func nearestPowerOfTwo (cap int ) int {
75
+ n := cap - 1
76
+ n |= n >> 1
77
+ n |= n >> 2
78
+ n |= n >> 4
79
+ n |= n >> 8
80
+ n |= n >> 16
81
+ if n < 0 {
82
+ return 1
83
+ }
84
+ if n >= maximumCapacity {
85
+ return maximumCapacity
86
+ }
87
+ return n + 1
46
88
}
47
89
48
90
// Substring return part of a string.
0 commit comments