blob: 817e5711ba4beabcf42c17bc294d27f6625e0185 [file] [log] [blame]
Jonathan Hart382623d2014-04-03 09:48:11 -07001package net.onrc.onos.apps.bgproute;
Jonathan Hart8f5f4682013-08-07 22:13:39 +12002
3import java.util.Iterator;
4import java.util.NoSuchElementException;
5
Jonathan Hart29b972d2013-08-12 23:43:51 +12006public class PatriciaTrie<V> implements IPatriciaTrie<V> {
Ray Milkey45614c52014-04-07 16:30:54 -07007 private final byte[] maskBits = {(byte) 0x00, (byte) 0x80, (byte) 0xc0, (byte) 0xe0, (byte) 0xf0,
Ray Milkey269ffb92014-04-03 14:43:30 -07008 (byte) 0xf8, (byte) 0xfc, (byte) 0xfe, (byte) 0xff};
Jonathan Hart8f5f4682013-08-07 22:13:39 +12009
Ray Milkey269ffb92014-04-03 14:43:30 -070010 private int maxPrefixLength;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120011
Ray Milkey269ffb92014-04-03 14:43:30 -070012 private Node top;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120013
Ray Milkey269ffb92014-04-03 14:43:30 -070014 public PatriciaTrie(int maxPrefixLength) {
15 this.maxPrefixLength = maxPrefixLength;
16 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120017
Ray Milkey269ffb92014-04-03 14:43:30 -070018 @Override
19 public synchronized V put(Prefix prefix, V value) {
20 if (prefix == null || value == null) {
21 throw new NullPointerException();
22 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120023
Ray Milkey269ffb92014-04-03 14:43:30 -070024 if (prefix.getPrefixLength() > maxPrefixLength) {
25 throw new IllegalArgumentException(String.format(
26 "Prefix length %d is greater than max prefix length %d",
27 prefix.getPrefixLength(), maxPrefixLength));
28 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120029
Ray Milkey269ffb92014-04-03 14:43:30 -070030 Node node = top;
31 Node match = null;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120032
Ray Milkey269ffb92014-04-03 14:43:30 -070033 while (node != null
34 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
35 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
36 if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
37 /*
38 * Prefix is already in tree. This may be an aggregate node, in which case
39 * we are inserting a new prefix, or it could be an actual node, in which
40 * case we are inserting a new nexthop for the prefix and should return
41 * the old nexthop.
42 */
43 V oldValue = node.value;
44 node.value = value;
45 return oldValue;
46 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120047
Ray Milkey269ffb92014-04-03 14:43:30 -070048 match = node;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120049
Ray Milkey269ffb92014-04-03 14:43:30 -070050 if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
51 node = node.right;
52 } else {
53 node = node.left;
54 }
55 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120056
Ray Milkey269ffb92014-04-03 14:43:30 -070057 Node add = null;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120058
Ray Milkey269ffb92014-04-03 14:43:30 -070059 if (node == null) {
60 //add = new Node(p, r);
61 add = new Node(prefix);
62 add.value = value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120063
Ray Milkey269ffb92014-04-03 14:43:30 -070064 if (match != null) {
65 node_link(match, add);
66 } else {
67 top = add;
68 }
69 } else {
70 add = node_common(node, prefix.getAddress(), prefix.getPrefixLength());
71 if (add == null) {
72 //I think this is -ENOMEM?
73 //return null;
74 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120075
Ray Milkey269ffb92014-04-03 14:43:30 -070076 if (match != null) {
77 node_link(match, add);
78 } else {
79 top = add;
80 }
81 node_link(add, node);
82
83 if (add.prefix.getPrefixLength() != prefix.getPrefixLength()) {
84 match = add;
85
86 //add = new Node(p, r);
87 add = new Node(prefix);
88 add.value = value;
89 node_link(match, add);
90 } else {
91 add.value = value;
92 }
93 }
94
95 //If we added a new Node, there was no previous mapping
96 return null;
97 //return addReference(add);
98 }
99
100 /*exact match*/
101 @Override
102 public synchronized V lookup(Prefix prefix) {
103 if (prefix.getPrefixLength() > maxPrefixLength) {
104 return null;
105 }
106
107 /*
108 Node node = top;
109
110 while (node != null
111 && node.prefix.getPrefixLength() <= p.getPrefixLength()
112 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
113 if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
114 //return addReference(node);
115 return node.rib;
116 }
117
118 if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
119 node = node.right;
120 } else {
121 node = node.left;
122 }
123 }
124 */
125
126 Node node = findNode(prefix);
127
128 return node == null ? null : node.value;
129 }
130
131 /*closest containing prefix*/
132 @Override
133 public synchronized V match(Prefix prefix) {
134 //TODO
135 if (prefix.getPrefixLength() > maxPrefixLength) {
136 return null;
137 }
138
139 Node closestNode = findClosestNode(prefix);
140
141 return closestNode == null ? null : closestNode.value;
142 }
143
144 @Override
145 public synchronized boolean remove(Prefix prefix, V value) {
146 Node child;
147 Node parent;
148
149 if (prefix == null || value == null) {
150 return false;
151 }
152
153 Node node = findNode(prefix);
154
155 if (node == null || node.isAggregate() || !node.value.equals(value)) {
156 //Given <prefix, nexthop> mapping is not in the tree
157 return false;
158 }
159
160 if (node.left != null && node.right != null) {
161 //Remove the RibEntry entry and leave this node as an aggregate node
162 //In the future, maybe we should re-evaluate what the aggregate prefix should be?
163 //It shouldn't necessarily stay the same.
164 //More complicated if the above prefix is also aggregate.
165 node.value = null;
166 return true;
167 }
168
169 if (node.left != null) {
170 child = node.left;
171 } else {
172 child = node.right;
173 }
174
175 parent = node.parent;
176
177 if (child != null) {
178 child.parent = parent;
179 }
180
181 if (parent != null) {
182 if (parent.left == node) {
183 parent.left = child;
184 } else {
185 parent.right = child;
186 }
187 } else {
188 top = child;
189 }
190
191 /*
192 * TODO not sure what to do here. I think this is lazily deleting aggregate nodes,
193 * notice that it used to do nothing if it detected both children were not null earlier.
194 * But here, what we really should do is reevaluate the aggregate prefix of the parent
195 * node (if it is indeed an aggregate). Because at the moment, no aggregate node will ever
196 * be removed. BUT, I don't actually think this presents a correctness problem, at
197 * least from an external point of view.
198 */
199 //if (parent != null && parent.refCount == 0) {
200 //node_remove(parent);
201 //}
202
203 return true;
204 }
205
206 @Override
207 public Iterator<Entry<V>> iterator() {
208 return new PatriciaTrieIterator(top);
209 }
210
211 private Node findNode(Prefix prefix) {
212 Node node = top;
213
214 while (node != null
215 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
216 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
217 if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
218 //return addReference(node);
219 return node;
220 }
221
222 if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
223 node = node.right;
224 } else {
225 node = node.left;
226 }
227 }
228
229 return null;
230 }
231
232 private Node findClosestNode(Prefix prefix) {
233 Node node = top;
234 Node match = null;
235
236 while (node != null
237 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
238 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
239 if (!node.isAggregate()) {
240 match = node;
241 }
242
243 if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
244 node = node.right;
245 } else {
246 node = node.left;
247 }
248 }
249
250 return match;
251 }
252
253 /*
254 * Receives a 1-based bit index
255 * Returns a 1-based byte index
256 * eg. (0 => 1), 1 => 1, 8 => 1, 9 => 2, 17 => 3
257 */
258 private int getByteContainingBit(int bitNumber) {
259 return Math.max((bitNumber + 7) / 8, 1);
260 }
261
262 private boolean key_match(byte[] key1, int key1_len, byte[] key2, int key2_len) {
263 //int offset;
264 //int shift;
265
266 if (key1_len > key2_len) {
267 return false;
268 }
269
270 int offset = (Math.min(key1_len, key2_len)) / 8;
271 int shift = (Math.min(key1_len, key2_len)) % 8;
272
273 if (shift != 0) {
274 if ((maskBits[shift] & (key1[offset] ^ key2[offset])) != 0) {
275 return false;
276 }
277 }
278
279 while (offset != 0) {
280 offset--;
281 if (key1[offset] != key2[offset]) {
282 return false;
283 }
284 }
285 return true;
286 }
287
288 private boolean bit_check(byte[] key, int key_bits) {
289 int offset = key_bits / 8;
290 int shift = 7 - (key_bits % 8);
291 int bit = key[offset] & 0xff;
292
293 bit >>= shift;
294
295 if ((bit & 1) == 1) {
296 return true;
297 } else {
298 return false;
299 }
300 }
301
302 private void node_link(Node node, Node add) {
303 boolean bit = bit_check(add.prefix.getAddress(), node.prefix.getPrefixLength());
304
305 if (bit == true) {
306 node.right = add;
307 } else {
308 node.left = add;
309 }
310 add.parent = node;
311 }
312
313 private Node node_common(Node node, byte[] key, int key_bits) {
314 int i;
315 int limit = Math.min(node.prefix.getPrefixLength(), key_bits) / 8;
316
317 for (i = 0; i < limit; i++) {
318 if (node.prefix.getAddress()[i] != key[i]) {
319 break;
320 }
321 }
322
323 int common_len = i * 8;
324 int boundary = 0;
325
326 if (common_len != key_bits) {
327 byte diff = (byte) (node.prefix.getAddress()[i] ^ key[i]);
328 byte mask = (byte) 0x80;
329 int shift_mask = 0;
330
331 while (common_len < key_bits && ((mask & diff) == 0)) {
332 boundary = 1;
333
334 shift_mask = (mask & 0xff);
335 shift_mask >>= 1;
336 mask = (byte) shift_mask;
337
338 common_len++;
339 }
340 }
341
342 //Node add = new Node(null, common_len, maxKeyOctets);
343 //if (add == null)
344 //Another -ENOMEM;
345 //return null;
346
347 //Creating a new Prefix with a prefix length of common_len
348 //Bits are copied from node's up until the common_len'th bit
349 //RibEntry is null, because this is an aggregate prefix - it's not
350 //actually been added to the trie.
351
352 byte[] newPrefix = new byte[getByteContainingBit(maxPrefixLength)];
353
354 int j;
355 for (j = 0; j < i; j++)
356 newPrefix[j] = node.prefix.getAddress()[j];
357
358 if (boundary != 0)
359 newPrefix[j] = (byte) (node.prefix.getAddress()[j] & maskBits[common_len % 8]);
360
361 //return new Node(new Prefix(newPrefix, common_len), null);
362 return new Node(new Prefix(newPrefix, common_len));
363 //return add;
364 }
365
366 private class Node {
367 public Node parent = null;
368 public Node left = null;
369 public Node right = null;
370
371 public final Prefix prefix;
372 public V value;
373
374 //public Node(Prefix p, RibEntry r) {
375 // this.prefix = p;
376 // this.rib = r;
377 //}
378 public Node(Prefix p) {
379 this.prefix = p;
380 }
381
382 public boolean isAggregate() {
383 return value == null;
384 }
385
386 public Entry<V> getEntry() {
387 return new PatriciaTrieEntry(prefix, value);
388 }
389 }
390
391 private class PatriciaTrieEntry implements Entry<V> {
392 private Prefix prefix;
393 private V value;
394
395 public PatriciaTrieEntry(Prefix prefix, V value) {
396 this.prefix = prefix;
397 this.value = value;
398 }
399
400 @Override
401 public Prefix getPrefix() {
402 return prefix;
403 }
404
405 @Override
406 public V getValue() {
407 return value;
408 }
409 }
410
411 private class PatriciaTrieIterator implements Iterator<Entry<V>> {
412
413 private Node current;
414 private boolean started = false;
415
416 public PatriciaTrieIterator(Node start) {
417 current = start;
418
419 //If the start is an aggregate node fast forward to find the next valid node
420 if (current != null && current.isAggregate()) {
421 current = findNext(current);
422 }
423 }
424
425 @Override
426 public boolean hasNext() {
427 if (current == null) {
428 return false;
429 }
430
431 if (!started) {
432 return true;
433 }
434
435 return findNext(current) != null;
436 }
437
438 @Override
439 public Entry<V> next() {
440 if (current == null) {
441 throw new NoSuchElementException();
442 }
443
444 if (!started) {
445 started = true;
446 return current.getEntry();
447 }
448
449 current = findNext(current);
450 if (current == null) {
451 throw new NoSuchElementException();
452 }
453
454 return current.getEntry();
455 }
456
457 @Override
458 public void remove() {
459 // TODO This could be implemented, if it were needed
460 throw new NoSuchElementException();
461 }
462
463 private Node findNext(Node node) {
464 Node next = null;
465
466 if (node.left != null) {
467 next = node.left;
468 //addReference(next);
469 //delReference(node);
470 //return next;
471 } else if (node.right != null) {
472 next = node.right;
473 //addReference(next);
474 //delReference(node);
475 //return next;
476 } else {
477 //Node start = node;
478 while (node.parent != null) {
479 if (node.parent.left == node && node.parent.right != null) {
480 next = node.parent.right;
481 //addReference(next);
482 //delReference(start);
483 //return next;
484 break;
485 }
486 node = node.parent;
487 }
488 }
489
490 if (next == null) {
491 return null;
492 }
493
494 //If the node doesn't have a value, it's not an actual node, it's an artifically
495 //inserted aggregate node. We don't want to return these to the user.
496 if (next.isAggregate()) {
497 return findNext(next);
498 }
499
500 return next;
501 }
502 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200503}