mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-20 23:14:37 +00:00
libs/common: refactor libs/common 2 (#4231)
* libs/common: refactor libs/common 2 - move random function to there own pkg Signed-off-by: Marko Baricevic <marbar3778@yahoo.com> * change imports and usage throughout repo * fix goimports * add changelog entry
This commit is contained in:
@@ -6,6 +6,8 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/rand"
|
||||
)
|
||||
|
||||
// BitArray is a thread-safe implementation of a bit array.
|
||||
@@ -250,7 +252,7 @@ func (bA *BitArray) PickRandom() (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return trueIndices[RandIntn(len(trueIndices))], true
|
||||
return trueIndices[rand.RandIntn(len(trueIndices))], true
|
||||
}
|
||||
|
||||
func (bA *BitArray) getTrueIndices() []int {
|
||||
|
||||
@@ -8,10 +8,12 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/rand"
|
||||
)
|
||||
|
||||
func randBitArray(bits int) (*BitArray, []byte) {
|
||||
src := RandBytes((bits + 7) / 8)
|
||||
src := rand.RandBytes((bits + 7) / 8)
|
||||
bA := NewBitArray(bits)
|
||||
for i := 0; i < len(src); i++ {
|
||||
for j := 0; j < 8; j++ {
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
mrand "math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
strChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // 62 characters
|
||||
)
|
||||
|
||||
// Rand is a prng, that is seeded with OS randomness.
|
||||
// The OS randomness is obtained from crypto/rand, however none of the provided
|
||||
// methods are suitable for cryptographic usage.
|
||||
// They all utilize math/rand's prng internally.
|
||||
//
|
||||
// All of the methods here are suitable for concurrent use.
|
||||
// This is achieved by using a mutex lock on all of the provided methods.
|
||||
type Rand struct {
|
||||
sync.Mutex
|
||||
rand *mrand.Rand
|
||||
}
|
||||
|
||||
var grand *Rand
|
||||
|
||||
func init() {
|
||||
grand = NewRand()
|
||||
grand.init()
|
||||
}
|
||||
|
||||
func NewRand() *Rand {
|
||||
rand := &Rand{}
|
||||
rand.init()
|
||||
return rand
|
||||
}
|
||||
|
||||
func (r *Rand) init() {
|
||||
bz := cRandBytes(8)
|
||||
var seed uint64
|
||||
for i := 0; i < 8; i++ {
|
||||
seed |= uint64(bz[i])
|
||||
seed <<= 8
|
||||
}
|
||||
r.reset(int64(seed))
|
||||
}
|
||||
|
||||
func (r *Rand) reset(seed int64) {
|
||||
r.rand = mrand.New(mrand.NewSource(seed))
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Global functions
|
||||
|
||||
func Seed(seed int64) {
|
||||
grand.Seed(seed)
|
||||
}
|
||||
|
||||
func RandStr(length int) string {
|
||||
return grand.Str(length)
|
||||
}
|
||||
|
||||
func RandUint16() uint16 {
|
||||
return grand.Uint16()
|
||||
}
|
||||
|
||||
func RandUint32() uint32 {
|
||||
return grand.Uint32()
|
||||
}
|
||||
|
||||
func RandUint64() uint64 {
|
||||
return grand.Uint64()
|
||||
}
|
||||
|
||||
func RandUint() uint {
|
||||
return grand.Uint()
|
||||
}
|
||||
|
||||
func RandInt16() int16 {
|
||||
return grand.Int16()
|
||||
}
|
||||
|
||||
func RandInt32() int32 {
|
||||
return grand.Int32()
|
||||
}
|
||||
|
||||
func RandInt64() int64 {
|
||||
return grand.Int64()
|
||||
}
|
||||
|
||||
func RandInt() int {
|
||||
return grand.Int()
|
||||
}
|
||||
|
||||
func RandInt31() int32 {
|
||||
return grand.Int31()
|
||||
}
|
||||
|
||||
func RandInt31n(n int32) int32 {
|
||||
return grand.Int31n(n)
|
||||
}
|
||||
|
||||
func RandInt63() int64 {
|
||||
return grand.Int63()
|
||||
}
|
||||
|
||||
func RandInt63n(n int64) int64 {
|
||||
return grand.Int63n(n)
|
||||
}
|
||||
|
||||
func RandBool() bool {
|
||||
return grand.Bool()
|
||||
}
|
||||
|
||||
func RandFloat32() float32 {
|
||||
return grand.Float32()
|
||||
}
|
||||
|
||||
func RandFloat64() float64 {
|
||||
return grand.Float64()
|
||||
}
|
||||
|
||||
func RandTime() time.Time {
|
||||
return grand.Time()
|
||||
}
|
||||
|
||||
func RandBytes(n int) []byte {
|
||||
return grand.Bytes(n)
|
||||
}
|
||||
|
||||
func RandIntn(n int) int {
|
||||
return grand.Intn(n)
|
||||
}
|
||||
|
||||
func RandPerm(n int) []int {
|
||||
return grand.Perm(n)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Rand methods
|
||||
|
||||
func (r *Rand) Seed(seed int64) {
|
||||
r.Lock()
|
||||
r.reset(seed)
|
||||
r.Unlock()
|
||||
}
|
||||
|
||||
// Str constructs a random alphanumeric string of given length.
|
||||
func (r *Rand) Str(length int) string {
|
||||
chars := []byte{}
|
||||
MAIN_LOOP:
|
||||
for {
|
||||
val := r.Int63()
|
||||
for i := 0; i < 10; i++ {
|
||||
v := int(val & 0x3f) // rightmost 6 bits
|
||||
if v >= 62 { // only 62 characters in strChars
|
||||
val >>= 6
|
||||
continue
|
||||
} else {
|
||||
chars = append(chars, strChars[v])
|
||||
if len(chars) == length {
|
||||
break MAIN_LOOP
|
||||
}
|
||||
val >>= 6
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string(chars)
|
||||
}
|
||||
|
||||
func (r *Rand) Uint16() uint16 {
|
||||
return uint16(r.Uint32() & (1<<16 - 1))
|
||||
}
|
||||
|
||||
func (r *Rand) Uint32() uint32 {
|
||||
r.Lock()
|
||||
u32 := r.rand.Uint32()
|
||||
r.Unlock()
|
||||
return u32
|
||||
}
|
||||
|
||||
func (r *Rand) Uint64() uint64 {
|
||||
return uint64(r.Uint32())<<32 + uint64(r.Uint32())
|
||||
}
|
||||
|
||||
func (r *Rand) Uint() uint {
|
||||
r.Lock()
|
||||
i := r.rand.Int()
|
||||
r.Unlock()
|
||||
return uint(i)
|
||||
}
|
||||
|
||||
func (r *Rand) Int16() int16 {
|
||||
return int16(r.Uint32() & (1<<16 - 1))
|
||||
}
|
||||
|
||||
func (r *Rand) Int32() int32 {
|
||||
return int32(r.Uint32())
|
||||
}
|
||||
|
||||
func (r *Rand) Int64() int64 {
|
||||
return int64(r.Uint64())
|
||||
}
|
||||
|
||||
func (r *Rand) Int() int {
|
||||
r.Lock()
|
||||
i := r.rand.Int()
|
||||
r.Unlock()
|
||||
return i
|
||||
}
|
||||
|
||||
func (r *Rand) Int31() int32 {
|
||||
r.Lock()
|
||||
i31 := r.rand.Int31()
|
||||
r.Unlock()
|
||||
return i31
|
||||
}
|
||||
|
||||
func (r *Rand) Int31n(n int32) int32 {
|
||||
r.Lock()
|
||||
i31n := r.rand.Int31n(n)
|
||||
r.Unlock()
|
||||
return i31n
|
||||
}
|
||||
|
||||
func (r *Rand) Int63() int64 {
|
||||
r.Lock()
|
||||
i63 := r.rand.Int63()
|
||||
r.Unlock()
|
||||
return i63
|
||||
}
|
||||
|
||||
func (r *Rand) Int63n(n int64) int64 {
|
||||
r.Lock()
|
||||
i63n := r.rand.Int63n(n)
|
||||
r.Unlock()
|
||||
return i63n
|
||||
}
|
||||
|
||||
func (r *Rand) Float32() float32 {
|
||||
r.Lock()
|
||||
f32 := r.rand.Float32()
|
||||
r.Unlock()
|
||||
return f32
|
||||
}
|
||||
|
||||
func (r *Rand) Float64() float64 {
|
||||
r.Lock()
|
||||
f64 := r.rand.Float64()
|
||||
r.Unlock()
|
||||
return f64
|
||||
}
|
||||
|
||||
func (r *Rand) Time() time.Time {
|
||||
return time.Unix(int64(r.Uint64()), 0)
|
||||
}
|
||||
|
||||
// Bytes returns n random bytes generated from the internal
|
||||
// prng.
|
||||
func (r *Rand) Bytes(n int) []byte {
|
||||
// cRandBytes isn't guaranteed to be fast so instead
|
||||
// use random bytes generated from the internal PRNG
|
||||
bs := make([]byte, n)
|
||||
for i := 0; i < len(bs); i++ {
|
||||
bs[i] = byte(r.Int() & 0xFF)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
// Intn returns, as an int, a uniform pseudo-random number in the range [0, n).
|
||||
// It panics if n <= 0.
|
||||
func (r *Rand) Intn(n int) int {
|
||||
r.Lock()
|
||||
i := r.rand.Intn(n)
|
||||
r.Unlock()
|
||||
return i
|
||||
}
|
||||
|
||||
// Bool returns a uniformly random boolean
|
||||
func (r *Rand) Bool() bool {
|
||||
// See https://github.com/golang/go/issues/23804#issuecomment-365370418
|
||||
// for reasoning behind computing like this
|
||||
return r.Int63()%2 == 0
|
||||
}
|
||||
|
||||
// Perm returns a pseudo-random permutation of n integers in [0, n).
|
||||
func (r *Rand) Perm(n int) []int {
|
||||
r.Lock()
|
||||
perm := r.rand.Perm(n)
|
||||
r.Unlock()
|
||||
return perm
|
||||
}
|
||||
|
||||
// NOTE: This relies on the os's random number generator.
|
||||
// For real security, we should salt that with some seed.
|
||||
// See github.com/tendermint/tendermint/crypto for a more secure reader.
|
||||
func cRandBytes(numBytes int) []byte {
|
||||
b := make([]byte, numBytes)
|
||||
_, err := crand.Read(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
mrand "math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRandStr(t *testing.T) {
|
||||
l := 243
|
||||
s := RandStr(l)
|
||||
assert.Equal(t, l, len(s))
|
||||
}
|
||||
|
||||
func TestRandBytes(t *testing.T) {
|
||||
l := 243
|
||||
b := RandBytes(l)
|
||||
assert.Equal(t, l, len(b))
|
||||
}
|
||||
|
||||
func TestRandIntn(t *testing.T) {
|
||||
n := 243
|
||||
for i := 0; i < 100; i++ {
|
||||
x := RandIntn(n)
|
||||
assert.True(t, x < n)
|
||||
}
|
||||
}
|
||||
|
||||
// Test to make sure that we never call math.rand().
|
||||
// We do this by ensuring that outputs are deterministic.
|
||||
func TestDeterminism(t *testing.T) {
|
||||
var firstOutput string
|
||||
|
||||
// Set math/rand's seed for the sake of debugging this test.
|
||||
// (It isn't strictly necessary).
|
||||
mrand.Seed(1)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
output := testThemAll()
|
||||
if i == 0 {
|
||||
firstOutput = output
|
||||
} else if firstOutput != output {
|
||||
t.Errorf("run #%d's output was different from first run.\nfirst: %v\nlast: %v",
|
||||
i, firstOutput, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testThemAll() string {
|
||||
|
||||
// Such determinism.
|
||||
grand.reset(1)
|
||||
|
||||
// Use it.
|
||||
out := new(bytes.Buffer)
|
||||
perm := RandPerm(10)
|
||||
blob, _ := json.Marshal(perm)
|
||||
fmt.Fprintf(out, "perm: %s\n", blob)
|
||||
fmt.Fprintf(out, "randInt: %d\n", RandInt())
|
||||
fmt.Fprintf(out, "randUint: %d\n", RandUint())
|
||||
fmt.Fprintf(out, "randIntn: %d\n", RandIntn(97))
|
||||
fmt.Fprintf(out, "randInt31: %d\n", RandInt31())
|
||||
fmt.Fprintf(out, "randInt32: %d\n", RandInt32())
|
||||
fmt.Fprintf(out, "randInt63: %d\n", RandInt63())
|
||||
fmt.Fprintf(out, "randInt64: %d\n", RandInt64())
|
||||
fmt.Fprintf(out, "randUint32: %d\n", RandUint32())
|
||||
fmt.Fprintf(out, "randUint64: %d\n", RandUint64())
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func TestRngConcurrencySafety(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
_ = RandUint64()
|
||||
<-time.After(time.Millisecond * time.Duration(RandIntn(100)))
|
||||
_ = RandPerm(3)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkRandBytes10B(b *testing.B) {
|
||||
benchmarkRandBytes(b, 10)
|
||||
}
|
||||
func BenchmarkRandBytes100B(b *testing.B) {
|
||||
benchmarkRandBytes(b, 100)
|
||||
}
|
||||
func BenchmarkRandBytes1KiB(b *testing.B) {
|
||||
benchmarkRandBytes(b, 1024)
|
||||
}
|
||||
func BenchmarkRandBytes10KiB(b *testing.B) {
|
||||
benchmarkRandBytes(b, 10*1024)
|
||||
}
|
||||
func BenchmarkRandBytes100KiB(b *testing.B) {
|
||||
benchmarkRandBytes(b, 100*1024)
|
||||
}
|
||||
func BenchmarkRandBytes1MiB(b *testing.B) {
|
||||
benchmarkRandBytes(b, 1024*1024)
|
||||
}
|
||||
|
||||
func benchmarkRandBytes(b *testing.B, n int) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = RandBytes(n)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
@@ -10,12 +10,14 @@ import (
|
||||
testing "testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/rand"
|
||||
)
|
||||
|
||||
func TestWriteFileAtomic(t *testing.T) {
|
||||
var (
|
||||
data = []byte(RandStr(RandIntn(2048)))
|
||||
old = RandBytes(RandIntn(2048))
|
||||
data = []byte(rand.RandStr(rand.RandIntn(2048)))
|
||||
old = rand.RandBytes(rand.RandIntn(2048))
|
||||
perm os.FileMode = 0600
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user