blob: 54f8129b31dd976640987988c091ea3bcdf5a44d [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
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070010 private final 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
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070019 public V put(Prefix prefix, V value) {
20 synchronized (this) {
21 if (prefix == null || value == null) {
22 throw new IllegalArgumentException("Null argument");
Ray Milkey269ffb92014-04-03 14:43:30 -070023 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120024
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070025 if (prefix.getPrefixLength() > maxPrefixLength) {
26 throw new IllegalArgumentException(String.format(
27 "Prefix length %d is greater than max prefix length %d",
28 prefix.getPrefixLength(), maxPrefixLength));
Ray Milkey269ffb92014-04-03 14:43:30 -070029 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120030
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070031 Node node = top;
32 Node match = null;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120033
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070034 while (node != null
35 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
36 && checkKeyMatch(node.prefix.getAddress(),
37 node.prefix.getPrefixLength(),
38 prefix.getAddress(),
39 prefix.getPrefixLength())) {
40 if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
41 /*
42 * Prefix is already in tree. This may be an aggregate node, in which case
43 * we are inserting a new prefix, or it could be an actual node, in which
44 * case we are inserting a new nexthop for the prefix and should return
45 * the old nexthop.
46 */
47 V oldValue = node.value;
48 node.value = value;
49 return oldValue;
50 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120051
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070052 match = node;
53
54 if (checkBit(prefix.getAddress(), node.prefix.getPrefixLength())) {
55 node = node.right;
56 } else {
57 node = node.left;
58 }
Ray Milkey269ffb92014-04-03 14:43:30 -070059 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120060
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070061 Node add = null;
Ray Milkey269ffb92014-04-03 14:43:30 -070062
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070063 if (node == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -070064 //add = new Node(p, r);
65 add = new Node(prefix);
66 add.value = value;
Ray Milkey269ffb92014-04-03 14:43:30 -070067
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070068 if (match == null) {
69 top = add;
70 } else {
71 linkNodes(match, add);
72 }
73 } else {
74 add = findCommonNode(node, prefix.getAddress(), prefix.getPrefixLength());
75
76 if (match == null) {
77 top = add;
78 } else {
79 linkNodes(match, add);
80 }
81 linkNodes(add, node);
82
83 if (add.prefix.getPrefixLength() == prefix.getPrefixLength()) {
84 add.value = value;
85 } else {
86 match = add;
87
88 //add = new Node(p, r);
89 add = new Node(prefix);
90 add.value = value;
91 linkNodes(match, add);
92 }
93 }
94
95 //If we added a new Node, there was no previous mapping
96 return null;
97 //return addReference(add);
98 }
Ray Milkey269ffb92014-04-03 14:43:30 -070099 }
100
101 /*exact match*/
102 @Override
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700103 public V lookup(Prefix prefix) {
104 synchronized (this) {
105 if (prefix.getPrefixLength() > maxPrefixLength) {
106 return null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700107 }
108
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700109 /*
110 Node node = top;
111
112 while (node != null
113 && node.prefix.getPrefixLength() <= p.getPrefixLength()
114 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
115 if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
116 //return addReference(node);
117 return node.rib;
118 }
119
120 if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
121 node = node.right;
122 } else {
123 node = node.left;
124 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700125 }
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700126 */
127
128 Node node = findNode(prefix);
129
130 return node == null ? null : node.value;
Ray Milkey269ffb92014-04-03 14:43:30 -0700131 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700132 }
133
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700134 // closest containing prefix
Ray Milkey269ffb92014-04-03 14:43:30 -0700135 @Override
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700136 public V match(Prefix prefix) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700137 //TODO
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700138 synchronized (this) {
139 if (prefix.getPrefixLength() > maxPrefixLength) {
140 return null;
141 }
142
143 Node closestNode = findClosestNode(prefix);
144
145 return closestNode == null ? null : closestNode.value;
Ray Milkey269ffb92014-04-03 14:43:30 -0700146 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700147 }
148
149 @Override
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700150 public boolean remove(Prefix prefix, V value) {
151 synchronized (this) {
152 if (prefix == null || value == null) {
153 return false;
154 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700155
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700156 Node node = findNode(prefix);
Ray Milkey269ffb92014-04-03 14:43:30 -0700157
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700158 if (node == null || node.isAggregate() || !node.value.equals(value)) {
159 //Given <prefix, nexthop> mapping is not in the tree
160 return false;
161 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700162
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700163 if (node.left != null && node.right != null) {
164 //Remove the RibEntry entry and leave this node as an aggregate node
165 //In the future, maybe we should re-evaluate what the aggregate prefix should be?
166 //It shouldn't necessarily stay the same.
167 //More complicated if the above prefix is also aggregate.
168 node.value = null;
169 return true;
170 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700171
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700172 Node child;
173 if (node.left != null) {
174 child = node.left;
175 } else {
176 child = node.right;
177 }
178
179 Node parent = node.parent;
180
181 if (child != null) {
182 child.parent = parent;
183 }
184
185 if (parent != null) {
186 if (parent.left == node) {
187 parent.left = child;
188 } else {
189 parent.right = child;
190 }
191 } else {
192 top = child;
193 }
194
195 /*
196 * TODO not sure what to do here. I think this is lazily deleting aggregate nodes,
197 * notice that it used to do nothing if it detected both children were not null earlier.
198 * But here, what we really should do is reevaluate the aggregate prefix of the parent
199 * node (if it is indeed an aggregate). Because at the moment, no aggregate node will ever
200 * be removed. BUT, I don't actually think this presents a correctness problem, at
201 * least from an external point of view.
202 */
203 //if (parent != null && parent.refCount == 0) {
204 //node_remove(parent);
205 //}
206
Ray Milkey269ffb92014-04-03 14:43:30 -0700207 return true;
208 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700209 }
210
211 @Override
212 public Iterator<Entry<V>> iterator() {
213 return new PatriciaTrieIterator(top);
214 }
215
216 private Node findNode(Prefix prefix) {
217 Node node = top;
218
219 while (node != null
220 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700221 && checkKeyMatch(node.prefix.getAddress(),
222 node.prefix.getPrefixLength(),
223 prefix.getAddress(), prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700224 if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
225 //return addReference(node);
226 return node;
227 }
228
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700229 if (checkBit(prefix.getAddress(), node.prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700230 node = node.right;
231 } else {
232 node = node.left;
233 }
234 }
235
236 return null;
237 }
238
239 private Node findClosestNode(Prefix prefix) {
240 Node node = top;
241 Node match = null;
242
243 while (node != null
244 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700245 && checkKeyMatch(node.prefix.getAddress(),
246 node.prefix.getPrefixLength(),
247 prefix.getAddress(), prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700248 if (!node.isAggregate()) {
249 match = node;
250 }
251
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700252 if (checkBit(prefix.getAddress(), node.prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700253 node = node.right;
254 } else {
255 node = node.left;
256 }
257 }
258
259 return match;
260 }
261
262 /*
263 * Receives a 1-based bit index
264 * Returns a 1-based byte index
265 * eg. (0 => 1), 1 => 1, 8 => 1, 9 => 2, 17 => 3
266 */
267 private int getByteContainingBit(int bitNumber) {
268 return Math.max((bitNumber + 7) / 8, 1);
269 }
270
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700271 private boolean checkKeyMatch(byte[] key1, int key1Length, byte[] key2, int key2Length) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700272 //int offset;
273 //int shift;
274
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700275 if (key1Length > key2Length) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700276 return false;
277 }
278
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700279 int offset = (Math.min(key1Length, key2Length)) / 8;
280 int shift = (Math.min(key1Length, key2Length)) % 8;
Ray Milkey269ffb92014-04-03 14:43:30 -0700281
282 if (shift != 0) {
283 if ((maskBits[shift] & (key1[offset] ^ key2[offset])) != 0) {
284 return false;
285 }
286 }
287
288 while (offset != 0) {
289 offset--;
290 if (key1[offset] != key2[offset]) {
291 return false;
292 }
293 }
294 return true;
295 }
296
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700297 private boolean checkBit(byte[] key, int keyBits) {
298 int offset = keyBits / 8;
299 int shift = 7 - (keyBits % 8);
Ray Milkey269ffb92014-04-03 14:43:30 -0700300 int bit = key[offset] & 0xff;
301
302 bit >>= shift;
303
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700304 return ((bit & 1) == 1);
Ray Milkey269ffb92014-04-03 14:43:30 -0700305 }
306
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700307 private void linkNodes(Node node, Node add) {
308 boolean bit = checkBit(add.prefix.getAddress(), node.prefix.getPrefixLength());
Ray Milkey269ffb92014-04-03 14:43:30 -0700309
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700310 if (bit) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700311 node.right = add;
312 } else {
313 node.left = add;
314 }
315 add.parent = node;
316 }
317
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700318 private Node findCommonNode(Node node, byte[] key, int keyBits) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700319 int i;
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700320 int limit = Math.min(node.prefix.getPrefixLength(), keyBits) / 8;
Ray Milkey269ffb92014-04-03 14:43:30 -0700321
322 for (i = 0; i < limit; i++) {
323 if (node.prefix.getAddress()[i] != key[i]) {
324 break;
325 }
326 }
327
Ray Milkey2476cac2014-04-08 11:03:21 -0700328 int commonLen = i * 8;
Ray Milkey269ffb92014-04-03 14:43:30 -0700329 int boundary = 0;
330
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700331 if (commonLen != keyBits) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700332 byte diff = (byte) (node.prefix.getAddress()[i] ^ key[i]);
333 byte mask = (byte) 0x80;
Ray Milkey2476cac2014-04-08 11:03:21 -0700334 int shiftMask = 0;
Ray Milkey269ffb92014-04-03 14:43:30 -0700335
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700336 while (commonLen < keyBits && ((mask & diff) == 0)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700337 boundary = 1;
338
Ray Milkey2476cac2014-04-08 11:03:21 -0700339 shiftMask = (mask & 0xff);
340 shiftMask >>= 1;
341 mask = (byte) shiftMask;
Ray Milkey269ffb92014-04-03 14:43:30 -0700342
Ray Milkey2476cac2014-04-08 11:03:21 -0700343 commonLen++;
Ray Milkey269ffb92014-04-03 14:43:30 -0700344 }
345 }
346
Ray Milkey269ffb92014-04-03 14:43:30 -0700347 //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;
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700355 for (j = 0; j < i; j++) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700356 newPrefix[j] = node.prefix.getAddress()[j];
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700357 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700358
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700359 if (boundary != 0) {
360 newPrefix[j] =
361 (byte) (node.prefix.getAddress()[j] & maskBits[commonLen % 8]);
362 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700363
364 //return new Node(new Prefix(newPrefix, common_len), null);
Ray Milkey2476cac2014-04-08 11:03:21 -0700365 return new Node(new Prefix(newPrefix, commonLen));
Ray Milkey269ffb92014-04-03 14:43:30 -0700366 //return add;
367 }
368
369 private class Node {
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700370 public Node parent;
371 public Node left;
372 public Node right;
Ray Milkey269ffb92014-04-03 14:43:30 -0700373
374 public final Prefix prefix;
375 public V value;
376
377 //public Node(Prefix p, RibEntry r) {
378 // this.prefix = p;
379 // this.rib = r;
380 //}
381 public Node(Prefix p) {
382 this.prefix = p;
383 }
384
385 public boolean isAggregate() {
386 return value == null;
387 }
388
389 public Entry<V> getEntry() {
390 return new PatriciaTrieEntry(prefix, value);
391 }
392 }
393
394 private class PatriciaTrieEntry implements Entry<V> {
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700395 private final Prefix prefix;
396 private final V value;
Ray Milkey269ffb92014-04-03 14:43:30 -0700397
398 public PatriciaTrieEntry(Prefix prefix, V value) {
399 this.prefix = prefix;
400 this.value = value;
401 }
402
403 @Override
404 public Prefix getPrefix() {
405 return prefix;
406 }
407
408 @Override
409 public V getValue() {
410 return value;
411 }
412 }
413
414 private class PatriciaTrieIterator implements Iterator<Entry<V>> {
Ray Milkey269ffb92014-04-03 14:43:30 -0700415 private Node current;
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700416 private boolean started; // initialized to false
Ray Milkey269ffb92014-04-03 14:43:30 -0700417
418 public PatriciaTrieIterator(Node start) {
419 current = start;
420
421 //If the start is an aggregate node fast forward to find the next valid node
422 if (current != null && current.isAggregate()) {
423 current = findNext(current);
424 }
425 }
426
427 @Override
428 public boolean hasNext() {
429 if (current == null) {
430 return false;
431 }
432
433 if (!started) {
434 return true;
435 }
436
437 return findNext(current) != null;
438 }
439
440 @Override
441 public Entry<V> next() {
442 if (current == null) {
443 throw new NoSuchElementException();
444 }
445
446 if (!started) {
447 started = true;
448 return current.getEntry();
449 }
450
451 current = findNext(current);
452 if (current == null) {
453 throw new NoSuchElementException();
454 }
455
456 return current.getEntry();
457 }
458
459 @Override
460 public void remove() {
461 // TODO This could be implemented, if it were needed
462 throw new NoSuchElementException();
463 }
464
465 private Node findNext(Node node) {
466 Node next = null;
467
468 if (node.left != null) {
469 next = node.left;
470 //addReference(next);
471 //delReference(node);
472 //return next;
473 } else if (node.right != null) {
474 next = node.right;
475 //addReference(next);
476 //delReference(node);
477 //return next;
478 } else {
479 //Node start = node;
480 while (node.parent != null) {
481 if (node.parent.left == node && node.parent.right != null) {
482 next = node.parent.right;
483 //addReference(next);
484 //delReference(start);
485 //return next;
486 break;
487 }
488 node = node.parent;
489 }
490 }
491
492 if (next == null) {
493 return null;
494 }
495
496 //If the node doesn't have a value, it's not an actual node, it's an artifically
497 //inserted aggregate node. We don't want to return these to the user.
498 if (next.isAggregate()) {
499 return findNext(next);
500 }
501
502 return next;
503 }
504 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200505}