|
1 | 1 | package keyspace
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "slices" |
| 5 | + |
4 | 6 | "github.com/libp2p/go-libp2p/core/peer"
|
5 | 7 | mh "github.com/multiformats/go-multihash"
|
6 | 8 |
|
@@ -185,46 +187,92 @@ func pruneSubtrieAtDepth[K0 kad.Key[K0], K1 kad.Key[K1], D any](t *trie.Trie[K0,
|
185 | 187 | }
|
186 | 188 |
|
187 | 189 | // TrieGaps returns all prefixes that aren't covered by a key (prefix) in the
|
188 |
| -// trie. Combining the prefixes included in the trie with the gap prefixes |
189 |
| -// results in a full keyspace coverage. |
| 190 | +// trie, at the `target` location. Combining the prefixes included in the trie |
| 191 | +// with the gap prefixes results in a full keyspace coverage of the `target` |
| 192 | +// subtrie. |
| 193 | +// |
| 194 | +// Results are sorted according to the provided `order`. |
190 | 195 | //
|
191 |
| -// E.g Trie: ["00", "100"], GapsInTrie: ["01", "101", "11"] |
192 |
| -func TrieGaps[D any](t *trie.Trie[bitstr.Key, D]) []bitstr.Key { |
| 196 | +// Example: |
| 197 | +// - Trie: ["0000", "0010", "100"] |
| 198 | +// - Target: "0" |
| 199 | +// - Order: "0000", |
| 200 | +// - GapsInTrie: ["0001", "0011", "01"] |
| 201 | +func TrieGaps[K kad.Key[K], D any](t *trie.Trie[bitstr.Key, D], target bitstr.Key, order K) []bitstr.Key { |
193 | 202 | if t.IsLeaf() {
|
194 |
| - if t.HasKey() { |
195 |
| - return SiblingPrefixes(*t.Key()) |
| 203 | + if k := t.Key(); k != nil { |
| 204 | + if IsBitstrPrefix(target, *k) { |
| 205 | + siblingPrefixes := SiblingPrefixes(*k)[len(target):] |
| 206 | + sortBitstrKeysByOrder(siblingPrefixes, order) |
| 207 | + return siblingPrefixes |
| 208 | + } |
| 209 | + if IsBitstrPrefix(*k, target) { |
| 210 | + // The only key in the trie is a prefix of target, meaning the whole |
| 211 | + // target is covered. |
| 212 | + return nil |
| 213 | + } |
196 | 214 | }
|
197 |
| - return []bitstr.Key{""} |
| 215 | + return []bitstr.Key{target} |
198 | 216 | }
|
199 |
| - return trieGapsAtDepth(t, 0) |
| 217 | + return trieGapsAtDepth(t, 0, target, order) |
200 | 218 | }
|
201 | 219 |
|
202 |
| -func trieGapsAtDepth[D any](t *trie.Trie[bitstr.Key, D], depth int) []bitstr.Key { |
| 220 | +func trieGapsAtDepth[K kad.Key[K], D any](t *trie.Trie[bitstr.Key, D], depth int, target bitstr.Key, order K) []bitstr.Key { |
203 | 221 | var gaps []bitstr.Key
|
204 |
| - for i := range 2 { |
| 222 | + insideTarget := depth >= target.BitLen() |
| 223 | + b := int(order.Bit(depth)) |
| 224 | + for _, i := range []int{b, 1 - b} { |
| 225 | + if !insideTarget && i != int(target.Bit(depth)) { |
| 226 | + continue |
| 227 | + } |
205 | 228 | bstr := bitstr.Key(byte('0' + i))
|
206 | 229 | if b := t.Branch(i); b == nil {
|
207 | 230 | gaps = append(gaps, bstr)
|
208 | 231 | } else if b.IsLeaf() {
|
209 | 232 | if b.HasKey() {
|
210 | 233 | k := *b.Key()
|
211 | 234 | if len(k) > depth+1 {
|
212 |
| - for _, siblingPrefix := range SiblingPrefixes(k)[depth+1:] { |
| 235 | + siblingPrefixes := SiblingPrefixes(k)[depth+1:] |
| 236 | + sortBitstrKeysByOrder(siblingPrefixes, order) |
| 237 | + for _, siblingPrefix := range siblingPrefixes { |
213 | 238 | gaps = append(gaps, siblingPrefix[depth:])
|
214 | 239 | }
|
215 | 240 | }
|
216 | 241 | } else {
|
217 | 242 | gaps = append(gaps, bstr)
|
218 | 243 | }
|
219 | 244 | } else {
|
220 |
| - for _, gap := range trieGapsAtDepth(b, depth+1) { |
| 245 | + for _, gap := range trieGapsAtDepth(b, depth+1, target, order) { |
221 | 246 | gaps = append(gaps, bstr+gap)
|
222 | 247 | }
|
223 | 248 | }
|
224 | 249 | }
|
225 | 250 | return gaps
|
226 | 251 | }
|
227 | 252 |
|
| 253 | +// sortBitstrKeysByOrder sorts the provided bitstr keys according to the |
| 254 | +// provided order. |
| 255 | +func sortBitstrKeysByOrder[K kad.Key[K]](keys []bitstr.Key, order K) { |
| 256 | + slices.SortFunc(keys, func(a, b bitstr.Key) int { |
| 257 | + maxLen := min(len(a), len(b), order.BitLen()) |
| 258 | + for i := range maxLen { |
| 259 | + if a[i] != b[i] { |
| 260 | + if a.Bit(i) == order.Bit(i) { |
| 261 | + return -1 |
| 262 | + } |
| 263 | + return 1 |
| 264 | + } |
| 265 | + } |
| 266 | + if len(a) == len(b) || maxLen == order.BitLen() { |
| 267 | + return 0 |
| 268 | + } |
| 269 | + if len(a) < len(b) { |
| 270 | + return 1 |
| 271 | + } |
| 272 | + return -1 |
| 273 | + }) |
| 274 | +} |
| 275 | + |
228 | 276 | // mapMerge merges all key-value pairs from the source map into the destination
|
229 | 277 | // map. Values from the source are appended to existing slices in the
|
230 | 278 | // destination.
|
|
0 commit comments