rand.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  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. // Intn returns a deterministically random integer between 0 and n-1, inclusive.
  25. func Intn(n int) int {
  26. if n <= 0 {
  27. panic("must be positive")
  28. }
  29. return int(randSeed % uint64(n))
  30. }
  31. // randSeed is a best-effort at an approximate hash of the Go binary.
  32. var randSeed = binaryHash()
  33. func binaryHash() uint64 {
  34. // Open the Go binary.
  35. s, err := os.Executable()
  36. if err != nil {
  37. return 0
  38. }
  39. f, err := os.Open(s)
  40. if err != nil {
  41. return 0
  42. }
  43. defer f.Close()
  44. // Hash the size and several samples of the Go binary.
  45. const numSamples = 8
  46. var buf [64]byte
  47. h := fnv.New64()
  48. fi, err := f.Stat()
  49. if err != nil {
  50. return 0
  51. }
  52. binary.LittleEndian.PutUint64(buf[:8], uint64(fi.Size()))
  53. h.Write(buf[:8])
  54. for i := int64(0); i < numSamples; i++ {
  55. if _, err := f.ReadAt(buf[:], i*fi.Size()/numSamples); err != nil {
  56. return 0
  57. }
  58. h.Write(buf[:])
  59. }
  60. return h.Sum64()
  61. }