blob: 28093fc265fb73585d98d2c03fa7858293cd9c82 [file] [log] [blame]
Jonathan Hart8f5f4682013-08-07 22:13:39 +12001package net.onrc.onos.ofcontroller.bgproute;
2
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) {
20 if (prefix.getPrefixLength() > maxPrefixLength) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +120021 throw new IllegalArgumentException(String.format(
22 "Prefix length %d is greater than max prefix length %d",
Jonathan Hart29b972d2013-08-12 23:43:51 +120023 prefix.getPrefixLength(), maxPrefixLength));
Jonathan Hart8f5f4682013-08-07 22:13:39 +120024 }
25
Jonathan Hart29b972d2013-08-12 23:43:51 +120026 if (prefix == null || value == null) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +120027 throw new NullPointerException();
28 }
29
30 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 Hart8f5f4682013-08-07 22:13:39 +1200104 //TODO
105
Jonathan Hart29b972d2013-08-12 23:43:51 +1200106 if (prefix.getPrefixLength() > maxPrefixLength) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200107 return null;
108 }
109
110 /*
111 Node node = top;
112
113 while (node != null
114 && node.prefix.getPrefixLength() <= p.getPrefixLength()
115 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
116 if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
117 //return addReference(node);
118 return node.rib;
119 }
120
121 if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
122 node = node.right;
123 } else {
124 node = node.left;
125 }
126 }
127 */
128
Jonathan Hart29b972d2013-08-12 23:43:51 +1200129 Node node = findNode(prefix);
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200130
Jonathan Hart29b972d2013-08-12 23:43:51 +1200131 return node == null ? null : node.value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200132 }
133
134 /*closest containing prefix*/
Jonathan Hart29b972d2013-08-12 23:43:51 +1200135 @Override
136 public synchronized V match(Prefix prefix) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200137 //TODO
138 return null;
139 }
140
Jonathan Hart29b972d2013-08-12 23:43:51 +1200141 @Override
142 public synchronized boolean remove(Prefix prefix, V value) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200143 Node child;
144 Node parent;
145
Jonathan Hart29b972d2013-08-12 23:43:51 +1200146 if (prefix == null || value == null) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200147 return false;
148 }
149
Jonathan Hart29b972d2013-08-12 23:43:51 +1200150 Node node = findNode(prefix);
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200151
Jonathan Hart29b972d2013-08-12 23:43:51 +1200152 if (node == null || node.isAggregate() || !node.value.equals(value)) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200153 //Given <prefix, nexthop> mapping is not in the tree
154 return false;
155 }
156
157 if (node.left != null && node.right != null) {
Jonathan Hartb39a67d2013-08-10 23:59:50 +1200158 //Remove the RibEntry entry and leave this node as an aggregate node
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200159 //In the future, maybe we should re-evaluate what the aggregate prefix should be?
160 //It shouldn't necessarily stay the same.
161 //More complicated if the above prefix is also aggregate.
Jonathan Hart29b972d2013-08-12 23:43:51 +1200162 node.value = null;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200163 return true;
164 }
165
166 if (node.left != null) {
167 child = node.left;
168 } else {
169 child = node.right;
170 }
171
172 parent = node.parent;
173
174 if (child != null) {
175 child.parent = parent;
176 }
177
178 if (parent != null) {
179 if (parent.left == node) {
180 parent.left = child;
181 } else {
182 parent.right = child;
183 }
184 } else {
185 top = child;
186 }
187
188 /*
189 * TODO not sure what to do here. I think this is lazily deleting aggregate nodes,
190 * notice that it used to do nothing if it detected both children were not null earlier.
191 * But here, what we really should do is reevaluate the aggregate prefix of the parent
192 * node (if it is indeed an aggregate). Because at the moment, no aggregate node will ever
193 * be removed. BUT, I don't actually think this presents a correctness problem, at
194 * least from an external point of view.
195 */
196 //if (parent != null && parent.refCount == 0) {
197 //node_remove(parent);
198 //}
199
200 return true;
201 }
202
Jonathan Hart29b972d2013-08-12 23:43:51 +1200203 @Override
204 public Iterator<Entry<V>> iterator() {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200205 return new PatriciaTrieIterator(top);
206 }
207
Jonathan Hart29b972d2013-08-12 23:43:51 +1200208 private Node findNode(Prefix prefix) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200209 Node node = top;
210
211 while (node != null
Jonathan Hart29b972d2013-08-12 23:43:51 +1200212 && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
213 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
214 if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200215 //return addReference(node);
216 return node;
217 }
218
Jonathan Hart29b972d2013-08-12 23:43:51 +1200219 if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200220 node = node.right;
221 } else {
222 node = node.left;
223 }
224 }
225
226 return null;
227 }
228
229 /*
230 * Receives a 1-based bit index
231 * Returns a 1-based byte index
232 * eg. (0 => 1), 1 => 1, 8 => 1, 9 => 2, 17 => 3
233 */
234 private int getByteContainingBit(int bitNumber) {
235 return Math.max((bitNumber + 7) / 8, 1);
236 }
237
238 private boolean key_match(byte [] key1, int key1_len, byte [] key2, int key2_len) {
239 //int offset;
240 //int shift;
241
242 if (key1_len > key2_len) {
243 return false;
244 }
245
246 int offset = (Math.min(key1_len, key2_len)) / 8;
247 int shift = (Math.min(key1_len, key2_len)) % 8;
248
249 if (shift != 0) {
250 if ((maskBits[shift] & (key1[offset] ^ key2[offset])) != 0) {
251 return false;
252 }
253 }
254
255 while (offset != 0) {
256 offset--;
257 if (key1[offset] != key2[offset]) {
258 return false;
259 }
260 }
261 return true;
262 }
263
264 private boolean bit_check(byte [] key, int key_bits) {
265 int offset = key_bits / 8;
266 int shift = 7 - (key_bits % 8);
267 int bit = key[offset] & 0xff;
268
269 bit >>= shift;
270
271 if ((bit & 1) == 1) {
272 return true;
273 } else {
274 return false;
275 }
276 }
277
278 private void node_link(Node node, Node add) {
279 boolean bit = bit_check(add.prefix.getAddress(), node.prefix.getPrefixLength());
280
281 if (bit == true) {
282 node.right = add;
283 } else {
284 node.left = add;
285 }
286 add.parent = node;
287 }
288
289 private Node node_common(Node node, byte [] key, int key_bits) {
290 int i;
291 int limit = Math.min(node.prefix.getPrefixLength(), key_bits) / 8;
292
293 for (i = 0; i < limit; i++) {
294 if (node.prefix.getAddress()[i] != key[i]) {
295 break;
296 }
297 }
298
299 int common_len = i * 8;
300 int boundary = 0;
301
302 if (common_len != key_bits) {
303 byte diff = (byte)(node.prefix.getAddress()[i] ^ key[i]);
304 byte mask = (byte)0x80;
305 int shift_mask = 0;
306
307 while (common_len < key_bits && ((mask & diff) == 0)) {
308 boundary = 1;
309
310 shift_mask = (mask & 0xff);
311 shift_mask >>= 1;
312 mask = (byte)shift_mask;
313
314 common_len++;
315 }
316 }
317
318 //Node add = new Node(null, common_len, maxKeyOctets);
319 //if (add == null)
320 //Another -ENOMEM;
321 //return null;
322
323 //Creating a new Prefix with a prefix length of common_len
324 //Bits are copied from node's up until the common_len'th bit
Jonathan Hartb39a67d2013-08-10 23:59:50 +1200325 //RibEntry is null, because this is an aggregate prefix - it's not
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200326 //actually been added to the trie.
327
328 byte[] newPrefix = new byte[getByteContainingBit(maxPrefixLength)];
329
330 int j;
331 for (j = 0; j < i; j++)
332 newPrefix[j] = node.prefix.getAddress()[j];
333
334 if (boundary != 0)
335 newPrefix[j] = (byte)(node.prefix.getAddress()[j] & maskBits[common_len % 8]);
336
Jonathan Hart29b972d2013-08-12 23:43:51 +1200337 //return new Node(new Prefix(newPrefix, common_len), null);
338 return new Node(new Prefix(newPrefix, common_len));
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200339 //return add;
340 }
341
342 private class Node {
343 public Node parent = null;
344 public Node left = null;
345 public Node right = null;
346
Jonathan Hart29b972d2013-08-12 23:43:51 +1200347 public final Prefix prefix;
348 public V value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200349
Jonathan Hart29b972d2013-08-12 23:43:51 +1200350 //public Node(Prefix p, RibEntry r) {
351 // this.prefix = p;
352 // this.rib = r;
353 //}
354 public Node(Prefix p) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200355 this.prefix = p;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200356 }
357
Jonathan Hart29b972d2013-08-12 23:43:51 +1200358 public boolean isAggregate() {
359 return value == null;
360 }
361
362 public Entry<V> getEntry() {
363 return new PatriciaTrieEntry(prefix, value);
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200364 }
365 }
366
Jonathan Hart29b972d2013-08-12 23:43:51 +1200367 private class PatriciaTrieEntry implements Entry<V> {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200368 private Prefix prefix;
Jonathan Hart29b972d2013-08-12 23:43:51 +1200369 private V value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200370
Jonathan Hart29b972d2013-08-12 23:43:51 +1200371 public PatriciaTrieEntry(Prefix prefix, V value) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200372 this.prefix = prefix;
Jonathan Hart29b972d2013-08-12 23:43:51 +1200373 this.value = value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200374 }
375
376 @Override
377 public Prefix getPrefix() {
378 return prefix;
379 }
380
381 @Override
Jonathan Hart29b972d2013-08-12 23:43:51 +1200382 public V getValue() {
383 return value;
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200384 }
385 }
386
Jonathan Hart29b972d2013-08-12 23:43:51 +1200387 private class PatriciaTrieIterator implements Iterator<Entry<V>> {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200388
389 private Node current;
390 private boolean started = false;
391
392 public PatriciaTrieIterator(Node start) {
393 current = start;
394
395 //If the start is an aggregate node fast forward to find the next valid node
Jonathan Hart29b972d2013-08-12 23:43:51 +1200396 if (current != null && current.isAggregate()) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200397 current = findNext(current);
398 }
399 }
400
401 @Override
402 public boolean hasNext() {
403 if (current == null) {
404 return false;
405 }
406
407 if (!started) {
408 return true;
409 }
410
411 return findNext(current) != null;
412 }
413
414 @Override
Jonathan Hart29b972d2013-08-12 23:43:51 +1200415 public Entry<V> next() {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200416 if (current == null) {
417 throw new NoSuchElementException();
418 }
419
420 if (!started) {
421 started = true;
422 return current.getEntry();
423 }
424
425 current = findNext(current);
426 if (current == null) {
427 throw new NoSuchElementException();
428 }
429
430 return current.getEntry();
431 }
432
433 @Override
434 public void remove() {
435 // TODO This could be implemented, if it were needed
436 throw new NoSuchElementException();
437 }
438
439 private Node findNext(Node node) {
440 Node next = null;
441
442 if (node.left != null) {
443 next = node.left;
444 //addReference(next);
445 //delReference(node);
446 //return next;
447 }
448 else if (node.right != null) {
449 next = node.right;
450 //addReference(next);
451 //delReference(node);
452 //return next;
453 }
454 else {
455 //Node start = node;
456 while (node.parent != null) {
457 if (node.parent.left == node && node.parent.right != null) {
458 next = node.parent.right;
459 //addReference(next);
460 //delReference(start);
461 //return next;
462 break;
463 }
464 node = node.parent;
465 }
466 }
467
468 if (next == null) {
469 return null;
470 }
471
Jonathan Hart29b972d2013-08-12 23:43:51 +1200472 //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 +1200473 //inserted aggregate node. We don't want to return these to the user.
Jonathan Hart29b972d2013-08-12 23:43:51 +1200474 if (next.isAggregate()) {
Jonathan Hart8f5f4682013-08-07 22:13:39 +1200475 return findNext(next);
476 }
477
478 return next;
479 }
480 }
481}