blob: 514758fb7f248fc81c7946b1b4cb9b2b3903d808 [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
6public class PatriciaTrie implements IPatriciaTrie{
7 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
18 public synchronized Rib put(Prefix p, Rib r) {
19 if (p.getPrefixLength() > maxPrefixLength) {
20 throw new IllegalArgumentException(String.format(
21 "Prefix length %d is greater than max prefix length %d",
22 p.getPrefixLength(), maxPrefixLength));
23 }
24
25 if (p == null || r == null) {
26 throw new NullPointerException();
27 }
28
29 Node node = top;
30 Node match = null;
31
32 while (node != null
33 && node.prefix.getPrefixLength() <= p.getPrefixLength()
34 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
35 if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
36 /*
37 * Prefix is already in tree. This may be an aggregate node, in which case
38 * we are inserting a new prefix, or it could be an actual node, in which
39 * case we are inserting a new nexthop for the prefix and should return
40 * the old nexthop.
41 */
42 Rib oldRib = node.rib;
43 node.rib = r;
44 return oldRib;
45 }
46
47 match = node;
48
49 if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
50 node = node.right;
51 } else {
52 node = node.left;
53 }
54 }
55
56 Node add = null;
57
58 if (node == null) {
59 add = new Node(p, r);
60
61 if (match != null) {
62 node_link(match, add);
63 } else {
64 top = add;
65 }
66 } else {
67 add = node_common(node, p.getAddress(), p.getPrefixLength());
68 if (add == null) {
69 //I think this is -ENOMEM?
70 //return null;
71 }
72
73 if (match != null) {
74 node_link(match, add);
75 } else {
76 top = add;
77 }
78 node_link(add, node);
79
80 if (add.prefix.getPrefixLength() != p.getPrefixLength()) {
81 match = add;
82
83 add = new Node(p, r);
84 node_link(match, add);
85 }
86 else {
87 add.rib = r;
88 }
89 }
90
91 //If we added a new Node, there was no previous mapping
92 return null;
93 //return addReference(add);
94 }
95
96 /*exact match*/
97 public synchronized Rib lookup(Prefix p) {
98 //TODO
99
100 if (p.getPrefixLength() > maxPrefixLength) {
101 return null;
102 }
103
104 /*
105 Node node = top;
106
107 while (node != null
108 && node.prefix.getPrefixLength() <= p.getPrefixLength()
109 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
110 if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
111 //return addReference(node);
112 return node.rib;
113 }
114
115 if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
116 node = node.right;
117 } else {
118 node = node.left;
119 }
120 }
121 */
122
123 Node node = findNode(p);
124
125 return node == null ? null : node.rib;
126 }
127
128 /*closest containing prefix*/
129 public synchronized Rib match(Prefix p) {
130 //TODO
131 return null;
132 }
133
134 public synchronized boolean remove(Prefix p, Rib r) {
135 Node child;
136 Node parent;
137
138 if (p == null || r == null) {
139 return false;
140 }
141
142 Node node = findNode(p);
143
144 if (node == null || node.rib == null || !node.rib.equals(r)) {
145 //Given <prefix, nexthop> mapping is not in the tree
146 return false;
147 }
148
149 if (node.left != null && node.right != null) {
150 //Remove the Rib entry and leave this node as an aggregate node
151 //In the future, maybe we should re-evaluate what the aggregate prefix should be?
152 //It shouldn't necessarily stay the same.
153 //More complicated if the above prefix is also aggregate.
154 node.rib = null;
155 return true;
156 }
157
158 if (node.left != null) {
159 child = node.left;
160 } else {
161 child = node.right;
162 }
163
164 parent = node.parent;
165
166 if (child != null) {
167 child.parent = parent;
168 }
169
170 if (parent != null) {
171 if (parent.left == node) {
172 parent.left = child;
173 } else {
174 parent.right = child;
175 }
176 } else {
177 top = child;
178 }
179
180 /*
181 * TODO not sure what to do here. I think this is lazily deleting aggregate nodes,
182 * notice that it used to do nothing if it detected both children were not null earlier.
183 * But here, what we really should do is reevaluate the aggregate prefix of the parent
184 * node (if it is indeed an aggregate). Because at the moment, no aggregate node will ever
185 * be removed. BUT, I don't actually think this presents a correctness problem, at
186 * least from an external point of view.
187 */
188 //if (parent != null && parent.refCount == 0) {
189 //node_remove(parent);
190 //}
191
192 return true;
193 }
194
195 public Iterator<Entry> iterator() {
196 return new PatriciaTrieIterator(top);
197 }
198
199 private Node findNode(Prefix p) {
200 Node node = top;
201
202 while (node != null
203 && node.prefix.getPrefixLength() <= p.getPrefixLength()
204 && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
205 if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
206 //return addReference(node);
207 return node;
208 }
209
210 if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
211 node = node.right;
212 } else {
213 node = node.left;
214 }
215 }
216
217 return null;
218 }
219
220 /*
221 * Receives a 1-based bit index
222 * Returns a 1-based byte index
223 * eg. (0 => 1), 1 => 1, 8 => 1, 9 => 2, 17 => 3
224 */
225 private int getByteContainingBit(int bitNumber) {
226 return Math.max((bitNumber + 7) / 8, 1);
227 }
228
229 private boolean key_match(byte [] key1, int key1_len, byte [] key2, int key2_len) {
230 //int offset;
231 //int shift;
232
233 if (key1_len > key2_len) {
234 return false;
235 }
236
237 int offset = (Math.min(key1_len, key2_len)) / 8;
238 int shift = (Math.min(key1_len, key2_len)) % 8;
239
240 if (shift != 0) {
241 if ((maskBits[shift] & (key1[offset] ^ key2[offset])) != 0) {
242 return false;
243 }
244 }
245
246 while (offset != 0) {
247 offset--;
248 if (key1[offset] != key2[offset]) {
249 return false;
250 }
251 }
252 return true;
253 }
254
255 private boolean bit_check(byte [] key, int key_bits) {
256 int offset = key_bits / 8;
257 int shift = 7 - (key_bits % 8);
258 int bit = key[offset] & 0xff;
259
260 bit >>= shift;
261
262 if ((bit & 1) == 1) {
263 return true;
264 } else {
265 return false;
266 }
267 }
268
269 private void node_link(Node node, Node add) {
270 boolean bit = bit_check(add.prefix.getAddress(), node.prefix.getPrefixLength());
271
272 if (bit == true) {
273 node.right = add;
274 } else {
275 node.left = add;
276 }
277 add.parent = node;
278 }
279
280 private Node node_common(Node node, byte [] key, int key_bits) {
281 int i;
282 int limit = Math.min(node.prefix.getPrefixLength(), key_bits) / 8;
283
284 for (i = 0; i < limit; i++) {
285 if (node.prefix.getAddress()[i] != key[i]) {
286 break;
287 }
288 }
289
290 int common_len = i * 8;
291 int boundary = 0;
292
293 if (common_len != key_bits) {
294 byte diff = (byte)(node.prefix.getAddress()[i] ^ key[i]);
295 byte mask = (byte)0x80;
296 int shift_mask = 0;
297
298 while (common_len < key_bits && ((mask & diff) == 0)) {
299 boundary = 1;
300
301 shift_mask = (mask & 0xff);
302 shift_mask >>= 1;
303 mask = (byte)shift_mask;
304
305 common_len++;
306 }
307 }
308
309 //Node add = new Node(null, common_len, maxKeyOctets);
310 //if (add == null)
311 //Another -ENOMEM;
312 //return null;
313
314 //Creating a new Prefix with a prefix length of common_len
315 //Bits are copied from node's up until the common_len'th bit
316 //Rib is null, because this is an aggregate prefix - it's not
317 //actually been added to the trie.
318
319 byte[] newPrefix = new byte[getByteContainingBit(maxPrefixLength)];
320
321 int j;
322 for (j = 0; j < i; j++)
323 newPrefix[j] = node.prefix.getAddress()[j];
324
325 if (boundary != 0)
326 newPrefix[j] = (byte)(node.prefix.getAddress()[j] & maskBits[common_len % 8]);
327
328 return new Node(new Prefix(newPrefix, common_len), null);
329 //return add;
330 }
331
332 private class Node {
333 public Node parent = null;
334 public Node left = null;
335 public Node right = null;
336
337 public Prefix prefix;
338 public Rib rib;
339
340 public Node(Prefix p, Rib r) {
341 this.prefix = p;
342 this.rib = r;
343 }
344
345 public Entry getEntry() {
346 return new PatriciaTrieEntry(prefix, rib);
347 }
348 }
349
350 private class PatriciaTrieEntry implements Entry {
351 private Prefix prefix;
352 private Rib rib;
353
354 public PatriciaTrieEntry(Prefix prefix, Rib rib) {
355 this.prefix = prefix;
356 this.rib = rib;
357 }
358
359 @Override
360 public Prefix getPrefix() {
361 return prefix;
362 }
363
364 @Override
365 public Rib getRib() {
366 return rib;
367 }
368 }
369
370 private class PatriciaTrieIterator implements Iterator<Entry> {
371
372 private Node current;
373 private boolean started = false;
374
375 public PatriciaTrieIterator(Node start) {
376 current = start;
377
378 //If the start is an aggregate node fast forward to find the next valid node
379 if (current != null && current.rib == null) {
380 current = findNext(current);
381 }
382 }
383
384 @Override
385 public boolean hasNext() {
386 if (current == null) {
387 return false;
388 }
389
390 if (!started) {
391 return true;
392 }
393
394 return findNext(current) != null;
395 }
396
397 @Override
398 public Entry next() {
399 if (current == null) {
400 throw new NoSuchElementException();
401 }
402
403 if (!started) {
404 started = true;
405 return current.getEntry();
406 }
407
408 current = findNext(current);
409 if (current == null) {
410 throw new NoSuchElementException();
411 }
412
413 return current.getEntry();
414 }
415
416 @Override
417 public void remove() {
418 // TODO This could be implemented, if it were needed
419 throw new NoSuchElementException();
420 }
421
422 private Node findNext(Node node) {
423 Node next = null;
424
425 if (node.left != null) {
426 next = node.left;
427 //addReference(next);
428 //delReference(node);
429 //return next;
430 }
431 else if (node.right != null) {
432 next = node.right;
433 //addReference(next);
434 //delReference(node);
435 //return next;
436 }
437 else {
438 //Node start = node;
439 while (node.parent != null) {
440 if (node.parent.left == node && node.parent.right != null) {
441 next = node.parent.right;
442 //addReference(next);
443 //delReference(start);
444 //return next;
445 break;
446 }
447 node = node.parent;
448 }
449 }
450
451 if (next == null) {
452 return null;
453 }
454
455 //If the node doesn't have a rib, it's not an actual node, it's an artifically
456 //inserted aggregate node. We don't want to return these to the user.
457 if (next.rib == null) {
458 return findNext(next);
459 }
460
461 return next;
462 }
463 }
464}