rand.go 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  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. // Package detrand provides deterministically random functionality.
  5. //
  6. // The pseudo-randomness of these functions is seeded by the program binary
  7. // itself and guarantees that the output does not change within a program,
  8. // while ensuring that the output is unstable across different builds.
  9. package detrand
  10. import (
  11. "encoding/binary"
  12. "hash/fnv"
  13. "os"
  14. )
  15. // Disable disables detrand such that all functions returns the zero value.
  16. // This function is not concurrent-safe and must be called during program init.
  17. func Disable() {
  18. randSeed = 0
  19. }
  20. // Bool returns a deterministically random boolean.
  21. func Bool() bool {
  22. return randSeed%2 == 1
  23. }
  24. // randSeed is a best-effort at an approximate hash of the Go binary.
  25. var randSeed = binaryHash()
  26. func binaryHash() uint64 {
  27. // Open the Go binary.
  28. s, err := os.Executable()
  29. if err != nil {
  30. return 0
  31. }
  32. f, err := os.Open(s)
  33. if err != nil {
  34. return 0
  35. }
  36. defer f.Close()
  37. // Hash the size and several samples of the Go binary.
  38. const numSamples = 8
  39. var buf [64]byte
  40. h := fnv.New64()
  41. fi, err := f.Stat()
  42. if err != nil {
  43. return 0
  44. }
  45. binary.LittleEndian.PutUint64(buf[:8], uint64(fi.Size()))
  46. h.Write(buf[:8])
  47. for i := int64(0); i < numSamples; i++ {
  48. if _, err := f.ReadAt(buf[:], i*fi.Size()/numSamples); err != nil {
  49. return 0
  50. }
  51. h.Write(buf[:])
  52. }
  53. return h.Sum64()
  54. }