trie/trie_test.go
363 lines
1package trie23import (4"bytes"5"crypto/sha256"6"encoding/binary"7"math/rand"8"sort"9"testing"10)1112func k(b ...byte) Key {13var key Key14copy(key[:], b)15return key16}1718func mustInsert(t *testing.T, tr *Trie, key Key, v []byte) *Trie {19t.Helper()20n, err := tr.Insert(key, v)21if err != nil {22t.Fatalf("insert %x: %v", key[:2], err)23}24return n25}2627func TestEmptyRoot(t *testing.T) {28if NewStorage().Root() != (Hash{}) || NewPlain().Root() != (Hash{}) {29t.Fatal("empty root must be the zero hash")30}31if NewStorage().Count() != 0 {32t.Fatal("empty count")33}34}3536func TestSingleLeafRootIsLeafHash(t *testing.T) {37key, val := k(0xAB), []byte("value")38tr := mustInsert(t, NewStorage(), key, val)39want := sha256.Sum256(append(append([]byte{}, key[:]...), val...))40if tr.Root() != Hash(want) {41t.Fatal("single leaf root must equal SHA256(key ‖ value)")42}43}4445// Two keys diverging at bit 0: the root is a branch at position 0 with the46// smaller key on the left. Checked by hand against the hash formula, with47// and without the count aggregate.48func TestTwoLeafBranchHash(t *testing.T) {49a, b := k(0x00, 0x01), k(0x80, 0x02)50va, vb := []byte("a"), []byte("b")51la, lb := LeafHash(a, va), LeafHash(b, vb)5253for _, counted := range []bool{true, false} {54tr := NewPlain()55if counted {56tr = NewStorage()57}58tr = mustInsert(t, mustInsert(t, tr, b, vb), a, va)5960h := sha256.New()61h.Write([]byte{0}) // bit_position62if counted {63var c [8]byte64binary.BigEndian.PutUint64(c[:], 2)65h.Write(c[:])66}67h.Write(la[:])68h.Write(lb[:])69var want Hash70copy(want[:], h.Sum(nil))71if tr.Root() != want {72t.Fatalf("counted=%v: root mismatch", counted)73}74}75// The two flavours must not collide.76if mustInsert(t, mustInsert(t, NewStorage(), a, va), b, vb).Root() ==77mustInsert(t, mustInsert(t, NewPlain(), a, va), b, vb).Root() {78t.Fatal("storage and plain roots should differ")79}80}8182// Divergence deeper than bit 0: keys sharing the first 13 bits.83func TestDeepDivergencePosition(t *testing.T) {84a := k(0xFF, 0xF8, 0x00) // 1111 1111 1111 100085b := k(0xFF, 0xFC, 0x00) // 1111 1111 1111 1100 — differs at bit 1386tr := mustInsert(t, mustInsert(t, NewStorage(), a, []byte{1}), b, []byte{2})87br, ok := tr.root.(*branch)88if !ok || br.pos != 13 {89t.Fatalf("expected branch at bit 13, got %+v", tr.root)90}91if br.left.(*leaf).key != a || br.right.(*leaf).key != b {92t.Fatal("bit 0 goes left, bit 1 goes right")93}94}9596func TestErrors(t *testing.T) {97tr := mustInsert(t, NewStorage(), k(1), []byte{1})98if _, err := tr.Insert(k(1), []byte{9}); err != ErrExists {99t.Fatalf("duplicate insert: %v", err)100}101if _, err := tr.Update(k(2), nil); err != ErrNotFound {102t.Fatalf("update missing: %v", err)103}104if _, err := tr.Delete(k(2)); err != ErrNotFound {105t.Fatalf("delete missing: %v", err)106}107if _, err := NewStorage().Delete(k(2)); err != ErrNotFound {108t.Fatalf("delete from empty: %v", err)109}110}111112func TestPersistence(t *testing.T) {113t1 := mustInsert(t, NewStorage(), k(1), []byte{1})114r1 := t1.Root()115t2 := mustInsert(t, t1, k(2), []byte{2})116t3, err := t2.Update(k(1), []byte{7})117if err != nil {118t.Fatal(err)119}120t4, err := t3.Delete(k(2))121if err != nil {122t.Fatal(err)123}124if t1.Root() != r1 || t1.Count() != 1 || t1.Has(k(2)) {125t.Fatal("older version was mutated")126}127if v, _ := t2.Get(k(1)); !bytes.Equal(v, []byte{1}) {128t.Fatal("update leaked into older version")129}130if v, _ := t4.Get(k(1)); !bytes.Equal(v, []byte{7}) || t4.Count() != 1 {131t.Fatal("wrong content after update+delete")132}133// Same content as a fresh trie holding {1: 7}.134if t4.Root() != mustInsert(t, NewStorage(), k(1), []byte{7}).Root() {135t.Fatal("root must depend only on content")136}137}138139func randomKeys(r *rand.Rand, n int) []Key {140seen := map[Key]bool{}141keys := make([]Key, 0, n)142for len(keys) < n {143var key Key144r.Read(key[:])145if r.Intn(4) == 0 && len(keys) > 0 {146// Share a long prefix with an existing key to exercise deep splices.147base := keys[r.Intn(len(keys))]148copy(key[:], base[:r.Intn(31)+1])149}150if !seen[key] {151seen[key] = true152keys = append(keys, key)153}154}155return keys156}157158func TestOrderIndependence(t *testing.T) {159r := rand.New(rand.NewSource(1))160keys := randomKeys(r, 300)161build := func(order []int) *Trie {162tr := NewStorage()163for _, i := range order {164tr = mustInsert(t, tr, keys[i], keys[i][:4])165}166return tr167}168order := r.Perm(len(keys))169a := build(order)170r.Shuffle(len(order), func(i, j int) { order[i], order[j] = order[j], order[i] })171b := build(order)172if a.Root() != b.Root() {173t.Fatal("root depends on insertion order")174}175}176177// checkInvariants verifies branch positions strictly increase along every178// path, every branch has two children, counts add up, and keys under a179// branch agree on all bits before its position and split at it.180func checkInvariants(t *testing.T, tr *Trie) {181t.Helper()182var walk func(n node, prefix *Key, upto int) uint64183walk = func(n node, prefix *Key, upto int) uint64 {184switch x := n.(type) {185case *leaf:186for p := 0; p < upto; p++ {187if bit(&x.key, uint8(p)) != bit(prefix, uint8(p)) {188t.Fatalf("leaf disagrees with prefix at bit %d", p)189}190}191return 1192case *branch:193if x.left == nil || x.right == nil {194t.Fatal("branch with a missing child")195}196if int(x.pos) < upto {197t.Fatalf("branch position %d not increasing (parent %d)", x.pos, upto-1)198}199lk := leftmost(x.left)200rk := leftmost(x.right)201if bit(&lk, x.pos) != 0 || bit(&rk, x.pos) != 1 {202t.Fatal("children on the wrong side")203}204c := walk(x.left, &lk, int(x.pos)+1) + walk(x.right, &rk, int(x.pos)+1)205if c != x.n {206t.Fatalf("count %d, want %d", x.n, c)207}208if x.h != BranchHash(tr.counted, x.pos, x.n, x.left.hash(), x.right.hash()) {209t.Fatal("stale branch hash")210}211return c212}213return 0214}215if tr.root != nil {216lk := leftmost(tr.root)217walk(tr.root, &lk, 0)218}219}220221func leftmost(n node) Key {222for {223switch x := n.(type) {224case *leaf:225return x.key226case *branch:227n = x.left228}229}230}231232func TestRandomizedAgainstReference(t *testing.T) {233r := rand.New(rand.NewSource(42))234keys := randomKeys(r, 2000)235ref := map[Key][]byte{}236tr := NewStorage()237for _, key := range keys {238v := make([]byte, r.Intn(40))239r.Read(v)240ref[key] = v241tr = mustInsert(t, tr, key, v)242}243checkInvariants(t, tr)244compare(t, tr, ref)245246// Update a third, delete a third.247for i, key := range keys {248switch i % 3 {249case 0:250v := []byte{byte(i)}251ref[key] = v252n, err := tr.Update(key, v)253if err != nil {254t.Fatal(err)255}256tr = n257case 1:258delete(ref, key)259n, err := tr.Delete(key)260if err != nil {261t.Fatal(err)262}263tr = n264}265}266checkInvariants(t, tr)267compare(t, tr, ref)268269// Rebuild from scratch; must reach the same root.270fresh := NewStorage()271for key, v := range ref {272fresh = mustInsert(t, fresh, key, v)273}274if fresh.Root() != tr.Root() {275t.Fatal("mutated trie differs from freshly built equivalent")276}277278// Delete everything.279for key := range ref {280n, err := tr.Delete(key)281if err != nil {282t.Fatal(err)283}284tr = n285}286if tr.Root() != (Hash{}) || tr.Count() != 0 {287t.Fatal("trie not empty after deleting all")288}289}290291func compare(t *testing.T, tr *Trie, ref map[Key][]byte) {292t.Helper()293sorted := make([]Key, 0, len(ref))294for key := range ref {295sorted = append(sorted, key)296}297sort.Slice(sorted, func(i, j int) bool { return bytes.Compare(sorted[i][:], sorted[j][:]) < 0 })298299if tr.Count() != uint64(len(sorted)) {300t.Fatalf("count %d, want %d", tr.Count(), len(sorted))301}302for i, key := range sorted {303v, ok := tr.Get(key)304if !ok || !bytes.Equal(v, ref[key]) {305t.Fatalf("get %x: ok=%v", key[:4], ok)306}307idx, ok := tr.Rank(key)308if !ok || idx != uint64(i) {309t.Fatalf("rank %x: got %d ok=%v, want %d", key[:4], idx, ok, i)310}311sk, sv, ok := tr.Select(uint64(i))312if !ok || sk != key || !bytes.Equal(sv, ref[key]) {313t.Fatalf("select %d: wrong entry", i)314}315}316if _, _, ok := tr.Select(uint64(len(sorted))); ok {317t.Fatal("select past the end")318}319var absent Key320absent[0] = 0x55321if _, ok := tr.Rank(absent); ok && ref[absent] == nil {322t.Fatal("rank of absent key")323}324i := 0325tr.Walk(func(key Key, v []byte) bool {326if key != sorted[i] {327t.Fatalf("walk order broken at %d", i)328}329i++330return true331})332if i != len(sorted) {333t.Fatal("walk visited wrong number of entries")334}335}336337func TestSelectDistribution(t *testing.T) {338// Adjacent keys packed into one byte: index must equal the key value.339tr := NewStorage()340for i := 0; i < 256; i++ {341tr = mustInsert(t, tr, k(byte(i)), nil)342}343for i := 0; i < 256; i++ {344key, _, ok := tr.Select(uint64(i))345if !ok || key[0] != byte(i) {346t.Fatalf("select %d gave %d", i, key[0])347}348}349}350351func BenchmarkInsert(b *testing.B) {352r := rand.New(rand.NewSource(7))353keys := randomKeys(r, 100000)354tr := NewStorage()355b.ResetTimer()356for i := 0; i < b.N; i++ {357tr, _ = tr.Insert(keys[i%len(keys)], nil)358if i%len(keys) == len(keys)-1 {359tr = NewStorage()360}361}362}