package org.batfish.datamodel;
import java.io.Serializable;
import java.util.BitSet;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
public class Prefix6Trie implements Serializable {
private class ByteTrie implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private ByteTrieNode _root;
public ByteTrie() {
_root = new ByteTrieNode();
}
public void addPrefix(Prefix6 prefix6) {
int prefixLength = prefix6.getPrefixLength();
BitSet bits = prefix6.getAddress().getAddressBits();
_root.addPrefix6(prefix6, bits, prefixLength, 0);
}
public boolean containsPathFromPrefix(Prefix6 prefix6) {
int prefixLength = prefix6.getPrefixLength();
BitSet bits = prefix6.getAddress().getAddressBits();
return _root.containsPathFromPrefix(bits, prefixLength, 0);
}
public Prefix6 getLongestPrefixMatch(Ip6 address6) {
BitSet address6Bits = address6.getAddressBits();
return _root.getLongestPrefixMatch(address6, address6Bits, 0);
}
}
private class ByteTrieNode implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private ByteTrieNode _left;
private Prefix6 _prefix6;
private ByteTrieNode _right;
public void addPrefix6(Prefix6 prefix6, BitSet bits, int prefixLength,
int depth) {
if (prefixLength == depth) {
_prefix6 = prefix6;
return;
}
else {
boolean currentBit = bits.get(depth);
if (currentBit) {
if (_right == null) {
_right = new ByteTrieNode();
}
_right.addPrefix6(prefix6, bits, prefixLength, depth + 1);
}
else {
if (_left == null) {
_left = new ByteTrieNode();
}
_left.addPrefix6(prefix6, bits, prefixLength, depth + 1);
}
}
}
public boolean containsPathFromPrefix(BitSet bits, int prefixLength,
int depth) {
if (prefixLength == depth) {
if (depth == 0 && _prefix6 == 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 Prefix6 getLongestPrefixMatch(Ip6 address6, BitSet bits) {
if (_prefix6.contains(address6)) {
return _prefix6;
}
else {
return null;
}
}
public Prefix6 getLongestPrefixMatch(Ip6 address6, BitSet bits,
int index) {
Prefix6 longestPrefixMatch = getLongestPrefixMatch(address6, bits);
if (index == Prefix6.MAX_PREFIX_LENGTH) {
return longestPrefixMatch;
}
boolean currentBit = bits.get(index);
Prefix6 longerMatch = null;
if (currentBit) {
if (_right != null) {
longerMatch = _right.getLongestPrefixMatch(address6, bits,
index + 1);
}
}
else {
if (_left != null) {
longerMatch = _left.getLongestPrefixMatch(address6, bits,
index + 1);
}
}
if (longerMatch == null) {
return longestPrefixMatch;
}
else {
return longerMatch;
}
}
}
/**
*
*/
private static final long serialVersionUID = 1L;
private SortedSet<Prefix6> _prefixes;
private ByteTrie _trie;
public Prefix6Trie() {
_trie = new ByteTrie();
_prefixes = new TreeSet<>();
}
@JsonCreator
public Prefix6Trie(SortedSet<Prefix6> prefixes) {
_trie = new ByteTrie();
_prefixes = prefixes;
for (Prefix6 prefix6 : prefixes) {
_trie.addPrefix(prefix6);
}
}
public boolean add(Prefix6 prefix) {
boolean changed = _prefixes.add(prefix);
if (changed) {
_trie.addPrefix(prefix);
}
return changed;
}
public boolean containsPathFromPrefix(Prefix6 prefix) {
return _trie.containsPathFromPrefix(prefix);
}
public Prefix6 getLongestPrefixMatch(Ip6 address) {
return _trie.getLongestPrefixMatch(address);
}
@JsonValue
public SortedSet<Prefix6> getPrefixes() {
return Collections.unmodifiableSortedSet(_prefixes);
}
}