@@ -18,19 +18,6 @@ const (
1818 debugStrideDelete = false
1919)
2020
21- // strideEntry is a strideTable entry.
22- type strideEntry [T any ] struct {
23- // prefixIndex is the prefixIndex(...) value that caused this stride entry's
24- // value to be populated, or 0 if value is nil.
25- //
26- // We need to keep track of this because allot() uses it to determine
27- // whether an entry was propagated from a parent entry, or if it's a
28- // different independent route.
29- prefixIndex int
30- // value is the value associated with the strideEntry, if any.
31- value * T
32- }
33-
3421// strideTable is a binary tree that implements an 8-bit routing table.
3522//
3623// The leaves of the binary tree are host routes (/8s). Each parent is a
@@ -54,7 +41,9 @@ type strideTable[T any] struct {
5441 // paper, it's hijacked through sneaky C memory trickery to store
5542 // the refcount, but this is Go, where we don't store random bits
5643 // in pointers lest we confuse the GC)
57- entries [lastHostIndex + 1 ]strideEntry [T ]
44+ //
45+ // A nil value means no route matches the queried route.
46+ entries [lastHostIndex + 1 ]* T
5847 // children are the child tables of this table. Each child
5948 // represents the address space within one of this table's host
6049 // routes (/8).
@@ -112,13 +101,6 @@ func (t *strideTable[T]) getOrCreateChild(addr uint8) (child *strideTable[T], cr
112101 return ret , false
113102}
114103
115- // getValAndChild returns both the prefix and child strideTable for
116- // addr. Both returned values can be nil if no entry of that type
117- // exists for addr.
118- func (t * strideTable [T ]) getValAndChild (addr uint8 ) (* T , * strideTable [T ]) {
119- return t .entries [hostIndex (addr )].value , t .children [addr ]
120- }
121-
122104// findFirstChild returns the first child strideTable in t, or nil if
123105// t has no children.
124106func (t * strideTable [T ]) findFirstChild () * strideTable [T ] {
@@ -130,73 +112,115 @@ func (t *strideTable[T]) findFirstChild() *strideTable[T] {
130112 return nil
131113}
132114
115+ // hasPrefixRootedAt reports whether t.entries[idx] is the root node of
116+ // a prefix.
117+ func (t * strideTable [T ]) hasPrefixRootedAt (idx int ) bool {
118+ val := t .entries [idx ]
119+ if val == nil {
120+ return false
121+ }
122+
123+ parentIdx := parentIndex (idx )
124+ if parentIdx == 0 {
125+ // idx is non-nil, and is at the 0/0 route position.
126+ return true
127+ }
128+ if parent := t .entries [parentIdx ]; val != parent {
129+ // parent node in the tree isn't the same prefix, so idx must
130+ // be a root.
131+ return true
132+ }
133+ return false
134+ }
135+
133136// allot updates entries whose stored prefixIndex matches oldPrefixIndex, in the
134137// subtree rooted at idx. Matching entries have their stored prefixIndex set to
135138// newPrefixIndex, and their value set to val.
136139//
137140// allot is the core of the ART algorithm, enabling efficient insertion/deletion
138141// while preserving very fast lookups.
139- func (t * strideTable [T ]) allot (idx int , oldPrefixIndex , newPrefixIndex int , val * T ) {
140- if t .entries [idx ]. prefixIndex != oldPrefixIndex {
141- // current prefixIndex isn't what we expect. This is a recursive call
142- // that found a child subtree that already has a more specific route
143- // installed. Don't touch it.
142+ func (t * strideTable [T ]) allot (idx int , old , new * T ) {
143+ if t .entries [idx ] != old {
144+ // current idx isn't what we expect. This is a recursive call
145+ // that found a child subtree that already has a more specific
146+ // route installed. Don't touch it.
144147 return
145148 }
146- t .entries [idx ].value = val
147- t .entries [idx ].prefixIndex = newPrefixIndex
149+ t .entries [idx ] = new
148150 if idx >= firstHostIndex {
149151 // The entry we just updated was a host route, we're at the bottom of
150152 // the binary tree.
151153 return
152154 }
153155 // Propagate the allotment to this node's children.
154156 left := idx << 1
155- t .allot (left , oldPrefixIndex , newPrefixIndex , val )
157+ t .allot (left , old , new )
156158 right := left + 1
157- t .allot (right , oldPrefixIndex , newPrefixIndex , val )
159+ t .allot (right , old , new )
158160}
159161
160162// insert adds the route addr/prefixLen to t, with value val.
161- func (t * strideTable [T ]) insert (addr uint8 , prefixLen int , val * T ) {
163+ func (t * strideTable [T ]) insert (addr uint8 , prefixLen int , val T ) {
162164 idx := prefixIndex (addr , prefixLen )
163- old := t .entries [idx ].value
164- oldIdx := t .entries [idx ].prefixIndex
165- if oldIdx == idx && old == val {
166- // This exact prefix+value is already in the table.
167- return
168- }
169- t .allot (idx , oldIdx , idx , val )
170- if oldIdx != idx {
171- // This route entry was freshly created (not just updated), that's a new
172- // reference.
165+ if ! t .hasPrefixRootedAt (idx ) {
166+ // This route entry is being freshly created (not just
167+ // updated), that's a new reference.
173168 t .routeRefs ++
174169 }
170+
171+ old := t .entries [idx ]
172+
173+ // For allot to work correctly, each distinct prefix in the
174+ // strideTable must have a different value pointer, even if val is
175+ // identical. This new()+assignment guarantees that each inserted
176+ // prefix gets a unique address.
177+ p := new (T )
178+ * p = val
179+
180+ t .allot (idx , old , p )
175181 return
176182}
177183
178- // delete removes the route addr/prefixLen from t. Returns the value
179- // that was associated with the deleted prefix, or nil if the prefix
180- // wasn't in the strideTable.
181- func (t * strideTable [T ]) delete (addr uint8 , prefixLen int ) * T {
184+ // delete removes the route addr/prefixLen from t. Reports whether the
185+ // prefix existed in the table prior to deletion.
186+ func (t * strideTable [T ]) delete (addr uint8 , prefixLen int ) (wasPresent bool ) {
182187 idx := prefixIndex (addr , prefixLen )
183- recordedIdx := t .entries [idx ].prefixIndex
184- if recordedIdx != idx {
188+ if ! t .hasPrefixRootedAt (idx ) {
185189 // Route entry doesn't exist
186- return nil
190+ return false
187191 }
188- val := t .entries [idx ].value
189192
190- parentIdx := idx >> 1
191- t .allot (idx , idx , t .entries [parentIdx ].prefixIndex , t .entries [parentIdx ].value )
193+ val := t .entries [idx ]
194+ var parentVal * T
195+ if parentIdx := parentIndex (idx ); parentIdx != 0 {
196+ parentVal = t .entries [parentIdx ]
197+ }
198+
199+ t .allot (idx , val , parentVal )
192200 t .routeRefs --
193- return val
201+ return true
194202}
195203
196- // get does a route lookup for addr and returns the associated value, or nil if
197- // no route matched.
198- func (t * strideTable [T ]) get (addr uint8 ) * T {
199- return t .entries [hostIndex (addr )].value
204+ // get does a route lookup for addr and (value, true) if a matching
205+ // route exists, or (zero, false) otherwise.
206+ func (t * strideTable [T ]) get (addr uint8 ) (ret T , ok bool ) {
207+ if val := t .entries [hostIndex (addr )]; val != nil {
208+ return * val , true
209+ }
210+ return ret , false
211+ }
212+
213+ // getValAndChild returns both the prefix value and child strideTable
214+ // for addr. valOK reports whether a prefix value exists for addr, and
215+ // child is non-nil if a child exists for addr.
216+ func (t * strideTable [T ]) getValAndChild (addr uint8 ) (val T , valOK bool , child * strideTable [T ]) {
217+ vp := t .entries [hostIndex (addr )]
218+ if vp != nil {
219+ val = * vp
220+ valOK = true
221+ }
222+ child = t .children [addr ]
223+ return
200224}
201225
202226// TableDebugString returns the contents of t, formatted as a table with one
@@ -208,10 +232,10 @@ func (t *strideTable[T]) tableDebugString() string {
208232 continue
209233 }
210234 v := "(nil)"
211- if ent . value != nil {
212- v = fmt .Sprint (* ent . value )
235+ if ent != nil {
236+ v = fmt .Sprint (* ent )
213237 }
214- fmt .Fprintf (& ret , "idx=%3d (%s), parent=%3d (%s), val=%v\n " , i , formatPrefixTable (inversePrefixIndex (i )), ent . prefixIndex , formatPrefixTable ( inversePrefixIndex (( ent . prefixIndex ) )), v )
238+ fmt .Fprintf (& ret , "idx=%3d (%s), val=%v\n " , i , formatPrefixTable (inversePrefixIndex (i )), v )
215239 }
216240 return ret .String ()
217241}
@@ -227,8 +251,8 @@ func (t *strideTable[T]) treeDebugString() string {
227251
228252func (t * strideTable [T ]) treeDebugStringRec (w io.Writer , idx , indent int ) {
229253 addr , len := inversePrefixIndex (idx )
230- if t .entries [ idx ]. prefixIndex != 0 && t . entries [ idx ]. prefixIndex == idx {
231- fmt .Fprintf (w , "%s%d/%d (%02x/%d) = %v\n " , strings .Repeat (" " , indent ), addr , len , addr , len , * t .entries [idx ]. value )
254+ if t .hasPrefixRootedAt ( idx ) {
255+ fmt .Fprintf (w , "%s%d/%d (%02x/%d) = %v\n " , strings .Repeat (" " , indent ), addr , len , addr , len , * t .entries [idx ])
232256 indent += 2
233257 }
234258 if idx >= firstHostIndex {
@@ -251,6 +275,12 @@ func prefixIndex(addr uint8, prefixLen int) int {
251275 return (int (addr ) >> (8 - prefixLen )) + (1 << prefixLen )
252276}
253277
278+ // parentIndex returns the index of idx's parent prefix, or 0 if idx
279+ // is the index of 0/0.
280+ func parentIndex (idx int ) int {
281+ return idx >> 1
282+ }
283+
254284// hostIndex returns the array index of the host route for addr.
255285// It is equivalent to prefixIndex(addr, 8).
256286func hostIndex (addr uint8 ) int {
0 commit comments