2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.lispflowmapping.inmemorydb.radixtrie;
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.util.ArrayDeque;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.Deque;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.ListIterator;
23 * Radix trie/tree (also known as Patricia tree) implementation. Supports CRD operations for
24 * generic, big endian (network byte order) prefixes. It can do exact and longest prefix matching,
25 * post order iteration over the entries in the tree and can lookup widest negative prefixes (i.e.,
26 * shortest overlapping prefix not registered in the tree).
28 * @author Florin Coras
30 * @param <T> Data type stored in each tree node
32 public class RadixTrie<T> {
34 private TrieNode root;
35 private long nbActiveNodes;
36 private boolean rootZero;
39 * RadixTrie constructor.
41 * @param bits Maximum prefix length supported.
43 public RadixTrie(int bits) {
50 * Radix trie constructors.
52 * @param bits Maximum prefix length supported
53 * @param rootZero Flag that decides if 0/0 should be inserted as a non-prefix root node or not
55 public RadixTrie(int bits, boolean rootZero) {
57 root = rootZero ? new TrieNode(null, 0, null) : null;
59 this.rootZero = rootZero;
62 public TrieNode getRoot() {
66 private void setRoot(TrieNode root) {
70 public int getMaxbits() {
74 public long getSize() {
79 * Test bit in byte. Assumes bits are numbered from 0 to 7
81 * @param bitPosition the position to be tested
82 * @return 1 if bit at position is 1, 0 otherwise.
84 private boolean testBitInByte(byte byteArg, int bitPosition) {
85 return (byteArg & (0x80 >> bitPosition)) != 0;
88 private boolean testBitInPrefixByte(byte[] prefix, int bitPosition) {
89 return testBitInByte(prefix[bitPosition / 8], bitPosition & 0x07);
95 * @param prefix Prefix to be masked
96 * @param prefLen Prefix length
97 * @return Prefix after masking
99 private byte [] maskPrefix(byte[] prefix, int prefLen) {
100 byte[] res = prefix.clone();
101 res[prefLen / 8] = (byte) (res[prefLen / 8] & (0xFF << (7 - (prefLen & 0x07))));
102 for (int i = prefLen / 8 + 1; i < maxBits / 8; i++) {
109 * Insert prefix-data tuple into radix trie.
111 * @param prefix Big endian (network order) byte array representation of the prefix
112 * @param preflen Prefix length
113 * @param data Data to be stored in the tree
114 * @return Newly inserted TrieNode
116 public TrieNode insert(byte[] prefix, int preflen, T data) {
117 if (preflen > maxBits) {
123 root = new TrieNode(prefix, preflen, data);
127 // find closest prefix starting at ROOT
128 TrieNode closest = root.findClosest(prefix, preflen, false);
130 // find first different bit <= min(closestNode.preflen, preflen)
131 int diffbit = closest.firstDifferentBit(prefix, preflen);
133 // find the first node with bit less than diffbit
134 TrieNode node = closest.parentWithBitLessThan(diffbit);
137 return node.insert(prefix, preflen, diffbit, data, closest.prefix());
141 * Longest prefix match of prefix/preflen.
143 * @param prefix Big endian byte array representation of the prefix to be looked up.
144 * @param preflen Prefix length
145 * @return Node with longest prefix match or null if nothing is found.
147 public TrieNode lookupBest(byte[] prefix, int preflen) {
148 if (root == null || preflen > maxBits) {
152 TrieNode node = root;
153 ArrayList<TrieNode> candidates = new ArrayList<>();
155 while (node != null && node.bit < preflen) {
156 if (node.prefix != null) {
157 candidates.add(node);
160 if (testBitInPrefixByte(prefix, node.bit)) {
167 if (node != null && node.prefix != null) {
168 candidates.add(node);
171 ListIterator<TrieNode> it = candidates.listIterator(candidates.size());
172 while (it.hasPrevious()) {
173 node = it.previous();
174 if (node.comparePrefix(prefix)) {
183 * Look up the covering prefix for the argument, but exclude the argument itself, so the result is always less
184 * specific than the lookup key.
186 * @param prefix Big endian byte array representation of the prefix argument
187 * @param preflen Prefix length
188 * @return Covering node
190 public TrieNode lookupCoveringLessSpecific(byte[] prefix, int preflen) {
191 if (root == null || preflen > maxBits) {
195 TrieNode node = root.findClosest(prefix, preflen, true);
199 } else if (node.bit < preflen && node.prefix != null) {
200 // If the current node is not virtual and is less specific than the query, we can return it directly
204 // Else, we need to find a non-virtual parent
207 while (node != null && node.prefix == null) {
215 * Given an EID, lookup the longest prefix match, then return its parent node.
217 * @param prefix Prefix looked up.
218 * @param preflen Prefix length.
219 * @return Parent node of longest prefix match or null if nothing is found.
221 public TrieNode lookupParent(byte[] prefix, int preflen) {
222 TrieNode node = lookupBest(prefix, preflen);
230 while (node != null && node.prefix == null) {
238 * Given an EID, lookup the longest prefix match, then return its sibling node.
240 * @param prefix Prefix looked up.
241 * @param preflen Prefix length.
242 * @return Sibling node of longest prefix match or null if nothing is found.
244 public TrieNode lookupSibling(byte[] prefix, int preflen) {
245 TrieNode node = lookupBest(prefix, preflen);
246 TrieNode sibling = node.sibling();
248 if (sibling.prefix != null) {
256 * Given an EID, lookup the longest prefix match, then return its direct parent's sibling node, if the parent is a
259 * @param prefix Prefix looked up.
260 * @param preflen Prefix length.
261 * @return The longest prefix match node's virtual parent's sibling or null if nothing is found.
263 public TrieNode lookupVirtualParentSibling(byte[] prefix, int preflen) {
264 TrieNode node = lookupBest(prefix, preflen);
266 if (node == null || node.up == null) {
270 // Parent is not a virtual node
271 if (node.up.prefix != null) {
275 return node.up.sibling();
279 * Lookup widest negative (i.e., overlapping but not present in trie) prefix for given prefix and prefix length.
281 * @param prefix Prefix looked up.
282 * @param preflen Prefix length.
283 * @return Node containing the widest negative prefix.
285 public TrieNode lookupWidestNegative(byte [] prefix, int preflen) {
286 if (root == null || preflen > maxBits) {
290 TrieNode node = root.findClosest(prefix, preflen, false);
292 // not a negative match
293 if (node.prefix != null && node.prefixLength() <= preflen && node.comparePrefix(prefix)) {
296 int diffbit = node.firstDifferentBit(prefix, preflen);
297 return new TrieNode(maskPrefix(prefix, diffbit), diffbit + 1, null);
301 * Exact prefix match of prefix/preflen.
303 * @param prefix Big endian byte array representation of the prefix to be looked up.
304 * @param preflen Prefix length
305 * @return Node with exact prefix match or null
307 public TrieNode lookupExact(byte[] prefix, int preflen) {
308 if (root == null || preflen > maxBits) {
312 TrieNode node = root.findClosest(prefix, preflen, false);
314 // if no node is found or if node not a prefix or if mask is not the same
315 if (node == null || node.prefix == null || node.bit != preflen) {
319 // check that we have exact match
320 if (node.comparePrefix(prefix)) {
328 * Return the subtree for a prefix, including the prefix itself if present, excluding virtual nodes.
330 * @param prefix Big endian byte array representation of the prefix to be looked up.
331 * @param preflen Prefix length
332 * @return Subtree from the prefix
334 public Set<TrieNode> lookupSubtree(byte[] prefix, int preflen) {
335 if (root == null || preflen > maxBits) {
336 return Collections.emptySet();
339 TrieNode node = root.findClosest(prefix, preflen, true);
341 Set<TrieNode> children = new HashSet<>();
342 if (node.prefix != null && node.bit >= preflen) {
345 Iterator<TrieNode> it = node.iterator();
346 while (it.hasNext()) {
348 if (node.prefix != null && node.bit >= preflen) {
357 * Remove prefix from radix trie.
359 * @param prefix Big endian byte array representation of the prefix to be removed.
360 * @param preflen Prefix length.
362 public void remove(byte[] prefix, int preflen) {
363 TrieNode node = lookupExact(prefix, preflen);
370 * Remove all entries in the trie.
372 public void removeAll() {
373 Iterator<TrieNode> it = root.iterator();
374 while (it.hasNext()) {
375 TrieNode node = it.next();
381 * Trie node definition.
383 public class TrieNode implements Iterable<TrieNode> {
384 // since bits are counted from 0, bit and prefix length are equal
392 TrieNode(byte[] prefix, int prefixlen, T data) {
393 this.bit = prefixlen;
394 this.prefix = prefix;
400 public byte[] prefix() {
404 public int prefixLength() {
413 * Finds closest prefix NOT the longest prefix match.
415 * @param pref Searched prefix
416 * @param preflen Searched prefix length
417 * @param virtual Include virtual nodes in search
418 * @return The node found
420 public TrieNode findClosest(byte[] pref, int preflen, boolean virtual) {
421 TrieNode node = this;
423 while ((!virtual && node.prefix == null) || node.bit < preflen) {
424 if (testBitInPrefixByte(pref, node.bit)) {
425 if (node.right == null) {
430 if (node.left == null) {
440 private int toUnsignedInt(byte byteToConvert) {
441 return 0xFF & byteToConvert;
445 * Compares prefix to node's prefix and returns position of first different bit.
447 * @param pref Prefix to be compared.
448 * @param preflen Prefix length.
449 * @return Position of first different bit.
451 public int firstDifferentBit(byte[] pref, int preflen) {
454 int maxbit = (bit < preflen) ? bit : preflen;
456 for (int i = 0; i * 8 < maxbit; i++) {
457 bitxor = ((toUnsignedInt(this.prefix[i])) ^ toUnsignedInt(pref[i]));
458 // if match, move to next byte
460 diffbit = (i + 1) * 8;
463 // if not matched, find first diff bit (0 to 7)
464 diffbit = i * 8 + Integer.numberOfLeadingZeros(bitxor) - 24;
468 diffbit = (diffbit > maxbit) ? maxbit : diffbit;
473 * Find parent with bit less than given value.
475 * @param bitlen Bit value
476 * @return Parent with bit less than given value
478 public TrieNode parentWithBitLessThan(int bitlen) {
479 TrieNode node = this;
480 TrieNode parent = node.up;
481 while (parent != null && parent.bit >= bitlen) {
489 * Return sibling node.
491 * @return Sibling node, if there is a parent, else null
493 public TrieNode sibling() {
494 TrieNode node = this;
495 if (node == null || node.up == null) {
499 TrieNode sibling = null;
500 if (node.up.left == node) {
501 sibling = node.up.right;
503 sibling = node.up.left;
509 * Inserts node in trie near this node with prefix that has the first bit difference at diffbit.
511 * @param pref Prefix to be inserted.
512 * @param preflen Prefix length of the prefix to be inserted.
513 * @param diffbit Bit index of the first different bit between prefix and current node
514 * @param prefdata Data to be stored together with the prefix
515 * @return The trie node created or current node if it's an overwrite.
517 public TrieNode insert(byte[] pref, int preflen, int diffbit, T prefdata, byte[] closest) {
520 // same node, check if prefix needs saving
521 if (diffbit == preflen && bit == preflen) {
522 if (prefix != null) {
526 this.data = prefdata;
531 TrieNode newNode = new TrieNode(pref, preflen, prefdata);
533 // node is more specific, add new prefix as parent
534 if (preflen == diffbit) {
535 if (prefix == null ? testBitInPrefixByte(closest, preflen) : testBitInPrefixByte(prefix, preflen)) {
536 newNode.right = this;
544 } else if (this.equals(up.right)) {
551 // new prefix is more specific than node, add as child
552 } else if (bit == diffbit) {
554 if (testBitInPrefixByte(pref, bit)) {
559 // new prefix is a sibling of/on a different branch from node, add common parent
561 parent = new TrieNode(null, diffbit, null);
563 if (testBitInPrefixByte(pref, diffbit)) {
564 parent.right = newNode;
568 parent.left = newNode;
573 } else if (this.equals(up.right)) {
587 public void erase() {
589 boolean isRoot = false;
591 if (this.equals(root)) {
595 if (prefix != null) {
599 // as long as one of the children is null and this is an intermediary node
600 // link parent to child. If parent null, child becomes the root.
601 while (cur != null && cur.prefix == null && (cur.left == null || cur.right == null)) {
602 TrieNode parent = cur.up;
603 TrieNode child = cur.left != null ? cur.left : cur.right;
605 if (parent == null) {
615 if (parent.left != null && parent.left.equals(cur)) {
618 parent.right = child;
630 setRoot((rootZero ? new TrieNode(null, 0, null) : null));
639 public void resetData() {
645 * Compare node prefix with prefix.
647 * @param pref Prefix to be compared
648 * @return True if prefixes are equal, false otherwise
650 public boolean comparePrefix(byte[] pref) {
654 for (iterator = 0; iterator < bit / 8; iterator++) {
655 if (prefix[iterator] != pref[iterator]) {
661 if (remainder != 0) {
662 int mask = (0xFF << (8 - remainder)) & 0xFF;
663 return ((prefix[iterator] & mask) == (pref[iterator] & mask));
669 * Helper method that converts prefix and prefix length to dotted decimal, string, representation.
671 * @return String representation of prefix.
673 public String asIpPrefix() {
675 return InetAddress.getByAddress(prefix).getHostAddress() + "/" + bit;
676 } catch (UnknownHostException e) {
685 public Iterator<RadixTrie<T>.TrieNode> iterator() {
686 return new TriePostOrderIterator(this);
690 public String toString() {
691 StringBuilder sb = new StringBuilder();
692 sb.append(printPrefixAndMask());
694 sb.append(", parent: ");
695 sb.append(up.printPrefixAndMask());
698 sb.append(", left child: ");
699 sb.append(left.printPrefixAndMask());
702 sb.append(", right child: ");
703 sb.append(right.printPrefixAndMask());
706 sb.append(", data: ");
709 return sb.toString();
712 private String printPrefixAndMask() {
713 if (prefix == null) {
714 return "Virtual @ bit " + bit;
716 StringBuilder sb = new StringBuilder();
718 sb.append(InetAddress.getByAddress(prefix));
721 } catch (UnknownHostException e) {
724 return sb.toString();
728 * Post order iterator implementation for prefix trie. It's safe to use it to remove nodes.
730 private class TriePostOrderIterator implements Iterator<RadixTrie<T>.TrieNode> {
731 private TrieNode current;
732 private TrieNode lastNodeVisited;
733 private Deque<RadixTrie<T>.TrieNode> stack;
735 TriePostOrderIterator(TrieNode node) {
736 stack = new ArrayDeque<>();
738 lastNodeVisited = null;
742 public boolean hasNext() {
744 TrieNode last = lastNodeVisited;
745 if (current != null && (current.left != null || current.right != null)) {
748 Iterator<TrieNode> it = stack.iterator();
749 while (it.hasNext()) {
750 peekNode = it.next();
751 if (peekNode.right != null && !peekNode.right.equals(last)) {
755 if (peekNode.prefix != null) {
765 public RadixTrie<T>.TrieNode next() {
767 TrieNode next = current;
768 while (!stack.isEmpty() || next != null) {
773 peekNode = stack.peek();
774 if (peekNode.right != null && !peekNode.right.equals(lastNodeVisited)) {
775 next = peekNode.right;
777 lastNodeVisited = stack.pop();
778 if (peekNode.prefix != null) {