blob: 0b393b416af0d52358aab6c5ec2f8967936020ca [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.containers;
import org.jetbrains.annotations.NonNls;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
public class ByteTrie {
private static final String EMPTY_STRING = "";
@NonNls private static final String UTF_8_CHARSET_NAME = "UTF-8";
private final ArrayList<Node> myAllNodes;
private static class Node {
private final byte myChar;
private final int myParent;
private IntArrayList myChildren;
Node(int parent, byte b) {
myChar = b;
myParent = parent;
}
}
public ByteTrie() {
myAllNodes = new ArrayList<Node>();
final Node root = new Node(-1, (byte)0);
myAllNodes.add(root);
}
public int size() {
return myAllNodes.size();
}
/**
* Returns unique hash code for a string.
*
* @return negative - an error occured, 0 - no such string in trie, positive - actual hashcode
*/
public int getHashCode(String s) {
try {
return getHashCode(s.getBytes(UTF_8_CHARSET_NAME));
}
catch (UnsupportedEncodingException e) {
return -1;
}
}
/**
* Returns string by unique hash code.
*/
public String getString(int hashCode) {
try {
return new String(getBytes(hashCode), UTF_8_CHARSET_NAME);
}
catch (UnsupportedEncodingException e) {
return EMPTY_STRING;
}
}
/**
* Returns unique hash code for a reversed string.
*/
public int getHashCodeForReversedString(String s) {
try {
return getHashCodeForReversedBytes(s.getBytes(UTF_8_CHARSET_NAME));
}
catch (UnsupportedEncodingException e) {
return -1;
}
}
/**
* Returns reversed string by unique hash code.
*/
public String getReversedString(int hashCode) {
try {
return new String(getReversedBytes(hashCode), UTF_8_CHARSET_NAME);
}
catch (UnsupportedEncodingException e) {
return EMPTY_STRING;
}
}
public int getHashCode(byte[] bytes) {
return getHashCode(bytes, 0, bytes.length);
}
public int getHashCode(byte[] bytes, int offset, int length) {
int index = 0;
while (length-- > 0) {
index = getSubNode(index, bytes[offset++]);
}
return index;
}
public long getMaximumMatch(byte[] bytes, int offset, int length) {
int index = 0;
int resultingLength = 0;
while (length-- > 0) {
int nextIndex = searchForSubNode(index, bytes[offset++]);
if (nextIndex == 0) {
break;
}
index = nextIndex;
resultingLength++;
}
return index + (((long)resultingLength) << 32);
}
public byte[] getBytes(int hashCode) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
while (hashCode > 0) {
final ByteTrie.Node node = myAllNodes.get(hashCode);
writeByte(stream, node.myChar);
hashCode = node.myParent;
}
final byte[] bytes = stream.toByteArray();
// reverse bytes
for (int i = 0, j = bytes.length - 1; i < j; ++i, --j) {
byte swap = bytes[i];
bytes[i] = bytes[j];
bytes[j] = swap;
}
return bytes;
}
public int getHashCodeForReversedBytes(byte[] bytes) {
return getHashCodeForReversedBytes(bytes, bytes.length - 1, bytes.length);
}
public int getHashCodeForReversedBytes(byte[] bytes, int offset, int length) {
int index = 0;
while (length-- > 0) {
index = getSubNode(index, bytes[offset--]);
}
return index;
}
public byte[] getReversedBytes(int hashCode) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
while (hashCode > 0) {
final ByteTrie.Node node = myAllNodes.get(hashCode);
writeByte(stream, node.myChar);
hashCode = node.myParent;
}
return stream.toByteArray();
}
private int getSubNode(int parentIndex, byte b) {
Node parentNode = myAllNodes.get(parentIndex);
if (parentNode.myChildren == null) {
parentNode.myChildren = new IntArrayList(1);
}
IntArrayList children = parentNode.myChildren;
int left = 0;
int right = children.size() - 1;
int middle;
int index;
while (left <= right) {
middle = (left + right) >> 1;
index = children.get(middle);
Node node = myAllNodes.get(index);
int comp = node.myChar - b;
if (comp == 0) {
return index;
}
if (comp < 0) {
left = middle + 1;
}
else {
right = middle - 1;
}
}
index = myAllNodes.size();
children.add(left, index);
myAllNodes.add(new Node(parentIndex, b));
return index;
}
private int searchForSubNode(int parentIndex, byte b) {
Node parentNode = myAllNodes.get(parentIndex);
IntArrayList children = parentNode.myChildren;
if (children == null) {
return 0;
}
int left = 0;
int right = children.size() - 1;
int middle;
while (left <= right) {
middle = (left + right) >> 1;
int index = children.get(middle);
Node node = myAllNodes.get(index);
int comp = node.myChar - b;
if (comp == 0) {
return index;
}
if (comp < 0) {
left = middle + 1;
}
else {
right = middle - 1;
}
}
return 0;
}
void writeByte(ByteArrayOutputStream stream, byte b) {
int out = b;
if (out < 0) {
out += 256;
}
stream.write(out);
}
}