Home | History | Annotate | Download | only in LZ
      1 // LzBinTree.cs
      2 
      3 using System;
      4 
      5 namespace SevenZip.Compression.LZ
      6 {
      7 	public class BinTree : InWindow, IMatchFinder
      8 	{
      9 		UInt32 _cyclicBufferPos;
     10 		UInt32 _cyclicBufferSize = 0;
     11 		UInt32 _matchMaxLen;
     12 
     13 		UInt32[] _son;
     14 		UInt32[] _hash;
     15 
     16 		UInt32 _cutValue = 0xFF;
     17 		UInt32 _hashMask;
     18 		UInt32 _hashSizeSum = 0;
     19 
     20 		bool HASH_ARRAY = true;
     21 
     22 		const UInt32 kHash2Size = 1 << 10;
     23 		const UInt32 kHash3Size = 1 << 16;
     24 		const UInt32 kBT2HashSize = 1 << 16;
     25 		const UInt32 kStartMaxLen = 1;
     26 		const UInt32 kHash3Offset = kHash2Size;
     27 		const UInt32 kEmptyHashValue = 0;
     28 		const UInt32 kMaxValForNormalize = ((UInt32)1 << 31) - 1;
     29 	
     30 		UInt32 kNumHashDirectBytes = 0;
     31 		UInt32 kMinMatchCheck = 4;
     32 		UInt32 kFixHashSize = kHash2Size + kHash3Size;
     33 		
     34 		public void SetType(int numHashBytes)
     35 		{
     36 			HASH_ARRAY = (numHashBytes > 2);
     37 			if (HASH_ARRAY)
     38 			{
     39 				kNumHashDirectBytes = 0;
     40 				kMinMatchCheck = 4;
     41 				kFixHashSize = kHash2Size + kHash3Size;
     42 			}
     43 			else
     44 			{
     45 				kNumHashDirectBytes = 2;
     46 				kMinMatchCheck = 2 + 1;
     47 				kFixHashSize = 0;
     48 			}
     49 		}
     50 
     51 		public new void SetStream(System.IO.Stream stream) { base.SetStream(stream); }
     52 		public new void ReleaseStream() { base.ReleaseStream(); }
     53 		
     54 		public new void Init()
     55 		{
     56 			base.Init();
     57 			for (UInt32 i = 0; i < _hashSizeSum; i++)
     58 				_hash[i] = kEmptyHashValue;
     59 			_cyclicBufferPos = 0;
     60 			ReduceOffsets(-1);
     61 		}
     62 
     63 		public new void MovePos()
     64 		{
     65 			if (++_cyclicBufferPos >= _cyclicBufferSize)
     66 				_cyclicBufferPos = 0;
     67 			base.MovePos();
     68 			if (_pos == kMaxValForNormalize)
     69 				Normalize();
     70 		}
     71 
     72 		public new Byte GetIndexByte(Int32 index) { return base.GetIndexByte(index); }
     73 
     74 		public new UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit)
     75 		{ return base.GetMatchLen(index, distance, limit); }
     76 
     77 		public new UInt32 GetNumAvailableBytes() { return base.GetNumAvailableBytes(); }
     78 
     79 		public void Create(UInt32 historySize, UInt32 keepAddBufferBefore,
     80 				UInt32 matchMaxLen, UInt32 keepAddBufferAfter)
     81 		{
     82 			if (historySize > kMaxValForNormalize - 256)
     83 				throw new Exception();
     84 			_cutValue = 16 + (matchMaxLen >> 1);
     85 				
     86 			UInt32 windowReservSize = (historySize + keepAddBufferBefore +
     87 					matchMaxLen + keepAddBufferAfter) / 2 + 256;
     88 
     89 			base.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize);
     90 
     91 			_matchMaxLen = matchMaxLen;
     92 
     93 			UInt32 cyclicBufferSize = historySize + 1;
     94 			if (_cyclicBufferSize != cyclicBufferSize)
     95 				_son = new UInt32[(_cyclicBufferSize = cyclicBufferSize) * 2];
     96 
     97 			UInt32 hs = kBT2HashSize;
     98 
     99 			if (HASH_ARRAY)
    100 			{
    101 				hs = historySize - 1;
    102 				hs |= (hs >> 1);
    103 				hs |= (hs >> 2);
    104 				hs |= (hs >> 4);
    105 				hs |= (hs >> 8);
    106 				hs >>= 1;
    107 				hs |= 0xFFFF;
    108 				if (hs > (1 << 24))
    109 					hs >>= 1;
    110 				_hashMask = hs;
    111 				hs++;
    112 				hs += kFixHashSize;
    113 			}
    114 			if (hs != _hashSizeSum)
    115 				_hash = new UInt32[_hashSizeSum = hs];
    116 		}
    117 
    118 		public UInt32 GetMatches(UInt32[] distances)
    119 		{
    120 			UInt32 lenLimit;
    121 			if (_pos + _matchMaxLen <= _streamPos)
    122 				lenLimit = _matchMaxLen;
    123 			else
    124 			{
    125 				lenLimit = _streamPos - _pos;
    126 				if (lenLimit < kMinMatchCheck)
    127 				{
    128 					MovePos();
    129 					return 0;
    130 				}
    131 			}
    132 
    133 			UInt32 offset = 0;
    134 			UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
    135 			UInt32 cur = _bufferOffset + _pos;
    136 			UInt32 maxLen = kStartMaxLen; // to avoid items for len < hashSize;
    137 			UInt32 hashValue, hash2Value = 0, hash3Value = 0;
    138 
    139 			if (HASH_ARRAY)
    140 			{
    141 				UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
    142 				hash2Value = temp & (kHash2Size - 1);
    143 				temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8);
    144 				hash3Value = temp & (kHash3Size - 1);
    145 				hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
    146 			}
    147 			else
    148 				hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8);
    149 
    150 			UInt32 curMatch = _hash[kFixHashSize + hashValue];
    151 			if (HASH_ARRAY)
    152 			{
    153 				UInt32 curMatch2 = _hash[hash2Value];
    154 				UInt32 curMatch3 = _hash[kHash3Offset + hash3Value];
    155 				_hash[hash2Value] = _pos;
    156 				_hash[kHash3Offset + hash3Value] = _pos;
    157 				if (curMatch2 > matchMinPos)
    158 					if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur])
    159 					{
    160 						distances[offset++] = maxLen = 2;
    161 						distances[offset++] = _pos - curMatch2 - 1;
    162 					}
    163 				if (curMatch3 > matchMinPos)
    164 					if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur])
    165 					{
    166 						if (curMatch3 == curMatch2)
    167 							offset -= 2;
    168 						distances[offset++] = maxLen = 3;
    169 						distances[offset++] = _pos - curMatch3 - 1;
    170 						curMatch2 = curMatch3;
    171 					}
    172 				if (offset != 0 && curMatch2 == curMatch)
    173 				{
    174 					offset -= 2;
    175 					maxLen = kStartMaxLen;
    176 				}
    177 			}
    178 
    179 			_hash[kFixHashSize + hashValue] = _pos;
    180 
    181 			UInt32 ptr0 = (_cyclicBufferPos << 1) + 1;
    182 			UInt32 ptr1 = (_cyclicBufferPos << 1);
    183 
    184 			UInt32 len0, len1;
    185 			len0 = len1 = kNumHashDirectBytes;
    186 			
    187 			if (kNumHashDirectBytes != 0)
    188 			{
    189 				if (curMatch > matchMinPos)
    190 				{
    191 					if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] !=
    192 							_bufferBase[cur + kNumHashDirectBytes])
    193 					{
    194 						distances[offset++] = maxLen = kNumHashDirectBytes;
    195 						distances[offset++] = _pos - curMatch - 1;
    196 					}
    197 				}
    198 			}
    199 			
    200 			UInt32 count = _cutValue;
    201 			
    202 			while(true)
    203 			{
    204 				if(curMatch <= matchMinPos || count-- == 0)
    205 				{
    206 					_son[ptr0] = _son[ptr1] = kEmptyHashValue;
    207 					break;
    208 				}
    209 				UInt32 delta = _pos - curMatch;
    210 				UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ?
    211 							(_cyclicBufferPos - delta) :
    212 							(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
    213 
    214 				UInt32 pby1 = _bufferOffset + curMatch;
    215 				UInt32 len = Math.Min(len0, len1);
    216 				if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
    217 				{
    218 					while(++len != lenLimit)
    219 						if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
    220 							break;
    221 					if (maxLen < len)
    222 					{
    223 						distances[offset++] = maxLen = len;
    224 						distances[offset++] = delta - 1;
    225 						if (len == lenLimit)
    226 						{
    227 							_son[ptr1] = _son[cyclicPos];
    228 							_son[ptr0] = _son[cyclicPos + 1];
    229 							break;
    230 						}
    231 					}
    232 				}
    233 				if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
    234 				{
    235 					_son[ptr1] = curMatch;
    236 					ptr1 = cyclicPos + 1;
    237 					curMatch = _son[ptr1];
    238 					len1 = len;
    239 				}
    240 				else
    241 				{
    242 					_son[ptr0] = curMatch;
    243 					ptr0 = cyclicPos;
    244 					curMatch = _son[ptr0];
    245 					len0 = len;
    246 				}
    247 			}
    248 			MovePos();
    249 			return offset;
    250 		}
    251 
    252 		public void Skip(UInt32 num)
    253 		{
    254 			do
    255 			{
    256 				UInt32 lenLimit;
    257 				if (_pos + _matchMaxLen <= _streamPos)
    258 					lenLimit = _matchMaxLen;
    259 				else
    260 				{
    261 					lenLimit = _streamPos - _pos;
    262 					if (lenLimit < kMinMatchCheck)
    263 					{
    264 						MovePos();
    265 						continue;
    266 					}
    267 				}
    268 
    269 				UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
    270 				UInt32 cur = _bufferOffset + _pos;
    271 
    272 				UInt32 hashValue;
    273 
    274 				if (HASH_ARRAY)
    275 				{
    276 					UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
    277 					UInt32 hash2Value = temp & (kHash2Size - 1);
    278 					_hash[hash2Value] = _pos;
    279 					temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8);
    280 					UInt32 hash3Value = temp & (kHash3Size - 1);
    281 					_hash[kHash3Offset + hash3Value] = _pos;
    282 					hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
    283 				}
    284 				else
    285 					hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8);
    286 
    287 				UInt32 curMatch = _hash[kFixHashSize + hashValue];
    288 				_hash[kFixHashSize + hashValue] = _pos;
    289 
    290 				UInt32 ptr0 = (_cyclicBufferPos << 1) + 1;
    291 				UInt32 ptr1 = (_cyclicBufferPos << 1);
    292 
    293 				UInt32 len0, len1;
    294 				len0 = len1 = kNumHashDirectBytes;
    295 
    296 				UInt32 count = _cutValue;
    297 				while (true)
    298 				{
    299 					if (curMatch <= matchMinPos || count-- == 0)
    300 					{
    301 						_son[ptr0] = _son[ptr1] = kEmptyHashValue;
    302 						break;
    303 					}
    304 
    305 					UInt32 delta = _pos - curMatch;
    306 					UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ?
    307 								(_cyclicBufferPos - delta) :
    308 								(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
    309 
    310 					UInt32 pby1 = _bufferOffset + curMatch;
    311 					UInt32 len = Math.Min(len0, len1);
    312 					if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
    313 					{
    314 						while (++len != lenLimit)
    315 							if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
    316 								break;
    317 						if (len == lenLimit)
    318 						{
    319 							_son[ptr1] = _son[cyclicPos];
    320 							_son[ptr0] = _son[cyclicPos + 1];
    321 							break;
    322 						}
    323 					}
    324 					if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
    325 					{
    326 						_son[ptr1] = curMatch;
    327 						ptr1 = cyclicPos + 1;
    328 						curMatch = _son[ptr1];
    329 						len1 = len;
    330 					}
    331 					else
    332 					{
    333 						_son[ptr0] = curMatch;
    334 						ptr0 = cyclicPos;
    335 						curMatch = _son[ptr0];
    336 						len0 = len;
    337 					}
    338 				}
    339 				MovePos();
    340 			}
    341 			while (--num != 0);
    342 		}
    343 
    344 		void NormalizeLinks(UInt32[] items, UInt32 numItems, UInt32 subValue)
    345 		{
    346 			for (UInt32 i = 0; i < numItems; i++)
    347 			{
    348 				UInt32 value = items[i];
    349 				if (value <= subValue)
    350 					value = kEmptyHashValue;
    351 				else
    352 					value -= subValue;
    353 				items[i] = value;
    354 			}
    355 		}
    356 
    357 		void Normalize()
    358 		{
    359 			UInt32 subValue = _pos - _cyclicBufferSize;
    360 			NormalizeLinks(_son, _cyclicBufferSize * 2, subValue);
    361 			NormalizeLinks(_hash, _hashSizeSum, subValue);
    362 			ReduceOffsets((Int32)subValue);
    363 		}
    364 
    365 		public void SetCutValue(UInt32 cutValue) { _cutValue = cutValue; }
    366 	}
    367 }
    368