blob: 9badd117c7074aacc38d2965b0fc23e22f2a2cde [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> {
Jonathan Hart8f5f4682013-08-07 22:13:39 +12007 private final byte maskBits[] = {(byte)0x00, (byte)0x80, (byte)0xc0, (byte)0xe0, (byte)0xf0,
8 (byte)0xf8, (byte)0xfc, (byte)0xfe, (byte)0xff};
9
10 private int maxPrefixLength;
11
12 private Node top;
13
14 public PatriciaTrie(int maxPrefixLength) {
15 this.maxPrefixLength = maxPrefixLength;
16 }
17
Jonathan Hart29b972d2013-08-12 23:43:51 +120018 @Override
19 public synchronized V put(Prefix prefix, V value) {
Naoki Shiota1a5ca912014-01-03 17:02:31 -080020 if (prefix == null || value == null) {
21 throw new NullPointerException();
22 }
23
Jonathan Hart29b972d2013-08-12 23:43:51 +120024 if (prefix.getPrefixLength() > maxPrefixLength) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +120025 throw new IllegalArgumentException(String.format(
26 "Prefix length %d is greater than max prefix length %d",
Jonathan Hart29b972d2013-08-12 23:43:51 +120027 prefix.getPrefixLength(), maxPrefixLength));
Jonathan Hart8f5f4682013-08-07 22:13:39 +120028 }
29
Jonathan Hart8f5f4682013-08-07 22:13:39 +120030 Node node = top;
31 Node match = null;
32
33 while (node != null
Jonathan Hart29b972d2013-08-12 23:43:51 +120034 && 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()) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +120037 /*
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 */
Jonathan Hart29b972d2013-08-12 23:43:51 +120043 V oldValue = node.value;
44 node.value = value;
45 return oldValue;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120046 }
47
48 match = node;
49
Jonathan Hart29b972d2013-08-12 23:43:51 +120050 if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +120051 node = node.right;
52 } else {
53 node = node.left;
54 }
55 }
56
57 Node add = null;
58
59 if (node == null) {
Jonathan Hart29b972d2013-08-12 23:43:51 +120060 //add = new Node(p, r);
61 add = new Node(prefix);
62 add.value = value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120063
64 if (match != null) {
65 node_link(match, add);
66 } else {
67 top = add;
68 }
69 } else {
Jonathan Hart29b972d2013-08-12 23:43:51 +120070 add = node_common(node, prefix.getAddress(), prefix.getPrefixLength());
Jonathan Hart8f5f4682013-08-07 22:13:39 +120071 if (add == null) {
72 //I think this is -ENOMEM?
73 //return null;
74 }
75
76 if (match != null) {
77 node_link(match, add);
78 } else {
79 top = add;
80 }
81 node_link(add, node);
82
Jonathan Hart29b972d2013-08-12 23:43:51 +120083 if (add.prefix.getPrefixLength() != prefix.getPrefixLength()) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +120084 match = add;
85
Jonathan Hart29b972d2013-08-12 23:43:51 +120086 //add = new Node(p, r);
87 add = new Node(prefix);
88 add.value = value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120089 node_link(match, add);
90 }
91 else {
Jonathan Hart29b972d2013-08-12 23:43:51 +120092 add.value = value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +120093 }
94 }
95
96 //If we added a new Node, there was no previous mapping
97 return null;
98 //return addReference(add);
99 }
100
101 /*exact match*/
Jonathan Hart29b972d2013-08-12 23:43:51 +1200102 @Override
103 public synchronized V lookup(Prefix prefix) {
Jonathan Hart29b972d2013-08-12 23:43:51 +1200104 if (prefix.getPrefixLength() > maxPrefixLength) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200105 return null;
106 }
107
108 /*
109 Node node = top;
110
111 while (node != null
112 && node.prefix.getPrefixLength() <= p.getPrefixLength()
113 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
114 if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
115 //return addReference(node);
116 return node.rib;
117 }
118
119 if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
120 node = node.right;
121 } else {
122 node = node.left;
123 }
124 }
125 */
126
Jonathan Hart29b972d2013-08-12 23:43:51 +1200127 Node node = findNode(prefix);
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200128
Jonathan Hart29b972d2013-08-12 23:43:51 +1200129 return node == null ? null : node.value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200130 }
131
132 /*closest containing prefix*/
Jonathan Hart29b972d2013-08-12 23:43:51 +1200133 @Override
134 public synchronized V match(Prefix prefix) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200135 //TODO
Jonathan Hartabf10222013-08-13 10:19:34 +1200136 if (prefix.getPrefixLength() > maxPrefixLength) {
137 return null;
138 }
139
140 Node closestNode = findClosestNode(prefix);
141
142 return closestNode == null ? null : closestNode.value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200143 }
144
Jonathan Hart29b972d2013-08-12 23:43:51 +1200145 @Override
146 public synchronized boolean remove(Prefix prefix, V value) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200147 Node child;
148 Node parent;
149
Jonathan Hart29b972d2013-08-12 23:43:51 +1200150 if (prefix == null || value == null) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200151 return false;
152 }
153
Jonathan Hart29b972d2013-08-12 23:43:51 +1200154 Node node = findNode(prefix);
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200155
Jonathan Hart29b972d2013-08-12 23:43:51 +1200156 if (node == null || node.isAggregate() || !node.value.equals(value)) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200157 //Given <prefix, nexthop> mapping is not in the tree
158 return false;
159 }
160
161 if (node.left != null && node.right != null) {
Jonathan Hartb39a67d2013-08-10 23:59:50 +1200162 //Remove the RibEntry entry and leave this node as an aggregate node
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200163 //In the future, maybe we should re-evaluate what the aggregate prefix should be?
164 //It shouldn't necessarily stay the same.
165 //More complicated if the above prefix is also aggregate.
Jonathan Hart29b972d2013-08-12 23:43:51 +1200166 node.value = null;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200167 return true;
168 }
169
170 if (node.left != null) {
171 child = node.left;
172 } else {
173 child = node.right;
174 }
175
176 parent = node.parent;
177
178 if (child != null) {
179 child.parent = parent;
180 }
181
182 if (parent != null) {
183 if (parent.left == node) {
184 parent.left = child;
185 } else {
186 parent.right = child;
187 }
188 } else {
189 top = child;
190 }
191
192 /*
193 * TODO not sure what to do here. I think this is lazily deleting aggregate nodes,
194 * notice that it used to do nothing if it detected both children were not null earlier.
195 * But here, what we really should do is reevaluate the aggregate prefix of the parent
196 * node (if it is indeed an aggregate). Because at the moment, no aggregate node will ever
197 * be removed. BUT, I don't actually think this presents a correctness problem, at
198 * least from an external point of view.
199 */
200 //if (parent != null && parent.refCount == 0) {
201 //node_remove(parent);
202 //}
203
204 return true;
205 }
206
Jonathan Hart29b972d2013-08-12 23:43:51 +1200207 @Override
208 public Iterator<Entry<V>> iterator() {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200209 return new PatriciaTrieIterator(top);
210 }
211
Jonathan Hart29b972d2013-08-12 23:43:51 +1200212 private Node findNode(Prefix prefix) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200213 Node node = top;
214
215 while (node != null
Jonathan Hart29b972d2013-08-12 23:43:51 +1200216 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
217 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
218 if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200219 //return addReference(node);
220 return node;
221 }
222
Jonathan Hart29b972d2013-08-12 23:43:51 +1200223 if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200224 node = node.right;
225 } else {
226 node = node.left;
227 }
228 }
229
230 return null;
231 }
232
Jonathan Hartabf10222013-08-13 10:19:34 +1200233 private Node findClosestNode(Prefix prefix) {
234 Node node = top;
235 Node match = null;
236
237 while (node != null
238 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
239 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
240 if (!node.isAggregate()) {
241 match = node;
242 }
243
244 if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
245 node = node.right;
246 } else {
247 node = node.left;
248 }
249 }
250
251 return match;
252 }
253
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200254 /*
255 * Receives a 1-based bit index
256 * Returns a 1-based byte index
257 * eg. (0 => 1), 1 => 1, 8 => 1, 9 => 2, 17 => 3
258 */
259 private int getByteContainingBit(int bitNumber) {
260 return Math.max((bitNumber + 7) / 8, 1);
261 }
262
263 private boolean key_match(byte [] key1, int key1_len, byte [] key2, int key2_len) {
264 //int offset;
265 //int shift;
266
267 if (key1_len > key2_len) {
268 return false;
269 }
270
271 int offset = (Math.min(key1_len, key2_len)) / 8;
272 int shift = (Math.min(key1_len, key2_len)) % 8;
273
274 if (shift != 0) {
275 if ((maskBits[shift] & (key1[offset] ^ key2[offset])) != 0) {
276 return false;
277 }
278 }
279
280 while (offset != 0) {
281 offset--;
282 if (key1[offset] != key2[offset]) {
283 return false;
284 }
285 }
286 return true;
287 }
288
289 private boolean bit_check(byte [] key, int key_bits) {
290 int offset = key_bits / 8;
291 int shift = 7 - (key_bits % 8);
292 int bit = key[offset] & 0xff;
293
294 bit >>= shift;
295
296 if ((bit & 1) == 1) {
297 return true;
298 } else {
299 return false;
300 }
301 }
302
303 private void node_link(Node node, Node add) {
304 boolean bit = bit_check(add.prefix.getAddress(), node.prefix.getPrefixLength());
305
306 if (bit == true) {
307 node.right = add;
308 } else {
309 node.left = add;
310 }
311 add.parent = node;
312 }
313
314 private Node node_common(Node node, byte [] key, int key_bits) {
315 int i;
316 int limit = Math.min(node.prefix.getPrefixLength(), key_bits) / 8;
317
318 for (i = 0; i < limit; i++) {
319 if (node.prefix.getAddress()[i] != key[i]) {
320 break;
321 }
322 }
323
324 int common_len = i * 8;
325 int boundary = 0;
326
327 if (common_len != key_bits) {
328 byte diff = (byte)(node.prefix.getAddress()[i] ^ key[i]);
329 byte mask = (byte)0x80;
330 int shift_mask = 0;
331
332 while (common_len < key_bits && ((mask & diff) == 0)) {
333 boundary = 1;
334
335 shift_mask = (mask & 0xff);
336 shift_mask >>= 1;
337 mask = (byte)shift_mask;
338
339 common_len++;
340 }
341 }
342
343 //Node add = new Node(null, common_len, maxKeyOctets);
344 //if (add == null)
345 //Another -ENOMEM;
346 //return null;
347
348 //Creating a new Prefix with a prefix length of common_len
349 //Bits are copied from node's up until the common_len'th bit
Jonathan Hartb39a67d2013-08-10 23:59:50 +1200350 //RibEntry is null, because this is an aggregate prefix - it's not
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200351 //actually been added to the trie.
352
353 byte[] newPrefix = new byte[getByteContainingBit(maxPrefixLength)];
354
355 int j;
356 for (j = 0; j < i; j++)
357 newPrefix[j] = node.prefix.getAddress()[j];
358
359 if (boundary != 0)
360 newPrefix[j] = (byte)(node.prefix.getAddress()[j] & maskBits[common_len % 8]);
361
Jonathan Hart29b972d2013-08-12 23:43:51 +1200362 //return new Node(new Prefix(newPrefix, common_len), null);
363 return new Node(new Prefix(newPrefix, common_len));
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200364 //return add;
365 }
366
367 private class Node {
368 public Node parent = null;
369 public Node left = null;
370 public Node right = null;
371
Jonathan Hart29b972d2013-08-12 23:43:51 +1200372 public final Prefix prefix;
373 public V value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200374
Jonathan Hart29b972d2013-08-12 23:43:51 +1200375 //public Node(Prefix p, RibEntry r) {
376 // this.prefix = p;
377 // this.rib = r;
378 //}
379 public Node(Prefix p) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200380 this.prefix = p;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200381 }
382
Jonathan Hart29b972d2013-08-12 23:43:51 +1200383 public boolean isAggregate() {
384 return value == null;
385 }
386
387 public Entry<V> getEntry() {
388 return new PatriciaTrieEntry(prefix, value);
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200389 }
390 }
391
Jonathan Hart29b972d2013-08-12 23:43:51 +1200392 private class PatriciaTrieEntry implements Entry<V> {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200393 private Prefix prefix;
Jonathan Hart29b972d2013-08-12 23:43:51 +1200394 private V value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200395
Jonathan Hart29b972d2013-08-12 23:43:51 +1200396 public PatriciaTrieEntry(Prefix prefix, V value) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200397 this.prefix = prefix;
Jonathan Hart29b972d2013-08-12 23:43:51 +1200398 this.value = value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200399 }
400
401 @Override
402 public Prefix getPrefix() {
403 return prefix;
404 }
405
406 @Override
Jonathan Hart29b972d2013-08-12 23:43:51 +1200407 public V getValue() {
408 return value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200409 }
410 }
411
Jonathan Hart29b972d2013-08-12 23:43:51 +1200412 private class PatriciaTrieIterator implements Iterator<Entry<V>> {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200413
414 private Node current;
415 private boolean started = false;
416
417 public PatriciaTrieIterator(Node start) {
418 current = start;
419
420 //If the start is an aggregate node fast forward to find the next valid node
Jonathan Hart29b972d2013-08-12 23:43:51 +1200421 if (current != null && current.isAggregate()) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200422 current = findNext(current);
423 }
424 }
425
426 @Override
427 public boolean hasNext() {
428 if (current == null) {
429 return false;
430 }
431
432 if (!started) {
433 return true;
434 }
435
436 return findNext(current) != null;
437 }
438
439 @Override
Jonathan Hart29b972d2013-08-12 23:43:51 +1200440 public Entry<V> next() {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200441 if (current == null) {
442 throw new NoSuchElementException();
443 }
444
445 if (!started) {
446 started = true;
447 return current.getEntry();
448 }
449
450 current = findNext(current);
451 if (current == null) {
452 throw new NoSuchElementException();
453 }
454
455 return current.getEntry();
456 }
457
458 @Override
459 public void remove() {
460 // TODO This could be implemented, if it were needed
461 throw new NoSuchElementException();
462 }
463
464 private Node findNext(Node node) {
465 Node next = null;
466
467 if (node.left != null) {
468 next = node.left;
469 //addReference(next);
470 //delReference(node);
471 //return next;
472 }
473 else if (node.right != null) {
474 next = node.right;
475 //addReference(next);
476 //delReference(node);
477 //return next;
478 }
479 else {
480 //Node start = node;
481 while (node.parent != null) {
482 if (node.parent.left == node && node.parent.right != null) {
483 next = node.parent.right;
484 //addReference(next);
485 //delReference(start);
486 //return next;
487 break;
488 }
489 node = node.parent;
490 }
491 }
492
493 if (next == null) {
494 return null;
495 }
496
Jonathan Hart29b972d2013-08-12 23:43:51 +1200497 //If the node doesn't have a value, it's not an actual node, it's an artifically
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200498 //inserted aggregate node. We don't want to return these to the user.
Jonathan Hart29b972d2013-08-12 23:43:51 +1200499 if (next.isAggregate()) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200500 return findNext(next);
501 }
502
503 return next;
504 }
505 }
506}