-
Notifications
You must be signed in to change notification settings - Fork 110
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Delegations prereq] Add hash bins helpers
Splitting up #175
- Loading branch information
1 parent
5dad9b6
commit 9e07992
Showing
2 changed files
with
177 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package targets | ||
|
||
import ( | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// hexEncode formats x as a hex string, left padded with zeros to padWidth. | ||
func hexEncode(x uint64, padWidth int) string { | ||
// Benchmarked to be more than 10x faster than padding with Sprintf. | ||
s := strconv.FormatUint(x, 16) | ||
if len(s) >= padWidth { | ||
return s | ||
} | ||
return strings.Repeat("0", padWidth-len(s)) + s | ||
} | ||
|
||
// HashBin represents a hex prefix range. First should be less than Last. | ||
type HashBin struct { | ||
First uint64 | ||
Last uint64 | ||
} | ||
|
||
// Name returns the of the role that signs for the HashBin. | ||
func (b HashBin) Name(prefix string, padWidth int) string { | ||
if b.First == b.Last { | ||
return prefix + hexEncode(b.First, padWidth) | ||
} | ||
|
||
return prefix + hexEncode(b.First, padWidth) + "-" + hexEncode(b.Last, padWidth) | ||
} | ||
|
||
// Enumerate returns a slice of hash prefixes in the range from First to Last. | ||
func (b HashBin) Enumerate(padWidth int) []string { | ||
n := int(b.Last - b.First + 1) | ||
ret := make([]string, int(n)) | ||
|
||
x := b.First | ||
for i := 0; i < n; i++ { | ||
ret[i] = hexEncode(x, padWidth) | ||
x++ | ||
} | ||
|
||
return ret | ||
} | ||
|
||
// HashPrefixLength returns the width of hash prefixes if there are | ||
// 2^(log2NumBins) hash bins. | ||
func HashPrefixLength(log2NumBins uint8) int { | ||
if log2NumBins == 0 { | ||
// Hash prefix of "" is represented equivalently as "0-f". | ||
return 1 | ||
} | ||
|
||
// ceil(log2NumBins / 4.0) | ||
return int((log2NumBins-1)/4) + 1 | ||
} | ||
|
||
// GenerateHashBins returns a slice of length 2^(log2NumBins) that partitions | ||
// the space of path hashes into HashBin ranges. | ||
func GenerateHashBins(log2NumBins uint8) []HashBin { | ||
numBins := uint64(1) << log2NumBins | ||
|
||
// numPrefixes = 16^(HashPrefixLength(log2NumBins)) | ||
numPrefixes := uint64(1) << (4 * HashPrefixLength(log2NumBins)) | ||
|
||
p := make([]HashBin, numBins) | ||
|
||
first := uint64(0) | ||
interval := numPrefixes / numBins | ||
last := first + interval - 1 | ||
for i := uint64(0); i < numBins; i++ { | ||
p[i] = HashBin{ | ||
First: first, | ||
Last: last, | ||
} | ||
first += interval | ||
last += interval | ||
} | ||
|
||
return p | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package targets | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func BenchmarkHexEncode1(b *testing.B) { | ||
for n := 0; n <= b.N; n++ { | ||
for x := uint64(0); x <= 0xf; x += 1 { | ||
hexEncode(x, 1) | ||
} | ||
} | ||
} | ||
|
||
func BenchmarkHexEncode4(b *testing.B) { | ||
for n := 0; n <= b.N; n++ { | ||
for x := uint64(0); x <= 0xffff; x += 1 { | ||
hexEncode(x, 4) | ||
} | ||
} | ||
} | ||
|
||
func TestHashBin(t *testing.T) { | ||
h := HashBin{ | ||
First: 0x0, | ||
Last: 0xf, | ||
} | ||
assert.Equal(t, "abc_0-f", h.Name("abc_", 1)) | ||
assert.Equal(t, "abc_0000-000f", h.Name("abc_", 4)) | ||
assert.Equal(t, []string{ | ||
"00", "01", "02", "03", "04", "05", "06", "07", | ||
"08", "09", "0a", "0b", "0c", "0d", "0e", "0f", | ||
}, h.Enumerate(2)) | ||
|
||
h = HashBin{ | ||
First: 0xcd, | ||
Last: 0xce, | ||
} | ||
assert.Equal(t, "abc_00cd-00ce", h.Name("abc_", 4)) | ||
assert.Equal(t, []string{"00cd", "00ce"}, h.Enumerate(4)) | ||
|
||
h = HashBin{ | ||
First: 0x0abc, | ||
Last: 0xbcde, | ||
} | ||
assert.Equal(t, "test_0abc-bcde", h.Name("test_", 4)) | ||
} | ||
|
||
func TestHashPrefixLength(t *testing.T) { | ||
assert.Equal(t, 1, HashPrefixLength(0)) | ||
assert.Equal(t, 1, HashPrefixLength(1)) | ||
assert.Equal(t, 1, HashPrefixLength(2)) | ||
assert.Equal(t, 1, HashPrefixLength(3)) | ||
assert.Equal(t, 1, HashPrefixLength(4)) | ||
assert.Equal(t, 2, HashPrefixLength(5)) | ||
assert.Equal(t, 2, HashPrefixLength(6)) | ||
assert.Equal(t, 2, HashPrefixLength(7)) | ||
assert.Equal(t, 2, HashPrefixLength(8)) | ||
assert.Equal(t, 3, HashPrefixLength(9)) | ||
assert.Equal(t, 3, HashPrefixLength(10)) | ||
assert.Equal(t, 3, HashPrefixLength(11)) | ||
assert.Equal(t, 3, HashPrefixLength(12)) | ||
} | ||
|
||
func TestGenerateHashBins(t *testing.T) { | ||
tcs := []struct { | ||
Log2NumBins uint8 | ||
BinNames []string | ||
}{ | ||
{0, []string{"0-f"}}, | ||
{1, []string{"0-7", "8-f"}}, | ||
{2, []string{"0-3", "4-7", "8-b", "c-f"}}, | ||
{3, []string{"0-1", "2-3", "4-5", "6-7", "8-9", "a-b", "c-d", "e-f"}}, | ||
{4, []string{ | ||
"0", "1", "2", "3", "4", "5", "6", "7", | ||
"8", "9", "a", "b", "c", "d", "e", "f", | ||
}}, | ||
{5, []string{ | ||
"00-07", "08-0f", "10-17", "18-1f", "20-27", "28-2f", "30-37", "38-3f", | ||
"40-47", "48-4f", "50-57", "58-5f", "60-67", "68-6f", "70-77", "78-7f", | ||
"80-87", "88-8f", "90-97", "98-9f", "a0-a7", "a8-af", "b0-b7", "b8-bf", | ||
"c0-c7", "c8-cf", "d0-d7", "d8-df", "e0-e7", "e8-ef", "f0-f7", "f8-ff", | ||
}}, | ||
} | ||
for _, tc := range tcs { | ||
bn := []string{} | ||
bins := GenerateHashBins(tc.Log2NumBins) | ||
for _, b := range bins { | ||
bn = append(bn, b.Name("", HashPrefixLength(tc.Log2NumBins))) | ||
} | ||
assert.Equal(t, tc.BinNames, bn, "GenerateHashBins(%v)", tc.Log2NumBins) | ||
} | ||
} |