Documentation
¶
Overview ¶
Package randbp provides some random generator related features:
1. A thread-safe, properly seeded global *math/rand.Rand implementation.
2. Helper functions for common use cases.
Index ¶
Examples ¶
Constants ¶
const Base64Runes = `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_+/=`
Base64Runes are all the runes allowed in standard and url safe base64 encodings.
This is a common, safe to use set of runes to be used with GenerateRandomString.
Variables ¶
var R = New(GetSeed())
R is a global thread-safe rng.
It embeds *math/rand.Rand, but properly seeded and safe for concurrent use.
It should be used instead of the global functions inside math/rand package.
For example, instead of this:
import "math/rand" i := rand.Uint64()
Use this:
import "github.com/reddit/baseplate.go/randbp" i := randbp.R.Uint64()
NOTE: Its Read function has worse performance comparing to rand's global rander or rand.Rand created with non-thread-safe source for small buffers. See the doc of Rand.Read for more details. All other functions (Uint64, Float64, etc.) have comparable performance to math/rand's implementations.
Functions ¶
func GenerateRandomString ¶
func GenerateRandomString(args RandomStringArgs) string
GenerateRandomString generates a random string with length [MinLength, MaxLength), and all characters limited to Runes.
It could be used to help implement testing/quick.Generator interface.
Example ¶
This example demonstrates how to use GenerateRandomString in your tests with testing/quick package.
package main
import (
"math/rand"
"reflect"
"testing"
"testing/quick"
"github.com/reddit/baseplate.go/randbp"
)
const (
MinLength = 1
MaxLength = 20
)
type RandomString string
func (RandomString) Generate(r *rand.Rand, _ int) reflect.Value {
return reflect.ValueOf(RandomString(randbp.GenerateRandomString(
randbp.RandomStringArgs{
R: r,
MinLength: MinLength,
MaxLength: MaxLength,
},
)))
}
var _ quick.Generator = RandomString("")
// In real code the function name should be TestRandomString,
// but using that name here will break the example.
func RandomStringTest(t *testing.T) {
f := func(input RandomString) bool {
s := string(input)
if len(s) < MinLength {
t.Errorf(
"Expected random string to have a minimal length of %d, got %q",
MinLength,
s,
)
}
if len(s) >= MaxLength {
t.Errorf(
"Expected random string to have a maximum length of %d, got %q",
MaxLength,
s,
)
}
return !t.Failed()
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
// This example demonstrates how to use GenerateRandomString in your tests with
// testing/quick package.
func main() {
// Nothing really here.
// The real example is on the other functions/types above.
}
func GetSeed ¶
func GetSeed() int64
GetSeed returns a seed for pseudo-random generator.
It tries to use crypto/rand to read an int64, and fallback to use current time if that fails for whatever reason.
func ShouldSampleWithRate ¶
ShouldSampleWithRate generates a random float64 in [0, 1) and check it against rate.
rate should be in the range of [0, 1]. When rate <= 0 this function always returns false; When rate >= 1 this function always returns true.
Types ¶
type LockedSource64 ¶
type LockedSource64 struct {
// contains filtered or unexported fields
}
LockedSource64 is a thread-safe implementation of rand.Source64.
NOTE: When using *LockedSource64 to create rand.Rand, its Read function will have a much worse performance comparing to rand's global rander or rand.Rand created with non-thread-safe source.
func NewLockedSource64 ¶
func NewLockedSource64(src rand.Source) *LockedSource64
NewLockedSource64 creates a *LockedSource64 from the given src.
func (*LockedSource64) Int63 ¶
func (ls *LockedSource64) Int63() (n int64)
Int63 implements rand.Source64.
It calls underlying source's Int63 with lock.
func (*LockedSource64) Seed ¶
func (ls *LockedSource64) Seed(seed int64)
Seed implements rand.Source64.
It calls underlying source's Seed with lock.
func (*LockedSource64) Uint64 ¶
func (ls *LockedSource64) Uint64() (n uint64)
Uint64 implements rand.Source64.
If the underlying source implements rand.Source64, it calls its Uint64 with lock. Otherwise, it calls its Int64 twice with lock.
type Rand ¶
Rand embeds *math/rand.Rand.
All functions besides Read are directly calling the embedded rand.Rand. When initialized with New(), all functions are safe for concurrent use, and have comparable performance to the top level math/rand functions.
See the doc of Read function for more details on that one.
func (Rand) Read ¶
Read overrides math/rand's Read implementation with thread-safety.
It's safe for concurrent use and always returns len(p) with nil error.
Compare to math/rand.Read (the top level one) performance-wise, it has a constant ~1us overhead, which is significant when len(p) is small, but less significant when len(p) grows larger, and starts to outperform math/rand.Read when len(p) is very large because it only need to lock once. See the following sample benchmark result:
$ go test -bench Rand/Read -benchmem goos: darwin goarch: amd64 pkg: github.com/reddit/baseplate.go/randbp BenchmarkRand/Read/size-16/math/rand-8 8213564 138 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-16/crypto/rand-8 9739500 123 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-16/randbp-8 979442 1329 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-64/math/rand-8 5289319 227 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-64/crypto/rand-8 6050103 197 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-64/randbp-8 911760 1301 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-256/math/rand-8 4223463 274 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-256/crypto/rand-8 2263252 534 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-256/randbp-8 940459 1333 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-512/math/rand-8 2455426 481 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-512/crypto/rand-8 1000000 1008 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-512/randbp-8 885555 1445 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-1024/math/rand-8 1275535 925 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-1024/crypto/rand-8 636202 1980 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-1024/randbp-8 800511 1630 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-4096/math/rand-8 310982 3765 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-4096/crypto/rand-8 159490 7538 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-4096/randbp-8 511124 2319 ns/op 0 B/op 0 allocs/op BenchmarkRand/Read/size-1048576/math/rand-8 1341 860809 ns/op 6255 B/op 0 allocs/op BenchmarkRand/Read/size-1048576/crypto/rand-8 838 1349225 ns/op 10016 B/op 0 allocs/op BenchmarkRand/Read/size-1048576/randbp-8 5330 238657 ns/op 1582 B/op 0 allocs/op PASS ok github.com/reddit/baseplate.go/randbp 29.982s
Regardless performance, it's never suitable for security purpose, and you should always use crypto/rand for that instead.
type RandomStringArgs ¶ added in v0.7.0
type RandomStringArgs struct {
// Required. If MaxLength <= MinLength it will cause panic.
MaxLength int
// Optional. Default is 0, which means it could generate empty strings.
// If MinLength < 0 or MinLength >= MaxLength it will cause panic.
MinLength int
// Optional. If nil randbp.R will be used instead.
R *rand.Rand
// Optional. If empty []rune(randbp.Base64Runes) will be used instead.
Runes []rune
}
RandomStringArgs defines the args used by GenerateRandomString.