void ZopfliBlockSplit(const ZopfliOptions* options, const unsigned char* in, size_t instart, size_t inend, size_t maxblocks, size_t** splitpoints, size_t* npoints) { size_t pos = 0; size_t i; ZopfliBlockState s; size_t* lz77splitpoints = 0; size_t nlz77points = 0; ZopfliLZ77Store store; ZopfliHash hash; ZopfliHash* h = &hash; ZopfliInitLZ77Store(in, &store); ZopfliInitBlockState(options, instart, inend, 0, &s); ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h); *npoints = 0; *splitpoints = 0; /* Unintuitively, Using a simple LZ77 method here instead of ZopfliLZ77Optimal results in better blocks. */ ZopfliLZ77Greedy(&s, in, instart, inend, &store, h); ZopfliBlockSplitLZ77(options, &store, maxblocks, &lz77splitpoints, &nlz77points); /* Convert LZ77 positions to positions in the uncompressed input. */ pos = instart; if (nlz77points > 0) { for (i = 0; i < store.size; i++) { size_t length = store.dists[i] == 0 ? 1 : store.litlens[i]; if (lz77splitpoints[*npoints] == i) { ZOPFLI_APPEND_DATA(pos, splitpoints, npoints); if (*npoints == nlz77points) break; } pos += length; } } assert(*npoints == nlz77points); free(lz77splitpoints); ZopfliCleanBlockState(&s); ZopfliCleanLZ77Store(&store); ZopfliCleanHash(h); }
void ZopfliLZ77Greedy(ZopfliBlockState* s, const unsigned char* in, size_t instart, size_t inend, ZopfliLZ77Store* store) { size_t i = 0, j; unsigned short leng; unsigned short dist; int lengvalue; size_t windowstart = instart > ZOPFLI_WINDOW_SIZE ? instart - ZOPFLI_WINDOW_SIZE : 0; unsigned short dummysublen[259]; ZopfliHash hash; ZopfliHash* h = &hash; #ifdef ZOPFLI_LAZY_MATCHING /* Lazy matching. */ unsigned prev_length = 0; unsigned prev_match = 0; int prevlengvalue; int match_available = 0; #endif if (instart == inend) return; ZopfliInitHash(ZOPFLI_WINDOW_SIZE, h); ZopfliWarmupHash(in, windowstart, inend, h); for (i = windowstart; i < instart; i++) { ZopfliUpdateHash(in, i, inend, h); } for (i = instart; i < inend; i++) { ZopfliUpdateHash(in, i, inend, h); ZopfliFindLongestMatch(s, h, in, i, inend, ZOPFLI_MAX_MATCH, dummysublen, &dist, &leng); lengvalue = GetLengthValue(leng, dist); #ifdef ZOPFLI_LAZY_MATCHING /* Lazy matching. */ prevlengvalue = GetLengthValue(prev_length, prev_match); if (match_available) { match_available = 0; if (lengvalue > prevlengvalue + 1) { ZopfliStoreLitLenDist(in[i - 1], 0, store); if (lengvalue >= ZOPFLI_MIN_MATCH && lengvalue < ZOPFLI_MAX_MATCH) { match_available = 1; prev_length = leng; prev_match = dist; continue; } } else { /* Add previous to output. */ leng = prev_length; dist = prev_match; lengvalue = prevlengvalue; /* Add to output. */ ZopfliVerifyLenDist(in, inend, i - 1, dist, leng); ZopfliStoreLitLenDist(leng, dist, store); for (j = 2; j < leng; j++) { assert(i < inend); i++; ZopfliUpdateHash(in, i, inend, h); } continue; } } else if (lengvalue >= ZOPFLI_MIN_MATCH && leng < ZOPFLI_MAX_MATCH) { match_available = 1; prev_length = leng; prev_match = dist; continue; } /* End of lazy matching. */ #endif /* Add to output. */ if (lengvalue >= ZOPFLI_MIN_MATCH) { ZopfliVerifyLenDist(in, inend, i, dist, leng); ZopfliStoreLitLenDist(leng, dist, store); } else { leng = 1; ZopfliStoreLitLenDist(in[i], 0, store); } for (j = 1; j < leng; j++) { assert(i < inend); i++; ZopfliUpdateHash(in, i, inend, h); } } ZopfliCleanHash(h); }