145 lines
3.3 KiB
Go
145 lines
3.3 KiB
Go
// Copyright 2020 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package fuzz
|
|
|
|
import (
|
|
"math/bits"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
type mutatorRand interface {
|
|
uint32() uint32
|
|
intn(int) int
|
|
uint32n(uint32) uint32
|
|
exp2() int
|
|
bool() bool
|
|
|
|
save(randState, randInc *uint64)
|
|
restore(randState, randInc uint64)
|
|
}
|
|
|
|
// The functions in pcg implement a 32 bit PRNG with a 64 bit period: pcg xsh rr
|
|
// 64 32. See https://www.pcg-random.org/ for more information. This
|
|
// implementation is geared specifically towards the needs of fuzzing: Simple
|
|
// creation and use, no reproducibility, no concurrency safety, just the
|
|
// necessary methods, optimized for speed.
|
|
|
|
var globalInc uint64 // PCG stream
|
|
|
|
const multiplier uint64 = 6364136223846793005
|
|
|
|
// pcgRand is a PRNG. It should not be copied or shared. No Rand methods are
|
|
// concurrency safe.
|
|
type pcgRand struct {
|
|
noCopy noCopy // help avoid mistakes: ask vet to ensure that we don't make a copy
|
|
state uint64
|
|
inc uint64
|
|
}
|
|
|
|
func godebugSeed() *int {
|
|
debug := strings.Split(os.Getenv("GODEBUG"), ",")
|
|
for _, f := range debug {
|
|
if strings.HasPrefix(f, "fuzzseed=") {
|
|
seed, err := strconv.Atoi(strings.TrimPrefix(f, "fuzzseed="))
|
|
if err != nil {
|
|
panic("malformed fuzzseed")
|
|
}
|
|
return &seed
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// newPcgRand generates a new, seeded Rand, ready for use.
|
|
func newPcgRand() *pcgRand {
|
|
r := new(pcgRand)
|
|
now := uint64(time.Now().UnixNano())
|
|
if seed := godebugSeed(); seed != nil {
|
|
now = uint64(*seed)
|
|
}
|
|
inc := atomic.AddUint64(&globalInc, 1)
|
|
r.state = now
|
|
r.inc = (inc << 1) | 1
|
|
r.step()
|
|
r.state += now
|
|
r.step()
|
|
return r
|
|
}
|
|
|
|
func (r *pcgRand) step() {
|
|
r.state *= multiplier
|
|
r.state += r.inc
|
|
}
|
|
|
|
func (r *pcgRand) save(randState, randInc *uint64) {
|
|
*randState = r.state
|
|
*randInc = r.inc
|
|
}
|
|
|
|
func (r *pcgRand) restore(randState, randInc uint64) {
|
|
r.state = randState
|
|
r.inc = randInc
|
|
}
|
|
|
|
// uint32 returns a pseudo-random uint32.
|
|
func (r *pcgRand) uint32() uint32 {
|
|
x := r.state
|
|
r.step()
|
|
return bits.RotateLeft32(uint32(((x>>18)^x)>>27), -int(x>>59))
|
|
}
|
|
|
|
// intn returns a pseudo-random number in [0, n).
|
|
// n must fit in a uint32.
|
|
func (r *pcgRand) intn(n int) int {
|
|
if int(uint32(n)) != n {
|
|
panic("large Intn")
|
|
}
|
|
return int(r.uint32n(uint32(n)))
|
|
}
|
|
|
|
// uint32n returns a pseudo-random number in [0, n).
|
|
//
|
|
// For implementation details, see:
|
|
// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction
|
|
// https://lemire.me/blog/2016/06/30/fast-random-shuffling
|
|
func (r *pcgRand) uint32n(n uint32) uint32 {
|
|
v := r.uint32()
|
|
prod := uint64(v) * uint64(n)
|
|
low := uint32(prod)
|
|
if low < n {
|
|
thresh := uint32(-int32(n)) % n
|
|
for low < thresh {
|
|
v = r.uint32()
|
|
prod = uint64(v) * uint64(n)
|
|
low = uint32(prod)
|
|
}
|
|
}
|
|
return uint32(prod >> 32)
|
|
}
|
|
|
|
// exp2 generates n with probability 1/2^(n+1).
|
|
func (r *pcgRand) exp2() int {
|
|
return bits.TrailingZeros32(r.uint32())
|
|
}
|
|
|
|
// bool generates a random bool.
|
|
func (r *pcgRand) bool() bool {
|
|
return r.uint32()&1 == 0
|
|
}
|
|
|
|
// noCopy may be embedded into structs which must not be copied
|
|
// after the first use.
|
|
//
|
|
// See https://golang.org/issues/8005#issuecomment-190753527
|
|
// for details.
|
|
type noCopy struct{}
|
|
|
|
// lock is a no-op used by -copylocks checker from `go vet`.
|
|
func (*noCopy) lock() {}
|
|
func (*noCopy) unlock() {}
|