Skip to content

Commit a6a53e1

Browse files
pigwantacatzhuhebinccoVeillesamber
authored
refactor:refactor RandomString function (#524)
* refactor:refactor RandomString function * feat:Upgrade math/rand to math/rand/v2 * Update string.go Co-authored-by: ccoVeille <[email protected]> * feat: improve comments for RandomString function * Update string.go Co-authored-by: Samuel Berthe <[email protected]> * feat:Upgrade math/rand/v2 to github.com/samber/lo/internal/rand * Update string.go * Update ordered_go118.go --------- Co-authored-by: zhuhebin <[email protected]> Co-authored-by: ccoVeille <[email protected]> Co-authored-by: Samuel Berthe <[email protected]>
1 parent fbc7f33 commit a6a53e1

File tree

3 files changed

+65
-7
lines changed

3 files changed

+65
-7
lines changed

internal/rand/ordered_go118.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,15 @@ func IntN(n int) int {
1212
// bearer:disable go_gosec_crypto_weak_random
1313
return rand.Intn(n)
1414
}
15+
16+
func Int64() int64 {
17+
// bearer:disable go_gosec_crypto_weak_random
18+
n := rand.Int63()
19+
20+
// bearer:disable go_gosec_crypto_weak_random
21+
if rand.Intn(2) == 0 {
22+
return -n
23+
}
24+
25+
return n
26+
}

internal/rand/ordered_go122.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ func Shuffle(n int, swap func(i, j int)) {
1111
func IntN(n int) int {
1212
return rand.IntN(n)
1313
}
14+
15+
func Int64() int64 {
16+
return rand.Int64()
17+
}

string.go

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package lo
22

33
import (
4+
"github.com/samber/lo/internal/rand"
5+
"math"
46
"regexp"
57
"strings"
68
"unicode"
79
"unicode/utf8"
810

9-
"github.com/samber/lo/internal/rand"
10-
1111
"golang.org/x/text/cases"
1212
"golang.org/x/text/language"
1313
)
@@ -25,6 +25,7 @@ var (
2525
splitWordReg = regexp.MustCompile(`([a-z])([A-Z0-9])|([a-zA-Z])([0-9])|([0-9])([a-zA-Z])|([A-Z])([A-Z])([a-z])`)
2626
// bearer:disable go_lang_permissive_regex_validation
2727
splitNumberLetterReg = regexp.MustCompile(`([0-9])([a-zA-Z])`)
28+
maximumCapacity = math.MaxInt>>1 + 1
2829
)
2930

3031
// RandomString return a random string.
@@ -37,12 +38,53 @@ func RandomString(size int, charset []rune) string {
3738
panic("lo.RandomString: Charset parameter must not be empty")
3839
}
3940

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--
4469
}
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
4688
}
4789

4890
// Substring return part of a string.

0 commit comments

Comments
 (0)