blob: cce2630a10ff48aef2d6887df063df50fe7abb00 [file] [log] [blame]
Jonathan Hart8f6dc092014-04-18 15:56:43 -07001package net.onrc.onos.apps.sdnip;
Jonathan Hart8f5f4682013-08-07 22:13:39 +12002
3import java.util.Iterator;
4import java.util.NoSuchElementException;
5
Jonathan Hart31e15f12014-04-10 10:33:00 -07006/**
Jonathan Hart5e54f2e2014-04-17 13:43:40 -07007 * Implements a patricia tree. See {@link IPatriciaTree} for a description of
Jonathan Hart31e15f12014-04-10 10:33:00 -07008 * how the tree works and its usage.
9 *
10 * @param <V> the type of objects that will be stored in the tree
11 */
Jonathan Hart5e54f2e2014-04-17 13:43:40 -070012public class PatriciaTree<V> implements IPatriciaTree<V> {
Ray Milkey45614c52014-04-07 16:30:54 -070013 private final byte[] maskBits = {(byte) 0x00, (byte) 0x80, (byte) 0xc0, (byte) 0xe0, (byte) 0xf0,
Ray Milkey269ffb92014-04-03 14:43:30 -070014 (byte) 0xf8, (byte) 0xfc, (byte) 0xfe, (byte) 0xff};
Jonathan Hart8f5f4682013-08-07 22:13:39 +120015
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070016 private final int maxPrefixLength;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120017
Ray Milkey269ffb92014-04-03 14:43:30 -070018 private Node top;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120019
Jonathan Hart31e15f12014-04-10 10:33:00 -070020 /**
21 * Class constructor which takes the maximum length of strings that can be
22 * stored in the tree. This is used as a sanity check to prevent
23 * excessively long strings from being added, as this could slow down
24 * lookups.
25 *
26 * @param maxPrefixLength the maximum length of prefixes
27 */
Jonathan Hart5e54f2e2014-04-17 13:43:40 -070028 public PatriciaTree(int maxPrefixLength) {
Ray Milkey269ffb92014-04-03 14:43:30 -070029 this.maxPrefixLength = maxPrefixLength;
30 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120031
Ray Milkey269ffb92014-04-03 14:43:30 -070032 @Override
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070033 public V put(Prefix prefix, V value) {
34 synchronized (this) {
35 if (prefix == null || value == null) {
36 throw new IllegalArgumentException("Null argument");
Ray Milkey269ffb92014-04-03 14:43:30 -070037 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120038
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070039 if (prefix.getPrefixLength() > maxPrefixLength) {
40 throw new IllegalArgumentException(String.format(
41 "Prefix length %d is greater than max prefix length %d",
42 prefix.getPrefixLength(), maxPrefixLength));
Ray Milkey269ffb92014-04-03 14:43:30 -070043 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120044
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070045 Node node = top;
46 Node match = null;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120047
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070048 while (node != null
49 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
50 && checkKeyMatch(node.prefix.getAddress(),
51 node.prefix.getPrefixLength(),
52 prefix.getAddress(),
53 prefix.getPrefixLength())) {
54 if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
55 /*
56 * Prefix is already in tree. This may be an aggregate node, in which case
57 * we are inserting a new prefix, or it could be an actual node, in which
58 * case we are inserting a new nexthop for the prefix and should return
59 * the old nexthop.
60 */
61 V oldValue = node.value;
62 node.value = value;
63 return oldValue;
64 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120065
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070066 match = node;
67
68 if (checkBit(prefix.getAddress(), node.prefix.getPrefixLength())) {
69 node = node.right;
70 } else {
71 node = node.left;
72 }
Ray Milkey269ffb92014-04-03 14:43:30 -070073 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +120074
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070075 Node add = null;
Ray Milkey269ffb92014-04-03 14:43:30 -070076
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070077 if (node == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -070078 //add = new Node(p, r);
79 add = new Node(prefix);
80 add.value = value;
Ray Milkey269ffb92014-04-03 14:43:30 -070081
Jonathan Hartec9ee2e2014-04-08 22:45:44 -070082 if (match == null) {
83 top = add;
84 } else {
85 linkNodes(match, add);
86 }
87 } else {
88 add = findCommonNode(node, prefix.getAddress(), prefix.getPrefixLength());
89
90 if (match == null) {
91 top = add;
92 } else {
93 linkNodes(match, add);
94 }
95 linkNodes(add, node);
96
97 if (add.prefix.getPrefixLength() == prefix.getPrefixLength()) {
98 add.value = value;
99 } else {
100 match = add;
101
102 //add = new Node(p, r);
103 add = new Node(prefix);
104 add.value = value;
105 linkNodes(match, add);
106 }
107 }
108
109 //If we added a new Node, there was no previous mapping
110 return null;
111 //return addReference(add);
112 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700113 }
114
115 /*exact match*/
116 @Override
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700117 public V lookup(Prefix prefix) {
118 synchronized (this) {
119 if (prefix.getPrefixLength() > maxPrefixLength) {
120 return null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700121 }
122
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700123 /*
124 Node node = top;
125
126 while (node != null
127 && node.prefix.getPrefixLength() <= p.getPrefixLength()
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700128 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(),
129 p.getAddress(), p.getPrefixLength()) == true) {
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700130 if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
131 //return addReference(node);
132 return node.rib;
133 }
134
135 if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
136 node = node.right;
137 } else {
138 node = node.left;
139 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700140 }
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700141 */
142
143 Node node = findNode(prefix);
144
145 return node == null ? null : node.value;
Ray Milkey269ffb92014-04-03 14:43:30 -0700146 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700147 }
148
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700149 // closest containing prefix
Ray Milkey269ffb92014-04-03 14:43:30 -0700150 @Override
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700151 public V match(Prefix prefix) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700152 //TODO
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700153 synchronized (this) {
154 if (prefix.getPrefixLength() > maxPrefixLength) {
155 return null;
156 }
157
158 Node closestNode = findClosestNode(prefix);
159
160 return closestNode == null ? null : closestNode.value;
Ray Milkey269ffb92014-04-03 14:43:30 -0700161 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700162 }
163
164 @Override
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700165 public boolean remove(Prefix prefix, V value) {
166 synchronized (this) {
167 if (prefix == null || value == null) {
168 return false;
169 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700170
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700171 Node node = findNode(prefix);
Ray Milkey269ffb92014-04-03 14:43:30 -0700172
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700173 if (node == null || node.isAggregate() || !node.value.equals(value)) {
174 //Given <prefix, nexthop> mapping is not in the tree
175 return false;
176 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700177
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700178 if (node.left != null && node.right != null) {
179 //Remove the RibEntry entry and leave this node as an aggregate node
180 //In the future, maybe we should re-evaluate what the aggregate prefix should be?
181 //It shouldn't necessarily stay the same.
182 //More complicated if the above prefix is also aggregate.
183 node.value = null;
184 return true;
185 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700186
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700187 Node child;
188 if (node.left != null) {
189 child = node.left;
190 } else {
191 child = node.right;
192 }
193
194 Node parent = node.parent;
195
196 if (child != null) {
197 child.parent = parent;
198 }
199
200 if (parent != null) {
201 if (parent.left == node) {
202 parent.left = child;
203 } else {
204 parent.right = child;
205 }
206 } else {
207 top = child;
208 }
209
210 /*
211 * TODO not sure what to do here. I think this is lazily deleting aggregate nodes,
212 * notice that it used to do nothing if it detected both children were not null earlier.
213 * But here, what we really should do is reevaluate the aggregate prefix of the parent
214 * node (if it is indeed an aggregate). Because at the moment, no aggregate node will ever
215 * be removed. BUT, I don't actually think this presents a correctness problem, at
216 * least from an external point of view.
217 */
218 //if (parent != null && parent.refCount == 0) {
219 //node_remove(parent);
220 //}
221
Ray Milkey269ffb92014-04-03 14:43:30 -0700222 return true;
223 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700224 }
225
226 @Override
227 public Iterator<Entry<V>> iterator() {
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700228 return new PatriciaTreeIterator(top);
Ray Milkey269ffb92014-04-03 14:43:30 -0700229 }
230
231 private Node findNode(Prefix prefix) {
232 Node node = top;
233
234 while (node != null
235 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700236 && checkKeyMatch(node.prefix.getAddress(),
237 node.prefix.getPrefixLength(),
238 prefix.getAddress(), prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700239 if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
240 //return addReference(node);
241 return node;
242 }
243
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700244 if (checkBit(prefix.getAddress(), node.prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700245 node = node.right;
246 } else {
247 node = node.left;
248 }
249 }
250
251 return null;
252 }
253
254 private Node findClosestNode(Prefix prefix) {
255 Node node = top;
256 Node match = null;
257
258 while (node != null
259 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700260 && checkKeyMatch(node.prefix.getAddress(),
261 node.prefix.getPrefixLength(),
262 prefix.getAddress(), prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700263 if (!node.isAggregate()) {
264 match = node;
265 }
266
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700267 if (checkBit(prefix.getAddress(), node.prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700268 node = node.right;
269 } else {
270 node = node.left;
271 }
272 }
273
274 return match;
275 }
276
277 /*
278 * Receives a 1-based bit index
279 * Returns a 1-based byte index
280 * eg. (0 => 1), 1 => 1, 8 => 1, 9 => 2, 17 => 3
281 */
282 private int getByteContainingBit(int bitNumber) {
283 return Math.max((bitNumber + 7) / 8, 1);
284 }
285
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700286 private boolean checkKeyMatch(byte[] key1, int key1Length, byte[] key2, int key2Length) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700287 //int offset;
288 //int shift;
289
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700290 if (key1Length > key2Length) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700291 return false;
292 }
293
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700294 int offset = (Math.min(key1Length, key2Length)) / 8;
295 int shift = (Math.min(key1Length, key2Length)) % 8;
Ray Milkey269ffb92014-04-03 14:43:30 -0700296
297 if (shift != 0) {
298 if ((maskBits[shift] & (key1[offset] ^ key2[offset])) != 0) {
299 return false;
300 }
301 }
302
303 while (offset != 0) {
304 offset--;
305 if (key1[offset] != key2[offset]) {
306 return false;
307 }
308 }
309 return true;
310 }
311
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700312 private boolean checkBit(byte[] key, int keyBits) {
313 int offset = keyBits / 8;
314 int shift = 7 - (keyBits % 8);
Ray Milkey269ffb92014-04-03 14:43:30 -0700315 int bit = key[offset] & 0xff;
316
317 bit >>= shift;
318
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700319 return ((bit & 1) == 1);
Ray Milkey269ffb92014-04-03 14:43:30 -0700320 }
321
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700322 private void linkNodes(Node node, Node add) {
323 boolean bit = checkBit(add.prefix.getAddress(), node.prefix.getPrefixLength());
Ray Milkey269ffb92014-04-03 14:43:30 -0700324
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700325 if (bit) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700326 node.right = add;
327 } else {
328 node.left = add;
329 }
330 add.parent = node;
331 }
332
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700333 private Node findCommonNode(Node node, byte[] key, int keyBits) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700334 int i;
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700335 int limit = Math.min(node.prefix.getPrefixLength(), keyBits) / 8;
Ray Milkey269ffb92014-04-03 14:43:30 -0700336
337 for (i = 0; i < limit; i++) {
338 if (node.prefix.getAddress()[i] != key[i]) {
339 break;
340 }
341 }
342
Ray Milkey2476cac2014-04-08 11:03:21 -0700343 int commonLen = i * 8;
Ray Milkey269ffb92014-04-03 14:43:30 -0700344 int boundary = 0;
345
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700346 if (commonLen != keyBits) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700347 byte diff = (byte) (node.prefix.getAddress()[i] ^ key[i]);
348 byte mask = (byte) 0x80;
Ray Milkey2476cac2014-04-08 11:03:21 -0700349 int shiftMask = 0;
Ray Milkey269ffb92014-04-03 14:43:30 -0700350
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700351 while (commonLen < keyBits && ((mask & diff) == 0)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700352 boundary = 1;
353
Ray Milkey2476cac2014-04-08 11:03:21 -0700354 shiftMask = (mask & 0xff);
355 shiftMask >>= 1;
356 mask = (byte) shiftMask;
Ray Milkey269ffb92014-04-03 14:43:30 -0700357
Ray Milkey2476cac2014-04-08 11:03:21 -0700358 commonLen++;
Ray Milkey269ffb92014-04-03 14:43:30 -0700359 }
360 }
361
Ray Milkey269ffb92014-04-03 14:43:30 -0700362 //Creating a new Prefix with a prefix length of common_len
363 //Bits are copied from node's up until the common_len'th bit
364 //RibEntry is null, because this is an aggregate prefix - it's not
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700365 //actually been added to the tree.
Ray Milkey269ffb92014-04-03 14:43:30 -0700366
367 byte[] newPrefix = new byte[getByteContainingBit(maxPrefixLength)];
368
369 int j;
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700370 for (j = 0; j < i; j++) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700371 newPrefix[j] = node.prefix.getAddress()[j];
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700372 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700373
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700374 if (boundary != 0) {
375 newPrefix[j] =
376 (byte) (node.prefix.getAddress()[j] & maskBits[commonLen % 8]);
377 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700378
379 //return new Node(new Prefix(newPrefix, common_len), null);
Ray Milkey2476cac2014-04-08 11:03:21 -0700380 return new Node(new Prefix(newPrefix, commonLen));
Ray Milkey269ffb92014-04-03 14:43:30 -0700381 //return add;
382 }
383
384 private class Node {
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700385 public Node parent;
386 public Node left;
387 public Node right;
Ray Milkey269ffb92014-04-03 14:43:30 -0700388
389 public final Prefix prefix;
390 public V value;
391
392 //public Node(Prefix p, RibEntry r) {
393 // this.prefix = p;
394 // this.rib = r;
395 //}
396 public Node(Prefix p) {
397 this.prefix = p;
398 }
399
400 public boolean isAggregate() {
401 return value == null;
402 }
403
404 public Entry<V> getEntry() {
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700405 return new PatriciaTreeEntry(prefix, value);
Ray Milkey269ffb92014-04-03 14:43:30 -0700406 }
407 }
408
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700409 private class PatriciaTreeEntry implements Entry<V> {
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700410 private final Prefix prefix;
411 private final V value;
Ray Milkey269ffb92014-04-03 14:43:30 -0700412
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700413 public PatriciaTreeEntry(Prefix prefix, V value) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700414 this.prefix = prefix;
415 this.value = value;
416 }
417
418 @Override
419 public Prefix getPrefix() {
420 return prefix;
421 }
422
423 @Override
424 public V getValue() {
425 return value;
426 }
427 }
428
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700429 private class PatriciaTreeIterator implements Iterator<Entry<V>> {
Ray Milkey269ffb92014-04-03 14:43:30 -0700430 private Node current;
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700431 private boolean started; // initialized to false
Ray Milkey269ffb92014-04-03 14:43:30 -0700432
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700433 public PatriciaTreeIterator(Node start) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700434 current = start;
435
436 //If the start is an aggregate node fast forward to find the next valid node
437 if (current != null && current.isAggregate()) {
438 current = findNext(current);
439 }
440 }
441
442 @Override
443 public boolean hasNext() {
444 if (current == null) {
445 return false;
446 }
447
448 if (!started) {
449 return true;
450 }
451
452 return findNext(current) != null;
453 }
454
455 @Override
456 public Entry<V> next() {
457 if (current == null) {
458 throw new NoSuchElementException();
459 }
460
461 if (!started) {
462 started = true;
463 return current.getEntry();
464 }
465
466 current = findNext(current);
467 if (current == null) {
468 throw new NoSuchElementException();
469 }
470
471 return current.getEntry();
472 }
473
474 @Override
475 public void remove() {
476 // TODO This could be implemented, if it were needed
477 throw new NoSuchElementException();
478 }
479
480 private Node findNext(Node node) {
481 Node next = null;
482
483 if (node.left != null) {
484 next = node.left;
485 //addReference(next);
486 //delReference(node);
487 //return next;
488 } else if (node.right != null) {
489 next = node.right;
490 //addReference(next);
491 //delReference(node);
492 //return next;
493 } else {
494 //Node start = node;
495 while (node.parent != null) {
496 if (node.parent.left == node && node.parent.right != null) {
497 next = node.parent.right;
498 //addReference(next);
499 //delReference(start);
500 //return next;
501 break;
502 }
503 node = node.parent;
504 }
505 }
506
507 if (next == null) {
508 return null;
509 }
510
511 //If the node doesn't have a value, it's not an actual node, it's an artifically
512 //inserted aggregate node. We don't want to return these to the user.
513 if (next.isAggregate()) {
514 return findNext(next);
515 }
516
517 return next;
518 }
519 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200520}