hash_bins.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package targets
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. )
  7. const MinDelegationHashPrefixBitLen = 1
  8. const MaxDelegationHashPrefixBitLen = 32
  9. // hexEncode formats x as a hex string. The hex string is left padded with
  10. // zeros to padWidth, if necessary.
  11. func hexEncode(x uint64, padWidth int) string {
  12. // Benchmarked to be more than 10x faster than padding with Sprintf.
  13. s := strconv.FormatUint(x, 16)
  14. if len(s) >= padWidth {
  15. return s
  16. }
  17. return strings.Repeat("0", padWidth-len(s)) + s
  18. }
  19. const bitsPerHexDigit = 4
  20. // numHexDigits returns the number of hex digits required to encode the given
  21. // number of bits.
  22. func numHexDigits(numBits int) int {
  23. // ceil(numBits / bitsPerHexDigit)
  24. return ((numBits - 1) / bitsPerHexDigit) + 1
  25. }
  26. // HashBins represents an ordered list of hash bin target roles, which together
  27. // partition the space of target path hashes equal-sized buckets based on path
  28. // has prefix.
  29. type HashBins struct {
  30. rolePrefix string
  31. bitLen int
  32. hexDigitLen int
  33. numBins uint64
  34. numPrefixesPerBin uint64
  35. }
  36. // NewHashBins creates a HashBins partitioning with 2^bitLen buckets.
  37. func NewHashBins(rolePrefix string, bitLen int) (*HashBins, error) {
  38. if bitLen < MinDelegationHashPrefixBitLen || bitLen > MaxDelegationHashPrefixBitLen {
  39. return nil, fmt.Errorf("bitLen is out of bounds, should be between %v and %v inclusive", MinDelegationHashPrefixBitLen, MaxDelegationHashPrefixBitLen)
  40. }
  41. hexDigitLen := numHexDigits(bitLen)
  42. numBins := uint64(1) << bitLen
  43. numPrefixesTotal := uint64(1) << (bitsPerHexDigit * hexDigitLen)
  44. numPrefixesPerBin := numPrefixesTotal / numBins
  45. return &HashBins{
  46. rolePrefix: rolePrefix,
  47. bitLen: bitLen,
  48. hexDigitLen: hexDigitLen,
  49. numBins: numBins,
  50. numPrefixesPerBin: numPrefixesPerBin,
  51. }, nil
  52. }
  53. // NumBins returns the number of hash bin partitions.
  54. func (hb *HashBins) NumBins() uint64 {
  55. return hb.numBins
  56. }
  57. // GetBin returns the HashBin at index i, or nil if i is out of bounds.
  58. func (hb *HashBins) GetBin(i uint64) *HashBin {
  59. if i >= hb.numBins {
  60. return nil
  61. }
  62. return &HashBin{
  63. rolePrefix: hb.rolePrefix,
  64. hexDigitLen: hb.hexDigitLen,
  65. first: i * hb.numPrefixesPerBin,
  66. last: ((i + 1) * hb.numPrefixesPerBin) - 1,
  67. }
  68. }
  69. // HashBin represents a hex prefix range. First should be less than Last.
  70. type HashBin struct {
  71. rolePrefix string
  72. hexDigitLen int
  73. first uint64
  74. last uint64
  75. }
  76. // RoleName returns the name of the role that signs for the HashBin.
  77. func (b *HashBin) RoleName() string {
  78. if b.first == b.last {
  79. return b.rolePrefix + hexEncode(b.first, b.hexDigitLen)
  80. }
  81. return b.rolePrefix + hexEncode(b.first, b.hexDigitLen) + "-" + hexEncode(b.last, b.hexDigitLen)
  82. }
  83. // HashPrefixes returns a slice of all hash prefixes in the bin.
  84. func (b *HashBin) HashPrefixes() []string {
  85. n := int(b.last - b.first + 1)
  86. ret := make([]string, int(n))
  87. x := b.first
  88. for i := 0; i < n; i++ {
  89. ret[i] = hexEncode(x, b.hexDigitLen)
  90. x++
  91. }
  92. return ret
  93. }