110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
package entropy
|
|
|
|
import (
|
|
crip "crypto/rand"
|
|
"encoding/binary"
|
|
"math/rand"
|
|
"sync"
|
|
"time"
|
|
|
|
"nullprogram.com/x/rng"
|
|
)
|
|
|
|
var (
|
|
sharedRand *rand.Rand
|
|
getSharedRand = &sync.Once{}
|
|
)
|
|
|
|
// RandomStrChoice returns a random item from an input slice of strings.
|
|
func RandomStrChoice(choice []string) string {
|
|
if len(choice) > 0 {
|
|
return choice[RNGUint32()%uint32(len(choice))]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetCryptoSeed returns a random int64 derived from crypto/rand.
|
|
// This can be used as a seed for the math/rand package.
|
|
func GetCryptoSeed() int64 {
|
|
var seed int64
|
|
_ = binary.Read(crip.Reader, binary.BigEndian, &seed)
|
|
return seed
|
|
}
|
|
|
|
// GetOptimizedRand returns a pointer to a *new* rand.Rand which uses crypto/rand to seed a splitmix64 rng.
|
|
// Does not use the global/shared instance of a splitmix64 rng, but instead creates a new one.
|
|
func GetOptimizedRand() *rand.Rand {
|
|
r := new(rng.SplitMix64)
|
|
r.Seed(GetCryptoSeed())
|
|
return rand.New(r)
|
|
}
|
|
|
|
// GetSharedRand returns a pointer to our shared optimized rand.Rand which uses crypto/rand to seed a splitmix64 rng.
|
|
func GetSharedRand() *rand.Rand {
|
|
getSharedRand.Do(func() {
|
|
sharedRand = GetOptimizedRand()
|
|
})
|
|
return sharedRand
|
|
}
|
|
|
|
// RNGUint32 returns a random uint32 using crypto/rand and splitmix64.
|
|
func RNGUint32() uint32 {
|
|
getSharedRand.Do(func() {
|
|
sharedRand = GetOptimizedRand()
|
|
})
|
|
return sharedRand.Uint32()
|
|
}
|
|
|
|
/*
|
|
RNG returns integer with a maximum amount of 'n' using a global/shared instance of a splitmix64 rng.
|
|
- Benchmark_FastRandStr5-24 25205089 47.03 ns/op
|
|
- Benchmark_FastRandStr25-24 7113620 169.8 ns/op
|
|
- Benchmark_FastRandStr55-24 3520297 340.7 ns/op
|
|
- Benchmark_FastRandStr500-24 414966 2837 ns/op
|
|
- Benchmark_FastRandStr55555-24 3717 315229 ns/op
|
|
*/
|
|
func RNG(n int) int {
|
|
getSharedRand.Do(func() {
|
|
sharedRand = GetOptimizedRand()
|
|
})
|
|
return sharedRand.Intn(n)
|
|
}
|
|
|
|
// OneInA generates a random number with a maximum of 'million' (input int).
|
|
// If the resulting random number is equal to 1, then the result is true.
|
|
func OneInA(million int) bool {
|
|
if million == 1 {
|
|
return true
|
|
}
|
|
return RNG(million) == 1
|
|
}
|
|
|
|
// RandSleepMS sleeps for a random period of time with a maximum of n milliseconds.
|
|
func RandSleepMS(n int) {
|
|
time.Sleep(time.Duration(RNG(n)) * time.Millisecond)
|
|
}
|
|
|
|
// characters used for the gerneration of random strings.
|
|
const charset = "abcdefghijklmnopqrstuvwxyz1234567890"
|
|
const charsetWithUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"
|
|
|
|
// RandStr generates a random alphanumeric string with a max length of size.
|
|
// Alpha charset used is a-z all lowercase.
|
|
func RandStr(size int) string {
|
|
buf := make([]byte, size)
|
|
for i := 0; i != size; i++ {
|
|
buf[i] = charset[uint32(RNG(36))%uint32(len(charset))]
|
|
}
|
|
return string(buf)
|
|
}
|
|
|
|
// RandStrWithUpper generates a random alphanumeric string with a max length of size.
|
|
// Alpha charset used is a-Z mixed case.
|
|
func RandStrWithUpper(size int) string {
|
|
buf := make([]byte, size)
|
|
for i := 0; i != size; i++ {
|
|
buf[i] = charsetWithUpper[uint32(RNG(62))%uint32(len(charsetWithUpper))]
|
|
}
|
|
return string(buf)
|
|
}
|