@@ -18,19 +18,6 @@ const (
18
18
debugStrideDelete = false
19
19
)
20
20
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
-
34
21
// strideTable is a binary tree that implements an 8-bit routing table.
35
22
//
36
23
// The leaves of the binary tree are host routes (/8s). Each parent is a
@@ -54,7 +41,9 @@ type strideTable[T any] struct {
54
41
// paper, it's hijacked through sneaky C memory trickery to store
55
42
// the refcount, but this is Go, where we don't store random bits
56
43
// 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
58
47
// children are the child tables of this table. Each child
59
48
// represents the address space within one of this table's host
60
49
// routes (/8).
@@ -112,13 +101,6 @@ func (t *strideTable[T]) getOrCreateChild(addr uint8) (child *strideTable[T], cr
112
101
return ret , false
113
102
}
114
103
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
-
122
104
// findFirstChild returns the first child strideTable in t, or nil if
123
105
// t has no children.
124
106
func (t * strideTable [T ]) findFirstChild () * strideTable [T ] {
@@ -130,73 +112,115 @@ func (t *strideTable[T]) findFirstChild() *strideTable[T] {
130
112
return nil
131
113
}
132
114
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
+
133
136
// allot updates entries whose stored prefixIndex matches oldPrefixIndex, in the
134
137
// subtree rooted at idx. Matching entries have their stored prefixIndex set to
135
138
// newPrefixIndex, and their value set to val.
136
139
//
137
140
// allot is the core of the ART algorithm, enabling efficient insertion/deletion
138
141
// 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.
144
147
return
145
148
}
146
- t .entries [idx ].value = val
147
- t .entries [idx ].prefixIndex = newPrefixIndex
149
+ t .entries [idx ] = new
148
150
if idx >= firstHostIndex {
149
151
// The entry we just updated was a host route, we're at the bottom of
150
152
// the binary tree.
151
153
return
152
154
}
153
155
// Propagate the allotment to this node's children.
154
156
left := idx << 1
155
- t .allot (left , oldPrefixIndex , newPrefixIndex , val )
157
+ t .allot (left , old , new )
156
158
right := left + 1
157
- t .allot (right , oldPrefixIndex , newPrefixIndex , val )
159
+ t .allot (right , old , new )
158
160
}
159
161
160
162
// 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 ) {
162
164
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.
173
168
t .routeRefs ++
174
169
}
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 )
175
181
return
176
182
}
177
183
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 ) {
182
187
idx := prefixIndex (addr , prefixLen )
183
- recordedIdx := t .entries [idx ].prefixIndex
184
- if recordedIdx != idx {
188
+ if ! t .hasPrefixRootedAt (idx ) {
185
189
// Route entry doesn't exist
186
- return nil
190
+ return false
187
191
}
188
- val := t .entries [idx ].value
189
192
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 )
192
200
t .routeRefs --
193
- return val
201
+ return true
194
202
}
195
203
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
200
224
}
201
225
202
226
// TableDebugString returns the contents of t, formatted as a table with one
@@ -208,10 +232,10 @@ func (t *strideTable[T]) tableDebugString() string {
208
232
continue
209
233
}
210
234
v := "(nil)"
211
- if ent . value != nil {
212
- v = fmt .Sprint (* ent . value )
235
+ if ent != nil {
236
+ v = fmt .Sprint (* ent )
213
237
}
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 )
215
239
}
216
240
return ret .String ()
217
241
}
@@ -227,8 +251,8 @@ func (t *strideTable[T]) treeDebugString() string {
227
251
228
252
func (t * strideTable [T ]) treeDebugStringRec (w io.Writer , idx , indent int ) {
229
253
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 ])
232
256
indent += 2
233
257
}
234
258
if idx >= firstHostIndex {
@@ -251,6 +275,12 @@ func prefixIndex(addr uint8, prefixLen int) int {
251
275
return (int (addr ) >> (8 - prefixLen )) + (1 << prefixLen )
252
276
}
253
277
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
+
254
284
// hostIndex returns the array index of the host route for addr.
255
285
// It is equivalent to prefixIndex(addr, 8).
256
286
func hostIndex (addr uint8 ) int {
0 commit comments