File Coverage

lz4opt.h
Criterion Covered Total %
statement 0 168 0.0
branch 0 334 0.0
condition n/a
subroutine n/a
pod n/a
total 0 502 0.0


line stmt bran cond sub pod time code
1             /*
2             lz4opt.h - Optimal Mode of LZ4
3             Copyright (C) 2015-2017, Przemyslaw Skibinski
4             Note : this file is intended to be included within lz4hc.c
5              
6             BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
7              
8             Redistribution and use in source and binary forms, with or without
9             modification, are permitted provided that the following conditions are
10             met:
11              
12             * Redistributions of source code must retain the above copyright
13             notice, this list of conditions and the following disclaimer.
14             * Redistributions in binary form must reproduce the above
15             copyright notice, this list of conditions and the following disclaimer
16             in the documentation and/or other materials provided with the
17             distribution.
18              
19             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20             "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21             LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22             A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23             OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25             LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26             DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27             THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28             (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29             OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30              
31             You can contact the author at :
32             - LZ4 source repository : https://github.com/lz4/lz4
33             - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
34             */
35              
36             #define LZ4_OPT_NUM (1<<12)
37              
38              
39             typedef struct {
40             int off;
41             int len;
42             } LZ4HC_match_t;
43              
44             typedef struct {
45             int price;
46             int off;
47             int mlen;
48             int litlen;
49             } LZ4HC_optimal_t;
50              
51              
52             /* price in bytes */
53             LZ4_FORCE_INLINE size_t LZ4HC_literalsPrice(size_t litlen)
54             {
55 0           size_t price = litlen;
56 0 0         if (litlen >= (size_t)RUN_MASK)
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
57 0           price += 1 + (litlen-RUN_MASK)/255;
58 0           return price;
59             }
60              
61              
62             /* requires mlen >= MINMATCH */
63             LZ4_FORCE_INLINE size_t LZ4HC_sequencePrice(size_t litlen, size_t mlen)
64             {
65 0           size_t price = 2 + 1; /* 16-bit offset + token */
66              
67 0           price += LZ4HC_literalsPrice(litlen);
68              
69 0 0         if (mlen >= (size_t)(ML_MASK+MINMATCH))
    0          
    0          
    0          
70 0           price+= 1 + (mlen-(ML_MASK+MINMATCH))/255;
71              
72 0           return price;
73             }
74              
75              
76             /*-*************************************
77             * Binary Tree search
78             ***************************************/
79             LZ4_FORCE_INLINE int LZ4HC_BinTree_InsertAndGetAllMatches (
80             LZ4HC_CCtx_internal* ctx,
81             const BYTE* const ip,
82             const BYTE* const iHighLimit,
83             size_t best_mlen,
84             LZ4HC_match_t* matches,
85             int* matchNum)
86             {
87 0           U16* const chainTable = ctx->chainTable;
88 0           U32* const HashTable = ctx->hashTable;
89 0           const BYTE* const base = ctx->base;
90 0           const U32 dictLimit = ctx->dictLimit;
91 0           const U32 current = (U32)(ip - base);
92 0 0         const U32 lowLimit = (ctx->lowLimit + MAX_DISTANCE > current) ? ctx->lowLimit : current - (MAX_DISTANCE - 1);
    0          
    0          
    0          
    0          
    0          
93 0           const BYTE* const dictBase = ctx->dictBase;
94             const BYTE* match;
95 0           int nbAttempts = ctx->searchNum;
96 0           int mnum = 0;
97             U16 *ptr0, *ptr1, delta0, delta1;
98             U32 matchIndex;
99 0           size_t matchLength = 0;
100             U32* HashPos;
101              
102 0 0         if (ip + MINMATCH > iHighLimit) return 1;
    0          
    0          
    0          
    0          
    0          
103              
104             /* HC4 match finder */
105 0           HashPos = &HashTable[LZ4HC_hashPtr(ip)];
106 0           matchIndex = *HashPos;
107 0           *HashPos = current;
108              
109 0           ptr0 = &DELTANEXTMAXD(current*2+1);
110 0           ptr1 = &DELTANEXTMAXD(current*2);
111 0           delta0 = delta1 = (U16)(current - matchIndex);
112              
113 0 0         while ((matchIndex < current) && (matchIndex>=lowLimit) && (nbAttempts)) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
114 0           nbAttempts--;
115 0 0         if (matchIndex >= dictLimit) {
    0          
    0          
    0          
    0          
    0          
116 0           match = base + matchIndex;
117 0           matchLength = LZ4_count(ip, match, iHighLimit);
118             } else {
119 0           const BYTE* vLimit = ip + (dictLimit - matchIndex);
120 0           match = dictBase + matchIndex;
121 0 0         if (vLimit > iHighLimit) vLimit = iHighLimit;
    0          
    0          
    0          
    0          
    0          
122 0           matchLength = LZ4_count(ip, match, vLimit);
123 0 0         if ((ip+matchLength == vLimit) && (vLimit < iHighLimit))
    0          
124 0           matchLength += LZ4_count(ip+matchLength, base+dictLimit, iHighLimit);
125 0 0         if (matchIndex+matchLength >= dictLimit)
    0          
    0          
    0          
    0          
    0          
126 0           match = base + matchIndex; /* to prepare for next usage of match[matchLength] */
127             }
128              
129 0 0         if (matchLength > best_mlen) {
    0          
    0          
    0          
    0          
    0          
130 0           best_mlen = matchLength;
131 0 0         if (matches) {
    0          
    0          
    0          
    0          
    0          
132 0 0         if (matchIndex >= dictLimit)
    0          
    0          
    0          
    0          
    0          
133 0           matches[mnum].off = (int)(ip - match);
134             else
135 0           matches[mnum].off = (int)(ip - (base + matchIndex)); /* virtual matchpos */
136 0           matches[mnum].len = (int)matchLength;
137 0           mnum++;
138             }
139 0 0         if (best_mlen > LZ4_OPT_NUM) break;
    0          
    0          
    0          
    0          
    0          
140             }
141              
142 0 0         if (ip+matchLength >= iHighLimit) /* equal : no way to know if inf or sup */
    0          
    0          
    0          
    0          
    0          
143             break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt the tree */
144              
145             DEBUGLOG(6, "ip :%016llX", (U64)ip);
146             DEBUGLOG(6, "match:%016llX", (U64)match);
147 0 0         if (*(ip+matchLength) < *(match+matchLength)) {
    0          
    0          
    0          
    0          
    0          
148 0           *ptr0 = delta0;
149 0           ptr0 = &DELTANEXTMAXD(matchIndex*2);
150 0 0         if (*ptr0 == (U16)-1) break;
    0          
    0          
    0          
    0          
    0          
151 0           delta0 = *ptr0;
152 0           delta1 += delta0;
153 0           matchIndex -= delta0;
154             } else {
155 0           *ptr1 = delta1;
156 0           ptr1 = &DELTANEXTMAXD(matchIndex*2+1);
157 0 0         if (*ptr1 == (U16)-1) break;
    0          
    0          
    0          
    0          
    0          
158 0           delta1 = *ptr1;
159 0           delta0 += delta1;
160 0           matchIndex -= delta1;
161             }
162             }
163              
164 0           *ptr0 = (U16)-1;
165 0           *ptr1 = (U16)-1;
166 0 0         if (matchNum) *matchNum = mnum;
    0          
    0          
    0          
    0          
    0          
