File Coverage

lz4opt.h
Criterion Covered Total %
statement 0 159 0.0
branch 0 108 0.0
condition n/a
subroutine n/a
pod n/a
total 0 267 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             typedef struct {
39             int price;
40             int off;
41             int mlen;
42             int litlen;
43             } LZ4HC_optimal_t;
44              
45              
46             /* price in bytes */
47             LZ4_FORCE_INLINE int LZ4HC_literalsPrice(int const litlen)
48             {
49 0           int price = litlen;
50 0 0         if (litlen >= (int)RUN_MASK)
    0          
    0          
    0          
    0          
    0          
    0          
    0          
51 0           price += 1 + (litlen-RUN_MASK)/255;
52 0           return price;
53             }
54              
55              
56             /* requires mlen >= MINMATCH */
57             LZ4_FORCE_INLINE int LZ4HC_sequencePrice(int litlen, int mlen)
58             {
59 0           int price = 1 + 2 ; /* token + 16-bit offset */
60              
61 0           price += LZ4HC_literalsPrice(litlen);
62              
63 0 0         if (mlen >= (int)(ML_MASK+MINMATCH))
    0          
    0          
64 0           price += 1 + (mlen-(ML_MASK+MINMATCH))/255;
65              
66 0           return price;
67             }
68              
69              
70             /*-*************************************
71             * Match finder
72             ***************************************/
73             typedef struct {
74             int off;
75             int len;
76             } LZ4HC_match_t;
77              
78             LZ4_FORCE_INLINE
79             LZ4HC_match_t LZ4HC_FindLongerMatch(LZ4HC_CCtx_internal* const ctx,
80             const BYTE* ip, const BYTE* const iHighLimit,
81             int minLen, int nbSearches)
82             {
83 0           LZ4HC_match_t match = { 0 , 0 };
84 0           const BYTE* matchPtr = NULL;
85             /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos),
86             * but this won't be the case here, as we define iLowLimit==ip,
87             * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */
88 0           int const matchLength = LZ4HC_InsertAndGetWiderMatch(ctx,
89             ip, ip, iHighLimit, minLen, &matchPtr, &ip,
90             nbSearches, 1 /* patternAnalysis */);
91 0 0         if (matchLength <= minLen) return match;
    0          
    0          
