github.com/cilium/cilium@v1.16.2/pkg/container/bitlpm/unsigned_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package bitlpm
     5  
     6  import (
     7  	"fmt"
     8  	"math/bits"
     9  	"reflect"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"testing"
    14  )
    15  
    16  type uint16Range struct {
    17  	start, end uint16
    18  }
    19  
    20  func (pr uint16Range) prefix() uint {
    21  	return prefixFromRange(pr.start, max(pr.end, pr.start))
    22  }
    23  
    24  func prefixFromRange(start, end uint16) uint {
    25  	return 16 - uint(bits.TrailingZeros16(^uint16(end-start)))
    26  }
    27  
    28  func (pr uint16Range) String() string {
    29  	return fmt.Sprintf("%d-%d", pr.start, pr.end)
    30  }
    31  
    32  var uint16RangeMap = map[uint]uint16{
    33  	0:  0b1111_1111_1111_1111,
    34  	1:  0b111_1111_1111_1111,
    35  	2:  0b11_1111_1111_1111,
    36  	3:  0b1_1111_1111_1111,
    37  	4:  0b1111_1111_1111,
    38  	5:  0b111_1111_1111,
    39  	6:  0b11_1111_1111,
    40  	7:  0b1_1111_1111,
    41  	8:  0b1111_1111,
    42  	9:  0b111_1111,
    43  	10: 0b11_1111,
    44  	11: 0b1_1111,
    45  	12: 0b1111,
    46  	13: 0b111,
    47  	14: 0b11,
    48  	15: 0b1,
    49  	16: 0,
    50  }
    51  
    52  func endFromPrefix(prefix uint, start uint16) uint16 {
    53  	return start + uint16RangeMap[prefix]
    54  }
    55  
    56  var (
    57  	uint16Range65535 = []uint16Range{
    58  		{start: 65535, end: 65535},
    59  	}
    60  	uint16Range0_65535 = []uint16Range{
    61  		{start: 0, end: 65535},
    62  	}
    63  	uint16Range1_65534 = []uint16Range{
    64  		{start: 1, end: 1},
    65  		{start: 2, end: 3},
    66  		{start: 4, end: 7},
    67  		{start: 8, end: 15},
    68  		{start: 16, end: 31},
    69  		{start: 32, end: 63},
    70  		{start: 64, end: 127},
    71  		{start: 128, end: 255},
    72  		{start: 256, end: 511},
    73  		{start: 512, end: 1023},
    74  		{start: 1024, end: 2047},
    75  		{start: 2048, end: 4095},
    76  		{start: 4096, end: 8191},
    77  		{start: 8192, end: 16383},
    78  		{start: 16384, end: 32767},
    79  		{start: 32768, end: 49151},
    80  		{start: 49152, end: 57343},
    81  		{start: 57344, end: 61439},
    82  		{start: 61440, end: 63487},
    83  		{start: 63488, end: 64511},
    84  		{start: 64512, end: 65023},
    85  		{start: 65024, end: 65279},
    86  		{start: 65280, end: 65407},
    87  		{start: 65408, end: 65471},
    88  		{start: 65472, end: 65503},
    89  		{start: 65504, end: 65519},
    90  		{start: 65520, end: 65527},
    91  		{start: 65528, end: 65531},
    92  		{start: 65532, end: 65533},
    93  		{start: 65534, end: 65534},
    94  	}
    95  	uint16Range0_1023 = []uint16Range{
    96  		{start: 0, end: 1023},
    97  	}
    98  	uint16Range1_1023 = []uint16Range{
    99  		{start: 1, end: 1},
   100  		{start: 2, end: 3},
   101  		{start: 4, end: 7},
   102  		{start: 8, end: 15},
   103  		{start: 16, end: 31},
   104  		{start: 32, end: 63},
   105  		{start: 64, end: 127},
   106  		{start: 128, end: 255},
   107  		{start: 256, end: 511},
   108  		{start: 512, end: 1023},
   109  	}
   110  	uint16Range0_7 = []uint16Range{
   111  		{start: 0, end: 7},
   112  	}
   113  	uint16Range1_7 = []uint16Range{
   114  		{start: 1, end: 1},
   115  		{start: 2, end: 3},
   116  		{start: 4, end: 7},
   117  	}
   118  	uint16Range0_1 = []uint16Range{
   119  		{start: 0, end: 1},
   120  	}
   121  	uint16Range1_1 = []uint16Range{
   122  		{start: 1, end: 1},
   123  	}
   124  )
   125  
   126  // TestUnsignedUpsert tests to see that a trie contains
   127  // all the values it should after every update.
   128  func TestUnsignedUpsert(t *testing.T) {
   129  	tests := []struct {
   130  		name   string
   131  		ranges []uint16Range
   132  	}{
   133  		{
   134  			ranges: uint16Range65535,
   135  		},
   136  		{
   137  			name:   " least entries for largest range",
   138  			ranges: uint16Range0_65535,
   139  		},
   140  		{
   141  			name:   " most entries for largest range",
   142  			ranges: uint16Range1_65534,
   143  		},
   144  		{
   145  			ranges: uint16Range0_1023,
   146  		},
   147  		{
   148  			ranges: uint16Range1_1023,
   149  		},
   150  		{
   151  			ranges: uint16Range0_7,
   152  		},
   153  		{
   154  			ranges: uint16Range1_7,
   155  		},
   156  		{
   157  			ranges: uint16Range0_1,
   158  		},
   159  		{
   160  			ranges: uint16Range1_1,
   161  		},
   162  	}
   163  	for _, tt := range tests {
   164  		name := fmt.Sprintf("%d_%d%s", tt.ranges[0].start,
   165  			tt.ranges[len(tt.ranges)-1].end, tt.name)
   166  		// Check that the whole trie is what it should be
   167  		// on each update.
   168  		t.Run(name, func(t *testing.T) {
   169  			ut := NewUintTrie[uint16, string]()
   170  			for i, pr := range tt.ranges {
   171  				ut.Upsert(pr.prefix(), pr.start, fmt.Sprintf("%d-%d", pr.start, pr.end))
   172  				var got []uint16Range
   173  				ut.ForEach(func(prefix uint, key uint16, value string) bool {
   174  					got = append(got, uint16Range{start: key, end: endFromPrefix(prefix, key)})
   175  					return true
   176  				})
   177  				sort.Slice(got, func(i, j int) bool {
   178  					return got[i].start < got[j].start
   179  				})
   180  				if !reflect.DeepEqual(got, tt.ranges[:i+1]) {
   181  					t.Fatalf("When updating an unsigned trie with the key-prefix %d/%d: got %+v, but expected %+v", pr.start, pr.prefix(), got, tt.ranges[:i+1])
   182  				}
   183  			}
   184  		})
   185  	}
   186  }
   187  
   188  // TestUnsignedExactLookup looks up every entry expressed
   189  // in a trie structure to ensure that exact lookup only returns
   190  // on when keys match exactly.
   191  func TestUnsignedExactLookup(t *testing.T) {
   192  	tests := []struct {
   193  		name   string
   194  		ranges []uint16Range
   195  	}{
   196  		{
   197  			ranges: uint16Range65535,
   198  		},
   199  		{
   200  			name:   " least entries for largest range",
   201  			ranges: uint16Range0_65535,
   202  		},
   203  		{
   204  			name:   " most entries for largest range",
   205  			ranges: uint16Range1_65534,
   206  		},
   207  		{
   208  			ranges: uint16Range0_1023,
   209  		},
   210  		{
   211  			ranges: uint16Range1_1023,
   212  		},
   213  		{
   214  			ranges: uint16Range0_7,
   215  		},
   216  		{
   217  			ranges: uint16Range1_7,
   218  		},
   219  		{
   220  			ranges: uint16Range0_1,
   221  		},
   222  		{
   223  			ranges: uint16Range1_1,
   224  		},
   225  	}
   226  	for _, tt := range tests {
   227  		firstRange := tt.ranges[0]
   228  		lastRange := tt.ranges[len(tt.ranges)-1]
   229  		name := fmt.Sprintf("%d_%d%s", firstRange.start, lastRange.end, tt.name)
   230  		// Check that every valid key returns the correct
   231  		// entry and every invalid key returns nothing.
   232  		t.Run(name, func(t *testing.T) {
   233  			ut := NewUintTrie[uint16, string]()
   234  			for _, pr := range tt.ranges {
   235  				ut.Upsert(pr.prefix(), pr.start, fmt.Sprintf("%d-%d", pr.start, pr.end))
   236  			}
   237  			for _, pr := range tt.ranges {
   238  				entry := fmt.Sprintf("%d-%d", pr.start, pr.end)
   239  				pref := pr.prefix()
   240  				// check if one-less than an exact prefix returns anything
   241  				if pref > 0 {
   242  					_, ok := ut.ExactLookup(pref-1, pr.start)
   243  					if ok {
   244  						t.Fatalf("ExactLookup returned a non-existent key-entry for prefix (%d), key (%d)", pr.prefix()-1, pr.start)
   245  					}
   246  				}
   247  				// check if one-more than an exact prefix returns anything
   248  				if pref < 16 {
   249  					_, ok := ut.ExactLookup(pref+1, pr.start)
   250  					if ok {
   251  						t.Fatalf("ExactLookup returned a non-existent key-entry for prefix (%d), key (%d)", pr.prefix()+1, pr.start)
   252  					}
   253  				}
   254  				// check if an exact lookup works
   255  				got, ok := ut.ExactLookup(pr.prefix(), pr.start)
   256  				if !ok || got != entry {
   257  					t.Fatalf("ExactLookup did not return the expected prefix (%d), key (%d); got %s", pr.prefix(), pr.start, got)
   258  				}
   259  			}
   260  		})
   261  	}
   262  }
   263  
   264  // TestUnsignedLongestPrefixMatch looks up every possible value expressed
   265  // in a trie structure by the most specific prefix.
   266  func TestUnsignedLongestPrefixMatch(t *testing.T) {
   267  	tests := []struct {
   268  		name   string
   269  		ranges []uint16Range
   270  	}{
   271  		{
   272  			ranges: uint16Range65535,
   273  		},
   274  		{
   275  			name:   " least entries for largest range",
   276  			ranges: uint16Range0_65535,
   277  		},
   278  		{
   279  			name:   " most entries for largest range",
   280  			ranges: uint16Range1_65534,
   281  		},
   282  		{
   283  			ranges: uint16Range0_1023,
   284  		},
   285  		{
   286  			ranges: uint16Range1_1023,
   287  		},
   288  		{
   289  			ranges: uint16Range0_7,
   290  		},
   291  		{
   292  			ranges: uint16Range1_7,
   293  		},
   294  		{
   295  			ranges: uint16Range0_1,
   296  		},
   297  		{
   298  			ranges: uint16Range1_1,
   299  		},
   300  	}
   301  	for _, tt := range tests {
   302  		firstRange := tt.ranges[0]
   303  		lastRange := tt.ranges[len(tt.ranges)-1]
   304  		name := fmt.Sprintf("%d_%d%s", firstRange.start, lastRange.end, tt.name)
   305  		// Check that every valid key returns the correct
   306  		// entry and every invalid key returns nothing.
   307  		t.Run(name, func(t *testing.T) {
   308  			ut := NewUintTrie[uint16, string]()
   309  			for _, pr := range tt.ranges {
   310  				ut.Upsert(pr.prefix(), pr.start, fmt.Sprintf("%d-%d", pr.start, pr.end))
   311  			}
   312  			for _, pr := range tt.ranges {
   313  				entry := fmt.Sprintf("%d-%d", pr.start, pr.end)
   314  				start := pr.start
   315  				end := pr.end
   316  				// uint16 should be converted to uint for the
   317  				// purpose of the loop condition as some tests
   318  				// overflow uint16 causing an infinite loop.
   319  				for p := uint(start); p <= uint(end); p++ {
   320  					got, _ := ut.LongestPrefixMatch(uint16(p))
   321  					if entry != got {
   322  						t.Fatalf("Looking up key %d, expected entry %q, but got %q", p, entry, got)
   323  					}
   324  				}
   325  			}
   326  			// look up all the missing keys.
   327  			start := firstRange.start
   328  			end := lastRange.end
   329  			for p := uint(0); p < uint(start); p++ {
   330  				got, ok := ut.LongestPrefixMatch(uint16(p))
   331  				if ok {
   332  					t.Fatalf("Looking up key %d, expected no entry, but got %q", p, got)
   333  				}
   334  			}
   335  			for p := uint(end) + 1; p <= uint(65535); p++ {
   336  				got, ok := ut.LongestPrefixMatch(uint16(p))
   337  				if ok {
   338  					t.Fatalf("Looking up key %d, expected no entry, but got %q", p, got)
   339  				}
   340  			}
   341  		})
   342  	}
   343  }
   344  
   345  // TestUnsignedAncestorsRange tests looking up keys with
   346  // a non-full prefix (i.e. a range of keys), by creating tries
   347  // in different ranges and ensuring that the trie returns
   348  // in-range queries from other known in-range lookups, and that
   349  // known out-of-range lookups fail.
   350  func TestUnsignedAncestorsRange(t *testing.T) {
   351  	ranges := [][]uint16Range{
   352  		uint16Range65535,
   353  		uint16Range0_65535,
   354  		uint16Range1_65534,
   355  		uint16Range0_1023,
   356  		uint16Range1_1023,
   357  		uint16Range0_7,
   358  		uint16Range1_7,
   359  		uint16Range0_1,
   360  		uint16Range1_1,
   361  	}
   362  	// eliminate duplicate range lookups
   363  	rangeLookupMap := make(map[string]uint16Range)
   364  	for _, r := range ranges {
   365  		for _, pr := range r {
   366  			entry := fmt.Sprintf("%d-%d", pr.start, pr.end)
   367  			if _, ok := rangeLookupMap[entry]; !ok {
   368  				rangeLookupMap[entry] = pr
   369  			}
   370  		}
   371  	}
   372  	for _, r := range ranges {
   373  		rangeStart := r[0].start
   374  		rangeEnd := r[len(r)-1].end
   375  		name := fmt.Sprintf("%d_%d", rangeStart, rangeEnd)
   376  		t.Run(name, func(t *testing.T) {
   377  			tu := NewUintTrie[uint16, string]()
   378  			for _, pr := range r {
   379  				entry := fmt.Sprintf("%d-%d", pr.start, pr.end)
   380  				tu.Upsert(pr.prefix(), pr.start, entry)
   381  			}
   382  			for _, pr := range rangeLookupMap {
   383  				var gotEntry string
   384  				tu.Ancestors(pr.prefix(), pr.start, func(prefix uint, _ uint16, v string) bool {
   385  					gotEntry = v
   386  					return true
   387  				})
   388  				if pr.start < rangeStart || pr.end > rangeEnd {
   389  					if gotEntry != "" {
   390  						t.Fatalf("Expected to get an emty entry from key-prefix %d/%d, got %q",
   391  							pr.start, pr.prefix(), gotEntry)
   392  					}
   393  				} else {
   394  					if gotEntry == "" {
   395  						t.Fatalf("Expected to get an in range entry from key-prefix %d/%d, but got no entry",
   396  							pr.start, pr.prefix())
   397  					}
   398  					rangeS := strings.Split(gotEntry, "-")
   399  					start, err := strconv.ParseUint(rangeS[0], 10, 16)
   400  					if err != nil {
   401  						t.Fatalf("Error parsing start value of range entry %q", gotEntry)
   402  					}
   403  					if uint16(start) > pr.start {
   404  						t.Fatalf("Expected to get an in range entry from key-prefix %d/%d, but got %q",
   405  							pr.start, pr.prefix(), gotEntry)
   406  					}
   407  					end, err := strconv.ParseUint(rangeS[1], 10, 16)
   408  					if err != nil {
   409  						t.Fatalf("Error parsing end value of range entry %q", gotEntry)
   410  					}
   411  					if uint16(end) < pr.end {
   412  						t.Fatalf("Expected to get an in range entry from key-prefix %d/%d, but got %q",
   413  							pr.start, pr.prefix(), gotEntry)
   414  					}
   415  				}
   416  			}
   417  		})
   418  	}
   419  }
   420  
   421  // TestUnsignedAncestors tests searching for all keys
   422  // that match a searched-for key and prefix.
   423  func TestUnsignedAncestors(t *testing.T) {
   424  	// Create a uint Trie that contains overlapping ranges
   425  	// from 0-65535.
   426  	tu := NewUintTrie[uint16, string]()
   427  	for i := uint(0); i < 16; i++ {
   428  		rng := uint16RangeMap[i]
   429  		entry := fmt.Sprintf("%d-%d", 0, rng)
   430  		tu.Upsert(i, rng, entry)
   431  	}
   432  	// Check to see that each range
   433  	// lookup returns all ranges that contain
   434  	// it.
   435  	for i := uint(0); i < 16; i++ {
   436  		rng := uint16RangeMap[i]
   437  		entry := fmt.Sprintf("%d-%d", 0, rng)
   438  		t.Run(entry, func(t *testing.T) {
   439  			expectedRes := make([]string, 0, i+1)
   440  			for t := uint(0); t <= i; t++ {
   441  				rng2 := uint16RangeMap[t]
   442  				expectedRes = append(expectedRes, fmt.Sprintf("%d-%d", 0, rng2))
   443  			}
   444  			gotRes := make([]string, 0, i+1)
   445  			tu.Ancestors(i, rng, func(prefix uint, key uint16, v string) bool {
   446  				gotRes = append(gotRes, v)
   447  				return true
   448  			})
   449  			if !reflect.DeepEqual(expectedRes, gotRes) {
   450  				t.Fatalf("Ancestors range %s, expected to get %v, but got: %v", entry, expectedRes, gotRes)
   451  			}
   452  		})
   453  	}
   454  }
   455  
   456  // TestUnsignedDescendants tests searching for all keys
   457  // that are match by a searched-for key and prefix.
   458  func TestUnsignedDescendants(t *testing.T) {
   459  	// Create a uint Trie that contains overlapping ranges
   460  	// from 0-65535.
   461  	tu := NewUintTrie[uint16, string]()
   462  	for i := uint(0); i <= 16; i++ {
   463  		rng := uint16RangeMap[i]
   464  		entry := fmt.Sprintf("%d-%d", 0, rng)
   465  		tu.Upsert(i, rng, entry)
   466  	}
   467  	// Check to see that each range lookup returns
   468  	// all ranges that contain it.
   469  	for i := uint(0); i < 16; i++ {
   470  		rng := uint16RangeMap[i]
   471  		entry := fmt.Sprintf("%d-%d", 0, rng)
   472  		t.Run(entry, func(t *testing.T) {
   473  			expectedRes := make([]string, 0, 16-i)
   474  			for t := i; t <= 16; t++ {
   475  				rng2 := uint16RangeMap[t]
   476  				expectedRes = append(expectedRes, fmt.Sprintf("%d-%d", 0, rng2))
   477  			}
   478  			gotRes := make([]string, 0, 16-i)
   479  			tu.Descendants(i, rng, func(prefix uint, key uint16, v string) bool {
   480  				gotRes = append(gotRes, v)
   481  				return true
   482  			})
   483  			if !reflect.DeepEqual(expectedRes, gotRes) {
   484  				t.Fatalf("Descendants range %s, expected to get %v, but got: %v", entry, expectedRes, gotRes)
   485  			}
   486  			// It should still work even if the entry is not present
   487  			tu.Delete(i, rng)
   488  			expectedRes = expectedRes[1:]
   489  			gotRes = make([]string, 0, 16-i-1)
   490  			tu.Descendants(i, rng, func(prefix uint, key uint16, v string) bool {
   491  				gotRes = append(gotRes, v)
   492  				return true
   493  			})
   494  			if !reflect.DeepEqual(expectedRes, gotRes) {
   495  				t.Fatalf("Descendants range %s, expected to get %v, but got: %v", entry, expectedRes, gotRes)
   496  			}
   497  		})
   498  	}
   499  }
   500  
   501  // TestUnsignedDelete creates a trie from a set of ranges
   502  // and then incrementally deletes each entry, checking
   503  // that the trie contains all the values it should after
   504  // each delete. It checks deleting the trie both
   505  // from the bottom of a range up, and the top of the range
   506  // down.
   507  func TestUnsignedDelete(t *testing.T) {
   508  	tests := []struct {
   509  		name   string
   510  		ranges []uint16Range
   511  	}{
   512  		{
   513  			ranges: uint16Range65535,
   514  		},
   515  		{
   516  			name:   " least entries for largest range",
   517  			ranges: uint16Range0_65535,
   518  		},
   519  		{
   520  			name:   " most entries for largest range",
   521  			ranges: uint16Range1_65534,
   522  		},
   523  		{
   524  			ranges: uint16Range0_1023,
   525  		},
   526  		{
   527  			ranges: uint16Range1_1023,
   528  		},
   529  		{
   530  			ranges: uint16Range0_7,
   531  		},
   532  		{
   533  			ranges: uint16Range1_7,
   534  		},
   535  		{
   536  			ranges: uint16Range0_1,
   537  		},
   538  		{
   539  			ranges: uint16Range1_1,
   540  		},
   541  	}
   542  	for _, tt := range tests {
   543  		name := fmt.Sprintf("%d_%d%s", tt.ranges[0].start,
   544  			tt.ranges[len(tt.ranges)-1].end, tt.name)
   545  		// Check that the whole trie is what it should be
   546  		// on each deletion in order.
   547  		t.Run(name, func(t *testing.T) {
   548  			ut := NewUintTrie[uint16, string]()
   549  			for _, pr := range tt.ranges {
   550  				ut.Upsert(pr.prefix(), pr.start, fmt.Sprintf("%d-%d", pr.start, pr.end))
   551  			}
   552  			for i, pr := range tt.ranges {
   553  				// The "got" slice cannot be nil for the DeepEqual
   554  				// comparison, even if it is empty.
   555  				got := make([]uint16Range, 0, len(tt.ranges)-i-1)
   556  				ok := ut.Delete(pr.prefix(), pr.start)
   557  				if !ok {
   558  					t.Fatalf("Key-prefix %d/%d not deleted", pr.start, pr.prefix())
   559  				}
   560  				ut.ForEach(func(prefix uint, key uint16, value string) bool {
   561  					got = append(got, uint16Range{start: key, end: endFromPrefix(prefix, key)})
   562  					return true
   563  				})
   564  				sort.Slice(got, func(i, j int) bool {
   565  					return got[i].start < got[j].start
   566  				})
   567  				if !reflect.DeepEqual(got, tt.ranges[i+1:]) {
   568  					t.Fatalf("When deleting an entry from an unsigned trie with the key-prefix %d/%d: got %+v, but expected %+v", pr.start, pr.prefix(), got, tt.ranges[i+1:])
   569  				}
   570  			}
   571  		})
   572  		// Delete in reverse order.
   573  		t.Run(fmt.Sprintf("In_Reverse_%s", name), func(t *testing.T) {
   574  			ut := NewUintTrie[uint16, string]()
   575  			for _, pr := range tt.ranges {
   576  				ut.Upsert(pr.prefix(), pr.start, fmt.Sprintf("%d-%d", pr.start, pr.end))
   577  			}
   578  			for i := len(tt.ranges) - 1; i >= 0; i-- {
   579  				pr := tt.ranges[i]
   580  				// The "got" slice cannot be nil for the DeepEqual
   581  				// comparison, even if it is empty.
   582  				got := make([]uint16Range, 0, i+1)
   583  				ok := ut.Delete(pr.prefix(), pr.start)
   584  				if !ok {
   585  					t.Fatalf("Key-prefix %d/%d not deleted", pr.start, pr.prefix())
   586  				}
   587  				ut.ForEach(func(prefix uint, key uint16, value string) bool {
   588  					got = append(got, uint16Range{start: key, end: endFromPrefix(prefix, key)})
   589  					return true
   590  				})
   591  				sort.Slice(got, func(i, j int) bool {
   592  					return got[i].start < got[j].start
   593  				})
   594  				if !reflect.DeepEqual(got, tt.ranges[:i]) {
   595  					t.Fatalf("When deleting an entry from an unsigned trie with the key-prefix %d/%d: got %+v, but expected %+v", pr.start, pr.prefix(), got, tt.ranges[:i])
   596  				}
   597  			}
   598  		})
   599  	}
   600  }
   601  
   602  func BenchmarkTrieUpsert(b *testing.B) {
   603  	tri := NewUintTrie[uint32, struct{}]()
   604  	emptyS := struct{}{}
   605  	count := uint(0)
   606  	b.ReportAllocs()
   607  	b.ResetTimer()
   608  
   609  	// mimic adding 2 octets worth of addresses
   610  	tri.Upsert(16, 0xffff_0000, emptyS)
   611  	count++
   612  	for i := uint32(0); i < 255; i++ {
   613  		upperThree := 0xffff_0000 | i<<8
   614  		tri.Upsert(24, upperThree, emptyS)
   615  		count++
   616  		for t := uint32(0); t < 255; t++ {
   617  			tri.Upsert(32, upperThree|t, emptyS)
   618  			count++
   619  		}
   620  	}
   621  	b.StopTimer()
   622  	if tri.Len() != count {
   623  		b.Fatalf("expected count (%d) to agree with trie length (%d)", count, tri.Len())
   624  	}
   625  }
   626  
   627  func BenchmarkMapUpdate(b *testing.B) {
   628  	map32 := make(map[uint32]struct{}, 256*256)
   629  	emptyS := struct{}{}
   630  	count := 0
   631  	b.ReportAllocs()
   632  	b.ResetTimer()
   633  	// mimic adding 2 octets worth of addresses
   634  	for i := uint32(0); i < 255; i++ {
   635  		upperOct := i << 8
   636  		for t := uint32(0); t < 255; t++ {
   637  			map32[0xffff_0000|upperOct|t] = emptyS
   638  			count++
   639  		}
   640  	}
   641  	b.StopTimer()
   642  	if len(map32) != count {
   643  		b.Fatalf("expected count (%d) to agree with map length (%d)", count, len(map32))
   644  	}
   645  }
   646  
   647  func BenchmarkTrieAncestorsRange(b *testing.B) {
   648  	tri := NewUintTrie[uint32, *struct{}]()
   649  	emptyS := &struct{}{}
   650  	count := uint(0)
   651  	// mimic adding 2 octets worth of addresses
   652  	tri.Upsert(16, 0xffff_0000, emptyS)
   653  	count++
   654  	for i := uint32(0); i < 255; i++ {
   655  		upperOct := i << 8
   656  		tri.Upsert(24, 0xffff_0000|upperOct, emptyS)
   657  		count++
   658  		for t := uint32(0); t < 255; t++ {
   659  			tri.Upsert(32, 0xffff_0000|upperOct|t, emptyS)
   660  			count++
   661  		}
   662  	}
   663  	if tri.Len() != count {
   664  		b.Fatalf("expected count (%d) to agree with trie length (%d)", count, tri.Len())
   665  	}
   666  
   667  	b.ReportAllocs()
   668  	b.ResetTimer()
   669  	var st *struct{}
   670  	tri.Ancestors(16, 0xffff_0000, func(_ uint, _ uint32, v *struct{}) bool {
   671  		st = v
   672  		return true
   673  	})
   674  	if st == nil {
   675  		b.Fatal("expected valid lookup, but got nil")
   676  	}
   677  	for i := uint32(0); i < 255; i++ {
   678  		upperOct := i << 8
   679  		var st *struct{}
   680  		tri.Ancestors(24, 0xffff_0000|upperOct, func(_ uint, _ uint32, v *struct{}) bool {
   681  			st = v
   682  			return true
   683  		})
   684  		if st == nil {
   685  			b.Fatal("expected valid lookup, but got nil")
   686  		}
   687  		for t := uint32(0); t < 255; t++ {
   688  			var st *struct{}
   689  			tri.Ancestors(32, 0xffff_0000|upperOct|t, func(_ uint, _ uint32, v *struct{}) bool {
   690  				st = v
   691  				return true
   692  			})
   693  			if st == nil {
   694  				b.Fatal("expected valid lookup, but got nil")
   695  			}
   696  		}
   697  	}
   698  }
   699  
   700  func BenchmarkTrieLongestPrefixMatch(b *testing.B) {
   701  	tri := NewUintTrie[uint32, *struct{}]()
   702  	emptyS := &struct{}{}
   703  	count := uint(0)
   704  	// mimic adding 2 octets worth of addresses
   705  	for i := uint32(0); i < 255; i++ {
   706  		upperOct := i << 8
   707  		tri.Upsert(24, 0xffff_0000|upperOct, emptyS)
   708  		count++
   709  		for t := uint32(0); t < 255; t++ {
   710  			tri.Upsert(32, 0xffff_0000|upperOct|t, emptyS)
   711  			count++
   712  		}
   713  	}
   714  	if tri.Len() != count {
   715  		b.Fatalf("expected count (%d) to agree with trie length (%d)", count, tri.Len())
   716  	}
   717  
   718  	b.ReportAllocs()
   719  	b.ResetTimer()
   720  	for i := uint32(0); i < 255; i++ {
   721  		upperOct := i << 8
   722  		for t := uint32(0); t < 255; t++ {
   723  			_, ok := tri.LongestPrefixMatch(0xffff_0000 | upperOct | t)
   724  			if !ok {
   725  				b.Fatal("expected valid lookup, but got nil")
   726  			}
   727  		}
   728  	}
   729  }
   730  
   731  func BenchmarkMapLookup(b *testing.B) {
   732  	map32 := make(map[uint32]*struct{}, 256*256)
   733  	emptyS := &struct{}{}
   734  	count := 0
   735  	// mimic adding 2 octets worth of addresses
   736  	for i := uint32(0); i < 255; i++ {
   737  		upperOct := i << 8
   738  		for t := uint32(0); t < 255; t++ {
   739  			map32[0xffff_0000|upperOct|t] = emptyS
   740  			count++
   741  		}
   742  	}
   743  	if len(map32) != count {
   744  		b.Fatalf("expected count (%d) to agree with map length (%d)", count, len(map32))
   745  	}
   746  
   747  	b.ReportAllocs()
   748  	b.ResetTimer()
   749  	for i := uint32(0); i < 255; i++ {
   750  		upperOct := i << 8
   751  		for t := uint32(0); t < 255; t++ {
   752  			v, ok := map32[0xffff_0000|upperOct|t]
   753  			if !ok || v == nil {
   754  				b.Fatalf("expected to get value from map lookup, got nil")
   755  			}
   756  		}
   757  	}
   758  }
   759  
   760  func BenchmarkTrieDelete(b *testing.B) {
   761  	tri := NewUintTrie[uint32, *struct{}]()
   762  	emptyS := &struct{}{}
   763  	count := uint(0)
   764  	// mimic adding 2 octets worth of addresses
   765  	for i := uint32(0); i < 255; i++ {
   766  		upperOct := i << 8
   767  		tri.Upsert(24, 0xffff_0000|upperOct, emptyS)
   768  		count++
   769  		for t := uint32(0); t < 255; t++ {
   770  			tri.Upsert(32, 0xffff_0000|upperOct|t, emptyS)
   771  			count++
   772  		}
   773  	}
   774  	if tri.Len() != count {
   775  		b.Fatalf("expected count (%d) to agree with trie length (%d)", count, tri.Len())
   776  	}
   777  
   778  	b.ReportAllocs()
   779  	b.ResetTimer()
   780  	for i := uint32(0); i < 255; i++ {
   781  		upperOct := i << 8
   782  		if !tri.Delete(24, 0xffff_0000|upperOct) {
   783  			b.Fatal("expected valid delete, but got nil")
   784  		}
   785  		for t := uint32(0); t < 255; t++ {
   786  			if !tri.Delete(32, 0xffff_0000|upperOct|t) {
   787  				b.Fatal("expected valid lookup, but got nil")
   788  			}
   789  		}
   790  	}
   791  	b.StopTimer()
   792  	if tri.Len() != 0 {
   793  		b.Fatalf("expected Trie length of 0, but got %d", tri.Len())
   794  	}
   795  }
   796  
   797  func BenchmarkMapDelete(b *testing.B) {
   798  	map32 := make(map[uint32]*struct{}, 256*256)
   799  	emptyS := &struct{}{}
   800  	count := 0
   801  	// mimic adding 2 octets worth of addresses
   802  	for i := uint32(0); i < 255; i++ {
   803  		upperOct := i << 8
   804  		for t := uint32(0); t < 255; t++ {
   805  			map32[0xffff_0000|upperOct|t] = emptyS
   806  			count++
   807  		}
   808  	}
   809  	if len(map32) != count {
   810  		b.Fatalf("expected count (%d) to agree with map length (%d)", count, len(map32))
   811  	}
   812  
   813  	b.ReportAllocs()
   814  	b.ResetTimer()
   815  	for i := uint32(0); i < 255; i++ {
   816  		upperOct := i << 8
   817  		for t := uint32(0); t < 255; t++ {
   818  			delete(map32, 0xffff_0000|upperOct|t)
   819  		}
   820  	}
   821  	b.StopTimer()
   822  	if len(map32) != 0 {
   823  		b.Fatalf("expected map length of 0, but got %d", len(map32))
   824  	}
   825  
   826  }
   827  
   828  func mask(v, bitcnt uint8) uint8 {
   829  	m := ^(^uint8(0) >> bitcnt)
   830  	return v & m
   831  }
   832  
   833  func FuzzUint8(f *testing.F) {
   834  	// has the fuzzing engine generate a set of []uint8, which it interprets as
   835  	// a sequence of (val, prefixlen) pairs.
   836  
   837  	// Then, checks invariants
   838  
   839  	f.Add([]byte{0b1111_1111, 4})
   840  
   841  	f.Fuzz(func(t *testing.T, sequence []byte) {
   842  
   843  		type testEntry struct {
   844  			k    uint8
   845  			plen uint8
   846  			val  uint16 // a placeholder
   847  		}
   848  
   849  		tree := NewUintTrie[uint8, testEntry]()
   850  
   851  		seen := map[string]testEntry{}
   852  
   853  		// Insert every item in to the tree, recording the prefix in to a hash as well
   854  		// so we know what we've set
   855  		for i := 0; i < len(sequence)-1; i += 2 {
   856  			k := sequence[i]
   857  			prefixLen := sequence[i+1] % 8
   858  
   859  			seenk := fmt.Sprintf("%#b/%d", mask(k, prefixLen), prefixLen)
   860  
   861  			seen[seenk] = testEntry{
   862  				k:    k,
   863  				plen: prefixLen,
   864  				val:  uint16(k)<<8 + uint16(prefixLen),
   865  			}
   866  
   867  			tree.Upsert(uint(prefixLen), k, seen[seenk]) // may overwrite
   868  
   869  		}
   870  
   871  		if tree.Len() != uint(len(seen)) {
   872  			t.Errorf("unexpected length: %d (expected %d)", tree.Len(), len(seen))
   873  		}
   874  
   875  		// Now, validate
   876  		for seenK, seenV := range seen {
   877  			var val testEntry
   878  			tree.Ancestors(uint(seenV.plen), seenV.k, func(_ uint, _ uint8, v testEntry) bool {
   879  				val = v
   880  				return true
   881  			})
   882  			if val.val != seenV.val {
   883  				t.Errorf("seenKey %s: got val %#b expected %#b", seenK, val.val, seenV.val)
   884  			}
   885  		}
   886  
   887  		// Now, delete seen keys and validate
   888  		expectedLength := len(seen)
   889  		for seenK, seenV := range seen {
   890  			t.Logf("Deleting key %s", seenK)
   891  			tree.Delete(uint(seenV.plen), seenV.k)
   892  			expectedLength--
   893  
   894  			if tree.Len() != uint(expectedLength) {
   895  				t.Errorf("unexpected length: %d (expected %d)", tree.Len(), expectedLength)
   896  			}
   897  		}
   898  	})
   899  }