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 ¶
- Constants
- Variables
- func GenerateRandomString(args RandomStringArgs) string
- func GetSeed() int64
- func JitterDuration(center time.Duration, jitter float64) time.Duration
- func JitterRatio(jitter float64) float64
- func ShouldSampleWithRate(rate float64) bool
- type LockedSource64
- type Rand
- type RandomStringArgs
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. }
Output:
func GetSeed ¶
func GetSeed() int64
GetSeed returns an int64 seed for pseudo-random generator.
It tries to use crypto/rand to read an int64, and if an error happens, it logs the error to stderr, then fallback to use current time bitwise xor the part it read from crypto/rand instead.
The bitwise xor part in the fallback is in order to try to prevent two processes starting at the same time getting the same seed from happening.
func JitterDuration ¶ added in v0.9.1
JitterDuration applies jitter on the center time duration so the returned duration is center +/- jitter.
It uses JitterRatio under-the-hood. See doc of JitterRatio for more info.
NOTE: If center is very large, some precision loss could occur when casting it into float64 to apply jitter, but that would only happen when the time duration is prohibitively long.
func JitterRatio ¶ added in v0.9.1
JitterRatio calculates the ratio to be multiplied by base, with +/- jitter.
For example, JitterRatio(0.1) would return a float64 between (0.9, 1.1) (exclusive on both ends).
jitter > 1 will be normalized to 1. jitter <= 0 will always return 1.
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.