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 matching,
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.
49 * @param bits Maximum prefix length supported
50 * @param rootZero Flag that decides if 0/0 should be inserted as a non-prefix root node or not
52 public RadixTrie(int bits, boolean rootZero) {
54 root = rootZero ? new TrieNode(null, 0, null) : null;
56 this.rootZero = rootZero;
59 public TrieNode getRoot() {
63 private void setRoot(TrieNode root) {
67 public int getMaxbits() {
71 public long getSize() {
76 * Test bit in byte. Assumes bits are numbered from 0 to 7
78 * @param bitPosition the position to be tested
79 * @return 1 if bit at position is 1, 0 otherwise.
81 private boolean testBitInByte(byte byteArg, int bitPosition) {
82 return (byteArg & (0x80 >> bitPosition)) != 0;
85 private boolean testBitInPrefixByte(byte[] prefix, int bitPosition) {
86 return testBitInByte(prefix[bitPosition / 8], bitPosition & 0x07);
92 * @param prefix Prefix to be masked
93 * @param prefLen Prefix length
94 * @return Prefix after masking
96 private byte [] maskPrefix(byte[] prefix, int prefLen) {
97 byte[] res = prefix.clone();
98 res[prefLen / 8] = (byte) (res[prefLen / 8] & (0xFF << (7 - (prefLen & 0x07))));
99 for (int i = prefLen / 8 + 1; i < maxBits / 8; i++) {
106 * Insert prefix-data tuple into radix trie.
108 * @param prefix Big endian (network order) byte array representation of the prefix
109 * @param preflen Prefix length
110 * @param data Data to be stored in the tree
111 * @return Newly inserted TrieNode
113 public TrieNode insert(byte[] prefix, int preflen, T data) {
114 if (preflen > maxBits) {
120 root = new TrieNode(prefix, preflen, data);
124 // find closest prefix starting at ROOT
125 TrieNode closest = root.findClosest(prefix, preflen);
127 // find first different bit <= min(closestNode.preflen, preflen)
128 int diffbit = closest.firstDifferentBit(prefix, preflen);
130 // find the first node with bit less than diffbit
131 TrieNode node = closest.parentWithBitLessThan(diffbit);
134 return node.insert(prefix, preflen, diffbit, data, closest.prefix());
138 * 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) {
145 if (root == null || preflen > maxBits) {
149 TrieNode node = root;
150 ArrayList<TrieNode> candidates = new ArrayList<>();
152 while (node != null && node.bit < preflen) {
153 if (node.prefix != null) {
154 candidates.add(node);
157 if (testBitInPrefixByte(prefix, node.bit)) {
164 if (node != null && node.prefix != null) {
165 candidates.add(node);
168 ListIterator<TrieNode> it = candidates.listIterator(candidates.size());
169 while (it.hasPrevious()) {
170 node = it.previous();
171 if (node.comparePrefix(prefix)) {
180 * Given an EID, lookup the longest prefix match, then return its parent node.
182 * @param prefix Prefix looked up.
183 * @param preflen Prefix length.
184 * @return Parent node of longest prefix match or null if nothing is found.
186 public TrieNode lookupParent(byte[] prefix, int preflen) {
187 TrieNode node = lookupBest(prefix, preflen);
195 while (node != null && node.prefix == null) {
203 * Given an EID, lookup the longest prefix match, then return its sibling node.
205 * @param prefix Prefix looked up.
206 * @param preflen Prefix length.
207 * @return Sibling node of longest prefix match or null if nothing is found.
209 public TrieNode lookupSibling(byte[] prefix, int preflen) {
210 TrieNode node = lookupBest(prefix, preflen);
212 if (node == null || node.up == null) {
216 TrieNode sibling = null;
217 if (node.up.left == node) {
218 sibling = node.up.right;
220 sibling = node.up.left;
223 if (sibling.prefix != null) {
230 * Lookup widest negative (i.e., overlapping but not present in trie) prefix for given prefix and prefix length.
232 * @param prefix Prefix looked up.
233 * @param preflen Prefix length.
234 * @return Node containing the widest negative prefix.
236 public TrieNode lookupWidestNegative(byte [] prefix, int preflen) {
237 if (root == null || preflen > maxBits) {
241 TrieNode node = root.findClosest(prefix, preflen);
243 // not a negative match
244 if (node.prefix != null && node.prefixLength() <= preflen && node.comparePrefix(prefix)) {
247 int diffbit = node.firstDifferentBit(prefix, preflen);
248 return new TrieNode(maskPrefix(prefix, diffbit), diffbit + 1, null);
252 * Exact prefix match of prefix/preflen.
254 * @param prefix Big endian byte array representation of the prefix to be looked up.
255 * @param preflen Prefix length
256 * @return Node with exact prefix match or null
258 public TrieNode lookupExact(byte[] prefix, int preflen) {
259 if (root == null || preflen > maxBits) {
263 TrieNode node = root.findClosest(prefix, preflen);
265 // if no node is found or if node not a prefix or if mask is not the same
266 if (node == null || node.prefix == null || node.bit != preflen) {
270 // check that we have exact match
271 if (node.comparePrefix(prefix)) {
280 * Remove prefix from radix trie.
282 * @param prefix Big endian byte array representation of the prefix to be removed.
283 * @param preflen Prefix length.
285 public void remove(byte[] prefix, int preflen) {
286 TrieNode node = lookupExact(prefix, preflen);
293 * Remove node's subtree.
295 * @param node Node who's subtree is to be removed
297 public void removeSubtree(TrieNode node) {
299 Iterator<TrieNode> it = node.iterator();
301 while (it.hasNext()) {
308 * Remove subtree for longest match.
310 * @param prefix Prefix to be looked up
311 * @param preflen Prefix length
313 public void removeSubtree(byte[] prefix, int preflen) {
314 TrieNode itNode = lookupBest(prefix, preflen);
316 if (itNode == null) {
320 Iterator<TrieNode> it = itNode.iterator();
322 while (it.hasNext()) {
329 * Remove all entries in the trie.
331 public void removeAll() {
332 Iterator<TrieNode> it = root.iterator();
333 while (it.hasNext()) {
334 TrieNode node = it.next();
340 * Trie node definition.
342 public class TrieNode implements Iterable<TrieNode> {
343 // since bits are counted from 0, bit and prefix length are equal
351 TrieNode(byte[] prefix, int prefixlen, T data) {
352 this.bit = prefixlen;
353 this.prefix = prefix;
358 public byte[] prefix() {
362 public int prefixLength() {
371 * Finds closest prefix NOT the longest prefix match.
373 * @param prefix Searched prefix
374 * @param preflen Searched prefix length
375 * @return The node found
377 public TrieNode findClosest(byte[] prefix, int preflen) {
378 TrieNode node = this;
380 while (node.prefix == null || node.bit < preflen) {
381 if (testBitInPrefixByte(prefix, node.bit)) {
382 if (node.right == null) {
387 if (node.left == null) {
397 private int toUnsignedInt(byte byteToConvert) {
398 return 0xFF & byteToConvert;
402 * Compares prefix to node's prefix and returns position of first different bit.
404 * @param prefix Prefix to be compared.
405 * @param preflen Prefix length.
406 * @return Position of first different bit.
408 public int firstDifferentBit(byte[] prefix, int preflen) {
411 int maxbit = (bit < preflen) ? bit : preflen;
413 for (int i = 0; i * 8 < maxbit; i++) {
414 // if match, move to next byte
415 if ((bitxor = ((toUnsignedInt(this.prefix[i])) ^ toUnsignedInt(prefix[i]))) == 0) {
416 diffbit = (i + 1) * 8;
419 // if not matched, find first diff bit (0 to 7)
420 diffbit = i * 8 + Integer.numberOfLeadingZeros(bitxor) - 24;
424 diffbit = (diffbit > maxbit) ? maxbit : diffbit;
429 * Find parent with bit less than given value.
431 * @param bitlen Bit value
432 * @return Parent with bit less than given value
434 public TrieNode parentWithBitLessThan(int bitlen) {
435 TrieNode node = this;
436 TrieNode parent = node.up;
437 while (parent != null && parent.bit >= bitlen) {
445 * Inserts node in trie near this node with prefix that has the first bit difference at diffbit.
447 * @param pref Prefix to be inserted.
448 * @param preflen Prefix length of the prefix to be inserted.
449 * @param diffbit Bit index of the first different bit between prefix and current node
450 * @param data Data to be stored together with the prefix
451 * @return The trie node created or current node if it's an overwrite.
453 public TrieNode insert(byte[] pref, int preflen, int diffbit, T data, byte[] closest) {
456 // same node, check if prefix needs saving
457 if (diffbit == preflen && bit == preflen) {
458 if (prefix != null) {
467 TrieNode newNode = new TrieNode(pref, preflen, data);
469 // node is more specific, add new prefix as parent
470 if (preflen == diffbit) {
471 if (prefix == null ? testBitInPrefixByte(closest, preflen) : testBitInPrefixByte(prefix, preflen)) {
472 newNode.right = this;
480 } else if (this.equals(up.right)) {
487 // new prefix is more specific than node, add as child
488 } else if (bit == diffbit) {
490 if (testBitInPrefixByte(pref, bit)) {
495 // new prefix is a sibling of/on a different branch from node, add common parent
497 parent = new TrieNode(null, diffbit, null);
499 if (testBitInPrefixByte(pref, diffbit)) {
500 parent.right = newNode;
504 parent.left = newNode;
509 } else if (this.equals(up.right)) {
523 public void erase() {
525 boolean isRoot = false;
527 if (this.equals(root)) {
531 if (prefix != null) {
535 // as long as one of the children is null and this is an intermediary node
536 // link parent to child. If parent null, child becomes the root.
537 while (cur != null && cur.prefix == null && (cur.left == null || cur.right == null)) {
538 TrieNode parent = cur.up;
539 TrieNode child = cur.left != null ? cur.left : cur.right;
541 if (parent == null) {
551 if (parent.left != null && parent.left.equals(cur)) {
554 parent.right = child;
566 setRoot((rootZero ? new TrieNode(null, 0, null) : null));
575 public void resetData() {
581 * Compare node prefix with prefix.
583 * @param pref Prefix to be compared
584 * @return True if prefixes are equal, false otherwise
586 public boolean comparePrefix(byte[] pref) {
590 for (iterator = 0; iterator < bit / 8; iterator++) {
591 if (prefix[iterator] != pref[iterator]) {
595 if ((remainder = bit % 8) != 0) {
596 int mask = (0xFF << remainder) & 0xFF;
597 return ((prefix[iterator] & mask) == (pref[iterator] & mask));
603 * Helper method that converts prefix and prefix length to dotted decimal, string, representation.
605 * @return String representation of prefix.
607 public String asIpPrefix() {
609 return InetAddress.getByAddress(prefix).getHostAddress() + "/" + bit;
610 } catch (UnknownHostException e) {
619 public Iterator<RadixTrie<T>.TrieNode> iterator() {
620 return new TriePostOrderIterator(this);
624 * Post order iterator implementation for prefix trie. It's safe to use it to remove nodes.
626 private class TriePostOrderIterator implements Iterator<RadixTrie<T>.TrieNode> {
627 private TrieNode current;
628 private TrieNode lastNodeVisited;
629 private Deque<RadixTrie<T>.TrieNode> stack;
631 TriePostOrderIterator(TrieNode node) {
632 stack = new ArrayDeque<>();
634 lastNodeVisited = null;
638 public boolean hasNext() {
640 TrieNode last = lastNodeVisited;
641 if (current != null && (current.left != null || current.right != null)) {
644 Iterator<TrieNode> it = stack.iterator();
645 while (it.hasNext()) {
646 peekNode = it.next();
647 if (peekNode.right != null && !peekNode.right.equals(last)) {
651 if (peekNode.prefix != null) {
661 public RadixTrie<T>.TrieNode next() {
663 TrieNode next = current;
664 while (!stack.isEmpty() || next != null) {
669 peekNode = stack.peek();
670 if (peekNode.right != null && !peekNode.right.equals(lastNodeVisited)) {
671 next = peekNode.right;
673 lastNodeVisited = stack.pop();
674 if (peekNode.prefix != null) {