blob: c702f8f89803a9e2dc4090d943fea8664b9fc26e [file] [log] [blame]
Joe Tsai492a4762018-11-26 17:16:32 -08001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package detrand provides deterministically random functionality.
6//
7// The pseudo-randomness of these functions is seeded by the program binary
8// itself and guarantees that the output does not change within a program,
9// while ensuring that the output is unstable across different builds.
10package detrand
11
12import (
13 "encoding/binary"
14 "hash/fnv"
15 "os"
16)
17
18// Bool returns a deterministically random boolean.
19func Bool() bool {
20 return binHash%2 == 0
21}
22
23// Intn returns a deterministically random integer within [0,n).
24func Intn(n int) int {
25 if n <= 0 {
26 panic("invalid argument to Intn")
27 }
28 return int(binHash % uint64(n))
29}
30
31// binHash is a best-effort at an approximate hash of the Go binary.
32var binHash = binaryHash()
33
34func binaryHash() uint64 {
35 // Open the Go binary.
36 s, err := os.Executable()
37 if err != nil {
38 return 0
39 }
40 f, err := os.Open(s)
41 if err != nil {
42 return 0
43 }
44 defer f.Close()
45
46 // Hash the size and several samples of the Go binary.
47 const numSamples = 8
48 var buf [64]byte
49 h := fnv.New64()
50 fi, err := f.Stat()
51 if err != nil {
52 return 0
53 }
54 binary.LittleEndian.PutUint64(buf[:8], uint64(fi.Size()))
55 h.Write(buf[:8])
56 for i := int64(0); i < numSamples; i++ {
57 if _, err := f.ReadAt(buf[:], i*fi.Size()/numSamples); err != nil {
58 return 0
59 }
60 h.Write(buf[:])
61 }
62 return h.Sum64()
63}