167             /* if (best_mlen > 8) return best_mlen-8; */
168 0 0         if (!matchNum) return 1;
    0          
    0          
    0          
    0          
    0          
169 0           return 1;
170             }
171              
172              
173             LZ4_FORCE_INLINE void LZ4HC_updateBinTree(LZ4HC_CCtx_internal* ctx, const BYTE* const ip, const BYTE* const iHighLimit)
174             {
175 0           const BYTE* const base = ctx->base;
176 0           const U32 target = (U32)(ip - base);
177 0           U32 idx = ctx->nextToUpdate;
178 0 0         while(idx < target)
    0          
    0          
    0          
179 0           idx += LZ4HC_BinTree_InsertAndGetAllMatches(ctx, base+idx, iHighLimit, 8, NULL, NULL);
180             }
181              
182              
183             /** Tree updater, providing best match */
184             LZ4_FORCE_INLINE int LZ4HC_BinTree_GetAllMatches (
185             LZ4HC_CCtx_internal* ctx,
186             const BYTE* const ip, const BYTE* const iHighLimit,
187             size_t best_mlen, LZ4HC_match_t* matches, const int fullUpdate)
188             {
189 0           int mnum = 0;
190 0 0         if (ip < ctx->base + ctx->nextToUpdate) return 0; /* skipped area */
    0          
191 0 0         if (fullUpdate) LZ4HC_updateBinTree(ctx, ip, iHighLimit);
    0          
192 0           best_mlen = LZ4HC_BinTree_InsertAndGetAllMatches(ctx, ip, iHighLimit, best_mlen, matches, &mnum);
193 0           ctx->nextToUpdate = (U32)(ip - ctx->base + best_mlen);
194 0           return mnum;
195             }
196              
197              
198             #define SET_PRICE(pos, ml, offset, ll, cost) \
199             { \
200             while (last_pos < pos) { opt[last_pos+1].price = 1<<30; last_pos++; } \
201             opt[pos].mlen = (int)ml; \
202             opt[pos].off = (int)offset; \
203             opt[pos].litlen = (int)ll; \
204             opt[pos].price = (int)cost; \
205             }
206              
207              
208 0           static int LZ4HC_compress_optimal (
209             LZ4HC_CCtx_internal* ctx,
210             const char* const source,
211             char* dest,
212             int inputSize,
213             int maxOutputSize,
214             limitedOutput_directive limit,
215             size_t sufficient_len,
216             const int fullUpdate
217             )
218             {
219             LZ4HC_optimal_t opt[LZ4_OPT_NUM + 1]; /* this uses a bit too much stack memory to my taste ... */
220             LZ4HC_match_t matches[LZ4_OPT_NUM + 1];
221              
222 0           const BYTE* ip = (const BYTE*) source;
223 0           const BYTE* anchor = ip;
224 0           const BYTE* const iend = ip + inputSize;
225 0           const BYTE* const mflimit = iend - MFLIMIT;
226 0           const BYTE* const matchlimit = (iend - LASTLITERALS);
227 0           BYTE* op = (BYTE*) dest;
228 0           BYTE* const oend = op + maxOutputSize;
229              
230             /* init */
231             DEBUGLOG(5, "LZ4HC_compress_optimal");
232 0 0         if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1;
233 0           ctx->end += inputSize;
234 0           ip++;
235              
236             /* Main Loop */
237 0 0         while (ip < mflimit) {
238 0           size_t const llen = ip - anchor;
239 0           size_t last_pos = 0;
240             size_t match_num, cur, best_mlen, best_off;
241 0           memset(opt, 0, sizeof(LZ4HC_optimal_t)); /* memset only the first one */
242              
243 0           match_num = LZ4HC_BinTree_GetAllMatches(ctx, ip, matchlimit, MINMATCH-1, matches, fullUpdate);
244 0 0         if (!match_num) { ip++; continue; }
245              
246 0 0         if ((size_t)matches[match_num-1].len > sufficient_len) {
247             /* good enough solution : immediate encoding */
248 0           best_mlen = matches[match_num-1].len;
249 0           best_off = matches[match_num-1].off;
250 0           cur = 0;
251 0           last_pos = 1;
252 0           goto encode;
253             }
254              
255             /* set prices using matches at position = 0 */
256             { size_t matchNb;
257 0 0         for (matchNb = 0; matchNb < match_num; matchNb++) {
258 0 0         size_t mlen = (matchNb>0) ? (size_t)matches[matchNb-1].len+1 : MINMATCH;
259 0           best_mlen = matches[matchNb].len; /* necessarily < sufficient_len < LZ4_OPT_NUM */
260 0 0         for ( ; mlen <= best_mlen ; mlen++) {
261 0           size_t const cost = LZ4HC_sequencePrice(llen, mlen) - LZ4HC_literalsPrice(llen);
262 0 0         SET_PRICE(mlen, mlen, matches[matchNb].off, 0, cost); /* updates last_pos and opt[pos] */
263             } } }
264              
265 0 0         if (last_pos < MINMATCH) { ip++; continue; } /* note : on clang at least, this test improves performance */
266              
267             /* check further positions */
268 0           opt[0].mlen = opt[1].mlen = 1;
269 0 0         for (cur = 1; cur <= last_pos; cur++) {
270 0           const BYTE* const curPtr = ip + cur;
271              
272             /* establish baseline price if cur is literal */
273             { size_t price, litlen;
274 0 0         if (opt[cur-1].mlen == 1) {
275             /* no match at previous position */
276 0           litlen = opt[cur-1].litlen + 1;
277 0 0         if (cur > litlen) {
278 0           price = opt[cur - litlen].price + LZ4HC_literalsPrice(litlen);
279             } else {
280 0           price = LZ4HC_literalsPrice(llen + litlen) - LZ4HC_literalsPrice(llen);
281             }
282             } else {
283 0           litlen = 1;
284 0           price = opt[cur - 1].price + LZ4HC_literalsPrice(1);
285             }
286              
287 0 0         if (price < (size_t)opt[cur].price)
288 0 0         SET_PRICE(cur, 1 /*mlen*/, 0 /*off*/, litlen, price); /* note : increases last_pos */
289             }
290              
291 0 0         if (cur == last_pos || curPtr >= mflimit) break;
    0          
292              
293 0           match_num = LZ4HC_BinTree_GetAllMatches(ctx, curPtr, matchlimit, MINMATCH-1, matches, fullUpdate);
294 0 0         if ((match_num > 0) && (size_t)matches[match_num-1].len > sufficient_len) {
    0          
295             /* immediate encoding */
296 0           best_mlen = matches[match_num-1].len;
297 0           best_off = matches[match_num-1].off;
298 0           last_pos = cur + 1;
299 0           goto encode;
300             }
301              
302             /* set prices using matches at position = cur */
303             { size_t matchNb;
304 0 0         for (matchNb = 0; matchNb < match_num; matchNb++) {
305 0 0         size_t ml = (matchNb>0) ? (size_t)matches[matchNb-1].len+1 : MINMATCH;
306 0           best_mlen = (cur + matches[matchNb].len < LZ4_OPT_NUM) ?
307 0 0         (size_t)matches[matchNb].len : LZ4_OPT_NUM - cur;
308              
309 0 0         for ( ; ml <= best_mlen ; ml++) {
310             size_t ll, price;
311 0 0         if (opt[cur].mlen == 1) {
312 0           ll = opt[cur].litlen;
313 0 0         if (cur > ll)
314 0           price = opt[cur - ll].price + LZ4HC_sequencePrice(ll, ml);
315             else
316 0           price = LZ4HC_sequencePrice(llen + ll, ml) - LZ4HC_literalsPrice(llen);
317             } else {
318 0           ll = 0;
319 0           price = opt[cur].price + LZ4HC_sequencePrice(0, ml);
320             }
321              
322 0 0         if (cur + ml > last_pos || price < (size_t)opt[cur + ml].price) {
    0          
323 0 0         SET_PRICE(cur + ml, ml, matches[matchNb].off, ll, price);
324             } } } }
325             } /* for (cur = 1; cur <= last_pos; cur++) */
326              
327 0           best_mlen = opt[last_pos].mlen;
328 0           best_off = opt[last_pos].off;
329 0           cur = last_pos - best_mlen;
330              
331             encode: /* cur, last_pos, best_mlen, best_off must be set */
332 0           opt[0].mlen = 1;
333             while (1) { /* from end to beginning */
334 0           size_t const ml = opt[cur].mlen;
335 0           int const offset = opt[cur].off;
336 0           opt[cur].mlen = (int)best_mlen;
337 0           opt[cur].off = (int)best_off;
338 0           best_mlen = ml;
339 0           best_off = offset;
340 0 0         if (ml > cur) break; /* can this happen ? */
341 0           cur -= ml;
342 0           }
343              
344             /* encode all recorded sequences */
345 0           cur = 0;
346 0 0         while (cur < last_pos) {
347 0           int const ml = opt[cur].mlen;
348 0           int const offset = opt[cur].off;
349 0 0         if (ml == 1) { ip++; cur++; continue; }
350 0           cur += ml;
351 0 0         if ( LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ip - offset, limit, oend) ) return 0;
352             }
353             } /* while (ip < mflimit) */
354              
355             /* Encode Last Literals */
356 0           { int lastRun = (int)(iend - anchor);
357 0 0         if ((limit) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */
    0          
358 0 0         if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK< 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; }
    0          
359 0           else *op++ = (BYTE)(lastRun<
360 0           memcpy(op, anchor, iend - anchor);
361 0           op += iend-anchor;
362             }
363              
364             /* End */
365 0           return (int) ((char*)op-dest);
366             }