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);
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 * Given an EID, lookup the longest prefix match, then return its parent node.
185 * @param prefix Prefix looked up.
186 * @param preflen Prefix length.
187 * @return Parent node of longest prefix match or null if nothing is found.
189 public TrieNode lookupParent(byte[] prefix, int preflen) {
190 TrieNode node = lookupBest(prefix, preflen);
198 while (node != null && node.prefix == null) {
206 * Given an EID, lookup the longest prefix match, then return its sibling node.
208 * @param prefix Prefix looked up.
209 * @param preflen Prefix length.
210 * @return Sibling node of longest prefix match or null if nothing is found.
212 public TrieNode lookupSibling(byte[] prefix, int preflen) {
213 TrieNode node = lookupBest(prefix, preflen);
214 TrieNode sibling = node.sibling();
216 if (sibling.prefix != null) {
224 * Given an EID, lookup the longest prefix match, then return its direct parent's sibling node, if the parent is a
227 * @param prefix Prefix looked up.
228 * @param preflen Prefix length.
229 * @return The longest prefix match node's virtual parent's sibling or null if nothing is found.
231 public TrieNode lookupVirtualParentSibling(byte[] prefix, int preflen) {
232 TrieNode node = lookupBest(prefix, preflen);
234 if (node == null || node.up == null) {
238 // Parent is not a virtual node
239 if (node.up.prefix != null) {
243 return node.up.sibling();
247 * Lookup widest negative (i.e., overlapping but not present in trie) prefix for given prefix and prefix length.
249 * @param prefix Prefix looked up.
250 * @param preflen Prefix length.
251 * @return Node containing the widest negative prefix.
253 public TrieNode lookupWidestNegative(byte [] prefix, int preflen) {
254 if (root == null || preflen > maxBits) {
258 TrieNode node = root.findClosest(prefix, preflen);
260 // not a negative match
261 if (node.prefix != null && node.prefixLength() <= preflen && node.comparePrefix(prefix)) {
264 int diffbit = node.firstDifferentBit(prefix, preflen);
265 return new TrieNode(maskPrefix(prefix, diffbit), diffbit + 1, null);
269 * Exact prefix match of prefix/preflen.
271 * @param prefix Big endian byte array representation of the prefix to be looked up.
272 * @param preflen Prefix length
273 * @return Node with exact prefix match or null
275 public TrieNode lookupExact(byte[] prefix, int preflen) {
276 if (root == null || preflen > maxBits) {
280 TrieNode node = root.findClosest(prefix, preflen);
282 // if no node is found or if node not a prefix or if mask is not the same
283 if (node == null || node.prefix == null || node.bit != preflen) {
287 // check that we have exact match
288 if (node.comparePrefix(prefix)) {
296 * Return the subtree for a prefix, including the prefix itself if present, excluding virtual nodes.
298 * @param prefix Big endian byte array representation of the prefix to be looked up.
299 * @param preflen Prefix length
300 * @return Subtree from the prefix
302 public Set<TrieNode> lookupSubtree(byte[] prefix, int preflen) {
303 if (root == null || preflen > maxBits) {
304 return Collections.emptySet();
307 TrieNode node = root.findClosest(prefix, preflen);
309 // if no node is found or if node not a prefix
310 if (node == null || node.prefix == null) {
311 return Collections.emptySet();
314 Set<TrieNode> children = new HashSet<>();
316 Iterator<TrieNode> it = node.iterator();
317 while (it.hasNext()) {
319 if (node.prefix != null) {
328 * Remove prefix from radix trie.
330 * @param prefix Big endian byte array representation of the prefix to be removed.
331 * @param preflen Prefix length.
333 public void remove(byte[] prefix, int preflen) {
334 TrieNode node = lookupExact(prefix, preflen);
341 * Remove node's subtree.
343 * @param node Node who's subtree is to be removed
345 public void removeSubtree(TrieNode node) {
347 Iterator<TrieNode> it = node.iterator();
349 while (it.hasNext()) {
356 * Remove subtree for longest match.
358 * @param prefix Prefix to be looked up
359 * @param preflen Prefix length
361 public void removeSubtree(byte[] prefix, int preflen) {
362 TrieNode itNode = lookupBest(prefix, preflen);
364 if (itNode == null) {
368 Iterator<TrieNode> it = itNode.iterator();
370 while (it.hasNext()) {
377 * Remove all entries in the trie.
379 public void removeAll() {
380 Iterator<TrieNode> it = root.iterator();
381 while (it.hasNext()) {
382 TrieNode node = it.next();
388 * Trie node definition.
390 public class TrieNode implements Iterable<TrieNode> {
391 // since bits are counted from 0, bit and prefix length are equal
399 TrieNode(byte[] prefix, int prefixlen, T data) {
400 this.bit = prefixlen;
401 this.prefix = prefix;
407 public byte[] prefix() {
411 public int prefixLength() {
420 * Finds closest prefix NOT the longest prefix match.
422 * @param pref Searched prefix
423 * @param preflen Searched prefix length
424 * @return The node found
426 public TrieNode findClosest(byte[] pref, int preflen) {
427 TrieNode node = this;
429 while (node.prefix == null || node.bit < preflen) {
430 if (testBitInPrefixByte(pref, node.bit)) {
431 if (node.right == null) {
436 if (node.left == null) {
446 private int toUnsignedInt(byte byteToConvert) {
447 return 0xFF & byteToConvert;
451 * Compares prefix to node's prefix and returns position of first different bit.
453 * @param pref Prefix to be compared.
454 * @param preflen Prefix length.
455 * @return Position of first different bit.
457 public int firstDifferentBit(byte[] pref, int preflen) {
460 int maxbit = (bit < preflen) ? bit : preflen;
462 for (int i = 0; i * 8 < maxbit; i++) {
463 bitxor = ((toUnsignedInt(this.prefix[i])) ^ toUnsignedInt(pref[i]));
464 // if match, move to next byte
466 diffbit = (i + 1) * 8;
469 // if not matched, find first diff bit (0 to 7)
470 diffbit = i * 8 + Integer.numberOfLeadingZeros(bitxor) - 24;
474 diffbit = (diffbit > maxbit) ? maxbit : diffbit;
479 * Find parent with bit less than given value.
481 * @param bitlen Bit value
482 * @return Parent with bit less than given value
484 public TrieNode parentWithBitLessThan(int bitlen) {
485 TrieNode node = this;
486 TrieNode parent = node.up;
487 while (parent != null && parent.bit >= bitlen) {
495 * Return sibling node.
497 * @return Sibling node, if there is a parent, else null
499 public TrieNode sibling() {
500 TrieNode node = this;
501 if (node == null || node.up == null) {
505 TrieNode sibling = null;
506 if (node.up.left == node) {
507 sibling = node.up.right;
509 sibling = node.up.left;
515 * Inserts node in trie near this node with prefix that has the first bit difference at diffbit.
517 * @param pref Prefix to be inserted.
518 * @param preflen Prefix length of the prefix to be inserted.
519 * @param diffbit Bit index of the first different bit between prefix and current node
520 * @param prefdata Data to be stored together with the prefix
521 * @return The trie node created or current node if it's an overwrite.
523 public TrieNode insert(byte[] pref, int preflen, int diffbit, T prefdata, byte[] closest) {
526 // same node, check if prefix needs saving
527 if (diffbit == preflen && bit == preflen) {
528 if (prefix != null) {
532 this.data = prefdata;
537 TrieNode newNode = new TrieNode(pref, preflen, prefdata);
539 // node is more specific, add new prefix as parent
540 if (preflen == diffbit) {
541 if (prefix == null ? testBitInPrefixByte(closest, preflen) : testBitInPrefixByte(prefix, preflen)) {
542 newNode.right = this;
550 } else if (this.equals(up.right)) {
557 // new prefix is more specific than node, add as child
558 } else if (bit == diffbit) {
560 if (testBitInPrefixByte(pref, bit)) {
565 // new prefix is a sibling of/on a different branch from node, add common parent
567 parent = new TrieNode(null, diffbit, null);
569 if (testBitInPrefixByte(pref, diffbit)) {
570 parent.right = newNode;
574 parent.left = newNode;
579 } else if (this.equals(up.right)) {
593 public void erase() {
595 boolean isRoot = false;
597 if (this.equals(root)) {
601 if (prefix != null) {
605 // as long as one of the children is null and this is an intermediary node
606 // link parent to child. If parent null, child becomes the root.
607 while (cur != null && cur.prefix == null && (cur.left == null || cur.right == null)) {
608 TrieNode parent = cur.up;
609 TrieNode child = cur.left != null ? cur.left : cur.right;
611 if (parent == null) {
621 if (parent.left != null && parent.left.equals(cur)) {
624 parent.right = child;
636 setRoot((rootZero ? new TrieNode(null, 0, null) : null));
645 public void resetData() {
651 * Compare node prefix with prefix.
653 * @param pref Prefix to be compared
654 * @return True if prefixes are equal, false otherwise
656 public boolean comparePrefix(byte[] pref) {
660 for (iterator = 0; iterator < bit / 8; iterator++) {
661 if (prefix[iterator] != pref[iterator]) {
667 if (remainder != 0) {
668 int mask = (0xFF << (8 - remainder)) & 0xFF;
669 return ((prefix[iterator] & mask) == (pref[iterator] & mask));
675 * Helper method that converts prefix and prefix length to dotted decimal, string, representation.
677 * @return String representation of prefix.
679 public String asIpPrefix() {
681 return InetAddress.getByAddress(prefix).getHostAddress() + "/" + bit;
682 } catch (UnknownHostException e) {
691 public Iterator<RadixTrie<T>.TrieNode> iterator() {
692 return new TriePostOrderIterator(this);
696 * Post order iterator implementation for prefix trie. It's safe to use it to remove nodes.
698 private class TriePostOrderIterator implements Iterator<RadixTrie<T>.TrieNode> {
699 private TrieNode current;
700 private TrieNode lastNodeVisited;
701 private Deque<RadixTrie<T>.TrieNode> stack;
703 TriePostOrderIterator(TrieNode node) {
704 stack = new ArrayDeque<>();
706 lastNodeVisited = null;
710 public boolean hasNext() {
712 TrieNode last = lastNodeVisited;
713 if (current != null && (current.left != null || current.right != null)) {
716 Iterator<TrieNode> it = stack.iterator();
717 while (it.hasNext()) {
718 peekNode = it.next();
719 if (peekNode.right != null && !peekNode.right.equals(last)) {
723 if (peekNode.prefix != null) {
733 public RadixTrie<T>.TrieNode next() {
735 TrieNode next = current;
736 while (!stack.isEmpty() || next != null) {
741 peekNode = stack.peek();
742 if (peekNode.right != null && !peekNode.right.equals(lastNodeVisited)) {
743 next = peekNode.right;
745 lastNodeVisited = stack.pop();
746 if (peekNode.prefix != null) {