void LZHLEncoderStat::calcStat( HUFFINT* groups ) { HuffStatTmpStruct s[ NHUFFSYMBOLS ]; int total = makeSortedTmp( s ); nextStat = HUFFRECALCLEN; int pos = 0; int nTotal = 0; for ( HUFFINT group=0; group < 14 ; ++group ) { int avgGroup = ( total - nTotal )/( 16 - group ); int i = 0, n = 0, nn = 0; for ( HUFFINT nBits=0 ;; ++nBits ) { int over = 0; int nItems = 1 << nBits; if ( pos + i + nItems > NHUFFSYMBOLS ) { nItems = NHUFFSYMBOLS - pos; over = 1; } for ( ; i < nItems ; ++i ) nn += s[ pos + i ].n; if ( over || nBits >= 8 || nn > avgGroup ) { if ( nBits == 0 || abs( n - avgGroup ) > abs( nn - avgGroup ) ) { n = nn; } else { --nBits; } _addGroup( groups, group, nBits ); nTotal += n; pos += 1 << nBits; break; } else { n = nn; } } } HUFFINT bestNBits = 0, bestNBits15 = 0; int best = 0x7FFFFFFF; int i = 0, nn = 0, left = 0; for ( int j=pos; j < NHUFFSYMBOLS ; ++j ) left += s[ j ].n; for ( HUFFINT nBits = 0 ;; ++nBits ) { int nItems = 1 << nBits; if ( pos + i + nItems > NHUFFSYMBOLS ) break; for ( ; i < nItems ; ++i ) nn += s[ pos + i ].n; int nItems15 = NHUFFSYMBOLS - ( pos + i ); HUFFINT nBits15; for ( nBits15=0 ;; ++nBits15 ) if ( 1 << nBits15 >= nItems15 ) break; assert( left >= nn ); if ( nBits <= 8 && nBits15 <= 8 ) { int n = nn * nBits + ( left - nn ) * nBits15; if ( n < best ) { best = n; bestNBits = nBits; bestNBits15 = nBits15; } else { break; // PERF optimization } } } // (unused) int pos15 = pos + ( 1 << bestNBits ); _addGroup( groups, 14, bestNBits ); _addGroup( groups, 15, bestNBits15 ); pos = 0; for ( HUFFUINT j=0; j < 16 ; ++j ) { HUFFINT nBits = groups[ j ]; int nItems = 1 << nBits; int maxK = std::min( nItems, NHUFFSYMBOLS - pos ); for ( HUFFUINT k=0; k < maxK ; ++k ) { HUFFINT symbol = s[ pos + k ].i; symbolTable[ symbol ].nBits = nBits + 4; symbolTable[ symbol ].code = (HUFFUINT)( j << nBits ) | k; } pos += 1 << nBits; } }
bool LZHLDecompressor::decompress( uint8_t* dst, size_t* dstSz, const uint8_t* src, size_t* srcSz ) { uint8_t* startDst = dst; const uint8_t* startSrc = src; const uint8_t* endSrc = src + *srcSz; const uint8_t* endDst = dst + *dstSz; nBits = 0; for (;;) { int grp = _get( src, endSrc, 4 ); if ( grp < 0 ) { return false; } Group& group = groupTable[ grp ]; HUFFINT symbol; int nBits = group.nBits; if ( nBits == 0 ) { symbol = symbolTable[ group.pos ]; } else { assert( nBits <= 8 ); int got = _get( src, endSrc, nBits ); if ( got < 0 ) { return false; } int pos = group.pos + got; if ( pos >= NHUFFSYMBOLS ) { return false; } symbol = symbolTable[ pos ]; } assert( symbol < NHUFFSYMBOLS ); ++stat[ symbol ]; int matchOver; if ( symbol < 256 ) { if ( dst >= endDst ) { return false; } *dst++ = (uint8_t)symbol; _toBuf( (uint8_t)symbol ); continue; //forever } else if ( symbol == NHUFFSYMBOLS - 2 ) { HuffStatTmpStruct s[ NHUFFSYMBOLS ]; makeSortedTmp( s ); for ( int i=0; i < NHUFFSYMBOLS ; ++i ) symbolTable[ i ] = s[ i ].i; int lastNBits = 0; int pos = 0; for ( int i=0; i < 16 ; ++i ) { int n; for ( n=0 ;; ++n ) if ( _get( src, endSrc, 1 ) ) break; lastNBits += n; groupTable[ i ].nBits = lastNBits; groupTable[ i ].pos = pos; pos += 1 << lastNBits; } assert( pos < NHUFFSYMBOLS + 255 ); continue; //forever } else if ( symbol == NHUFFSYMBOLS - 1 ) break; //forever static struct MatchOverItem { int nExtraBits; int base; } _matchOverTable[] = { { 1, 8 }, { 2, 10 }, { 3, 14 }, { 4, 22 }, { 5, 38 }, { 6, 70 }, { 7, 134 }, { 8, 262 } }; if ( symbol < 256 + 8 ) { matchOver = symbol - 256; } else { MatchOverItem* item = &_matchOverTable[ symbol - 256 - 8 ]; int extra = _get( src, endSrc, item->nExtraBits ); if ( extra < 0 ) { return false; } matchOver = item->base + extra; } int dispPrefix = _get( src, endSrc, 3 ); if ( dispPrefix < 0 ) { return false; } static struct DispItem { int nBits; int disp; } _dispTable[] = { { 0, 0 }, { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, { 4, 16 }, { 5, 32 }, { 6, 64 } }; DispItem* item = &_dispTable[ dispPrefix ]; nBits = item->nBits + LZBUFBITS - 7; int disp = 0; assert( nBits <= 16 ); if ( nBits > 8 ) { nBits -= 8; disp |= _get( src, endSrc, 8 ) << nBits; } assert( nBits <= 8 ); int got = _get( src, endSrc, nBits ); if ( got < 0 ) { return false; } disp |= got; disp += item->disp << (LZBUFBITS - 7); assert( disp >=0 && disp < LZBUFSIZE ); int matchLen = matchOver + LZMIN; if ( dst + matchLen > endDst ) { return false; } int pos = bufPos - disp; if ( matchLen < disp ) { _bufCpy( dst, pos, matchLen ); } else { _bufCpy( dst, pos, disp ); for ( int i=0; i < matchLen - disp; ++i ) { dst[ i + disp ] = dst[ i ]; } } _toBuf( dst, matchLen ); dst += matchLen; } // forever if ( dstSz ) *dstSz = dst - startDst; if ( srcSz ) *srcSz = src - startSrc; return true; }