blob: 844ba35177e44f8cf37d0a52c26e0fb3c5fe5d44 [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()
128 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
129 if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
130 //return addReference(node);
131 return node.rib;
132 }
133
134 if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
135 node = node.right;
136 } else {
137 node = node.left;
138 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700139 }
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700140 */
141
142 Node node = findNode(prefix);
143
144 return node == null ? null : node.value;
Ray Milkey269ffb92014-04-03 14:43:30 -0700145 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700146 }
147
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700148 // closest containing prefix
Ray Milkey269ffb92014-04-03 14:43:30 -0700149 @Override
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700150 public V match(Prefix prefix) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700151 //TODO
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700152 synchronized (this) {
153 if (prefix.getPrefixLength() > maxPrefixLength) {
154 return null;
155 }
156
157 Node closestNode = findClosestNode(prefix);
158
159 return closestNode == null ? null : closestNode.value;
Ray Milkey269ffb92014-04-03 14:43:30 -0700160 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700161 }
162
163 @Override
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700164 public boolean remove(Prefix prefix, V value) {
165 synchronized (this) {
166 if (prefix == null || value == null) {
167 return false;
168 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700169
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700170 Node node = findNode(prefix);
Ray Milkey269ffb92014-04-03 14:43:30 -0700171
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700172 if (node == null || node.isAggregate() || !node.value.equals(value)) {
173 //Given <prefix, nexthop> mapping is not in the tree
174 return false;
175 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700176
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700177 if (node.left != null && node.right != null) {
178 //Remove the RibEntry entry and leave this node as an aggregate node
179 //In the future, maybe we should re-evaluate what the aggregate prefix should be?
180 //It shouldn't necessarily stay the same.
181 //More complicated if the above prefix is also aggregate.
182 node.value = null;
183 return true;
184 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700185
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700186 Node child;
187 if (node.left != null) {
188 child = node.left;
189 } else {
190 child = node.right;
191 }
192
193 Node parent = node.parent;
194
195 if (child != null) {
196 child.parent = parent;
197 }
198
199 if (parent != null) {
200 if (parent.left == node) {
201 parent.left = child;
202 } else {
203 parent.right = child;
204 }
205 } else {
206 top = child;
207 }
208
209 /*
210 * TODO not sure what to do here. I think this is lazily deleting aggregate nodes,
211 * notice that it used to do nothing if it detected both children were not null earlier.
212 * But here, what we really should do is reevaluate the aggregate prefix of the parent
213 * node (if it is indeed an aggregate). Because at the moment, no aggregate node will ever
214 * be removed. BUT, I don't actually think this presents a correctness problem, at
215 * least from an external point of view.
216 */
217 //if (parent != null && parent.refCount == 0) {
218 //node_remove(parent);
219 //}
220
Ray Milkey269ffb92014-04-03 14:43:30 -0700221 return true;
222 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700223 }
224
225 @Override
226 public Iterator<Entry<V>> iterator() {
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700227 return new PatriciaTreeIterator(top);
Ray Milkey269ffb92014-04-03 14:43:30 -0700228 }
229
230 private Node findNode(Prefix prefix) {
231 Node node = top;
232
233 while (node != null
234 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700235 && checkKeyMatch(node.prefix.getAddress(),
236 node.prefix.getPrefixLength(),
237 prefix.getAddress(), prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700238 if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
239 //return addReference(node);
240 return node;
241 }
242
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700243 if (checkBit(prefix.getAddress(), node.prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700244 node = node.right;
245 } else {
246 node = node.left;
247 }
248 }
249
250 return null;
251 }
252
253 private Node findClosestNode(Prefix prefix) {
254 Node node = top;
255 Node match = null;
256
257 while (node != null
258 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700259 && checkKeyMatch(node.prefix.getAddress(),
260 node.prefix.getPrefixLength(),
261 prefix.getAddress(), prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700262 if (!node.isAggregate()) {
263 match = node;
264 }
265
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700266 if (checkBit(prefix.getAddress(), node.prefix.getPrefixLength())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700267 node = node.right;
268 } else {
269 node = node.left;
270 }
271 }
272
273 return match;
274 }
275
276 /*
277 * Receives a 1-based bit index
278 * Returns a 1-based byte index
279 * eg. (0 => 1), 1 => 1, 8 => 1, 9 => 2, 17 => 3
280 */
281 private int getByteContainingBit(int bitNumber) {
282 return Math.max((bitNumber + 7) / 8, 1);
283 }
284
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700285 private boolean checkKeyMatch(byte[] key1, int key1Length, byte[] key2, int key2Length) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700286 //int offset;
287 //int shift;
288
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700289 if (key1Length > key2Length) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700290 return false;
291 }
292
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700293 int offset = (Math.min(key1Length, key2Length)) / 8;
294 int shift = (Math.min(key1Length, key2Length)) % 8;
Ray Milkey269ffb92014-04-03 14:43:30 -0700295
296 if (shift != 0) {
297 if ((maskBits[shift] & (key1[offset] ^ key2[offset])) != 0) {
298 return false;
299 }
300 }
301
302 while (offset != 0) {
303 offset--;
304 if (key1[offset] != key2[offset]) {
305 return false;
306 }
307 }
308 return true;
309 }
310
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700311 private boolean checkBit(byte[] key, int keyBits) {
312 int offset = keyBits / 8;
313 int shift = 7 - (keyBits % 8);
Ray Milkey269ffb92014-04-03 14:43:30 -0700314 int bit = key[offset] & 0xff;
315
316 bit >>= shift;
317
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700318 return ((bit & 1) == 1);
Ray Milkey269ffb92014-04-03 14:43:30 -0700319 }
320
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700321 private void linkNodes(Node node, Node add) {
322 boolean bit = checkBit(add.prefix.getAddress(), node.prefix.getPrefixLength());
Ray Milkey269ffb92014-04-03 14:43:30 -0700323
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700324 if (bit) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700325 node.right = add;
326 } else {
327 node.left = add;
328 }
329 add.parent = node;
330 }
331
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700332 private Node findCommonNode(Node node, byte[] key, int keyBits) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700333 int i;
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700334 int limit = Math.min(node.prefix.getPrefixLength(), keyBits) / 8;
Ray Milkey269ffb92014-04-03 14:43:30 -0700335
336 for (i = 0; i < limit; i++) {
337 if (node.prefix.getAddress()[i] != key[i]) {
338 break;
339 }
340 }
341
Ray Milkey2476cac2014-04-08 11:03:21 -0700342 int commonLen = i * 8;
Ray Milkey269ffb92014-04-03 14:43:30 -0700343 int boundary = 0;
344
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700345 if (commonLen != keyBits) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700346 byte diff = (byte) (node.prefix.getAddress()[i] ^ key[i]);
347 byte mask = (byte) 0x80;
Ray Milkey2476cac2014-04-08 11:03:21 -0700348 int shiftMask = 0;
Ray Milkey269ffb92014-04-03 14:43:30 -0700349
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700350 while (commonLen < keyBits && ((mask & diff) == 0)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700351 boundary = 1;
352
Ray Milkey2476cac2014-04-08 11:03:21 -0700353 shiftMask = (mask & 0xff);
354 shiftMask >>= 1;
355 mask = (byte) shiftMask;
Ray Milkey269ffb92014-04-03 14:43:30 -0700356
Ray Milkey2476cac2014-04-08 11:03:21 -0700357 commonLen++;
Ray Milkey269ffb92014-04-03 14:43:30 -0700358 }
359 }
360
Ray Milkey269ffb92014-04-03 14:43:30 -0700361 //Creating a new Prefix with a prefix length of common_len
362 //Bits are copied from node's up until the common_len'th bit
363 //RibEntry is null, because this is an aggregate prefix - it's not
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700364 //actually been added to the tree.
Ray Milkey269ffb92014-04-03 14:43:30 -0700365
366 byte[] newPrefix = new byte[getByteContainingBit(maxPrefixLength)];
367
368 int j;
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700369 for (j = 0; j < i; j++) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700370 newPrefix[j] = node.prefix.getAddress()[j];
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700371 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700372
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700373 if (boundary != 0) {
374 newPrefix[j] =
375 (byte) (node.prefix.getAddress()[j] & maskBits[commonLen % 8]);
376 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700377
378 //return new Node(new Prefix(newPrefix, common_len), null);
Ray Milkey2476cac2014-04-08 11:03:21 -0700379 return new Node(new Prefix(newPrefix, commonLen));
Ray Milkey269ffb92014-04-03 14:43:30 -0700380 //return add;
381 }
382
383 private class Node {
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700384 public Node parent;
385 public Node left;
386 public Node right;
Ray Milkey269ffb92014-04-03 14:43:30 -0700387
388 public final Prefix prefix;
389 public V value;
390
391 //public Node(Prefix p, RibEntry r) {
392 // this.prefix = p;
393 // this.rib = r;
394 //}
395 public Node(Prefix p) {
396 this.prefix = p;
397 }
398
399 public boolean isAggregate() {
400 return value == null;
401 }
402
403 public Entry<V> getEntry() {
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700404 return new PatriciaTreeEntry(prefix, value);
Ray Milkey269ffb92014-04-03 14:43:30 -0700405 }
406 }
407
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700408 private class PatriciaTreeEntry implements Entry<V> {
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700409 private final Prefix prefix;
410 private final V value;
Ray Milkey269ffb92014-04-03 14:43:30 -0700411
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700412 public PatriciaTreeEntry(Prefix prefix, V value) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700413 this.prefix = prefix;
414 this.value = value;
415 }
416
417 @Override
418 public Prefix getPrefix() {
419 return prefix;
420 }
421
422 @Override
423 public V getValue() {
424 return value;
425 }
426 }
427
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700428 private class PatriciaTreeIterator implements Iterator<Entry<V>> {
Ray Milkey269ffb92014-04-03 14:43:30 -0700429 private Node current;
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700430 private boolean started; // initialized to false
Ray Milkey269ffb92014-04-03 14:43:30 -0700431
Jonathan Hart5e54f2e2014-04-17 13:43:40 -0700432 public PatriciaTreeIterator(Node start) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700433 current = start;
434
435 //If the start is an aggregate node fast forward to find the next valid node
436 if (current != null && current.isAggregate()) {
437 current = findNext(current);
438 }
439 }
440
441 @Override
442 public boolean hasNext() {
443 if (current == null) {
444 return false;
445 }
446
447 if (!started) {
448 return true;
449 }
450
451 return findNext(current) != null;
452 }
453
454 @Override
455 public Entry<V> next() {
456 if (current == null) {
457 throw new NoSuchElementException();
458 }
459
460 if (!started) {
461 started = true;
462 return current.getEntry();
463 }
464
465 current = findNext(current);
466 if (current == null) {
467 throw new NoSuchElementException();
468 }
469
470 return current.getEntry();
471 }
472
473 @Override
474 public void remove() {
475 // TODO This could be implemented, if it were needed
476 throw new NoSuchElementException();
477 }
478
479 private Node findNext(Node node) {
480 Node next = null;
481
482 if (node.left != null) {
483 next = node.left;
484 //addReference(next);
485 //delReference(node);
486 //return next;
487 } else if (node.right != null) {
488 next = node.right;
489 //addReference(next);
490 //delReference(node);
491 //return next;
492 } else {
493 //Node start = node;
494 while (node.parent != null) {
495 if (node.parent.left == node && node.parent.right != null) {
496 next = node.parent.right;
497 //addReference(next);
498 //delReference(start);
499 //return next;
500 break;
501 }
502 node = node.parent;
503 }
504 }
505
506 if (next == null) {
507 return null;
508 }
509
510 //If the node doesn't have a value, it's not an actual node, it's an artifically
511 //inserted aggregate node. We don't want to return these to the user.
512 if (next.isAggregate()) {
513 return findNext(next);
514 }
515
516 return next;
517 }
518 }
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200519}