From c44d56a0d37b72fc5eaf4ff0230d5c1cfc2fc37c Mon Sep 17 00:00:00 2001 From: zhuhebin Date: Thu, 22 Aug 2024 11:29:18 +0800 Subject: [PATCH 1/8] refactor:refactor RandomString function --- string.go | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/string.go b/string.go index 94dddde4..1c3c7408 100644 --- a/string.go +++ b/string.go @@ -1,13 +1,14 @@ package lo import ( + "math" + "math/rand" "regexp" "strings" + "time" "unicode" "unicode/utf8" - "github.com/samber/lo/internal/rand" - "golang.org/x/text/cases" "golang.org/x/text/language" ) @@ -25,6 +26,8 @@ var ( splitWordReg = regexp.MustCompile(`([a-z])([A-Z0-9])|([a-zA-Z])([0-9])|([0-9])([a-zA-Z])|([A-Z])([A-Z])([a-z])`) // bearer:disable go_lang_permissive_regex_validation splitNumberLetterReg = regexp.MustCompile(`([0-9])([a-zA-Z])`) + MaximumCapacity = math.MaxInt>>1 + 1 + random = rand.NewSource(time.Now().UnixNano()) ) // RandomString return a random string. @@ -37,12 +40,39 @@ func RandomString(size int, charset []rune) string { panic("lo.RandomString: Charset parameter must not be empty") } - b := make([]rune, size) - possibleCharactersCount := len(charset) - for i := range b { - b[i] = charset[rand.IntN(possibleCharactersCount)] + sb := strings.Builder{} + sb.Grow(size) + letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(len(charset))))) + var letterIdMask int64 = 1<= 0; { + if remain == 0 { + cache, remain = random.Int63(), letterIdMax + } + if idx := int(cache & letterIdMask); idx < len(charset) { + sb.WriteRune(charset[idx]) + i-- + } + cache >>= letterIdBits + remain-- } - return string(b) + return sb.String() +} + +// nearestPowerOfTwo returns the nearest power of two. +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 } // Substring return part of a string. From aaa2dfd09d37f109def4684dd18a2a99a5d6478f Mon Sep 17 00:00:00 2001 From: zhuhebin Date: Fri, 13 Sep 2024 09:05:11 +0800 Subject: [PATCH 2/8] feat:Upgrade math/rand to math/rand/v2 --- string.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/string.go b/string.go index 1c3c7408..b2eae9fe 100644 --- a/string.go +++ b/string.go @@ -2,10 +2,9 @@ package lo import ( "math" - "math/rand" + rand2 "math/rand/v2" "regexp" "strings" - "time" "unicode" "unicode/utf8" @@ -27,7 +26,6 @@ var ( // bearer:disable go_lang_permissive_regex_validation splitNumberLetterReg = regexp.MustCompile(`([0-9])([a-zA-Z])`) MaximumCapacity = math.MaxInt>>1 + 1 - random = rand.NewSource(time.Now().UnixNano()) ) // RandomString return a random string. @@ -45,9 +43,9 @@ func RandomString(size int, charset []rune) string { letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(len(charset))))) var letterIdMask int64 = 1<= 0; { + for i, cache, remain := size-1, rand2.Int64(), letterIdMax; i >= 0; { if remain == 0 { - cache, remain = random.Int63(), letterIdMax + cache, remain = rand2.Int64(), letterIdMax } if idx := int(cache & letterIdMask); idx < len(charset) { sb.WriteRune(charset[idx]) From 52a73c3ffda944e42c2d6f4fc1fb06a8eaf114c6 Mon Sep 17 00:00:00 2001 From: pigwantacat <79680042+pigwantacat@users.noreply.github.com> Date: Sat, 14 Sep 2024 09:33:00 +0800 Subject: [PATCH 3/8] Update string.go Co-authored-by: ccoVeille <3875889+ccoVeille@users.noreply.github.com> --- string.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/string.go b/string.go index b2eae9fe..e7263221 100644 --- a/string.go +++ b/string.go @@ -67,7 +67,8 @@ func nearestPowerOfTwo(cap int) int { n |= n >> 16 if n < 0 { return 1 - } else if n >= MaximumCapacity { + } + if n >= MaximumCapacity { return MaximumCapacity } return n + 1 From 27c8ff7d468355696344b17ac9695a6c90fca0a2 Mon Sep 17 00:00:00 2001 From: zhuhebin Date: Sat, 14 Sep 2024 14:39:14 +0800 Subject: [PATCH 4/8] feat: improve comments for RandomString function --- string.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/string.go b/string.go index b2eae9fe..f97c20f5 100644 --- a/string.go +++ b/string.go @@ -40,18 +40,30 @@ func RandomString(size int, charset []rune) string { sb := strings.Builder{} sb.Grow(size) + // Calculate the number of bits required to represent the charset, + // e.g., for 62 characters, it would need 6 bits (since 62 -> 64 = 2^6) letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(len(charset))))) + // Determine the corresponding bitmask, + // e.g., for 62 characters, the bitmask would be 111111. var letterIdMask int64 = 1<= 0; { + // Regenerate the random number if all available bits have been used if remain == 0 { cache, remain = rand2.Int64(), letterIdMax } + // Select a character from the charset if idx := int(cache & letterIdMask); idx < len(charset) { sb.WriteRune(charset[idx]) i-- } + // Shift the bits to the right to prepare for the next character selection, + // e.g., for 62 characters, shift by 6 bits. cache >>= letterIdBits + // Decrease the remaining number of uses for the current random number. remain-- } return sb.String() From 7ad841e0ce105c48b09642c162d643d158b1b9ba Mon Sep 17 00:00:00 2001 From: pigwantacat <79680042+pigwantacat@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:13:43 +0800 Subject: [PATCH 5/8] Update string.go Co-authored-by: Samuel Berthe --- string.go | 1 + 1 file changed, 1 insertion(+) diff --git a/string.go b/string.go index 49dce307..963567a6 100644 --- a/string.go +++ b/string.go @@ -38,6 +38,7 @@ func RandomString(size int, charset []rune) string { panic("lo.RandomString: Charset parameter must not be empty") } + // see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go sb := strings.Builder{} sb.Grow(size) // Calculate the number of bits required to represent the charset, From 67489b2add771f4698985bc3c773531ef405838e Mon Sep 17 00:00:00 2001 From: zhuhebin Date: Wed, 18 Sep 2024 09:21:10 +0800 Subject: [PATCH 6/8] feat:Upgrade math/rand/v2 to github.com/samber/lo/internal/rand --- internal/rand/ordered_go122.go | 4 ++++ string.go | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/rand/ordered_go122.go b/internal/rand/ordered_go122.go index 532ed339..65abf51a 100644 --- a/internal/rand/ordered_go122.go +++ b/internal/rand/ordered_go122.go @@ -11,3 +11,7 @@ func Shuffle(n int, swap func(i, j int)) { func IntN(n int) int { return rand.IntN(n) } + +func Int64() int64 { + return rand.Int64() +} diff --git a/string.go b/string.go index 963567a6..ce56288a 100644 --- a/string.go +++ b/string.go @@ -1,8 +1,8 @@ package lo import ( + "github.com/samber/lo/internal/rand" "math" - rand2 "math/rand/v2" "regexp" "strings" "unicode" @@ -38,7 +38,7 @@ func RandomString(size int, charset []rune) string { panic("lo.RandomString: Charset parameter must not be empty") } - // see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go + // see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go sb := strings.Builder{} sb.Grow(size) // Calculate the number of bits required to represent the charset, @@ -51,10 +51,10 @@ func RandomString(size int, charset []rune) string { // e.g., for 62 characters, this value is 10 (63 / 6). letterIdMax := 63 / letterIdBits // Generate the random string in a loop. - for i, cache, remain := size-1, rand2.Int64(), letterIdMax; i >= 0; { + for i, cache, remain := size-1, rand.Int64(), letterIdMax; i >= 0; { // Regenerate the random number if all available bits have been used if remain == 0 { - cache, remain = rand2.Int64(), letterIdMax + cache, remain = rand.Int64(), letterIdMax } // Select a character from the charset if idx := int(cache & letterIdMask); idx < len(charset) { From 0dd622e023aa3c470595c368a2b864c751eb1cdc Mon Sep 17 00:00:00 2001 From: Samuel Berthe Date: Fri, 20 Sep 2024 00:05:39 +0200 Subject: [PATCH 7/8] Update string.go --- string.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/string.go b/string.go index ce56288a..51b09899 100644 --- a/string.go +++ b/string.go @@ -25,7 +25,7 @@ var ( splitWordReg = regexp.MustCompile(`([a-z])([A-Z0-9])|([a-zA-Z])([0-9])|([0-9])([a-zA-Z])|([A-Z])([A-Z])([a-z])`) // bearer:disable go_lang_permissive_regex_validation splitNumberLetterReg = regexp.MustCompile(`([0-9])([a-zA-Z])`) - MaximumCapacity = math.MaxInt>>1 + 1 + maximumCapacity = math.MaxInt>>1 + 1 ) // RandomString return a random string. @@ -81,8 +81,8 @@ func nearestPowerOfTwo(cap int) int { if n < 0 { return 1 } - if n >= MaximumCapacity { - return MaximumCapacity + if n >= maximumCapacity { + return maximumCapacity } return n + 1 } From e8bb837bc169c682b8053e29934406f0934c0be9 Mon Sep 17 00:00:00 2001 From: Samuel Berthe Date: Fri, 20 Sep 2024 00:13:47 +0200 Subject: [PATCH 8/8] Update ordered_go118.go --- internal/rand/ordered_go118.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/internal/rand/ordered_go118.go b/internal/rand/ordered_go118.go index a31bb9f2..9fbc5385 100644 --- a/internal/rand/ordered_go118.go +++ b/internal/rand/ordered_go118.go @@ -12,3 +12,15 @@ func IntN(n int) int { // bearer:disable go_gosec_crypto_weak_random return rand.Intn(n) } + +func Int64() int64 { + // bearer:disable go_gosec_crypto_weak_random + n := rand.Int63() + + // bearer:disable go_gosec_crypto_weak_random + if rand.Intn(2) == 0 { + return -n + } + + return n +}