package org.batfish.datamodel;
import java.io.Serializable;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import org.batfish.common.BatfishException;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
public class PrefixTrie implements Serializable {
private class ByteTrie implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private ByteTrieNode _root;
public ByteTrie() {
_root = new ByteTrieNode();
}
public void addPrefix(Prefix prefix) {
int prefixLength = prefix.getPrefixLength();
BitSet bits = prefix.getAddress().getAddressBits();
_root.addPrefix(prefix, bits, prefixLength, 0);
}
public boolean containsPathFromPrefix(Prefix prefix) {
int prefixLength = prefix.getPrefixLength();
BitSet bits = prefix.getAddress().getAddressBits();
return _root.containsPathFromPrefix(bits, prefixLength, 0);
}
public Prefix getLongestPrefixMatch(Ip address) {
BitSet addressBits = address.getAddressBits();
return _root.getLongestPrefixMatch(address, addressBits, 0);
}
}
private class ByteTrieNode implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private ByteTrieNode _left;
private Prefix _prefix;
private ByteTrieNode _right;
public void addPrefix(Prefix prefix, BitSet bits, int prefixLength,
int depth) {
if (prefixLength == depth) {
_prefix = prefix;
return;
}
else {
boolean currentBit = bits.get(depth);
if (currentBit) {
if (_right == null) {
_right = new ByteTrieNode();
}
_right.addPrefix(prefix, bits, prefixLength, depth + 1);
}
else {
if (_left == null) {
_left = new ByteTrieNode();
}
_left.addPrefix(prefix, bits, prefixLength, depth + 1);
}
}
}
public boolean containsPathFromPrefix(BitSet bits, int prefixLength,
int depth) {
if (prefixLength == depth) {
if (depth == 0 && _prefix == null) {
return false;
}
else {
return true;
}
}
else {
boolean currentBit = bits.get(depth);
if (currentBit) {
if (_right == null) {
return false;
}
else {
return _right.containsPathFromPrefix(bits, prefixLength,
depth + 1);
}
}
else {
if (_left == null) {
return false;
}
else {
return _left.containsPathFromPrefix(bits, prefixLength,
depth + 1);
}
}
}
}
private Prefix getLongestPrefixMatch(Ip address, BitSet bits) {
if (_prefix != null && _prefix.contains(address)) {
return _prefix;
}
else {
return null;
}
}
public Prefix getLongestPrefixMatch(Ip address, BitSet bits, int index) {
Prefix longestPrefixMatch = getLongestPrefixMatch(address, bits);
if (index == Prefix.MAX_PREFIX_LENGTH) {
return longestPrefixMatch;
}
boolean currentBit = bits.get(index);
Prefix longerMatch = null;
if (currentBit) {
if (_right != null) {
longerMatch = _right.getLongestPrefixMatch(address, bits,
index + 1);
}
}
else {
if (_left != null) {
longerMatch = _left.getLongestPrefixMatch(address, bits,
index + 1);
}
}
if (longerMatch == null) {
return longestPrefixMatch;
}
else {
return longerMatch;
}
}
}
/**
*
*/
private static final long serialVersionUID = 1L;
private SortedSet<Prefix> _prefixes;
private ByteTrie _trie;
public PrefixTrie() {
_trie = new ByteTrie();
_prefixes = new TreeSet<>();
}
@JsonCreator
public PrefixTrie(SortedSet<Prefix> prefixes) {
_trie = new ByteTrie();
_prefixes = prefixes;
for (Prefix prefix : prefixes) {
_trie.addPrefix(prefix);
}
}
public boolean add(Prefix prefix) {
if (prefix == null) {
throw new BatfishException("Cannot add null prefix to trie");
}
boolean changed = _prefixes.add(prefix);
if (changed) {
_trie.addPrefix(prefix);
}
return changed;
}
public boolean addAll(Collection<Prefix> prefixes) {
boolean changed = false;
for (Prefix prefix : prefixes) {
changed = changed || add(prefix);
}
return changed;
}
public boolean containsIp(Ip address) {
return _trie.getLongestPrefixMatch(address) != null;
}
public boolean containsPathFromPrefix(Prefix prefix) {
return _trie.containsPathFromPrefix(prefix);
}
public Prefix getLongestPrefixMatch(Ip address) {
return _trie.getLongestPrefixMatch(address);
}
@JsonValue
public SortedSet<Prefix> getPrefixes() {
return Collections.unmodifiableSortedSet(_prefixes);
}
}