92 0           match.len = matchLength;
93 0           match.off = (int)(ip-matchPtr);
94 0           return match;
95             }
96              
97              
98 0           static int LZ4HC_compress_optimal (
99             LZ4HC_CCtx_internal* ctx,
100             const char* const source,
101             char* dst,
102             int* srcSizePtr,
103             int dstCapacity,
104             int const nbSearches,
105             size_t sufficient_len,
106             limitedOutput_directive limit,
107             int const fullUpdate
108             )
109             {
110             #define TRAILING_LITERALS 3
111             LZ4HC_optimal_t opt[LZ4_OPT_NUM + TRAILING_LITERALS]; /* this uses a bit too much stack memory to my taste ... */
112              
113 0           const BYTE* ip = (const BYTE*) source;
114 0           const BYTE* anchor = ip;
115 0           const BYTE* const iend = ip + *srcSizePtr;
116 0           const BYTE* const mflimit = iend - MFLIMIT;
117 0           const BYTE* const matchlimit = iend - LASTLITERALS;
118 0           BYTE* op = (BYTE*) dst;
119 0           BYTE* opSaved = (BYTE*) dst;
120 0           BYTE* oend = op + dstCapacity;
121              
122             /* init */
123             DEBUGLOG(5, "LZ4HC_compress_optimal");
124 0           *srcSizePtr = 0;
125 0 0         if (limit == limitedDestSize) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */
126 0 0         if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1;
127              
128             /* Main Loop */
129             assert(ip - anchor < LZ4_MAX_INPUT_SIZE);
130 0 0         while (ip < mflimit) {
131 0           int const llen = (int)(ip - anchor);
132             int best_mlen, best_off;
133 0           int cur, last_match_pos = 0;
134              
135 0           LZ4HC_match_t const firstMatch = LZ4HC_FindLongerMatch(ctx, ip, matchlimit, MINMATCH-1, nbSearches);
136 0 0         if (firstMatch.len==0) { ip++; continue; }
137              
138 0 0         if ((size_t)firstMatch.len > sufficient_len) {
139             /* good enough solution : immediate encoding */
140 0           int const firstML = firstMatch.len;
141 0           const BYTE* const matchPos = ip - firstMatch.off;
142 0           opSaved = op;
143 0 0         if ( LZ4HC_encodeSequence(&ip, &op, &anchor, firstML, matchPos, limit, oend) ) /* updates ip, op and anchor */
144 0           goto _dest_overflow;
145 0           continue;
146             }
147              
148             /* set prices for first positions (literals) */
149             { int rPos;
150 0 0         for (rPos = 0 ; rPos < MINMATCH ; rPos++) {
151 0           int const cost = LZ4HC_literalsPrice(llen + rPos);
152 0           opt[rPos].mlen = 1;
153 0           opt[rPos].off = 0;
154 0           opt[rPos].litlen = llen + rPos;
155 0           opt[rPos].price = cost;
156             DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup",
157             rPos, cost, opt[rPos].litlen);
158             } }
159             /* set prices using initial match */
160 0           { int mlen = MINMATCH;
161 0           int const matchML = firstMatch.len; /* necessarily < sufficient_len < LZ4_OPT_NUM */
162 0           int const offset = firstMatch.off;
163             assert(matchML < LZ4_OPT_NUM);
164 0 0         for ( ; mlen <= matchML ; mlen++) {
165 0           int const cost = LZ4HC_sequencePrice(llen, mlen);
166 0           opt[mlen].mlen = mlen;
167 0           opt[mlen].off = offset;
168 0           opt[mlen].litlen = llen;
169 0           opt[mlen].price = cost;
170             DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i) -- initial setup",
171             mlen, cost, mlen);
172             } }
173 0           last_match_pos = firstMatch.len;
174             { int addLit;
175 0 0         for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) {
176 0           opt[last_match_pos+addLit].mlen = 1; /* literal */
177 0           opt[last_match_pos+addLit].off = 0;
178 0           opt[last_match_pos+addLit].litlen = addLit;
179 0           opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit);
180             DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup",
181             last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit);
182             } }
183              
184             /* check further positions */
185 0 0         for (cur = 1; cur < last_match_pos; cur++) {
186 0           const BYTE* const curPtr = ip + cur;
187             LZ4HC_match_t newMatch;
188              
189 0 0         if (curPtr >= mflimit) break;
190             DEBUGLOG(7, "rPos:%u[%u] vs [%u]%u",
191             cur, opt[cur].price, opt[cur+1].price, cur+1);
192 0 0         if (fullUpdate) {
193             /* not useful to search here if next position has same (or lower) cost */
194 0 0         if ( (opt[cur+1].price <= opt[cur].price)
195             /* in some cases, next position has same cost, but cost rises sharply after, so a small match would still be beneficial */
196 0 0         && (opt[cur+MINMATCH].price < opt[cur].price + 3/*min seq price*/) )
197 0           continue;
198             } else {
199             /* not useful to search here if next position has same (or lower) cost */
200 0 0         if (opt[cur+1].price <= opt[cur].price) continue;
201             }
202              
203             DEBUGLOG(7, "search at rPos:%u", cur);
204 0 0         if (fullUpdate)
205             newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, MINMATCH-1, nbSearches);
206             else
207             /* only test matches of minimum length; slightly faster, but misses a few bytes */
208 0           newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, last_match_pos - cur, nbSearches);
209 0 0         if (!newMatch.len) continue;
210              
211 0 0         if ( ((size_t)newMatch.len > sufficient_len)
212 0 0         || (newMatch.len + cur >= LZ4_OPT_NUM) ) {
213             /* immediate encoding */
214 0           best_mlen = newMatch.len;
215 0           best_off = newMatch.off;
216 0           last_match_pos = cur + 1;
217 0           goto encode;
218             }
219              
220             /* before match : set price with literals at beginning */
221 0           { int const baseLitlen = opt[cur].litlen;
222             int litlen;
223 0 0         for (litlen = 1; litlen < MINMATCH; litlen++) {
224 0           int const price = opt[cur].price - LZ4HC_literalsPrice(baseLitlen) + LZ4HC_literalsPrice(baseLitlen+litlen);
225 0           int const pos = cur + litlen;
226 0 0         if (price < opt[pos].price) {
227 0           opt[pos].mlen = 1; /* literal */
228 0           opt[pos].off = 0;
229 0           opt[pos].litlen = baseLitlen+litlen;
230 0           opt[pos].price = price;
231             DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)",
232             pos, price, opt[pos].litlen);
233             } } }
234              
235             /* set prices using match at position = cur */
236 0           { int const matchML = newMatch.len;
237 0           int ml = MINMATCH;
238              
239             assert(cur + newMatch.len < LZ4_OPT_NUM);
240 0 0         for ( ; ml <= matchML ; ml++) {
241 0           int const pos = cur + ml;
242 0           int const offset = newMatch.off;
243             int price;
244             int ll;
245             DEBUGLOG(7, "testing price rPos %i (last_match_pos=%i)",
246             pos, last_match_pos);
247 0 0         if (opt[cur].mlen == 1) {
248 0           ll = opt[cur].litlen;
249 0 0         price = ((cur > ll) ? opt[cur - ll].price : 0)
250 0           + LZ4HC_sequencePrice(ll, ml);
251             } else {
252 0           ll = 0;
253 0           price = opt[cur].price + LZ4HC_sequencePrice(0, ml);
254             }
255              
256 0 0         if (pos > last_match_pos+TRAILING_LITERALS || price <= opt[pos].price) {
    0          
257             DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i)",
258             pos, price, ml);
259             assert(pos < LZ4_OPT_NUM);
260 0 0         if ( (ml == matchML) /* last pos of last match */
261 0 0         && (last_match_pos < pos) )
262 0           last_match_pos = pos;
263 0           opt[pos].mlen = ml;
264 0           opt[pos].off = offset;
265 0           opt[pos].litlen = ll;
266 0           opt[pos].price = price;
267             } } }
268             /* complete following positions with literals */
269             { int addLit;
270 0 0         for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) {
271 0           opt[last_match_pos+addLit].mlen = 1; /* literal */
272 0           opt[last_match_pos+addLit].off = 0;
273 0           opt[last_match_pos+addLit].litlen = addLit;
274 0           opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit);
275             DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit);
276             } }
277             } /* for (cur = 1; cur <= last_match_pos; cur++) */
278              
279 0           best_mlen = opt[last_match_pos].mlen;
280 0           best_off = opt[last_match_pos].off;
281 0           cur = last_match_pos - best_mlen;
282              
283             encode: /* cur, last_match_pos, best_mlen, best_off must be set */
284             assert(cur < LZ4_OPT_NUM);
285             assert(last_match_pos >= 1); /* == 1 when only one candidate */
286             DEBUGLOG(6, "reverse traversal, looking for shortest path")
287             DEBUGLOG(6, "last_match_pos = %i", last_match_pos);
288 0           { int candidate_pos = cur;
289 0           int selected_matchLength = best_mlen;
290 0           int selected_offset = best_off;
291             while (1) { /* from end to beginning */
292 0           int const next_matchLength = opt[candidate_pos].mlen; /* can be 1, means literal */
293 0           int const next_offset = opt[candidate_pos].off;
294             DEBUGLOG(6, "pos %i: sequence length %i", candidate_pos, selected_matchLength);
295 0           opt[candidate_pos].mlen = selected_matchLength;
296 0           opt[candidate_pos].off = selected_offset;
297 0           selected_matchLength = next_matchLength;
298 0           selected_offset = next_offset;
299 0 0         if (next_matchLength > candidate_pos) break; /* last match elected, first match to encode */
300             assert(next_matchLength > 0); /* can be 1, means literal */
301 0           candidate_pos -= next_matchLength;
302 0           } }
303              
304             /* encode all recorded sequences in order */
305 0           { int rPos = 0; /* relative position (to ip) */
306 0 0         while (rPos < last_match_pos) {
307 0           int const ml = opt[rPos].mlen;
308 0           int const offset = opt[rPos].off;
309 0 0         if (ml == 1) { ip++; rPos++; continue; } /* literal; note: can end up with several literals, in which case, skip them */
310 0           rPos += ml;
311             assert(ml >= MINMATCH);
312             assert((offset >= 1) && (offset <= MAX_DISTANCE));
313 0           opSaved = op;
314 0 0         if ( LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ip - offset, limit, oend) ) /* updates ip, op and anchor */
315 0           goto _dest_overflow;
316             } }
317             } /* while (ip < mflimit) */
318              
319             _last_literals:
320             /* Encode Last Literals */
321 0           { size_t lastRunSize = (size_t)(iend - anchor); /* literals */
322 0           size_t litLength = (lastRunSize + 255 - RUN_MASK) / 255;
323 0           size_t const totalSize = 1 + litLength + lastRunSize;
324 0 0         if (limit == limitedDestSize) oend += LASTLITERALS; /* restore correct value */
325 0 0         if (limit && (op + totalSize > oend)) {
    0          
326 0 0         if (limit == limitedOutput) return 0; /* Check output limit */
327             /* adapt lastRunSize to fill 'dst' */
328 0           lastRunSize = (size_t)(oend - op) - 1;
329 0           litLength = (lastRunSize + 255 - RUN_MASK) / 255;
330 0           lastRunSize -= litLength;
331             }
332 0           ip = anchor + lastRunSize;
333              
334 0 0         if (lastRunSize >= RUN_MASK) {
335 0           size_t accumulator = lastRunSize - RUN_MASK;
336 0           *op++ = (RUN_MASK << ML_BITS);
337 0 0         for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255;
338 0           *op++ = (BYTE) accumulator;
339             } else {
340 0           *op++ = (BYTE)(lastRunSize << ML_BITS);
341             }
342 0           memcpy(op, anchor, lastRunSize);
343 0           op += lastRunSize;
344             }
345              
346             /* End */
347 0           *srcSizePtr = (int) (((const char*)ip) - source);
348 0           return (int) ((char*)op-dst);
349              
350             _dest_overflow:
351 0 0         if (limit == limitedDestSize) {
352 0           op = opSaved; /* restore correct out pointer */
353 0           goto _last_literals;
354             }
355 0           return 0;
356             }