rand.go raw

   1  // 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.
  10  package detrand
  11  
  12  import (
  13  	"encoding/binary"
  14  	"hash/fnv"
  15  	"os"
  16  )
  17  
  18  // Disable disables detrand such that all functions returns the zero value.
  19  // This function is not concurrent-safe and must be called during program init.
  20  func Disable() {
  21  	randSeed = 0
  22  }
  23  
  24  // Bool returns a deterministically random boolean.
  25  func Bool() bool {
  26  	return randSeed%2 == 1
  27  }
  28  
  29  // Intn returns a deterministically random integer between 0 and n-1, inclusive.
  30  func Intn(n int) int {
  31  	if n <= 0 {
  32  		panic("must be positive")
  33  	}
  34  	return int(randSeed % uint64(n))
  35  }
  36  
  37  // randSeed is a best-effort at an approximate hash of the Go binary.
  38  var randSeed = binaryHash()
  39  
  40  func binaryHash() uint64 {
  41  	// Open the Go binary.
  42  	s, err := os.Executable()
  43  	if err != nil {
  44  		return 0
  45  	}
  46  	f, err := os.Open(s)
  47  	if err != nil {
  48  		return 0
  49  	}
  50  	defer f.Close()
  51  
  52  	// Hash the size and several samples of the Go binary.
  53  	const numSamples = 8
  54  	var buf [64]byte
  55  	h := fnv.New64()
  56  	fi, err := f.Stat()
  57  	if err != nil {
  58  		return 0
  59  	}
  60  	binary.LittleEndian.PutUint64(buf[:8], uint64(fi.Size()))
  61  	h.Write(buf[:8])
  62  	for i := int64(0); i < numSamples; i++ {
  63  		if _, err := f.ReadAt(buf[:], i*fi.Size()/numSamples); err != nil {
  64  			return 0
  65  		}
  66  		h.Write(buf[:])
  67  	}
  68  	return h.Sum64()
  69  }
  70