diff --git a/entropy/entropy.go b/entropy/entropy.go index b66541a..e941675 100644 --- a/entropy/entropy.go +++ b/entropy/entropy.go @@ -4,8 +4,11 @@ import ( crip "crypto/rand" "encoding/binary" "math/rand" + "os" + "reflect" "sync" "time" + "unsafe" "nullprogram.com/x/rng" @@ -16,11 +19,11 @@ type randPool struct { sync.Pool } -func (p *randPool) Get() *rand.Rand { - return p.Pool.Get().(*rand.Rand) +func (p *randPool) Get() *rng.SplitMix64 { + return p.Pool.Get().(*rng.SplitMix64) } -func (p *randPool) Put(r *rand.Rand) { +func (p *randPool) Put(r *rng.SplitMix64) { p.Pool.Put(r) } @@ -30,8 +33,7 @@ var ( New: func() interface{} { sm64 := new(rng.SplitMix64) sm64.Seed(GetCryptoSeed()) - prng := rand.New(sm64) //nolint:gosec - return prng + return sm64 }, }, } @@ -42,16 +44,18 @@ var ( func setSharedRand() { hardLocc.Lock() - sharedRand = lolXD.Get() + sharedRand = rand.New(lolXD.Get()) hardLocc.Unlock() } func AcquireRand() *rand.Rand { - return lolXD.Get() + return rand.New(lolXD.Get()) } func ReleaseRand(r *rand.Rand) { - lolXD.Put(r) + srcField := reflect.ValueOf(r).Elem().FieldByName("src") + src := reflect.NewAt(srcField.Type(), unsafe.Pointer(srcField.UnsafeAddr())).Elem().Interface().(*rng.SplitMix64) + lolXD.Put(src) r = nil } @@ -80,18 +84,17 @@ func GetOptimizedRand() *rand.Rand { } // GetSharedRand returns a pointer to our shared optimized rand.Rand which uses crypto/rand to seed a splitmix64 rng. -// WARNING - RACY - This is not thread safe, and should only be used in a single-threaded context. func GetSharedRand() *rand.Rand { getSharedRand.Do(func() { setSharedRand() }) - return sharedRand + return rand.New(sharedRand) } // RNGUint32 returns a random uint32 using crypto/rand and splitmix64. func RNGUint32() uint32 { r := lolXD.Get() - ui := r.Uint32() + ui := uint32(r.Int63() >> 31) lolXD.Put(r) return ui } @@ -106,8 +109,25 @@ RNG returns integer with a maximum amount of 'n' using a global/shared instance */ func RNG(n int) int { r := lolXD.Get() - i := r.Intn(n) - lolXD.Put(r) + defer lolXD.Put(r) + i := 0 + if n <= 0 { + // because panic is just rude. + _, _ = os.Stderr.WriteString("RNG: n must be greater than 0, returning 0") + return i + } + if n <= 1<<31-1 { + n32 := int32(n) + if n32&(n32-1) == 0 { + i = int(int32(r.Int63()>>32) & (n32 - 1)) + } + maximum := int32((1 << 31) - 1 - (1<<31)%uint32(n32)) + v := int32(r.Int63() >> 32) + for v > maximum { + v = int32(r.Int63() >> 32) + } + i = int(v % n32) + } return i } @@ -167,7 +187,7 @@ func randStr(upper bool, size int) string { buf := strBufs.Get() r := lolXD.Get() for i := 0; i != size; i++ { - ui32 := int(r.Uint32()) + ui32 := int(r.Int63() >> 31) switch upper { case true: _ = buf.WriteByte(charsetWithUpper[ui32%len(charsetWithUpper)]) diff --git a/entropy/entropy_test.go b/entropy/entropy_test.go index 5e78e64..b75646d 100644 --- a/entropy/entropy_test.go +++ b/entropy/entropy_test.go @@ -49,15 +49,15 @@ func Test_RNG(t *testing.T) { t.Errorf("GetSharedRand(55555) returned the same value twice!") } r := AcquireRand() - one := r.Intn(55555) - two := r.Intn(55555) + one := r.Uint64() + two := r.Uint64() if one == two { t.Errorf("AcquireRand() returned the same value twice!") } ReleaseRand(r) r = AcquireRand() - one1 := r.Intn(55555) - two1 := r.Intn(55555) + one1 := r.Uint64() + two1 := r.Uint64() if one1 == two1 { t.Errorf("AcquireRand() returned the same value twice!") }