randbp

package
v0.9.17 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 22, 2024 License: BSD-3-Clause Imports: 8 Imported by: 3

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

View Source
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

View Source
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

func JitterDuration(center time.Duration, jitter float64) time.Duration

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

func JitterRatio(jitter float64) float64

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

func ShouldSampleWithRate(rate float64) bool

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

type Rand struct {
	*rand.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 New

func New(seed int64) Rand

New initializes a thread-safe, properly seeded Rand.

func (Rand) Read

func (r Rand) Read(p []byte) (int, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL