summaryrefslogtreecommitdiffstats
path: root/src/internal/fuzz/pcg.go
blob: 4fe8aeb50cc83eefbb2db9ff17a96a79669d255a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// 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 atomic.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 := globalInc.Add(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() {}