// LZ.BinTree
package sevenzip.compression.lz;
import java.io.IOException;
public class BinTree extends InWindow {
int _cyclicBufferPos;
int _cyclicBufferSize = 0;
int _matchMaxLen;
int[] _son;
int[] _hash;
int _cutValue = 0xFF;
int _hashMask;
int _hashSizeSum = 0;
boolean HASH_ARRAY = true;
static final int kHash2Size = 1<<10;
static final int kHash3Size = 1<<16;
static final int kBT2HashSize = 1<<16;
static final int kStartMaxLen = 1;
static final int kHash3Offset = kHash2Size;
static final int kEmptyHashValue = 0;
static final int kMaxValForNormalize = (1<<30)-1;
int kNumHashDirectBytes = 0;
int kMinMatchCheck = 4;
int kFixHashSize = kHash2Size+kHash3Size;
public void SetType(int numHashBytes) {
HASH_ARRAY = (numHashBytes>2);
if (HASH_ARRAY) {
kNumHashDirectBytes = 0;
kMinMatchCheck = 4;
kFixHashSize = kHash2Size+kHash3Size;
} else {
kNumHashDirectBytes = 2;
kMinMatchCheck = 2+1;
kFixHashSize = 0;
}
}
@Override
public void Init() throws IOException {
super.Init();
for (int i = 0; i<_hashSizeSum; i++) {
_hash[i] = kEmptyHashValue;
}
_cyclicBufferPos = 0;
ReduceOffsets(-1);
}
@Override
public void MovePos() throws IOException {
if (++_cyclicBufferPos>=_cyclicBufferSize) {
_cyclicBufferPos = 0;
}
super.MovePos();
if (_pos==kMaxValForNormalize) {
Normalize();
}
}
public boolean Create(int historySize, int keepAddBufferBefore, int matchMaxLen, int keepAddBufferAfter) {
if (historySize>kMaxValForNormalize-256) {
return false;
}
_cutValue = 16+(matchMaxLen>>1);
int windowReservSize = (historySize+keepAddBufferBefore+matchMaxLen+keepAddBufferAfter)/2+256;
super.Create(historySize+keepAddBufferBefore, matchMaxLen+keepAddBufferAfter, windowReservSize);
_matchMaxLen = matchMaxLen;
int cyclicBufferSize = historySize+1;
if (_cyclicBufferSize!=cyclicBufferSize) {
_son = new int[(_cyclicBufferSize = cyclicBufferSize)*2];
}
int hs = kBT2HashSize;
if (HASH_ARRAY) {
hs = historySize-1;
hs |= (hs>>1);
hs |= (hs>>2);
hs |= (hs>>4);
hs |= (hs>>8);
hs >>= 1;
hs |= 0xFFFF;
if (hs>(1<<24)) {
hs >>= 1;
}
_hashMask = hs;
hs++;
hs += kFixHashSize;
}
if (hs!=_hashSizeSum) {
_hash = new int[_hashSizeSum = hs];
}
return true;
}
public int GetMatches(int[] distances) throws IOException {
int lenLimit;
if (_pos+_matchMaxLen<=_streamPos) {
lenLimit = _matchMaxLen;
} else {
lenLimit = _streamPos-_pos;
if (lenLimit<kMinMatchCheck) {
MovePos();
return 0;
}
}
int offset = 0;
int matchMinPos = (_pos>_cyclicBufferSize) ? (_pos-_cyclicBufferSize) : 0;
int cur = _bufferOffset+_pos;
int maxLen = kStartMaxLen; // to avoid items for len < hashSize;
int hashValue, hash2Value = 0, hash3Value = 0;
if (HASH_ARRAY) {
int temp = CrcTable[_bufferBase[cur]&0xFF]^(_bufferBase[cur+1]&0xFF);
hash2Value = temp&(kHash2Size-1);
temp ^= ((_bufferBase[cur+2]&0xFF)<<8);
hash3Value = temp&(kHash3Size-1);
hashValue = (temp^(CrcTable[_bufferBase[cur+3]&0xFF]<<5))&_hashMask;
} else {
hashValue = ((_bufferBase[cur]&0xFF)^((_bufferBase[cur+1]&0xFF)<<8));
}
int curMatch = _hash[kFixHashSize+hashValue];
if (HASH_ARRAY) {
int curMatch2 = _hash[hash2Value];
int curMatch3 = _hash[kHash3Offset+hash3Value];
_hash[hash2Value] = _pos;
_hash[kHash3Offset+hash3Value] = _pos;
if (curMatch2>matchMinPos) {
if (_bufferBase[_bufferOffset+curMatch2]==_bufferBase[cur]) {
distances[offset++] = maxLen = 2;
distances[offset++] = _pos-curMatch2-1;
}
}
if (curMatch3>matchMinPos) {
if (_bufferBase[_bufferOffset+curMatch3]==_bufferBase[cur]) {
if (curMatch3==curMatch2) {
offset -= 2;
}
distances[offset++] = maxLen = 3;
distances[offset++] = _pos-curMatch3-1;
curMatch2 = curMatch3;
}
}
if (offset!=0&&curMatch2==curMatch) {
offset -= 2;
maxLen = kStartMaxLen;
}
}
_hash[kFixHashSize+hashValue] = _pos;
int ptr0 = (_cyclicBufferPos<<1)+1;
int ptr1 = (_cyclicBufferPos<<1);
int len0, len1;
len0 = len1 = kNumHashDirectBytes;
if (kNumHashDirectBytes!=0) {
if (curMatch>matchMinPos) {
if (_bufferBase[_bufferOffset+curMatch+kNumHashDirectBytes]!=_bufferBase[cur+kNumHashDirectBytes]) {
distances[offset++] = maxLen = kNumHashDirectBytes;
distances[offset++] = _pos-curMatch-1;
}
}
}
int count = _cutValue;
while (true) {
if (curMatch<=matchMinPos||count--==0) {
_son[ptr0] = _son[ptr1] = kEmptyHashValue;
break;
}
int delta = _pos-curMatch;
int cyclicPos = ((delta<=_cyclicBufferPos) ? (_cyclicBufferPos-delta)
: (_cyclicBufferPos-delta+_cyclicBufferSize))<<1;
int pby1 = _bufferOffset+curMatch;
int len = Math.min(len0, len1);
if (_bufferBase[pby1+len]==_bufferBase[cur+len]) {
while (++len!=lenLimit) {
if (_bufferBase[pby1+len]!=_bufferBase[cur+len]) {
break;
}
}
if (maxLen<len) {
distances[offset++] = maxLen = len;
distances[offset++] = delta-1;
if (len==lenLimit) {
_son[ptr1] = _son[cyclicPos];
_son[ptr0] = _son[cyclicPos+1];
break;
}
}
}
if ((_bufferBase[pby1+len]&0xFF)<(_bufferBase[cur+len]&0xFF)) {
_son[ptr1] = curMatch;
ptr1 = cyclicPos+1;
curMatch = _son[ptr1];
len1 = len;
} else {
_son[ptr0] = curMatch;
ptr0 = cyclicPos;
curMatch = _son[ptr0];
len0 = len;
}
}
MovePos();
return offset;
}
public void Skip(int num) throws IOException {
do {
int lenLimit;
if (_pos+_matchMaxLen<=_streamPos) {
lenLimit = _matchMaxLen;
} else {
lenLimit = _streamPos-_pos;
if (lenLimit<kMinMatchCheck) {
MovePos();
continue;
}
}
int matchMinPos = (_pos>_cyclicBufferSize) ? (_pos-_cyclicBufferSize) : 0;
int cur = _bufferOffset+_pos;
int hashValue;
if (HASH_ARRAY) {
int temp = CrcTable[_bufferBase[cur]&0xFF]^(_bufferBase[cur+1]&0xFF);
int hash2Value = temp&(kHash2Size-1);
_hash[hash2Value] = _pos;
temp ^= ((_bufferBase[cur+2]&0xFF)<<8);
int hash3Value = temp&(kHash3Size-1);
_hash[kHash3Offset+hash3Value] = _pos;
hashValue = (temp^(CrcTable[_bufferBase[cur+3]&0xFF]<<5))&_hashMask;
} else {
hashValue = ((_bufferBase[cur]&0xFF)^((_bufferBase[cur+1]&0xFF)<<8));
}
int curMatch = _hash[kFixHashSize+hashValue];
_hash[kFixHashSize+hashValue] = _pos;
int ptr0 = (_cyclicBufferPos<<1)+1;
int ptr1 = (_cyclicBufferPos<<1);
int len0, len1;
len0 = len1 = kNumHashDirectBytes;
int count = _cutValue;
while (true) {
if (curMatch<=matchMinPos||count--==0) {
_son[ptr0] = _son[ptr1] = kEmptyHashValue;
break;
}
int delta = _pos-curMatch;
int cyclicPos = ((delta<=_cyclicBufferPos) ? (_cyclicBufferPos-delta)
: (_cyclicBufferPos-delta+_cyclicBufferSize))<<1;
int pby1 = _bufferOffset+curMatch;
int len = Math.min(len0, len1);
if (_bufferBase[pby1+len]==_bufferBase[cur+len]) {
while (++len!=lenLimit) {
if (_bufferBase[pby1+len]!=_bufferBase[cur+len]) {
break;
}
}
if (len==lenLimit) {
_son[ptr1] = _son[cyclicPos];
_son[ptr0] = _son[cyclicPos+1];
break;
}
}
if ((_bufferBase[pby1+len]&0xFF)<(_bufferBase[cur+len]&0xFF)) {
_son[ptr1] = curMatch;
ptr1 = cyclicPos+1;
curMatch = _son[ptr1];
len1 = len;
} else {
_son[ptr0] = curMatch;
ptr0 = cyclicPos;
curMatch = _son[ptr0];
len0 = len;
}
}
MovePos();
} while (--num!=0);
}
void NormalizeLinks(int[] items, int numItems, int subValue) {
for (int i = 0; i<numItems; i++) {
int value = items[i];
if (value<=subValue) {
value = kEmptyHashValue;
} else {
value -= subValue;
}
items[i] = value;
}
}
void Normalize() {
int subValue = _pos-_cyclicBufferSize;
NormalizeLinks(_son, _cyclicBufferSize*2, subValue);
NormalizeLinks(_hash, _hashSizeSum, subValue);
ReduceOffsets(subValue);
}
public void SetCutValue(int cutValue) {
_cutValue = cutValue;
}
private static final int[] CrcTable = new int[256];
static {
for (int i = 0; i<256; i++) {
int r = i;
for (int j = 0; j<8; j++) {
if ((r&1)!=0) {
r = (r>>>1)^0xEDB88320;
} else {
r >>>= 1;
}
}
CrcTable[i] = r;
}
}
}