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.Deque;
16 import java.util.Iterator;
17 import java.util.ListIterator;
20 * Radix trie/tree (also known as Patricia tree) implementation. Supports CRD operations for
21 * generic, big endian (network byte order) prefixes. It can do exact and longest prefix matchin,
22 * post order iteration over the entries in the tree and can lookup widest negative prefixes (i.e.,
23 * shortest overlapping prefix not registered in the tree).
25 * @author Florin Coras
27 * @param <T> Data type stored in each tree node
29 public class RadixTrie<T> {
31 private TrieNode ROOT;
32 private long nbActiveNodes;
33 private boolean rootZero;
36 * RadixTrie constructor
38 * @param bits Maximum prefix length supported.
40 public RadixTrie(int bits) {
47 * Radix trie constructors
48 * @param bits Maximum prefix length supported
49 * @param rootZero Flag that decides if 0/0 should be inserted as a non-prefix root node or not
51 public RadixTrie(int bits, boolean rootZero) {
53 ROOT = rootZero ? new TrieNode(null, 0, null) : null;
55 this.rootZero = rootZero;
58 public TrieNode getRoot() {
62 private void setRoot(TrieNode root) {
66 public int getMaxbits() {
70 public long getSize() {
75 * Test bit in byte. Assumes bits are numbered from 0 to 7
77 * @param bitPosition the position to be tested
78 * @return 1 if bit at position is 1, 0 otherwise.
80 private boolean testBitInByte(byte b, int bitPosition) {
81 return (b & (0x80 >> bitPosition)) != 0;
84 private boolean testBitInPrefixByte(byte[] prefix, int bitPosition) {
85 return testBitInByte(prefix[bitPosition / 8], bitPosition & 0x07);
90 * @param prefix Prefix to be masked
91 * @param prefLen Prefix length
92 * @return Prefix after masking
94 private byte [] maskPrefix(byte[] prefix, int prefLen) {
96 byte[] res = prefix.clone();
97 res[prefLen/8] = (byte) (res[prefLen/8] & (0xFF << (7 - (prefLen & 0x07))));
98 for (i = prefLen/8 + 1; i < MAXBITS/8; i++) {
105 * Insert prefix-data tuple into radix trie
106 * @param prefix Big endian (network order) byte array representation of the prefix
107 * @param preflen Prefix length
108 * @param data Data to be stored in the tree
109 * @return Newly inserted TrieNode
111 public TrieNode insert(byte[] prefix, int preflen, T data) {
115 if (preflen > MAXBITS) {
121 ROOT = new TrieNode(prefix, preflen, data);
125 // find closest prefix starting at ROOT
126 node = ROOT.findClosest(prefix, preflen);
128 // find first different bit
129 diffbit = node.firstDifferentBit(prefix, preflen);
131 // find the first node with bit less than diffbit
132 node = node.parentWithBitLessThan(diffbit);
135 return node.insert(prefix, preflen, diffbit, data);
139 * Longest prefix match of prefix/preflen
140 * @param prefix Big endian byte array representation of the prefix to be looked up.
141 * @param preflen Prefix length
142 * @return Node with longest prefix match or null if nothing is found.
144 public TrieNode lookupBest(byte[] prefix, int preflen) {
146 ArrayList<TrieNode> candidates;
147 ListIterator<TrieNode> it;
149 if (ROOT == null || preflen > MAXBITS) {
154 candidates = new ArrayList<TrieNode>();
156 while (node != null && node.bit < preflen) {
157 if (node.prefix != null) {
158 candidates.add(node);
161 if (testBitInPrefixByte(prefix, node.bit)) {
168 if (node != null && node.prefix != null) {
169 candidates.add(node);
172 it = candidates.listIterator(candidates.size());
173 while (it.hasPrevious()) {
174 node = it.previous();
175 if (node.comparePrefix(prefix)) {
184 * Lookup widest negative (i.e., overlapping but not present in trie) prefix for given prefix and prefix length
185 * @param prefix Prefix looked up.
186 * @param preflen Prefix length.
187 * @return Node containing the widest negative prefix.
189 public TrieNode lookupWidestNegative(byte [] prefix, int preflen) {
193 if (ROOT == null || preflen > MAXBITS) {
197 node = ROOT.findClosest(prefix, preflen);
199 // not a negative match
200 if (node.prefix != null && node.prefixLength() <= preflen && node.comparePrefix(prefix)) {
203 diffbit = node.firstDifferentBit(prefix, preflen);
204 return new TrieNode(maskPrefix(prefix, diffbit), diffbit + 1, null);
208 * Exact prefix match of prefix/preflen
209 * @param prefix Big endian byte array representation of the prefix to be looked up.
210 * @param preflen Prefix length
211 * @return Node with exact prefix match or null
213 public TrieNode lookupExact(byte[] prefix, int preflen) {
216 if (ROOT == null || preflen > MAXBITS) {
220 node = ROOT.findClosest(prefix, preflen);
222 // if no node is found or if node not a prefix or if mask is too long
223 if (node == null || node.prefix == null || node.bit > preflen) {
227 // check that we have exact match
228 if (node.comparePrefix(prefix)) {
237 * Remove prefix from radix trie
238 * @param prefix Big endian byte array representation of the prefix to be removed.
239 * @param preflen Prefix length.
241 public void remove(byte[] prefix, int preflen) {
243 node = lookupExact(prefix, preflen);
250 * Remove node's subtree
251 * @param node Node who's subtree is to be removed
253 public void removeSubtree(TrieNode node) {
255 Iterator<TrieNode> it;
256 it = node.iterator();
258 while (it.hasNext()) {
265 * Remove subtree for longest match
266 * @param prefix Prefix to be looked up
267 * @param preflen Prefix length
269 public void removeSubtree(byte[] prefix, int preflen) {
271 Iterator<TrieNode> it;
273 itNode = lookupBest(prefix, preflen);
275 if (itNode == null) {
279 it = itNode.iterator();
281 while (it.hasNext()) {
288 * Remove all entries in the trie.
290 public void removeAll() {
292 Iterator<TrieNode> it;
294 it = ROOT.iterator();
295 while (it.hasNext()) {
302 * Trie node definition.
305 public class TrieNode implements Iterable<TrieNode>{
306 // since bits are counted from 0, bit and prefix length are equal
309 TrieNode left, right;
313 TrieNode(byte[] prefix, int prefixlen, T data) {
314 this.bit = prefixlen;
315 this.prefix = prefix;
320 public byte[] prefix() {
324 public int prefixLength() {
333 * Finds closest prefix NOT the longest prefix match
334 * @param prefix Searched prefix
335 * @param preflen Searched prefix length
336 * @return The node found
338 public TrieNode findClosest(byte[] prefix, int preflen) {
339 TrieNode node = this;
341 while (node.prefix == null || node.bit < preflen) {
342 if (testBitInPrefixByte(prefix, node.bit)) {
343 if (node.right == null) {
348 if (node.left == null) {
358 * Compares prefix to node's prefix and returns position of first different bit
359 * @param prefix Prefix to be compared.
360 * @param preflen Prefix length.
361 * @return Position of first different bit.
363 public int firstDifferentBit(byte[] prefix, int preflen) {
364 int maxbit, diffbit, i;
367 maxbit = (bit < preflen) ? bit : preflen;
369 for (i = 0; i * 8 < maxbit; i++) {
370 // if match, move to next byte
371 if ((bitxor = (byte) (this.prefix[i] ^ prefix[i])) == 0) {
375 // if not matched, find first diff bit (0 to 7)
376 diffbit = i * 8 + Integer.numberOfLeadingZeros(bitxor) - 24;
377 diffbit = (diffbit > maxbit) ? maxbit : diffbit;
385 * Find parent with bit less than given value
386 * @param bitlen Bit value
387 * @return Parent with bit less than given value
389 public TrieNode parentWithBitLessThan(int bitlen) {
390 TrieNode node, parent;
393 while (parent != null && parent.bit >= bitlen) {
401 * Inserts node in trie near this node with prefix that has the first bit difference at diffbit
402 * @param pref Prefix to be inserted.
403 * @param preflen Prefix length of the prefix to be inserted.
404 * @param diffbit Bit index of the first different bit between prefix and current node
405 * @param data Data to be stored together with the prefix
406 * @return The trie node created or current node if it's an overwrite.
408 public TrieNode insert(byte[] pref, int preflen, int diffbit, T data) {
409 TrieNode parent, newNode;
411 // same node, check if prefix needs saving
412 if (diffbit == preflen && bit == preflen) {
413 if (prefix != null) {
422 newNode = new TrieNode(pref, preflen, data);
424 // node is more specific, add new prefix as parent
425 if (preflen == diffbit) {
426 if (testBitInPrefixByte(pref, preflen)) {
427 newNode.right = this;
435 } else if (this.equals(up.right)) {
442 // new prefix is more specific than node, add as child
443 } else if (bit == diffbit) {
445 if (testBitInPrefixByte(pref, bit)) {
450 // new prefix is a sibling of/on a different branch from node, add common parent
452 parent = new TrieNode(null, diffbit, null);
454 if (testBitInPrefixByte(pref, diffbit)) {
455 parent.right = newNode;
459 parent.left = newNode;
464 } else if (this.equals(up.right)) {
478 public void erase () {
479 TrieNode cur = this, child, parent;
480 boolean isRoot = false;
482 if (this.equals(ROOT)) {
486 if (prefix != null) {
490 // as long as one of the children is null and this is an intermediary node
491 // link parent to child. If parent null, child becomes the root.
492 while (cur != null && cur.prefix == null && (cur.left == null || cur.right == null)) {
494 child = cur.left != null ? cur.left : cur.right;
496 if (parent == null) {
499 if (parent.left != null && parent.left.equals(cur)) {
502 parent.right = child;
514 setRoot((rootZero ? new TrieNode(null, 0, null) : null));
523 public void resetData () {
529 * Compare node prefix with prefix
530 * @param pref Prefix to be compared
531 * @return True if prefixes are equal, false otherwise
533 public boolean comparePrefix(byte[] pref) {
536 for (i = 0; i < bit/8; i++) {
537 if (prefix[i] != pref[i]) {
541 if ((r = bit % 8) != 0) {
542 mask = (0xFF << r) & 0xFF;
543 return ((prefix[i] & mask) == (pref[i] & mask));
549 * Helper method that converts prefix and prefix length to dotted decimal, string, representation.
550 * @return String representation of prefix.
552 public String asIpPrefix() {
554 return InetAddress.getByAddress(prefix).getHostAddress() + "/" + bit;
555 } catch (UnknownHostException e) {
564 public Iterator<RadixTrie<T>.TrieNode> iterator() {
565 return new TriePostOrderIterator(this);
569 * Post order iterator implementation for prefix trie. It's safe to use it to remove nodes
572 private class TriePostOrderIterator implements Iterator<RadixTrie<T>.TrieNode> {
573 private TrieNode current, lastNodeVisited;
574 private Deque<RadixTrie<T>.TrieNode> stack;
576 TriePostOrderIterator(TrieNode node) {
577 stack = new ArrayDeque<RadixTrie<T>.TrieNode>();
579 lastNodeVisited = null;
583 public boolean hasNext() {
584 TrieNode peekNode, last = lastNodeVisited;
585 if (current != null && (current.left != null || current.right != null)) {
588 Iterator<TrieNode> it = stack.iterator();
589 while (it.hasNext()) {
590 peekNode = it.next();
591 if (peekNode.right != null && !peekNode.right.equals(last)) {
595 if (peekNode.prefix != null) {
605 public RadixTrie<T>.TrieNode next() {
607 TrieNode next = current;
608 while (!stack.isEmpty() || next != null) {
613 peekNode = stack.peek();
614 if (peekNode.right != null && !peekNode.right.equals(lastNodeVisited)) {
615 next = peekNode.right;
617 lastNodeVisited = stack.pop();
618 if (peekNode.prefix != null) {