xref: /aosp_15_r20/external/golang-protobuf/internal/detrand/rand.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
1*1c12ee1eSDan Willemsen// Copyright 2018 The Go Authors. All rights reserved.
2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style
3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file.
4*1c12ee1eSDan Willemsen
5*1c12ee1eSDan Willemsen// Package detrand provides deterministically random functionality.
6*1c12ee1eSDan Willemsen//
7*1c12ee1eSDan Willemsen// The pseudo-randomness of these functions is seeded by the program binary
8*1c12ee1eSDan Willemsen// itself and guarantees that the output does not change within a program,
9*1c12ee1eSDan Willemsen// while ensuring that the output is unstable across different builds.
10*1c12ee1eSDan Willemsenpackage detrand
11*1c12ee1eSDan Willemsen
12*1c12ee1eSDan Willemsenimport (
13*1c12ee1eSDan Willemsen	"encoding/binary"
14*1c12ee1eSDan Willemsen	"hash/fnv"
15*1c12ee1eSDan Willemsen	"os"
16*1c12ee1eSDan Willemsen)
17*1c12ee1eSDan Willemsen
18*1c12ee1eSDan Willemsen// Disable disables detrand such that all functions returns the zero value.
19*1c12ee1eSDan Willemsen// This function is not concurrent-safe and must be called during program init.
20*1c12ee1eSDan Willemsenfunc Disable() {
21*1c12ee1eSDan Willemsen	randSeed = 0
22*1c12ee1eSDan Willemsen}
23*1c12ee1eSDan Willemsen
24*1c12ee1eSDan Willemsen// Bool returns a deterministically random boolean.
25*1c12ee1eSDan Willemsenfunc Bool() bool {
26*1c12ee1eSDan Willemsen	return randSeed%2 == 1
27*1c12ee1eSDan Willemsen}
28*1c12ee1eSDan Willemsen
29*1c12ee1eSDan Willemsen// Intn returns a deterministically random integer between 0 and n-1, inclusive.
30*1c12ee1eSDan Willemsenfunc Intn(n int) int {
31*1c12ee1eSDan Willemsen	if n <= 0 {
32*1c12ee1eSDan Willemsen		panic("must be positive")
33*1c12ee1eSDan Willemsen	}
34*1c12ee1eSDan Willemsen	return int(randSeed % uint64(n))
35*1c12ee1eSDan Willemsen}
36*1c12ee1eSDan Willemsen
37*1c12ee1eSDan Willemsen// randSeed is a best-effort at an approximate hash of the Go binary.
38*1c12ee1eSDan Willemsenvar randSeed = binaryHash()
39*1c12ee1eSDan Willemsen
40*1c12ee1eSDan Willemsenfunc binaryHash() uint64 {
41*1c12ee1eSDan Willemsen	// Open the Go binary.
42*1c12ee1eSDan Willemsen	s, err := os.Executable()
43*1c12ee1eSDan Willemsen	if err != nil {
44*1c12ee1eSDan Willemsen		return 0
45*1c12ee1eSDan Willemsen	}
46*1c12ee1eSDan Willemsen	f, err := os.Open(s)
47*1c12ee1eSDan Willemsen	if err != nil {
48*1c12ee1eSDan Willemsen		return 0
49*1c12ee1eSDan Willemsen	}
50*1c12ee1eSDan Willemsen	defer f.Close()
51*1c12ee1eSDan Willemsen
52*1c12ee1eSDan Willemsen	// Hash the size and several samples of the Go binary.
53*1c12ee1eSDan Willemsen	const numSamples = 8
54*1c12ee1eSDan Willemsen	var buf [64]byte
55*1c12ee1eSDan Willemsen	h := fnv.New64()
56*1c12ee1eSDan Willemsen	fi, err := f.Stat()
57*1c12ee1eSDan Willemsen	if err != nil {
58*1c12ee1eSDan Willemsen		return 0
59*1c12ee1eSDan Willemsen	}
60*1c12ee1eSDan Willemsen	binary.LittleEndian.PutUint64(buf[:8], uint64(fi.Size()))
61*1c12ee1eSDan Willemsen	h.Write(buf[:8])
62*1c12ee1eSDan Willemsen	for i := int64(0); i < numSamples; i++ {
63*1c12ee1eSDan Willemsen		if _, err := f.ReadAt(buf[:], i*fi.Size()/numSamples); err != nil {
64*1c12ee1eSDan Willemsen			return 0
65*1c12ee1eSDan Willemsen		}
66*1c12ee1eSDan Willemsen		h.Write(buf[:])
67*1c12ee1eSDan Willemsen	}
68*1c12ee1eSDan Willemsen	return h.Sum64()
69*1c12ee1eSDan Willemsen}
70