| File: | root/firefox-clang/mozglue/static/lz4/lz4hc.c |
| Warning: | line 1864, column 9 Null pointer passed as 1st argument to memory copy function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | |||
| 2 | LZ4 HC - High Compression Mode of LZ4 | |||
| 3 | Copyright (c) Yann Collet. All rights reserved. | |||
| 4 | ||||
| 5 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) | |||
| 6 | ||||
| 7 | Redistribution and use in source and binary forms, with or without | |||
| 8 | modification, are permitted provided that the following conditions are | |||
| 9 | met: | |||
| 10 | ||||
| 11 | * Redistributions of source code must retain the above copyright | |||
| 12 | notice, this list of conditions and the following disclaimer. | |||
| 13 | * Redistributions in binary form must reproduce the above | |||
| 14 | copyright notice, this list of conditions and the following disclaimer | |||
| 15 | in the documentation and/or other materials provided with the | |||
| 16 | distribution. | |||
| 17 | ||||
| 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| 29 | ||||
| 30 | You can contact the author at : | |||
| 31 | - LZ4 source repository : https://github.com/lz4/lz4 | |||
| 32 | - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c | |||
| 33 | */ | |||
| 34 | /* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */ | |||
| 35 | ||||
| 36 | ||||
| 37 | /* ************************************* | |||
| 38 | * Tuning Parameter | |||
| 39 | ***************************************/ | |||
| 40 | ||||
| 41 | /*! HEAPMODE : | |||
| 42 | * Select how stateless HC compression functions like `LZ4_compress_HC()` | |||
| 43 | * allocate memory for their workspace: | |||
| 44 | * in stack (0:fastest), or in heap (1:default, requires malloc()). | |||
| 45 | * Since workspace is rather large, heap mode is recommended. | |||
| 46 | **/ | |||
| 47 | #ifndef LZ4HC_HEAPMODE1 | |||
| 48 | # define LZ4HC_HEAPMODE1 1 | |||
| 49 | #endif | |||
| 50 | ||||
| 51 | ||||
| 52 | /*=== Dependency ===*/ | |||
| 53 | #define LZ4_HC_STATIC_LINKING_ONLY | |||
| 54 | #include "lz4hc.h" | |||
| 55 | #include <limits.h> | |||
| 56 | ||||
| 57 | ||||
| 58 | /*=== Shared lz4.c code ===*/ | |||
| 59 | #ifndef LZ4_SRC_INCLUDED1 | |||
| 60 | # if defined(__GNUC__4) | |||
| 61 | # pragma GCC diagnostic ignored "-Wunused-function" | |||
| 62 | # endif | |||
| 63 | # if defined (__clang__1) | |||
| 64 | # pragma clang diagnostic ignored "-Wunused-function" | |||
| 65 | # endif | |||
| 66 | # define LZ4_COMMONDEFS_ONLY | |||
| 67 | # include "lz4.c" /* LZ4_count, constants, mem */ | |||
| 68 | #endif | |||
| 69 | ||||
| 70 | ||||
| 71 | /*=== Enums ===*/ | |||
| 72 | typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive; | |||
| 73 | ||||
| 74 | ||||
| 75 | /*=== Constants ===*/ | |||
| 76 | #define OPTIMAL_ML(int)((((1U<<4)-1)-1)+4) (int)((ML_MASK((1U<<4)-1)-1)+MINMATCH4) | |||
| 77 | #define LZ4_OPT_NUM(1<<12) (1<<12) | |||
| 78 | ||||
| 79 | ||||
| 80 | /*=== Macros ===*/ | |||
| 81 | #undef MIN | |||
| 82 | #define MIN(a,b)( (a) < (b) ? (a) : (b) ) ( (a) < (b) ? (a) : (b) ) | |||
| 83 | #undef MAX | |||
| 84 | #define MAX(a,b)( (a) > (b) ? (a) : (b) ) ( (a) > (b) ? (a) : (b) ) | |||
| 85 | ||||
| 86 | ||||
| 87 | /*=== Levels definition ===*/ | |||
| 88 | typedef enum { lz4mid, lz4hc, lz4opt } lz4hc_strat_e; | |||
| 89 | typedef struct { | |||
| 90 | lz4hc_strat_e strat; | |||
| 91 | int nbSearches; | |||
| 92 | U32 targetLength; | |||
| 93 | int nbSearchesSeq; /* lz4opt with favorDecSpeed only : budget at positions only worth searching to save a sequence */ | |||
| 94 | int dictCopyMin; /* attached dictionary : from this input size, its tables (256 KB) are copied rather than referenced (dictCtx), | |||
| 95 | * which compresses as well or better, and pays off sooner at levels which search more */ | |||
| 96 | } cParams_t; | |||
| 97 | static const cParams_t k_clTable[LZ4HC_CLEVEL_MAX12+1] = { | |||
| 98 | { lz4mid, 2, 16, 0, 4 KB*(1 <<10) }, /* 0, unused */ | |||
| 99 | { lz4mid, 2, 16, 0, 4 KB*(1 <<10) }, /* 1, unused */ | |||
| 100 | { lz4mid, 2, 16, 0, 4 KB*(1 <<10) }, /* 2 */ | |||
| 101 | { lz4hc, 4, 16, 0, 4 KB*(1 <<10) }, /* 3 */ | |||
| 102 | { lz4hc, 8, 16, 0, 4 KB*(1 <<10) }, /* 4 */ | |||
| 103 | { lz4hc, 16, 16, 0, 4 KB*(1 <<10) }, /* 5 */ | |||
| 104 | { lz4hc, 32, 16, 0, 4 KB*(1 <<10) }, /* 6 */ | |||
| 105 | { lz4hc, 64, 16, 0, 4 KB*(1 <<10) }, /* 7 */ | |||
| 106 | { lz4hc, 128, 16, 0, 4 KB*(1 <<10) }, /* 8 */ | |||
| 107 | { lz4hc, 256, 16, 0, 4 KB*(1 <<10) }, /* 9 */ | |||
| 108 | { lz4opt, 96, 64, 1, 4 KB*(1 <<10) }, /*10==LZ4HC_CLEVEL_OPT_MIN*/ | |||
| 109 | { lz4opt, 512,128, 2, 2 KB*(1 <<10) }, /*11 */ | |||
| 110 | { lz4opt,16384,LZ4_OPT_NUM(1<<12), 64, 1 KB*(1 <<10) }, /* 12==LZ4HC_CLEVEL_MAX */ | |||
| 111 | }; | |||
| 112 | ||||
| 113 | static cParams_t LZ4HC_getCLevelParams(int cLevel) | |||
| 114 | { | |||
| 115 | /* note : clevel convention is a bit different from lz4frame, | |||
| 116 | * possibly something worth revisiting for consistency */ | |||
| 117 | if (cLevel < 1) | |||
| 118 | cLevel = LZ4HC_CLEVEL_DEFAULT9; | |||
| 119 | cLevel = MIN(LZ4HC_CLEVEL_MAX, cLevel)( (12) < (cLevel) ? (12) : (cLevel) ); | |||
| 120 | return k_clTable[cLevel]; | |||
| 121 | } | |||
| 122 | ||||
| 123 | ||||
| 124 | /*=== Hashing ===*/ | |||
| 125 | #define LZ4HC_HASHSIZE4 4 | |||
| 126 | #define HASH_FUNCTION(i)(((i) * 2654435761U) >> ((4*8)-15)) (((i) * 2654435761U) >> ((MINMATCH4*8)-LZ4HC_HASH_LOG15)) | |||
| 127 | static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr))(((LZ4_read32(ptr)) * 2654435761U) >> ((4*8)-15)); } | |||
| 128 | ||||
| 129 | #if defined(LZ4_FORCE_MEMORY_ACCESS1) && (LZ4_FORCE_MEMORY_ACCESS1==2) | |||
| 130 | /* lie to the compiler about data alignment; use with caution */ | |||
| 131 | static U64 LZ4_read64(const void* memPtr) { return *(const U64*) memPtr; } | |||
| 132 | ||||
| 133 | #elif defined(LZ4_FORCE_MEMORY_ACCESS1) && (LZ4_FORCE_MEMORY_ACCESS1==1) | |||
| 134 | /* __pack instructions are safer, but compiler specific */ | |||
| 135 | LZ4_PACK(typedef struct { U64 u64; })typedef struct { U64 u64; } __attribute__((__packed__)) LZ4_unalign64; | |||
| 136 | static U64 LZ4_read64(const void* ptr) { return ((const LZ4_unalign64*)ptr)->u64; } | |||
| 137 | ||||
| 138 | #else /* safe and portable access using memcpy() */ | |||
| 139 | static U64 LZ4_read64(const void* memPtr) | |||
| 140 | { | |||
| 141 | U64 val; LZ4_memcpy(&val, memPtr, sizeof(val))__builtin_memcpy(&val, memPtr, sizeof(val)); return val; | |||
| 142 | } | |||
| 143 | ||||
| 144 | #endif /* LZ4_FORCE_MEMORY_ACCESS */ | |||
| 145 | ||||
| 146 | #define LZ4MID_HASHSIZE8 8 | |||
| 147 | #define LZ4MID_HASHLOG(15 -1) (LZ4HC_HASH_LOG15-1) | |||
| 148 | #define LZ4MID_HASHTABLESIZE(1 << (15 -1)) (1 << LZ4MID_HASHLOG(15 -1)) | |||
| 149 | ||||
| 150 | static U32 LZ4MID_hash4(U32 v) { return (v * 2654435761U) >> (32-LZ4MID_HASHLOG(15 -1)); } | |||
| 151 | static U32 LZ4MID_hash4Ptr(const void* ptr) { return LZ4MID_hash4(LZ4_read32(ptr)); } | |||
| 152 | /* note: hash7 hashes the lower 56-bits. | |||
| 153 | * It presumes input was read using little endian.*/ | |||
| 154 | static U32 LZ4MID_hash7(U64 v) { return (U32)(((v << (64-56)) * 58295818150454627ULL) >> (64-LZ4MID_HASHLOG(15 -1))) ; } | |||
| 155 | static U64 LZ4_readLE64(const void* memPtr); | |||
| 156 | static U32 LZ4MID_hash8Ptr(const void* ptr) { return LZ4MID_hash7(LZ4_readLE64(ptr)); } | |||
| 157 | ||||
| 158 | static U64 LZ4_readLE64(const void* memPtr) | |||
| 159 | { | |||
| 160 | if (LZ4_isLittleEndian()) { | |||
| 161 | return LZ4_read64(memPtr); | |||
| 162 | } else { | |||
| 163 | const BYTE* p = (const BYTE*)memPtr; | |||
| 164 | /* note: relies on the compiler to simplify this expression */ | |||
| 165 | return (U64)p[0] | ((U64)p[1]<<8) | ((U64)p[2]<<16) | ((U64)p[3]<<24) | |||
| 166 | | ((U64)p[4]<<32) | ((U64)p[5]<<40) | ((U64)p[6]<<48) | ((U64)p[7]<<56); | |||
| 167 | } | |||
| 168 | } | |||
| 169 | ||||
| 170 | ||||
| 171 | /*=== Count match length ===*/ | |||
| 172 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) | |||
| 173 | unsigned LZ4HC_NbCommonBytes32(U32 val) | |||
| 174 | { | |||
| 175 | assert(val != 0)((void)0); | |||
| 176 | if (LZ4_isLittleEndian()) { | |||
| 177 | # if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) | |||
| 178 | unsigned long r; | |||
| 179 | _BitScanReverse(&r, val); | |||
| 180 | return (unsigned)((31 - r) >> 3); | |||
| 181 | # elif (defined(__clang__1) || (defined(__GNUC__4) && ((__GNUC__4 > 3) || \ | |||
| 182 | ((__GNUC__4 == 3) && (__GNUC_MINOR__2 >= 4))))) && \ | |||
| 183 | !defined(LZ4_FORCE_SW_BITCOUNT) | |||
| 184 | return (unsigned)__builtin_clz(val) >> 3; | |||
| 185 | # else | |||
| 186 | val >>= 8; | |||
| 187 | val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | | |||
| 188 | (val + 0x00FF0000)) >> 24; | |||
| 189 | return (unsigned)val ^ 3; | |||
| 190 | # endif | |||
| 191 | } else { | |||
| 192 | # if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) | |||
| 193 | unsigned long r; | |||
| 194 | _BitScanForward(&r, val); | |||
| 195 | return (unsigned)(r >> 3); | |||
| 196 | # elif (defined(__clang__1) || (defined(__GNUC__4) && ((__GNUC__4 > 3) || \ | |||
| 197 | ((__GNUC__4 == 3) && (__GNUC_MINOR__2 >= 4))))) && \ | |||
| 198 | !defined(LZ4_FORCE_SW_BITCOUNT) | |||
| 199 | return (unsigned)__builtin_ctz(val) >> 3; | |||
| 200 | # else | |||
| 201 | const U32 m = 0x01010101; | |||
| 202 | return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; | |||
| 203 | # endif | |||
| 204 | } | |||
| 205 | } | |||
| 206 | ||||
| 207 | /** LZ4HC_countBack() : | |||
| 208 | * @return : negative value, nb of common bytes before ip/match */ | |||
| 209 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) | |||
| 210 | int LZ4HC_countBack(const BYTE* const ip, const BYTE* const match, | |||
| 211 | const BYTE* const iMin, const BYTE* const mMin) | |||
| 212 | { | |||
| 213 | int back = 0; | |||
| 214 | int const min = (int)MAX(iMin - ip, mMin - match)( (iMin - ip) > (mMin - match) ? (iMin - ip) : (mMin - match ) ); | |||
| 215 | assert(min <= 0)((void)0); | |||
| 216 | assert(ip >= iMin)((void)0); assert((size_t)(ip-iMin) < (1U<<31))((void)0); | |||
| 217 | assert(match >= mMin)((void)0); assert((size_t)(match - mMin) < (1U<<31))((void)0); | |||
| 218 | ||||
| 219 | while ((back - min) > 3) { | |||
| 220 | U32 const v = LZ4_read32(ip + back - 4) ^ LZ4_read32(match + back - 4); | |||
| 221 | if (v) { | |||
| 222 | return (back - (int)LZ4HC_NbCommonBytes32(v)); | |||
| 223 | } else back -= 4; /* 4-byte step */ | |||
| 224 | } | |||
| 225 | /* check remainder if any */ | |||
| 226 | while ( (back > min) | |||
| 227 | && (ip[back-1] == match[back-1]) ) | |||
| 228 | back--; | |||
| 229 | return back; | |||
| 230 | } | |||
| 231 | ||||
| 232 | /*=== Chain table updates ===*/ | |||
| 233 | #define DELTANEXTU16(table, pos)table[(U16)(pos)] table[(U16)(pos)] /* faster */ | |||
| 234 | /* Make fields passed to, and updated by LZ4HC_encodeSequence explicit */ | |||
| 235 | #define UPDATABLE(ip, op, anchor)&ip, &op, &anchor &ip, &op, &anchor | |||
| 236 | ||||
| 237 | ||||
| 238 | /************************************** | |||
| 239 | * Init | |||
| 240 | **************************************/ | |||
| 241 | static void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4) | |||
| 242 | { | |||
| 243 | MEM_INIT(hc4->hashTable, 0, sizeof(hc4->hashTable))memset(((hc4->hashTable)),((0)),((sizeof(hc4->hashTable )))); | |||
| 244 | MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable))memset(((hc4->chainTable)),((0xFF)),((sizeof(hc4->chainTable )))); | |||
| 245 | } | |||
| 246 | ||||
| 247 | static void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start) | |||
| 248 | { | |||
| 249 | size_t const bufferSize = (size_t)(hc4->end - hc4->prefixStart); | |||
| 250 | size_t newStartingOffset = bufferSize + hc4->dictLimit; | |||
| 251 | DEBUGLOG(5, "LZ4HC_init_internal"){}; | |||
| 252 | assert(newStartingOffset >= bufferSize)((void)0); /* check overflow */ | |||
| 253 | if (newStartingOffset > 1 GB*(1U<<30)) { | |||
| 254 | LZ4HC_clearTables(hc4); | |||
| 255 | newStartingOffset = 0; | |||
| 256 | } | |||
| 257 | newStartingOffset += 64 KB*(1 <<10); | |||
| 258 | hc4->nextToUpdate = (U32)newStartingOffset; | |||
| 259 | hc4->prefixStart = start; | |||
| 260 | hc4->end = start; | |||
| 261 | hc4->dictStart = start; | |||
| 262 | hc4->dictLimit = (U32)newStartingOffset; | |||
| 263 | hc4->lowLimit = (U32)newStartingOffset; | |||
| 264 | } | |||
| 265 | ||||
| 266 | ||||
| 267 | /************************************** | |||
| 268 | * Encode | |||
| 269 | **************************************/ | |||
| 270 | #if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 2) | |||
| 271 | # define RAWLOG(...) fprintf(stderr, __VA_ARGS__) | |||
| 272 | void LZ4HC_hexOut(const void* src, size_t len) | |||
| 273 | { | |||
| 274 | const BYTE* p = (const BYTE*)src; | |||
| 275 | size_t n; | |||
| 276 | for (n=0; n<len; n++) { | |||
| 277 | RAWLOG("%02X ", p[n]); | |||
| 278 | } | |||
| 279 | RAWLOG(" \n"); | |||
| 280 | } | |||
| 281 | ||||
| 282 | # define HEX_CMP(_lev, _ptr, _ref, _len) \ | |||
| 283 | if (LZ4_DEBUG >= _lev) { \ | |||
| 284 | RAWLOG("match bytes: "); \ | |||
| 285 | LZ4HC_hexOut(_ptr, _len); \ | |||
| 286 | RAWLOG("ref bytes: "); \ | |||
| 287 | LZ4HC_hexOut(_ref, _len); \ | |||
| 288 | } | |||
| 289 | ||||
| 290 | #else | |||
| 291 | # define HEX_CMP(l,p,r,_l) | |||
| 292 | #endif | |||
| 293 | ||||
| 294 | /* LZ4HC_encodeSequence() : | |||
| 295 | * @return : 0 if ok, | |||
| 296 | * 1 if buffer issue detected : @ip, @op and @anchor are then unchanged */ | |||
| 297 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) int LZ4HC_encodeSequence ( | |||
| 298 | const BYTE** _ip, | |||
| 299 | BYTE** _op, | |||
| 300 | const BYTE** _anchor, | |||
| 301 | int matchLength, | |||
| 302 | int offset, | |||
| 303 | limitedOutput_directive limit, | |||
| 304 | BYTE* oend) | |||
| 305 | { | |||
| 306 | #define ip (*_ip) | |||
| 307 | #define op (*_op) | |||
| 308 | #define anchor (*_anchor) | |||
| 309 | ||||
| 310 | BYTE* const token = op++; | |||
| 311 | ||||
| 312 | #if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6) | |||
| 313 | static const BYTE* start = NULL((void*)0); | |||
| 314 | static U32 totalCost = 0; | |||
| 315 | U32 const pos = (start==NULL((void*)0)) ? 0 : (U32)(anchor - start); /* only works for single segment */ | |||
| 316 | U32 const ll = (U32)(ip - anchor); | |||
| 317 | U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0; | |||
| 318 | U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0; | |||
| 319 | U32 const cost = 1 + llAdd + ll + 2 + mlAdd; | |||
| 320 | if (start==NULL((void*)0)) start = anchor; /* only works for single segment */ | |||
| 321 | DEBUGLOG(6, "pos:%7u -- literals:%4u, match:%4i, offset:%5i, cost:%4u + %5u",{} | |||
| 322 | pos,{} | |||
| 323 | (U32)(ip - anchor), matchLength, offset,{} | |||
| 324 | cost, totalCost){}; | |||
| 325 | # if 1 /* only works on single segment data */ | |||
| 326 | HEX_CMP(7, ip, ip-offset, matchLength); | |||
| 327 | # endif | |||
| 328 | totalCost += cost; | |||
| 329 | #endif | |||
| 330 | ||||
| 331 | /* Encode Literal length */ | |||
| 332 | { size_t litLen = (size_t)(ip - anchor); | |||
| 333 | LZ4_STATIC_ASSERT(notLimited == 0){ enum { LZ4_static_assert = 1/(int)(!!(notLimited == 0)) }; }; | |||
| 334 | /* Check output limit */ | |||
| 335 | if (limit && ((op + (litLen / 255) + litLen + (2 + 1 + LASTLITERALS5)) > oend)) { | |||
| 336 | DEBUGLOG(6, "Not enough room to write %i literals (%i bytes remaining)",{} | |||
| 337 | (int)litLen, (int)(oend - op)){}; | |||
| 338 | op = token; | |||
| 339 | return 1; | |||
| 340 | } | |||
| 341 | if (litLen >= RUN_MASK((1U<<(8-4))-1)) { | |||
| 342 | size_t len = litLen - RUN_MASK((1U<<(8-4))-1); | |||
| 343 | *token = (RUN_MASK((1U<<(8-4))-1) << ML_BITS4); | |||
| 344 | for(; len >= 255 ; len -= 255) *op++ = 255; | |||
| 345 | *op++ = (BYTE)len; | |||
| 346 | } else { | |||
| 347 | *token = (BYTE)(litLen << ML_BITS4); | |||
| 348 | } | |||
| 349 | ||||
| 350 | /* Copy Literals */ | |||
| 351 | LZ4_wildCopy8(op, anchor, op + litLen); | |||
| 352 | op += litLen; | |||
| 353 | } | |||
| 354 | ||||
| 355 | /* Encode Offset */ | |||
| 356 | assert(offset <= LZ4_DISTANCE_MAX )((void)0); | |||
| 357 | assert(offset > 0)((void)0); | |||
| 358 | LZ4_writeLE16(op, (U16)(offset)); op += 2; | |||
| 359 | ||||
| 360 | /* Encode MatchLength */ | |||
| 361 | assert(matchLength >= MINMATCH)((void)0); | |||
| 362 | { size_t mlCode = (size_t)matchLength - MINMATCH4; | |||
| 363 | if (limit && (op + (mlCode / 255) + (1 + LASTLITERALS5) > oend)) { | |||
| 364 | DEBUGLOG(6, "Not enough room to write match length"){}; | |||
| 365 | op = token; | |||
| 366 | return 1; | |||
| 367 | } | |||
| 368 | if (mlCode >= ML_MASK((1U<<4)-1)) { | |||
| 369 | *token += ML_MASK((1U<<4)-1); | |||
| 370 | mlCode -= ML_MASK((1U<<4)-1); | |||
| 371 | for(; mlCode >= 510 ; mlCode -= 510) { *op++ = 255; *op++ = 255; } | |||
| 372 | if (mlCode >= 255) { mlCode -= 255; *op++ = 255; } | |||
| 373 | *op++ = (BYTE)mlCode; | |||
| 374 | } else { | |||
| 375 | *token += (BYTE)(mlCode); | |||
| 376 | } } | |||
| 377 | ||||
| 378 | /* Prepare next loop */ | |||
| 379 | ip += matchLength; | |||
| 380 | anchor = ip; | |||
| 381 | ||||
| 382 | return 0; | |||
| 383 | ||||
| 384 | #undef ip | |||
| 385 | #undef op | |||
| 386 | #undef anchor | |||
| 387 | } | |||
| 388 | ||||
| 389 | /* LZ4HC_encodeLastLiterals() : | |||
| 390 | * Encode the final run of literals, from @anchor to @iend. | |||
| 391 | * In fillOutput mode, the run may be truncated, to fit into dst. | |||
| 392 | * @oend : same as LZ4HC_encodeSequence() : end of dst, minus LASTLITERALS in fillOutput mode | |||
| 393 | * @return : 0 if ok, | |||
| 394 | * 1 if dst is too small (limitedOutput) */ | |||
| 395 | static int LZ4HC_encodeLastLiterals ( | |||
| 396 | const BYTE** _ip, | |||
| 397 | BYTE** _op, | |||
| 398 | const BYTE** _anchor, | |||
| 399 | const BYTE* iend, | |||
| 400 | limitedOutput_directive limit, | |||
| 401 | BYTE* oend) | |||
| 402 | { | |||
| 403 | #define ip (*_ip) | |||
| 404 | #define op (*_op) | |||
| 405 | #define anchor (*_anchor) | |||
| 406 | ||||
| 407 | BYTE* const dstEnd = (limit == fillOutput) ? oend + LASTLITERALS5 : oend; | |||
| 408 | size_t lastRunSize = (size_t)(iend - anchor); /* literals */ | |||
| 409 | size_t llAdd = (lastRunSize + 255 - RUN_MASK((1U<<(8-4))-1)) / 255; | |||
| 410 | size_t const totalSize = 1 + llAdd + lastRunSize; | |||
| 411 | if (limit && (op + totalSize > dstEnd)) { | |||
| 412 | if (limit == limitedOutput) return 1; /* not enough space in dst */ | |||
| 413 | /* adapt lastRunSize to fill dst */ | |||
| 414 | lastRunSize = (size_t)(dstEnd - op) - 1 /*token*/; | |||
| 415 | llAdd = (lastRunSize + 256 - RUN_MASK((1U<<(8-4))-1)) / 256; | |||
| 416 | lastRunSize -= llAdd; | |||
| 417 | } | |||
| 418 | DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize){}; | |||
| 419 | ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ | |||
| 420 | ||||
| 421 | if (lastRunSize >= RUN_MASK((1U<<(8-4))-1)) { | |||
| 422 | size_t accumulator = lastRunSize - RUN_MASK((1U<<(8-4))-1); | |||
| 423 | *op++ = (RUN_MASK((1U<<(8-4))-1) << ML_BITS4); | |||
| 424 | for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; | |||
| 425 | *op++ = (BYTE) accumulator; | |||
| 426 | } else { | |||
| 427 | *op++ = (BYTE)(lastRunSize << ML_BITS4); | |||
| 428 | } | |||
| 429 | assert(lastRunSize <= (size_t)(dstEnd - op))((void)0); | |||
| 430 | LZ4_memcpy(op, anchor, lastRunSize)__builtin_memcpy(op, anchor, lastRunSize); | |||
| 431 | op += lastRunSize; | |||
| 432 | return 0; | |||
| 433 | ||||
| 434 | #undef ip | |||
| 435 | #undef op | |||
| 436 | #undef anchor | |||
| 437 | } | |||
| 438 | ||||
| 439 | /* LZ4HC_encodeLastSequence_fillOutput() : | |||
| 440 | * fillOutput mode only : current sequence doesn't fit into dst. | |||
| 441 | * Encode it anyway if all its literals fit, shortening its match if need be, | |||
| 442 | * but only if it leaves enough room for the final run of literals (MFLIMIT). | |||
| 443 | * @op : start of this sequence (before the failed LZ4HC_encodeSequence()) | |||
| 444 | * @oend : same as LZ4HC_encodeSequence() : end of dst minus LASTLITERALS */ | |||
| 445 | static void LZ4HC_encodeLastSequence_fillOutput ( | |||
| 446 | const BYTE** _ip, | |||
| 447 | BYTE** _op, | |||
| 448 | const BYTE** _anchor, | |||
| 449 | int matchLength, | |||
| 450 | int offset, | |||
| 451 | BYTE* oend) | |||
| 452 | { | |||
| 453 | #define ip (*_ip) | |||
| 454 | #define op (*_op) | |||
| 455 | #define anchor (*_anchor) | |||
| 456 | ||||
| 457 | BYTE* const dstEnd = oend + LASTLITERALS5; | |||
| 458 | size_t const ll = (size_t)(ip - anchor); | |||
| 459 | size_t const ll_addbytes = (ll + 240) / 255; | |||
| 460 | size_t const ll_totalCost = 1 + ll_addbytes + ll; | |||
| 461 | BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */ | |||
| 462 | DEBUGLOG(6, "Last sequence is overflowing : %u literals, %u remaining space",{} | |||
| 463 | (unsigned)ll, (unsigned)(oend - op)){}; | |||
| 464 | if (op + ll_totalCost <= maxLitPos) { | |||
| 465 | /* ll validated; now adjust match length */ | |||
| 466 | size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost)); | |||
| 467 | size_t const maxMlSize = MINMATCH4 + (ML_MASK((1U<<4)-1)-1) + (bytesLeftForMl * 255); | |||
| 468 | assert(maxMlSize < INT_MAX)((void)0); assert(matchLength >= 0)((void)0); | |||
| 469 | if ((size_t)matchLength > maxMlSize) matchLength = (int)maxMlSize; | |||
| 470 | if (dstEnd - (op + ll_totalCost + 2) - 1 + matchLength >= MFLIMIT12) { | |||
| 471 | DEBUGLOG(6, "Let's encode a last sequence (ll=%u, ml=%i)", (unsigned)ll, matchLength){}; | |||
| 472 | LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, matchLength, offset, notLimited, oend); | |||
| 473 | } } | |||
| 474 | ||||
| 475 | #undef ip | |||
| 476 | #undef op | |||
| 477 | #undef anchor | |||
| 478 | } | |||
| 479 | ||||
| 480 | ||||
| 481 | typedef struct { | |||
| 482 | int off; | |||
| 483 | int len; | |||
| 484 | int back; /* negative value */ | |||
| 485 | } LZ4HC_match_t; | |||
| 486 | ||||
| 487 | static LZ4HC_match_t LZ4HC_searchExtDict(const BYTE* ip, U32 ipIndex, | |||
| 488 | const BYTE* const iLowLimit, const BYTE* const iHighLimit, | |||
| 489 | const LZ4HC_CCtx_internal* dictCtx, U32 gDictEndIndex, | |||
| 490 | int currentBestML, int nbAttempts) | |||
| 491 | { | |||
| 492 | size_t const lDictEndIndex = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; | |||
| 493 | U32 lDictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; | |||
| 494 | U32 matchIndex = lDictMatchIndex + gDictEndIndex - (U32)lDictEndIndex; | |||
| 495 | int offset = 0, sBack = 0; | |||
| 496 | assert(lDictEndIndex <= 1 GB)((void)0); | |||
| 497 | if (lDictMatchIndex>0) | |||
| 498 | DEBUGLOG(7, "lDictEndIndex = %zu, lDictMatchIndex = %u", lDictEndIndex, lDictMatchIndex){}; | |||
| 499 | while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX65535 && nbAttempts--) { | |||
| 500 | const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + lDictMatchIndex; | |||
| 501 | ||||
| 502 | if (LZ4_read32(matchPtr) == LZ4_read32(ip)) { | |||
| 503 | int mlt; | |||
| 504 | int back = 0; | |||
| 505 | const BYTE* vLimit = ip + (lDictEndIndex - lDictMatchIndex); | |||
| 506 | if (vLimit > iHighLimit) vLimit = iHighLimit; | |||
| 507 | mlt = (int)LZ4_count(ip+MINMATCH4, matchPtr+MINMATCH4, vLimit) + MINMATCH4; | |||
| 508 | back = (ip > iLowLimit) ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0; | |||
| 509 | mlt -= back; | |||
| 510 | if (mlt > currentBestML) { | |||
| 511 | currentBestML = mlt; | |||
| 512 | offset = (int)(ipIndex - matchIndex); | |||
| 513 | sBack = back; | |||
| 514 | DEBUGLOG(7, "found match of length %i within extDictCtx", currentBestML){}; | |||
| 515 | } } | |||
| 516 | ||||
| 517 | { U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, lDictMatchIndex)dictCtx->chainTable[(U16)(lDictMatchIndex)]; | |||
| 518 | lDictMatchIndex -= nextOffset; | |||
| 519 | matchIndex -= nextOffset; | |||
| 520 | } } | |||
| 521 | ||||
| 522 | { LZ4HC_match_t md; | |||
| 523 | md.len = currentBestML; | |||
| 524 | md.off = offset; | |||
| 525 | md.back = sBack; | |||
| 526 | return md; | |||
| 527 | } | |||
| 528 | } | |||
| 529 | ||||
| 530 | typedef LZ4HC_match_t (*LZ4MID_searchIntoDict_f)(const BYTE* ip, U32 ipIndex, | |||
| 531 | const BYTE* const iHighLimit, | |||
| 532 | const LZ4HC_CCtx_internal* dictCtx, U32 gDictEndIndex); | |||
| 533 | ||||
| 534 | static LZ4HC_match_t LZ4MID_searchHCDict(const BYTE* ip, U32 ipIndex, | |||
| 535 | const BYTE* const iHighLimit, | |||
| 536 | const LZ4HC_CCtx_internal* dictCtx, U32 gDictEndIndex) | |||
| 537 | { | |||
| 538 | return LZ4HC_searchExtDict(ip,ipIndex, | |||
| 539 | ip, iHighLimit, | |||
| 540 | dictCtx, gDictEndIndex, | |||
| 541 | MINMATCH4-1, 2); | |||
| 542 | } | |||
| 543 | ||||
| 544 | static LZ4HC_match_t LZ4MID_searchExtDict(const BYTE* ip, U32 ipIndex, | |||
| 545 | const BYTE* const iHighLimit, | |||
| 546 | const LZ4HC_CCtx_internal* dictCtx, U32 gDictEndIndex) | |||
| 547 | { | |||
| 548 | size_t const lDictEndIndex = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; | |||
| 549 | const U32* const hash4Table = dictCtx->hashTable; | |||
| 550 | const U32* const hash8Table = hash4Table + LZ4MID_HASHTABLESIZE(1 << (15 -1)); | |||
| 551 | DEBUGLOG(7, "LZ4MID_searchExtDict (ipIdx=%u)", ipIndex){}; | |||
| 552 | ||||
| 553 | /* search long match first */ | |||
| 554 | { U32 l8DictMatchIndex = hash8Table[LZ4MID_hash8Ptr(ip)]; | |||
| 555 | U32 m8Index = l8DictMatchIndex + gDictEndIndex - (U32)lDictEndIndex; | |||
| 556 | assert(lDictEndIndex <= 1 GB)((void)0); | |||
| 557 | if (ipIndex - m8Index <= LZ4_DISTANCE_MAX65535) { | |||
| 558 | const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + l8DictMatchIndex; | |||
| 559 | const size_t safeLen = MIN(lDictEndIndex - l8DictMatchIndex, (size_t)(iHighLimit - ip))( (lDictEndIndex - l8DictMatchIndex) < ((size_t)(iHighLimit - ip)) ? (lDictEndIndex - l8DictMatchIndex) : ((size_t)(iHighLimit - ip)) ); | |||
| 560 | int mlt = (int)LZ4_count(ip, matchPtr, ip + safeLen); | |||
| 561 | if (mlt >= MINMATCH4) { | |||
| 562 | LZ4HC_match_t md; | |||
| 563 | DEBUGLOG(7, "Found long ExtDict match of len=%u", mlt){}; | |||
| 564 | md.len = mlt; | |||
| 565 | md.off = (int)(ipIndex - m8Index); | |||
| 566 | md.back = 0; | |||
| 567 | return md; | |||
| 568 | } | |||
| 569 | } | |||
| 570 | } | |||
| 571 | ||||
| 572 | /* search for short match second */ | |||
| 573 | { U32 l4DictMatchIndex = hash4Table[LZ4MID_hash4Ptr(ip)]; | |||
| 574 | U32 m4Index = l4DictMatchIndex + gDictEndIndex - (U32)lDictEndIndex; | |||
| 575 | if (ipIndex - m4Index <= LZ4_DISTANCE_MAX65535) { | |||
| 576 | const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + l4DictMatchIndex; | |||
| 577 | const size_t safeLen = MIN(lDictEndIndex - l4DictMatchIndex, (size_t)(iHighLimit - ip))( (lDictEndIndex - l4DictMatchIndex) < ((size_t)(iHighLimit - ip)) ? (lDictEndIndex - l4DictMatchIndex) : ((size_t)(iHighLimit - ip)) ); | |||
| 578 | int mlt = (int)LZ4_count(ip, matchPtr, ip + safeLen); | |||
| 579 | if (mlt >= MINMATCH4) { | |||
| 580 | LZ4HC_match_t md; | |||
| 581 | DEBUGLOG(7, "Found short ExtDict match of len=%u", mlt){}; | |||
| 582 | md.len = mlt; | |||
| 583 | md.off = (int)(ipIndex - m4Index); | |||
| 584 | md.back = 0; | |||
| 585 | return md; | |||
| 586 | } | |||
| 587 | } | |||
| 588 | } | |||
| 589 | ||||
| 590 | /* nothing found */ | |||
| 591 | { LZ4HC_match_t const md = {0, 0, 0 }; | |||
| 592 | return md; | |||
| 593 | } | |||
| 594 | } | |||
| 595 | ||||
| 596 | /* LZ4HC_dropPositions() : | |||
| 597 | * In fillOutput mode, compression can stop at @start, before positions the parser has already referenced. | |||
| 598 | * The next block compresses them : it must not find them as matches. | |||
| 599 | * Remove their references from the hash table, up to @end (excluded). | |||
| 600 | * Costs one pass on these positions, or on the hash table, whichever is shorter. */ | |||
| 601 | static void | |||
| 602 | LZ4HC_dropPositions(LZ4HC_CCtx_internal* ctx, const BYTE* start, const BYTE* end, lz4hc_strat_e strat) | |||
| 603 | { | |||
| 604 | U32* const hashTable = ctx->hashTable; | |||
| 605 | U32 const startIdx = ctx->dictLimit + (U32)(start - ctx->prefixStart); | |||
| 606 | if (end - start > LZ4HC_HASHTABLESIZE(1 << 15)) { | |||
| 607 | int i; | |||
| 608 | for (i = 0; i < LZ4HC_HASHTABLESIZE(1 << 15); i++) { | |||
| 609 | /* branchless, so that it can be vectorized */ | |||
| 610 | hashTable[i] = (hashTable[i] >= startIdx) ? 0 : hashTable[i]; | |||
| 611 | } | |||
| 612 | } else { | |||
| 613 | U32 idx = startIdx; | |||
| 614 | const BYTE* p; | |||
| 615 | for (p = start; p < end; p++, idx++) { | |||
| 616 | if (strat == lz4mid) { | |||
| 617 | U32 const h4 = LZ4MID_hash4Ptr(p); | |||
| 618 | U32 const h8 = LZ4MID_HASHTABLESIZE(1 << (15 -1)) + LZ4MID_hash8Ptr(p); | |||
| 619 | if (hashTable[h4] == idx) hashTable[h4] = 0; | |||
| 620 | if (hashTable[h8] == idx) hashTable[h8] = 0; | |||
| 621 | } else { | |||
| 622 | U32 const h = LZ4HC_hashPtr(p); | |||
| 623 | if (hashTable[h] == idx) hashTable[h] = 0; | |||
| 624 | } } } | |||
| 625 | } | |||
| 626 | ||||
| 627 | /************************************** | |||
| 628 | * Mid Compression (level 2) | |||
| 629 | **************************************/ | |||
| 630 | ||||
| 631 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) void | |||
| 632 | LZ4MID_addPosition(U32* hTable, U32 hValue, U32 index) | |||
| 633 | { | |||
| 634 | hTable[hValue] = index; | |||
| 635 | } | |||
| 636 | ||||
| 637 | #define ADDPOS8(_p, _idx)LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(_p), _idx) LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(_p), _idx) | |||
| 638 | #define ADDPOS4(_p, _idx)LZ4MID_addPosition(hash4Table, LZ4MID_hash4Ptr(_p), _idx) LZ4MID_addPosition(hash4Table, LZ4MID_hash4Ptr(_p), _idx) | |||
| 639 | ||||
| 640 | /* Fill hash tables with references into dictionary. | |||
| 641 | * The resulting table is only exploitable by LZ4MID (level 2) */ | |||
| 642 | static void | |||
| 643 | LZ4MID_fillHTable (LZ4HC_CCtx_internal* cctx, const void* dict, size_t size) | |||
| 644 | { | |||
| 645 | U32* const hash4Table = cctx->hashTable; | |||
| 646 | U32* const hash8Table = hash4Table + LZ4MID_HASHTABLESIZE(1 << (15 -1)); | |||
| 647 | const BYTE* const prefixPtr = (const BYTE*)dict; | |||
| 648 | U32 const prefixIdx = cctx->dictLimit; | |||
| 649 | U32 const target = prefixIdx + (U32)size - LZ4MID_HASHSIZE8; | |||
| 650 | U32 idx = cctx->nextToUpdate; | |||
| 651 | assert(dict == cctx->prefixStart)((void)0); | |||
| 652 | DEBUGLOG(4, "LZ4MID_fillHTable (size:%zu)", size){}; | |||
| 653 | if (size <= LZ4MID_HASHSIZE8) | |||
| 654 | return; | |||
| 655 | ||||
| 656 | for (; idx < target; idx += 3) { | |||
| 657 | ADDPOS4(prefixPtr+idx-prefixIdx, idx)LZ4MID_addPosition(hash4Table, LZ4MID_hash4Ptr(prefixPtr+idx- prefixIdx), idx); | |||
| 658 | ADDPOS8(prefixPtr+idx+1-prefixIdx, idx+1)LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(prefixPtr+idx+ 1-prefixIdx), idx+1); | |||
| 659 | } | |||
| 660 | ||||
| 661 | idx = (size > 32 KB*(1 <<10) + LZ4MID_HASHSIZE8) ? target - 32 KB*(1 <<10) : cctx->nextToUpdate; | |||
| 662 | for (; idx < target; idx += 1) { | |||
| 663 | ADDPOS8(prefixPtr+idx-prefixIdx, idx)LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(prefixPtr+idx- prefixIdx), idx); | |||
| 664 | } | |||
| 665 | ||||
| 666 | cctx->nextToUpdate = target; | |||
| 667 | } | |||
| 668 | ||||
| 669 | static LZ4MID_searchIntoDict_f select_searchDict_function(const LZ4HC_CCtx_internal* dictCtx) | |||
| 670 | { | |||
| 671 | if (dictCtx == NULL((void*)0)) return NULL((void*)0); | |||
| 672 | if (LZ4HC_getCLevelParams(dictCtx->compressionLevel).strat == lz4mid) | |||
| 673 | return LZ4MID_searchExtDict; | |||
| 674 | return LZ4MID_searchHCDict; | |||
| 675 | } | |||
| 676 | ||||
| 677 | /* Length of the match between @ip and the candidate at index @pos (in prefix or extDict), | |||
| 678 | * or 0 if the candidate is out of reach */ | |||
| 679 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) unsigned | |||
| 680 | LZ4MID_countMatch(const BYTE* ip, U32 ipIndex, U32 pos, const BYTE* matchlimit, | |||
| 681 | const BYTE* prefixPtr, U32 prefixIdx, | |||
| 682 | const BYTE* dictStart, U32 dictIdx) | |||
| 683 | { | |||
| 684 | if (ipIndex - pos > LZ4_DISTANCE_MAX65535) return 0; | |||
| 685 | if (pos >= prefixIdx) { | |||
| 686 | const BYTE* const matchPtr = prefixPtr + (pos - prefixIdx); | |||
| 687 | assert(matchPtr < ip)((void)0); | |||
| 688 | return LZ4_count(ip, matchPtr, matchlimit); | |||
| 689 | } | |||
| 690 | if (pos >= dictIdx) { | |||
| 691 | /* extDict : the match stops at the end of the dictionary */ | |||
| 692 | const BYTE* const matchPtr = dictStart + (pos - dictIdx); | |||
| 693 | size_t const safeLen = MIN(prefixIdx - pos, (size_t)(matchlimit - ip))( (prefixIdx - pos) < ((size_t)(matchlimit - ip)) ? (prefixIdx - pos) : ((size_t)(matchlimit - ip)) ); | |||
| 694 | return LZ4_count(ip, matchPtr, ip + safeLen); | |||
| 695 | } | |||
| 696 | return 0; | |||
| 697 | } | |||
| 698 | ||||
| 699 | /* preconditions: | |||
| 700 | * - *srcSizePtr within [1, LZ4_MAX_INPUT_SIZE] | |||
| 701 | * - src is valid | |||
| 702 | * - maxOutputSize >= 1 | |||
| 703 | * - dst is valid | |||
| 704 | */ | |||
| 705 | static int LZ4MID_compress ( | |||
| 706 | LZ4HC_CCtx_internal* const ctx, | |||
| 707 | const char* const src, | |||
| 708 | char* const dst, | |||
| 709 | int* srcSizePtr, | |||
| 710 | int const maxOutputSize, | |||
| 711 | const limitedOutput_directive limit, | |||
| 712 | const dictCtx_directive dict | |||
| 713 | ) | |||
| 714 | { | |||
| 715 | U32* const hash4Table = ctx->hashTable; | |||
| 716 | U32* const hash8Table = hash4Table + LZ4MID_HASHTABLESIZE(1 << (15 -1)); | |||
| 717 | const BYTE* ip = (const BYTE*)src; | |||
| 718 | const BYTE* anchor = ip; | |||
| 719 | const BYTE* const iend = ip + *srcSizePtr; | |||
| 720 | const BYTE* mflimit; /* set after the size check : would precede a small input */ | |||
| 721 | const BYTE* matchlimit; | |||
| 722 | const BYTE* ilimit; | |||
| 723 | BYTE* op = (BYTE*)dst; | |||
| 724 | /* fillOutput : keep room for the final run of literals (LZ4 format restriction) */ | |||
| 725 | BYTE* const oend = op + maxOutputSize - ((limit == fillOutput) ? LASTLITERALS5 : 0); | |||
| 726 | ||||
| 727 | const BYTE* const prefixPtr = ctx->prefixStart; | |||
| 728 | const U32 prefixIdx = ctx->dictLimit; | |||
| 729 | U32 ilimitIdx; | |||
| 730 | const BYTE* const dictStart = ctx->dictStart; | |||
| 731 | const U32 dictIdx = ctx->lowLimit; | |||
| 732 | const U32 gDictEndIndex = ctx->lowLimit; | |||
| 733 | const LZ4MID_searchIntoDict_f searchIntoDict = (dict == usingDictCtxHc) ? select_searchDict_function(ctx->dictCtx) : NULL((void*)0); | |||
| 734 | const BYTE* refEnd = ip; /* fillOutput : the hash tables reference positions before MAX(refEnd, ip) */ | |||
| 735 | ||||
| 736 | DEBUGLOG(5, "LZ4MID_compress (%i bytes)", *srcSizePtr){}; | |||
| 737 | ||||
| 738 | /* preconditions verifications */ | |||
| 739 | if (dict == usingDictCtxHc) DEBUGLOG(5, "usingDictCtxHc"){}; | |||
| 740 | assert(*srcSizePtr > 0)((void)0); | |||
| 741 | assert(*srcSizePtr <= LZ4_MAX_INPUT_SIZE)((void)0); | |||
| 742 | assert(src != NULL)((void)0); | |||
| 743 | assert(maxOutputSize >= 1)((void)0); | |||
| 744 | assert(dst != NULL)((void)0); | |||
| 745 | ||||
| 746 | if (*srcSizePtr < LZ4_minLength) | |||
| 747 | goto _lz4mid_last_literals; /* Input too small, no compression (all literals) */ | |||
| 748 | mflimit = iend - MFLIMIT12; | |||
| 749 | matchlimit = (iend - LASTLITERALS5); | |||
| 750 | ilimit = (iend - LZ4MID_HASHSIZE8); | |||
| 751 | ilimitIdx = (U32)(ilimit - prefixPtr) + prefixIdx; | |||
| 752 | ||||
| 753 | /* main loop */ | |||
| 754 | while (ip <= mflimit) { | |||
| 755 | const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx; | |||
| 756 | unsigned matchLength; | |||
| 757 | unsigned matchDistance; | |||
| 758 | /* search long match */ | |||
| 759 | { U32 const h8 = LZ4MID_hash8Ptr(ip); | |||
| 760 | U32 const pos8 = hash8Table[h8]; | |||
| 761 | assert(h8 < LZ4MID_HASHTABLESIZE)((void)0); | |||
| 762 | assert(pos8 < ipIndex)((void)0); | |||
| 763 | LZ4MID_addPosition(hash8Table, h8, ipIndex); | |||
| 764 | matchLength = LZ4MID_countMatch(ip, ipIndex, pos8, matchlimit, prefixPtr, prefixIdx, dictStart, dictIdx); | |||
| 765 | matchDistance = ipIndex - pos8; | |||
| 766 | } | |||
| 767 | /* search short match */ | |||
| 768 | if (matchLength < MINMATCH4) { | |||
| 769 | U32 const h4 = LZ4MID_hash4Ptr(ip); | |||
| 770 | U32 const pos4 = hash4Table[h4]; | |||
| 771 | assert(h4 < LZ4MID_HASHTABLESIZE)((void)0); | |||
| 772 | assert(pos4 < ipIndex)((void)0); | |||
| 773 | LZ4MID_addPosition(hash4Table, h4, ipIndex); | |||
| 774 | matchLength = LZ4MID_countMatch(ip, ipIndex, pos4, matchlimit, prefixPtr, prefixIdx, dictStart, dictIdx); | |||
| 775 | matchDistance = ipIndex - pos4; | |||
| 776 | } | |||
| 777 | if (matchLength >= MINMATCH4) { | |||
| 778 | if ((matchLength < 8) && likely(ip < mflimit)(__builtin_expect (((ip < mflimit) != 0),(1)) )) { | |||
| 779 | /* short match : check ip+1 for longer, except ip itself */ | |||
| 780 | U32 const h8 = LZ4MID_hash8Ptr(ip+1); | |||
| 781 | U32 const pos8 = hash8Table[h8]; | |||
| 782 | unsigned const ml2 = LZ4MID_countMatch(ip+1, ipIndex+1, pos8, matchlimit, prefixPtr, prefixIdx, dictStart, dictIdx); | |||
| 783 | if ((ml2 > matchLength) && (pos8 != ipIndex)) { | |||
| 784 | LZ4MID_addPosition(hash8Table, h8, ipIndex+1); | |||
| 785 | ip++; | |||
| 786 | matchLength = ml2; | |||
| 787 | matchDistance = ipIndex + 1 - pos8; | |||
| 788 | } } | |||
| 789 | goto _lz4mid_encode_sequence; | |||
| 790 | } | |||
| 791 | /* no match found in prefix nor extDict : search into the attached dictionary */ | |||
| 792 | if ( (dict == usingDictCtxHc) | |||
| 793 | && (ipIndex - gDictEndIndex < LZ4_DISTANCE_MAX65535 - 8) ) { | |||
| 794 | LZ4HC_match_t const dMatch = searchIntoDict(ip, ipIndex, | |||
| 795 | matchlimit, | |||
| 796 | ctx->dictCtx, gDictEndIndex); | |||
| 797 | assert(dMatch.back == 0)((void)0); | |||
| 798 | matchLength = (unsigned)dMatch.len; | |||
| 799 | matchDistance = (unsigned)dMatch.off; | |||
| 800 | if (matchLength >= MINMATCH4) goto _lz4mid_encode_sequence; | |||
| 801 | } | |||
| 802 | /* no match found */ | |||
| 803 | ip += 1 + ((ip-anchor) >> 9); /* skip faster over incompressible data */ | |||
| 804 | continue; | |||
| 805 | ||||
| 806 | _lz4mid_encode_sequence: | |||
| 807 | /* match found at ip : matchLength, matchDistance */ | |||
| 808 | DEBUGLOG(7, "found match at pos %u (len=%u, distance=%u)",{} | |||
| 809 | (U32)(ip - prefixPtr) + prefixIdx, matchLength, matchDistance){}; | |||
| 810 | ||||
| 811 | /* catch back */ | |||
| 812 | while (((ip > anchor) & ((U32)(ip-prefixPtr) > matchDistance)) && (unlikely(ip[-1] == ip[-(int)matchDistance-1])(__builtin_expect (((ip[-1] == ip[-(int)matchDistance-1]) != 0 ),(0)) ))) { | |||
| 813 | ip--; matchLength++; | |||
| 814 | }; | |||
| 815 | ||||
| 816 | /* fill table with beginning of match | |||
| 817 | * note : ip may have moved since ipIndex (longer match at ip+1, catch back) */ | |||
| 818 | { U32 const startMatchIdx = (U32)(ip-prefixPtr) + prefixIdx; | |||
| 819 | ADDPOS8(ip+1, startMatchIdx+1)LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(ip+1), startMatchIdx +1); | |||
| 820 | ADDPOS8(ip+2, startMatchIdx+2)LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(ip+2), startMatchIdx +2); | |||
| 821 | ADDPOS4(ip+1, startMatchIdx+1)LZ4MID_addPosition(hash4Table, LZ4MID_hash4Ptr(ip+1), startMatchIdx +1); | |||
| 822 | } | |||
| 823 | ||||
| 824 | /* encode */ | |||
| 825 | if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, | |||
| 826 | (int)matchLength, (int)matchDistance, | |||
| 827 | limit, oend) ) { | |||
| 828 | if (limit != fillOutput) return 0; /* compression failed */ | |||
| 829 | refEnd = ip + matchLength; | |||
| 830 | LZ4HC_encodeLastSequence_fillOutput(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, | |||
| 831 | (int)matchLength, (int)matchDistance, oend); | |||
| 832 | break; | |||
| 833 | } | |||
| 834 | ||||
| 835 | /* fill table with end of match */ | |||
| 836 | { U32 endMatchIdx = (U32)(ip-prefixPtr) + prefixIdx; | |||
| 837 | U32 pos_m2 = endMatchIdx - 2; | |||
| 838 | if (pos_m2 < ilimitIdx) { | |||
| 839 | if (likely(ip - prefixPtr > 5)(__builtin_expect (((ip - prefixPtr > 5) != 0),(1)) )) { | |||
| 840 | ADDPOS8(ip-5, endMatchIdx - 5)LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(ip-5), endMatchIdx - 5); | |||
| 841 | } | |||
| 842 | ADDPOS8(ip-3, endMatchIdx - 3)LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(ip-3), endMatchIdx - 3); | |||
| 843 | ADDPOS8(ip-2, endMatchIdx - 2)LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(ip-2), endMatchIdx - 2); | |||
| 844 | ADDPOS4(ip-2, endMatchIdx - 2)LZ4MID_addPosition(hash4Table, LZ4MID_hash4Ptr(ip-2), endMatchIdx - 2); | |||
| 845 | ADDPOS4(ip-1, endMatchIdx - 1)LZ4MID_addPosition(hash4Table, LZ4MID_hash4Ptr(ip-1), endMatchIdx - 1); | |||
| 846 | } | |||
| 847 | } | |||
| 848 | } | |||
| 849 | ||||
| 850 | _lz4mid_last_literals: | |||
| 851 | if (refEnd < ip) refEnd = ip; | |||
| 852 | if (LZ4HC_encodeLastLiterals(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, iend, limit, oend)) | |||
| 853 | return 0; /* not enough space in @dst */ | |||
| 854 | if (ip < refEnd) /* fillOutput : the rest of input is left to the next block (input >= LZ4_minLength) */ | |||
| 855 | LZ4HC_dropPositions(ctx, ip, MIN(refEnd, iend - LZ4MID_HASHSIZE)( (refEnd) < (iend - 8) ? (refEnd) : (iend - 8) ), lz4mid); | |||
| 856 | ||||
| 857 | /* End */ | |||
| 858 | DEBUGLOG(5, "compressed %i bytes into %i bytes", *srcSizePtr, (int)((char*)op - dst)){}; | |||
| 859 | assert(ip >= (const BYTE*)src)((void)0); | |||
| 860 | assert(ip <= iend)((void)0); | |||
| 861 | *srcSizePtr = (int)(ip - (const BYTE*)src); | |||
| 862 | assert((char*)op >= dst)((void)0); | |||
| 863 | assert((char*)op - dst <= maxOutputSize)((void)0); | |||
| 864 | assert((char*)op - dst < INT_MAX)((void)0); | |||
| 865 | return (int)((char*)op - dst); | |||
| 866 | } | |||
| 867 | ||||
| 868 | ||||
| 869 | /************************************** | |||
| 870 | * HC Compression - Search | |||
| 871 | **************************************/ | |||
| 872 | ||||
| 873 | /* Update chains up to ip (excluded) */ | |||
| 874 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip) | |||
| 875 | { | |||
| 876 | U16* const chainTable = hc4->chainTable; | |||
| 877 | U32* const hashTable = hc4->hashTable; | |||
| 878 | const BYTE* const prefixPtr = hc4->prefixStart; | |||
| 879 | U32 const prefixIdx = hc4->dictLimit; | |||
| 880 | U32 const target = (U32)(ip - prefixPtr) + prefixIdx; | |||
| 881 | U32 idx = hc4->nextToUpdate; | |||
| 882 | assert(ip >= prefixPtr)((void)0); | |||
| 883 | assert(target >= prefixIdx)((void)0); | |||
| 884 | ||||
| 885 | while (idx < target) { | |||
| 886 | U32 const h = LZ4HC_hashPtr(prefixPtr+idx-prefixIdx); | |||
| 887 | size_t delta = idx - hashTable[h]; | |||
| 888 | if (delta>LZ4_DISTANCE_MAX65535) delta = LZ4_DISTANCE_MAX65535; | |||
| 889 | DELTANEXTU16(chainTable, idx)chainTable[(U16)(idx)] = (U16)delta; | |||
| 890 | hashTable[h] = idx; | |||
| 891 | idx++; | |||
| 892 | } | |||
| 893 | ||||
| 894 | hc4->nextToUpdate = target; | |||
| 895 | } | |||
| 896 | ||||
| 897 | #if defined(_MSC_VER) | |||
| 898 | # define LZ4HC_rotl32(x,r)((x << r) | (x >> (32 - r))) _rotl(x,r) | |||
| 899 | #else | |||
| 900 | # define LZ4HC_rotl32(x,r)((x << r) | (x >> (32 - r))) ((x << r) | (x >> (32 - r))) | |||
| 901 | #endif | |||
| 902 | ||||
| 903 | ||||
| 904 | static U32 LZ4HC_rotatePattern(size_t const rotate, U32 const pattern) | |||
| 905 | { | |||
| 906 | size_t const bitsToRotate = (rotate & (sizeof(pattern) - 1)) << 3; | |||
| 907 | if (bitsToRotate == 0) return pattern; | |||
| 908 | return LZ4HC_rotl32(pattern, (int)bitsToRotate)((pattern << (int)bitsToRotate) | (pattern >> (32 - (int)bitsToRotate))); | |||
| 909 | } | |||
| 910 | ||||
| 911 | /* LZ4HC_countPattern() : | |||
| 912 | * pattern32 must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) */ | |||
| 913 | static unsigned | |||
| 914 | LZ4HC_countPattern(const BYTE* ip, const BYTE* const iEnd, U32 const pattern32) | |||
| 915 | { | |||
| 916 | const BYTE* const iStart = ip; | |||
| 917 | reg_t const pattern = (sizeof(pattern)==8) ? | |||
| 918 | (reg_t)pattern32 + (((reg_t)pattern32) << (sizeof(pattern)*4)) : pattern32; | |||
| 919 | ||||
| 920 | while (likely(ip < iEnd-(sizeof(pattern)-1))(__builtin_expect (((ip < iEnd-(sizeof(pattern)-1)) != 0), (1)) )) { | |||
| 921 | reg_t const diff = LZ4_read_ARCH(ip) ^ pattern; | |||
| 922 | if (!diff) { ip+=sizeof(pattern); continue; } | |||
| 923 | ip += LZ4_NbCommonBytes(diff); | |||
| 924 | return (unsigned)(ip - iStart); | |||
| 925 | } | |||
| 926 | ||||
| 927 | if (LZ4_isLittleEndian()) { | |||
| 928 | reg_t patternByte = pattern; | |||
| 929 | while ((ip<iEnd) && (*ip == (BYTE)patternByte)) { | |||
| 930 | ip++; patternByte >>= 8; | |||
| 931 | } | |||
| 932 | } else { /* big endian */ | |||
| 933 | U32 bitOffset = (sizeof(pattern)*8) - 8; | |||
| 934 | while (ip < iEnd) { | |||
| 935 | BYTE const byte = (BYTE)(pattern >> bitOffset); | |||
| 936 | if (*ip != byte) break; | |||
| 937 | ip ++; bitOffset -= 8; | |||
| 938 | } } | |||
| 939 | ||||
| 940 | return (unsigned)(ip - iStart); | |||
| 941 | } | |||
| 942 | ||||
| 943 | /* LZ4HC_reverseCountPattern() : | |||
| 944 | * pattern must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) | |||
| 945 | * read using natural platform endianness */ | |||
| 946 | static unsigned | |||
| 947 | LZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern) | |||
| 948 | { | |||
| 949 | const BYTE* const iStart = ip; | |||
| 950 | ||||
| 951 | while (likely(ip >= iLow+4)(__builtin_expect (((ip >= iLow+4) != 0),(1)) )) { | |||
| 952 | if (LZ4_read32(ip-4) != pattern) break; | |||
| 953 | ip -= 4; | |||
| 954 | } | |||
| 955 | { const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianness */ | |||
| 956 | while (likely(ip>iLow)(__builtin_expect (((ip>iLow) != 0),(1)) )) { | |||
| 957 | if (ip[-1] != *bytePtr) break; | |||
| 958 | ip--; bytePtr--; | |||
| 959 | } } | |||
| 960 | return (unsigned)(iStart - ip); | |||
| 961 | } | |||
| 962 | ||||
| 963 | /* LZ4HC_protectDictEnd() : | |||
| 964 | * Checks if the match is in the last 3 bytes of the dictionary, so reading the | |||
| 965 | * 4 byte MINMATCH would overflow. | |||
| 966 | * @returns true if the match index is okay. | |||
| 967 | */ | |||
| 968 | static int LZ4HC_protectDictEnd(U32 const dictLimit, U32 const matchIndex) | |||
| 969 | { | |||
| 970 | return ((U32)((dictLimit - 1) - matchIndex) >= 3); | |||
| 971 | } | |||
| 972 | ||||
| 973 | typedef enum { rep_untested, rep_not, rep_confirmed } repeat_state_e; | |||
| 974 | ||||
| 975 | /* LZ4HC_sparsestChain() : | |||
| 976 | * A longer match must also match at every position of the current one (at @matchIndex, @longest bytes). | |||
| 977 | * Finds, among these positions, the chain jumping farthest back (sampled, with acceleration). | |||
| 978 | * @return : distance to the next candidate of that chain (1 if none is better), | |||
| 979 | * *matchChainPosPtr is set to its position within the match, if better */ | |||
| 980 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) U32 | |||
| 981 | LZ4HC_sparsestChain(const U16* chainTable, U32 matchIndex, int longest, U32* matchChainPosPtr) | |||
| 982 | { | |||
| 983 | int const kTrigger = 4; | |||
| 984 | U32 distanceToNextMatch = 1; | |||
| 985 | int const end = longest - MINMATCH4 + 1; | |||
| 986 | int step = 1; | |||
| 987 | int accel = 1 << kTrigger; | |||
| 988 | int pos; | |||
| 989 | for (pos = 0; pos < end; pos += step) { | |||
| 990 | U32 const candidateDist = DELTANEXTU16(chainTable, matchIndex + (U32)pos)chainTable[(U16)(matchIndex + (U32)pos)]; | |||
| 991 | step = (accel++ >> kTrigger); | |||
| 992 | if (candidateDist > distanceToNextMatch) { | |||
| 993 | distanceToNextMatch = candidateDist; | |||
| 994 | *matchChainPosPtr = (U32)pos; | |||
| 995 | accel = 1 << kTrigger; | |||
| 996 | } } | |||
| 997 | return distanceToNextMatch; | |||
| 998 | } | |||
| 999 | ||||
| 1000 | /* LZ4HC_patternSegment() : | |||
| 1001 | * Measures the run of @pattern (a repeated byte, or 2 alternating bytes) around @candidateIdx, | |||
| 1002 | * across the external dictionary / prefix boundary, without going below @lowestMatchIndex. | |||
| 1003 | * @return : 0 if @pattern isn't present at @candidateIdx, | |||
| 1004 | * 1 otherwise, with *forwardPtr bytes from @candidateIdx (included) and *backPtr bytes before it */ | |||
| 1005 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) int | |||
| 1006 | LZ4HC_patternSegment(const LZ4HC_CCtx_internal* hc4, U32 candidateIdx, U32 pattern, | |||
| 1007 | const BYTE* iHighLimit, U32 lowestMatchIndex, | |||
| 1008 | size_t* forwardPtr, size_t* backPtr) | |||
| 1009 | { | |||
| 1010 | const BYTE* const prefixPtr = hc4->prefixStart; | |||
| 1011 | U32 const prefixIdx = hc4->dictLimit; | |||
| 1012 | const BYTE* const dictStart = hc4->dictStart; | |||
| 1013 | U32 const dictIdx = hc4->lowLimit; | |||
| 1014 | const BYTE* const dictEnd = dictStart + prefixIdx - dictIdx; | |||
| 1015 | int const extDict = candidateIdx < prefixIdx; | |||
| 1016 | const BYTE* const matchPtr = extDict ? dictStart + (candidateIdx - dictIdx) : prefixPtr + (candidateIdx - prefixIdx); | |||
| 1017 | size_t forward, back; | |||
| 1018 | if (LZ4_read32(matchPtr) != pattern) return 0; | |||
| 1019 | ||||
| 1020 | { const BYTE* const iLimit = extDict ? dictEnd : iHighLimit; | |||
| 1021 | forward = LZ4HC_countPattern(matchPtr+sizeof(pattern), iLimit, pattern) + sizeof(pattern); | |||
| 1022 | if (extDict && matchPtr + forward == iLimit) { /* continues into the prefix */ | |||
| 1023 | U32 const rotatedPattern = LZ4HC_rotatePattern(forward, pattern); | |||
| 1024 | forward += LZ4HC_countPattern(prefixPtr, iHighLimit, rotatedPattern); | |||
| 1025 | } } | |||
| 1026 | ||||
| 1027 | back = LZ4HC_reverseCountPattern(matchPtr, extDict ? dictStart : prefixPtr, pattern); | |||
| 1028 | if (!extDict && matchPtr - back == prefixPtr && dictIdx < prefixIdx) { /* continues into the external dictionary */ | |||
| 1029 | U32 const rotatedPattern = LZ4HC_rotatePattern((U32)(-(int)back), pattern); | |||
| 1030 | back += LZ4HC_reverseCountPattern(dictEnd, dictStart, rotatedPattern); | |||
| 1031 | } | |||
| 1032 | /* don't go below lowestMatchIndex */ | |||
| 1033 | back = candidateIdx - MAX(candidateIdx - (U32)back, lowestMatchIndex)( (candidateIdx - (U32)back) > (lowestMatchIndex) ? (candidateIdx - (U32)back) : (lowestMatchIndex) ); | |||
| 1034 | assert(candidateIdx - back >= lowestMatchIndex)((void)0); | |||
| 1035 | ||||
| 1036 | *forwardPtr = forward; | |||
| 1037 | *backPtr = back; | |||
| 1038 | return 1; | |||
| 1039 | } | |||
| 1040 | typedef enum { favorCompressionRatio=0, favorDecompressionSpeed } HCfavor_e; | |||
| 1041 | ||||
| 1042 | ||||
| 1043 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) LZ4HC_match_t | |||
| 1044 | LZ4HC_InsertAndGetWiderMatch ( | |||
| 1045 | LZ4HC_CCtx_internal* const hc4, | |||
| 1046 | const BYTE* const ip, | |||
| 1047 | const BYTE* const iLowLimit, const BYTE* const iHighLimit, | |||
| 1048 | int longest, | |||
| 1049 | const int maxNbAttempts, | |||
| 1050 | const int patternAnalysis, const int chainSwap, | |||
| 1051 | const dictCtx_directive dict, | |||
| 1052 | const HCfavor_e favorDecSpeed) | |||
| 1053 | { | |||
| 1054 | U16* const chainTable = hc4->chainTable; | |||
| 1055 | U32* const hashTable = hc4->hashTable; | |||
| 1056 | const LZ4HC_CCtx_internal* const dictCtx = hc4->dictCtx; | |||
| 1057 | const BYTE* const prefixPtr = hc4->prefixStart; | |||
| 1058 | const U32 prefixIdx = hc4->dictLimit; | |||
| 1059 | const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx; | |||
| 1060 | const int withinStartDistance = (hc4->lowLimit + (LZ4_DISTANCE_MAX65535 + 1) > ipIndex); | |||
| 1061 | const U32 lowestMatchIndex = (withinStartDistance) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX65535; | |||
| 1062 | const BYTE* const dictStart = hc4->dictStart; | |||
| 1063 | const U32 dictIdx = hc4->lowLimit; | |||
| 1064 | int const lookBackLength = (int)(ip-iLowLimit); | |||
| 1065 | int const maxLength = (int)(iHighLimit-iLowLimit); /* no match can be longer */ | |||
| 1066 | int nbAttempts = maxNbAttempts; | |||
| 1067 | U32 matchChainPos = 0; | |||
| 1068 | U32 const pattern = LZ4_read32(ip); | |||
| 1069 | U32 matchIndex; | |||
| 1070 | repeat_state_e repeat = rep_untested; | |||
| 1071 | size_t srcPatternLength = 0; | |||
| 1072 | int offset = 0, sBack = 0; | |||
| 1073 | ||||
| 1074 | DEBUGLOG(7, "LZ4HC_InsertAndGetWiderMatch"){}; | |||
| 1075 | /* First Match */ | |||
| 1076 | LZ4HC_Insert(hc4, ip); /* insert all prior positions up to ip (excluded) */ | |||
| 1077 | matchIndex = hashTable[LZ4HC_hashPtr(ip)]; | |||
| 1078 | DEBUGLOG(7, "First candidate match for pos %u found at index %u / %u (lowestMatchIndex)",{} | |||
| 1079 | ipIndex, matchIndex, lowestMatchIndex){}; | |||
| 1080 | ||||
| 1081 | /* Candidates only replace the current match if strictly longer, | |||
| 1082 | * so stopping once @longest reaches @maxLength doesn't change the result. */ | |||
| 1083 | while ((matchIndex>=lowestMatchIndex) && (nbAttempts>0) && (longest<maxLength)) { | |||
| 1084 | int matchLength=0; | |||
| 1085 | nbAttempts--; | |||
| 1086 | assert(matchIndex < ipIndex)((void)0); | |||
| 1087 | if (favorDecSpeed && (ipIndex - matchIndex < 8)) { | |||
| 1088 | /* do nothing: | |||
| 1089 | * favorDecSpeed intentionally skips matches with offset < 8 */ | |||
| 1090 | } else if (matchIndex >= prefixIdx) { /* within current Prefix */ | |||
| 1091 | const BYTE* const matchPtr = prefixPtr + (matchIndex - prefixIdx); | |||
| 1092 | assert(matchPtr < ip)((void)0); | |||
| 1093 | assert(longest >= 1)((void)0); | |||
| 1094 | if (LZ4_read16(iLowLimit + longest - 1) == LZ4_read16(matchPtr - lookBackLength + longest - 1)) { | |||
| 1095 | if (LZ4_read32(matchPtr) == pattern) { | |||
| 1096 | int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, prefixPtr) : 0; | |||
| 1097 | matchLength = MINMATCH4 + (int)LZ4_count(ip+MINMATCH4, matchPtr+MINMATCH4, iHighLimit); | |||
| 1098 | matchLength -= back; | |||
| 1099 | if (matchLength > longest) { | |||
| 1100 | longest = matchLength; | |||
| 1101 | offset = (int)(ipIndex - matchIndex); | |||
| 1102 | sBack = back; | |||
| 1103 | DEBUGLOG(7, "Found match of len=%i within prefix, offset=%i, back=%i", longest, offset, -back){}; | |||
| 1104 | HEX_CMP(7, ip + back, ip + back - offset, (size_t)matchLength); | |||
| 1105 | } } } | |||
| 1106 | } else { /* lowestMatchIndex <= matchIndex < dictLimit : within Ext Dict */ | |||
| 1107 | const BYTE* const matchPtr = dictStart + (matchIndex - dictIdx); | |||
| 1108 | assert(matchIndex >= dictIdx)((void)0); | |||
| 1109 | if ( likely(matchIndex <= prefixIdx - 4)(__builtin_expect (((matchIndex <= prefixIdx - 4) != 0),(1 )) ) | |||
| 1110 | && (LZ4_read32(matchPtr) == pattern) ) { | |||
| 1111 | int back = 0; | |||
| 1112 | const BYTE* vLimit = ip + (prefixIdx - matchIndex); | |||
| 1113 | if (vLimit > iHighLimit) vLimit = iHighLimit; | |||
| 1114 | matchLength = (int)LZ4_count(ip+MINMATCH4, matchPtr+MINMATCH4, vLimit) + MINMATCH4; | |||
| 1115 | if ((ip+matchLength == vLimit) && (vLimit < iHighLimit)) | |||
| 1116 | matchLength += LZ4_count(ip+matchLength, prefixPtr, iHighLimit); | |||
| 1117 | back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictStart) : 0; | |||
| 1118 | matchLength -= back; | |||
| 1119 | if (matchLength > longest) { | |||
| 1120 | longest = matchLength; | |||
| 1121 | offset = (int)(ipIndex - matchIndex); | |||
| 1122 | sBack = back; | |||
| 1123 | DEBUGLOG(7, "Found match of len=%i within dict, offset=%i, back=%i", longest, offset, -back){}; | |||
| 1124 | HEX_CMP(7, ip + back, matchPtr + back, (size_t)matchLength); | |||
| 1125 | } } } | |||
| 1126 | ||||
| 1127 | if (chainSwap && matchLength==longest && matchIndex + (U32)longest <= ipIndex) { /* better match => select a better chain */ | |||
| 1128 | U32 const distanceToNextMatch = LZ4HC_sparsestChain(chainTable, matchIndex, longest, &matchChainPos); | |||
| 1129 | assert(lookBackLength==0)((void)0); /* search forward only */ | |||
| 1130 | if (distanceToNextMatch > 1) { | |||
| 1131 | if (distanceToNextMatch > matchIndex) break; /* avoid overflow */ | |||
| 1132 | matchIndex -= distanceToNextMatch; | |||
| 1133 | continue; | |||
| 1134 | } } | |||
| 1135 | ||||
| 1136 | { U32 const distNextMatch = DELTANEXTU16(chainTable, matchIndex)chainTable[(U16)(matchIndex)]; | |||
| 1137 | if (patternAnalysis && distNextMatch==1 && matchChainPos==0) { | |||
| 1138 | U32 const matchCandidateIdx = matchIndex-1; | |||
| 1139 | size_t forwardPatternLength, backLength; | |||
| 1140 | /* may be a repeated pattern */ | |||
| 1141 | if (repeat == rep_untested) { | |||
| 1142 | if ( ((pattern & 0xFFFF) == (pattern >> 16)) | |||
| 1143 | & ((pattern & 0xFF) == (pattern >> 24)) ) { | |||
| 1144 | DEBUGLOG(7, "Repeat pattern detected, char %02X", pattern >> 24){}; | |||
| 1145 | repeat = rep_confirmed; | |||
| 1146 | srcPatternLength = LZ4HC_countPattern(ip+sizeof(pattern), iHighLimit, pattern) + sizeof(pattern); | |||
| 1147 | } else { | |||
| 1148 | repeat = rep_not; | |||
| 1149 | } } | |||
| 1150 | if ( (repeat == rep_confirmed) && (matchCandidateIdx >= lowestMatchIndex) | |||
| 1151 | && LZ4HC_protectDictEnd(prefixIdx, matchCandidateIdx) | |||
| 1152 | && LZ4HC_patternSegment(hc4, matchCandidateIdx, pattern, iHighLimit, lowestMatchIndex, | |||
| 1153 | &forwardPatternLength, &backLength) ) { | |||
| 1154 | size_t const currentSegmentLength = backLength + forwardPatternLength; | |||
| 1155 | /* if the full source pattern fits (and hasn't been reached yet), jump to its best position : | |||
| 1156 | * at the end of the segment, it might be followed by more match. | |||
| 1157 | * Otherwise, jump to the farthest position in the segment : the longest match, plus maybe some back */ | |||
| 1158 | int const fullPatternFits = (currentSegmentLength >= srcPatternLength) | |||
| 1159 | && (forwardPatternLength <= srcPatternLength); | |||
| 1160 | U32 const newMatchIndex = fullPatternFits ? | |||
| 1161 | matchCandidateIdx + (U32)forwardPatternLength - (U32)srcPatternLength : | |||
| 1162 | matchCandidateIdx - (U32)backLength; | |||
| 1163 | if (!LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) { | |||
| 1164 | /* Can only happen if started in the prefix */ | |||
| 1165 | assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && matchCandidateIdx >= prefixIdx)((void)0); | |||
| 1166 | matchIndex = prefixIdx; | |||
| 1167 | continue; | |||
| 1168 | } | |||
| 1169 | matchIndex = newMatchIndex; | |||
| 1170 | if (!fullPatternFits && lookBackLength==0) { /* no back possible : the match length is known */ | |||
| 1171 | size_t const maxML = MIN(currentSegmentLength, srcPatternLength)( (currentSegmentLength) < (srcPatternLength) ? (currentSegmentLength ) : (srcPatternLength) ); | |||
| 1172 | if ((size_t)longest < maxML) { | |||
| 1173 | assert(prefixPtr - prefixIdx + matchIndex != ip)((void)0); | |||
| 1174 | if ((size_t)(ip - prefixPtr) + prefixIdx - matchIndex > LZ4_DISTANCE_MAX65535) break; | |||
| 1175 | assert(maxML < 2 GB)((void)0); | |||
| 1176 | longest = (int)maxML; | |||
| 1177 | offset = (int)(ipIndex - matchIndex); | |||
| 1178 | assert(sBack == 0)((void)0); | |||
| 1179 | DEBUGLOG(7, "Found repeat pattern match of len=%i, offset=%i", longest, offset){}; | |||
| 1180 | } | |||
| 1181 | { U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex)chainTable[(U16)(matchIndex)]; | |||
| 1182 | if (distToNextPattern > matchIndex) break; /* avoid overflow */ | |||
| 1183 | matchIndex -= distToNextPattern; | |||
| 1184 | } } | |||
| 1185 | continue; | |||
| 1186 | } | |||
| 1187 | } } /* PA optimization */ | |||
| 1188 | ||||
| 1189 | /* follow current chain */ | |||
| 1190 | matchIndex -= DELTANEXTU16(chainTable, matchIndex + matchChainPos)chainTable[(U16)(matchIndex + matchChainPos)]; | |||
| 1191 | ||||
| 1192 | } /* while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) */ | |||
| 1193 | ||||
| 1194 | if ( dict == usingDictCtxHc | |||
| 1195 | && nbAttempts > 0 | |||
| 1196 | && longest < maxLength | |||
| 1197 | && withinStartDistance) { | |||
| 1198 | size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; | |||
| 1199 | U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; | |||
| 1200 | assert(dictEndOffset <= 1 GB)((void)0); | |||
| 1201 | matchIndex = dictMatchIndex + lowestMatchIndex - (U32)dictEndOffset; | |||
| 1202 | if (dictMatchIndex>0) DEBUGLOG(7, "dictEndOffset = %zu, dictMatchIndex = %u => relative matchIndex = %i", dictEndOffset, dictMatchIndex, (int)dictMatchIndex - (int)dictEndOffset){}; | |||
| 1203 | while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX65535 && longest < maxLength && nbAttempts--) { | |||
| 1204 | const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + dictMatchIndex; | |||
| 1205 | ||||
| 1206 | if (LZ4_read32(matchPtr) == pattern) { | |||
| 1207 | int mlt; | |||
| 1208 | int back = 0; | |||
| 1209 | const BYTE* vLimit = ip + (dictEndOffset - dictMatchIndex); | |||
| 1210 | if (vLimit > iHighLimit) vLimit = iHighLimit; | |||
| 1211 | mlt = (int)LZ4_count(ip+MINMATCH4, matchPtr+MINMATCH4, vLimit) + MINMATCH4; | |||
| 1212 | back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0; | |||
| 1213 | mlt -= back; | |||
| 1214 | if (mlt > longest) { | |||
| 1215 | longest = mlt; | |||
| 1216 | offset = (int)(ipIndex - matchIndex); | |||
| 1217 | sBack = back; | |||
| 1218 | DEBUGLOG(7, "found match of length %i within extDictCtx", longest){}; | |||
| 1219 | } } | |||
| 1220 | ||||
| 1221 | { U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, dictMatchIndex)dictCtx->chainTable[(U16)(dictMatchIndex)]; | |||
| 1222 | dictMatchIndex -= nextOffset; | |||
| 1223 | matchIndex -= nextOffset; | |||
| 1224 | } } } | |||
| 1225 | ||||
| 1226 | { LZ4HC_match_t md; | |||
| 1227 | assert(longest >= 0)((void)0); | |||
| 1228 | md.len = longest; | |||
| 1229 | md.off = offset; | |||
| 1230 | md.back = sBack; | |||
| 1231 | return md; | |||
| 1232 | } | |||
| 1233 | } | |||
| 1234 | ||||
| 1235 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) LZ4HC_match_t | |||
| 1236 | LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4, /* Index table will be updated */ | |||
| 1237 | const BYTE* const ip, const BYTE* const iLimit, | |||
| 1238 | const int maxNbAttempts, | |||
| 1239 | const int patternAnalysis, | |||
| 1240 | const dictCtx_directive dict) | |||
| 1241 | { | |||
| 1242 | DEBUGLOG(7, "LZ4HC_InsertAndFindBestMatch"){}; | |||
| 1243 | /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), | |||
| 1244 | * but this won't be the case here, as we define iLowLimit==ip, | |||
| 1245 | * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ | |||
| 1246 | return LZ4HC_InsertAndGetWiderMatch(hc4, ip, ip, iLimit, MINMATCH4-1, maxNbAttempts, patternAnalysis, 0 /*chainSwap*/, dict, favorCompressionRatio); | |||
| 1247 | } | |||
| 1248 | ||||
| 1249 | ||||
| 1250 | /* preconditions: | |||
| 1251 | * - *srcSizePtr within [1, LZ4_MAX_INPUT_SIZE] | |||
| 1252 | * - src is valid | |||
| 1253 | * - maxOutputSize >= 1 | |||
| 1254 | * - dst is valid | |||
| 1255 | */ | |||
| 1256 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) int LZ4HC_compress_hashChain ( | |||
| 1257 | LZ4HC_CCtx_internal* const ctx, | |||
| 1258 | const char* const src, | |||
| 1259 | char* const dst, | |||
| 1260 | int* srcSizePtr, | |||
| 1261 | int const maxOutputSize, | |||
| 1262 | int maxNbAttempts, | |||
| 1263 | const limitedOutput_directive limit, | |||
| 1264 | const dictCtx_directive dict | |||
| 1265 | ) | |||
| 1266 | { | |||
| 1267 | const int inputSize = *srcSizePtr; | |||
| 1268 | const int patternAnalysis = (maxNbAttempts > 128); /* levels 9+ */ | |||
| 1269 | ||||
| 1270 | const BYTE* ip = (const BYTE*)src; | |||
| 1271 | const BYTE* anchor = ip; | |||
| 1272 | const BYTE* const iend = ip + inputSize; | |||
| 1273 | const BYTE* mflimit; /* set after the size check : would precede a small input */ | |||
| 1274 | const BYTE* matchlimit; | |||
| 1275 | ||||
| 1276 | BYTE* op = (BYTE*) dst; | |||
| 1277 | /* fillOutput : keep room for the final run of literals (LZ4 format restriction) */ | |||
| 1278 | BYTE* const oend = op + maxOutputSize - ((limit == fillOutput) ? LASTLITERALS5 : 0); | |||
| 1279 | ||||
| 1280 | const BYTE* start0; | |||
| 1281 | const BYTE* start2 = NULL((void*)0); | |||
| 1282 | const BYTE* start3 = NULL((void*)0); | |||
| 1283 | LZ4HC_match_t m0, m1, m2, m3; | |||
| 1284 | const LZ4HC_match_t nomatch = {0, 0, 0}; | |||
| 1285 | ||||
| 1286 | /* init */ | |||
| 1287 | DEBUGLOG(5, "LZ4HC_compress_hashChain (dict?=>%i)", dict){}; | |||
| 1288 | ||||
| 1289 | /* preconditions verifications */ | |||
| 1290 | assert(*srcSizePtr >= 1)((void)0); | |||
| 1291 | assert(src != NULL)((void)0); | |||
| 1292 | assert(maxOutputSize >= 1)((void)0); | |||
| 1293 | assert(dst != NULL)((void)0); | |||
| 1294 | ||||
| 1295 | *srcSizePtr = 0; | |||
| 1296 | if (inputSize < LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ | |||
| 1297 | mflimit = iend - MFLIMIT12; | |||
| 1298 | matchlimit = (iend - LASTLITERALS5); | |||
| 1299 | ||||
| 1300 | /* Main Loop */ | |||
| 1301 | while (ip <= mflimit) { | |||
| 1302 | m1 = LZ4HC_InsertAndFindBestMatch(ctx, ip, matchlimit, maxNbAttempts, patternAnalysis, dict); | |||
| 1303 | if (m1.len<MINMATCH4) { ip++; continue; } | |||
| 1304 | ||||
| 1305 | /* saved, in case we would skip too much */ | |||
| 1306 | start0 = ip; m0 = m1; | |||
| 1307 | ||||
| 1308 | _Search2: | |||
| 1309 | /* m1 (at ip) is the next match to encode, m0 (at start0 <= ip) its fallback. | |||
| 1310 | * Search a longer match m2, starting before the end of m1. */ | |||
| 1311 | DEBUGLOG(7, "_Search2 (currently found match of size %i)", m1.len){}; | |||
| 1312 | if (ip+m1.len <= mflimit) { | |||
| 1313 | start2 = ip + m1.len - 2; | |||
| 1314 | m2 = LZ4HC_InsertAndGetWiderMatch(ctx, | |||
| 1315 | start2, ip + 0, matchlimit, m1.len, | |||
| 1316 | maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio); | |||
| 1317 | start2 += m2.back; | |||
| 1318 | } else { | |||
| 1319 | m2 = nomatch; /* do not search further */ | |||
| 1320 | } | |||
| 1321 | ||||
| 1322 | if (m2.len <= m1.len) { /* No better match => encode m1 immediately */ | |||
| 1323 | if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, | |||
| 1324 | m1.len, m1.off, | |||
| 1325 | limit, oend) ) | |||
| 1326 | goto _dest_overflow; | |||
| 1327 | continue; | |||
| 1328 | } | |||
| 1329 | ||||
| 1330 | if ((start0 < ip) && (start2 < ip + m0.len)) { | |||
| 1331 | /* m1 was skipped at least once, and m2 starts early enough : restore m0 */ | |||
| 1332 | ip = start0; m1 = m0; | |||
| 1333 | } | |||
| 1334 | ||||
| 1335 | if ((start2 - ip) < 3) { /* m1 too short before m2 : removed */ | |||
| 1336 | ip = start2; | |||
| 1337 | m1 = m2; | |||
| 1338 | goto _Search2; | |||
| 1339 | } | |||
| 1340 | ||||
| 1341 | /* m2 (at start2) is the candidate after m1. | |||
| 1342 | * Search a longer match m3, starting before the end of m2. */ | |||
| 1343 | while (1) { | |||
| 1344 | /* m1 will be cut where m2 starts, but keeps up to OPTIMAL_ML bytes (no extra length byte) : | |||
| 1345 | * m2 is shifted after them, as long as it keeps at least MINMATCH bytes. */ | |||
| 1346 | if ((start2 - ip) < OPTIMAL_ML(int)((((1U<<4)-1)-1)+4)) { | |||
| 1347 | int const gap = (int)(start2 - ip); | |||
| 1348 | int newML = MIN(m1.len, OPTIMAL_ML)( (m1.len) < ((int)((((1U<<4)-1)-1)+4)) ? (m1.len) : ((int)((((1U<<4)-1)-1)+4)) ); | |||
| 1349 | if (newML > gap + m2.len - MINMATCH4) | |||
| 1350 | newML = gap + m2.len - MINMATCH4; | |||
| 1351 | if (newML > gap) { /* shift m2 */ | |||
| 1352 | start2 += newML - gap; | |||
| 1353 | m2.len -= newML - gap; | |||
| 1354 | } | |||
| 1355 | } | |||
| 1356 | ||||
| 1357 | if (start2 + m2.len <= mflimit) { | |||
| 1358 | start3 = start2 + m2.len - 3; | |||
| 1359 | m3 = LZ4HC_InsertAndGetWiderMatch(ctx, | |||
| 1360 | start3, start2, matchlimit, m2.len, | |||
| 1361 | maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio); | |||
| 1362 | start3 += m3.back; | |||
| 1363 | } else { | |||
| 1364 | m3 = nomatch; /* do not search further */ | |||
| 1365 | } | |||
| 1366 | ||||
| 1367 | if ((m3.len > m2.len) && (start3 < ip+m1.len+3)) { /* not enough room for m2 between m1 and m3 */ | |||
| 1368 | if (start3 >= (ip+m1.len)) { | |||
| 1369 | /* m3 starts after m1 : encode m1, then m3 becomes m1, | |||
| 1370 | * and m2, moved after m1 if needed (replaced by m3 if too short), becomes the fallback m0 */ | |||
| 1371 | if (start2 < ip+m1.len) { | |||
| 1372 | int correction = (int)(ip+m1.len - start2); | |||
| 1373 | start2 += correction; | |||
| 1374 | m2.len -= correction; | |||
| 1375 | if (m2.len < MINMATCH4) { | |||
| 1376 | start2 = start3; | |||
| 1377 | m2 = m3; | |||
| 1378 | } | |||
| 1379 | } | |||
| 1380 | ||||
| 1381 | if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, | |||
| 1382 | m1.len, m1.off, | |||
| 1383 | limit, oend) ) | |||
| 1384 | goto _dest_overflow; | |||
| 1385 | ip = start3; | |||
| 1386 | m1 = m3; | |||
| 1387 | ||||
| 1388 | start0 = start2; | |||
| 1389 | m0 = m2; | |||
| 1390 | goto _Search2; | |||
| 1391 | } | |||
| 1392 | ||||
| 1393 | /* m3 starts within m1 : m3 replaces m2 */ | |||
| 1394 | start2 = start3; | |||
| 1395 | m2 = m3; | |||
| 1396 | continue; | |||
| 1397 | } | |||
| 1398 | ||||
| 1399 | /* encode m1, cut where m2 starts */ | |||
| 1400 | if (start2 < ip+m1.len) m1.len = (int)(start2 - ip); | |||
| 1401 | if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, | |||
| 1402 | m1.len, m1.off, | |||
| 1403 | limit, oend) ) | |||
| 1404 | goto _dest_overflow; | |||
| 1405 | ||||
| 1406 | /* m2 becomes m1 */ | |||
| 1407 | ip = start2; m1 = m2; | |||
| 1408 | ||||
| 1409 | if (m3.len <= m1.len) { /* No better match => encode m1 (former m2) */ | |||
| 1410 | if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, | |||
| 1411 | m1.len, m1.off, | |||
| 1412 | limit, oend) ) | |||
| 1413 | goto _dest_overflow; | |||
| 1414 | break; | |||
| 1415 | } | |||
| 1416 | ||||
| 1417 | /* m3 becomes m2 */ | |||
| 1418 | start2 = start3; m2 = m3; | |||
| 1419 | } | |||
| 1420 | } | |||
| 1421 | ||||
| 1422 | _last_literals: | |||
| 1423 | if (LZ4HC_encodeLastLiterals(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, iend, limit, oend)) | |||
| 1424 | return 0; /* not enough space in @dst */ | |||
| 1425 | ||||
| 1426 | /* End */ | |||
| 1427 | *srcSizePtr = (int) (((const char*)ip) - src); | |||
| 1428 | return (int) (((char*)op)-dst); | |||
| 1429 | ||||
| 1430 | _dest_overflow: | |||
| 1431 | if (limit != fillOutput) return 0; /* compression failed */ | |||
| 1432 | LZ4HC_encodeLastSequence_fillOutput(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, m1.len, m1.off, oend); | |||
| 1433 | goto _last_literals; | |||
| 1434 | } | |||
| 1435 | ||||
| 1436 | ||||
| 1437 | static int LZ4HC_compress_optimal( LZ4HC_CCtx_internal* ctx, | |||
| 1438 | const char* const source, char* dst, | |||
| 1439 | int* srcSizePtr, int dstCapacity, | |||
| 1440 | int const nbSearches, int const nbSearchesSeq, int const targetLength, | |||
| 1441 | const limitedOutput_directive limit, | |||
| 1442 | const dictCtx_directive dict, | |||
| 1443 | const HCfavor_e favorDecSpeed); | |||
| 1444 | ||||
| 1445 | static int | |||
| 1446 | LZ4HC_compress_generic_internal ( | |||
| 1447 | LZ4HC_CCtx_internal* const ctx, | |||
| 1448 | const char* const src, | |||
| 1449 | char* const dst, | |||
| 1450 | int* const srcSizePtr, | |||
| 1451 | int const dstCapacity, | |||
| 1452 | int cLevel, | |||
| 1453 | const limitedOutput_directive limit, | |||
| 1454 | const dictCtx_directive dict | |||
| 1455 | ) | |||
| 1456 | { | |||
| 1457 | DEBUGLOG(5, "LZ4HC_compress_generic_internal(src=%p, srcSize=%d, dstCapacity=%d)",{} | |||
| 1458 | src, *srcSizePtr, dstCapacity){}; | |||
| 1459 | ||||
| 1460 | /* input sanitization */ | |||
| 1461 | if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE0x7E000000) return 0; /* Unsupported input size (too large or negative) */ | |||
| 1462 | if (dstCapacity < 1) return 0; /* Invalid: impossible to store anything */ | |||
| 1463 | assert(dst)((void)0); /* since dstCapacity >= 1, dst must be valid */ | |||
| 1464 | if (*srcSizePtr == 0) { *dst = 0; return 1; } | |||
| 1465 | assert(src != NULL)((void)0); /* since *srcSizePtr >= 1, src must be valid */ | |||
| 1466 | ||||
| 1467 | ctx->end += *srcSizePtr; | |||
| 1468 | { cParams_t const cParam = LZ4HC_getCLevelParams(cLevel); | |||
| 1469 | HCfavor_e const favor = ctx->favorDecSpeed ? favorDecompressionSpeed : favorCompressionRatio; | |||
| 1470 | int const srcSize = *srcSizePtr; | |||
| 1471 | int result; | |||
| 1472 | ||||
| 1473 | if (cParam.strat == lz4mid) { | |||
| 1474 | result = LZ4MID_compress(ctx, | |||
| 1475 | src, dst, srcSizePtr, dstCapacity, | |||
| 1476 | limit, dict); | |||
| 1477 | } else if (cParam.strat == lz4hc) { | |||
| 1478 | result = LZ4HC_compress_hashChain(ctx, | |||
| 1479 | src, dst, srcSizePtr, dstCapacity, | |||
| 1480 | cParam.nbSearches, limit, dict); | |||
| 1481 | } else { | |||
| 1482 | assert(cParam.strat == lz4opt)((void)0); | |||
| 1483 | result = LZ4HC_compress_optimal(ctx, | |||
| 1484 | src, dst, srcSizePtr, dstCapacity, | |||
| 1485 | cParam.nbSearches, cParam.nbSearchesSeq, (int)cParam.targetLength, limit, | |||
| 1486 | dict, favor); | |||
| 1487 | } | |||
| 1488 | if ((limit == fillOutput) && (result > 0) && (*srcSizePtr < srcSize)) { | |||
| 1489 | /* fillOutput : compression stopped before the end of input. | |||
| 1490 | * The rest of input is left to the next block : it isn't history yet, | |||
| 1491 | * and the positions already inserted into it are dropped (lz4mid drops its own). | |||
| 1492 | * Note : a failed compression is different : lz4frame stores its block uncompressed, | |||
| 1493 | * and the next block still references it. */ | |||
| 1494 | const BYTE* const consumedEnd = (const BYTE*)src + *srcSizePtr; | |||
| 1495 | U32 const consumedIdx = ctx->dictLimit + (U32)(consumedEnd - ctx->prefixStart); | |||
| 1496 | if ((cParam.strat != lz4mid) && (ctx->nextToUpdate > consumedIdx)) { | |||
| 1497 | LZ4HC_dropPositions(ctx, consumedEnd, consumedEnd + (ctx->nextToUpdate - consumedIdx), cParam.strat); | |||
| 1498 | ctx->nextToUpdate = consumedIdx; | |||
| 1499 | } | |||
| 1500 | ctx->end = consumedEnd; | |||
| 1501 | } | |||
| 1502 | return result; | |||
| 1503 | } | |||
| 1504 | } | |||
| 1505 | ||||
| 1506 | static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock); | |||
| 1507 | ||||
| 1508 | static int | |||
| 1509 | LZ4HC_compress_generic_noDictCtx ( | |||
| 1510 | LZ4HC_CCtx_internal* const ctx, | |||
| 1511 | const char* const src, | |||
| 1512 | char* const dst, | |||
| 1513 | int* const srcSizePtr, | |||
| 1514 | int const dstCapacity, | |||
| 1515 | int cLevel, | |||
| 1516 | limitedOutput_directive limit | |||
| 1517 | ) | |||
| 1518 | { | |||
| 1519 | assert(ctx->dictCtx == NULL)((void)0); | |||
| 1520 | return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, noDictCtx); | |||
| 1521 | } | |||
| 1522 | ||||
| 1523 | static int isStateCompatible(const LZ4HC_CCtx_internal* ctx1, const LZ4HC_CCtx_internal* ctx2) | |||
| 1524 | { | |||
| 1525 | int const isMid1 = LZ4HC_getCLevelParams(ctx1->compressionLevel).strat == lz4mid; | |||
| 1526 | int const isMid2 = LZ4HC_getCLevelParams(ctx2->compressionLevel).strat == lz4mid; | |||
| 1527 | return !(isMid1 ^ isMid2); | |||
| 1528 | } | |||
| 1529 | ||||
| 1530 | static int | |||
| 1531 | LZ4HC_compress_generic_dictCtx ( | |||
| 1532 | LZ4HC_CCtx_internal* const ctx, | |||
| 1533 | const char* const src, | |||
| 1534 | char* const dst, | |||
| 1535 | int* const srcSizePtr, | |||
| 1536 | int const dstCapacity, | |||
| 1537 | int cLevel, | |||
| 1538 | limitedOutput_directive limit | |||
| 1539 | ) | |||
| 1540 | { | |||
| 1541 | const size_t position = (size_t)(ctx->end - ctx->prefixStart) + (ctx->dictLimit - ctx->lowLimit); | |||
| 1542 | assert(ctx->dictCtx != NULL)((void)0); | |||
| 1543 | if (position >= 64 KB*(1 <<10)) { | |||
| 1544 | ctx->dictCtx = NULL((void*)0); | |||
| 1545 | return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); | |||
| 1546 | } else if (position == 0 && *srcSizePtr >= LZ4HC_getCLevelParams(cLevel).dictCopyMin && isStateCompatible(ctx, ctx->dictCtx)) { | |||
| 1547 | /* parameters and extension belong to the working context, not to the dictionary */ | |||
| 1548 | LZ4_i8 const favorDecSpeed = ctx->favorDecSpeed; | |||
| 1549 | LZ4_i8 const hasWorkspace = ctx->hasWorkspace; | |||
| 1550 | LZ4_memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal))__builtin_memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal )); | |||
| 1551 | LZ4HC_setExternalDict(ctx, (const BYTE *)src); | |||
| 1552 | ctx->compressionLevel = (short)cLevel; | |||
| 1553 | ctx->favorDecSpeed = favorDecSpeed; | |||
| 1554 | ctx->hasWorkspace = hasWorkspace; | |||
| 1555 | return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); | |||
| 1556 | } else { | |||
| 1557 | return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, usingDictCtxHc); | |||
| 1558 | } | |||
| 1559 | } | |||
| 1560 | ||||
| 1561 | static int | |||
| 1562 | LZ4HC_compress_generic ( | |||
| 1563 | LZ4HC_CCtx_internal* const ctx, | |||
| 1564 | const char* const src, | |||
| 1565 | char* const dst, | |||
| 1566 | int* const srcSizePtr, | |||
| 1567 | int const dstCapacity, | |||
| 1568 | int cLevel, | |||
| 1569 | limitedOutput_directive limit | |||
| 1570 | ) | |||
| 1571 | { | |||
| 1572 | if (ctx->dictCtx == NULL((void*)0)) { | |||
| 1573 | return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); | |||
| 1574 | } else { | |||
| 1575 | return LZ4HC_compress_generic_dictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); | |||
| 1576 | } | |||
| 1577 | } | |||
| 1578 | ||||
| 1579 | ||||
| 1580 | int LZ4_sizeofStateHC(void) { return (int)sizeof(LZ4_streamHC_t); } | |||
| 1581 | ||||
| 1582 | static size_t LZ4_streamHC_t_alignment(void) | |||
| 1583 | { | |||
| 1584 | #if LZ4_ALIGN_TEST1 | |||
| 1585 | typedef struct { char c; LZ4_streamHC_t t; } t_a; | |||
| 1586 | return sizeof(t_a) - sizeof(LZ4_streamHC_t); | |||
| 1587 | #else | |||
| 1588 | return 1; /* effectively disabled */ | |||
| 1589 | #endif | |||
| 1590 | } | |||
| 1591 | ||||
| 1592 | /* state is presumed correctly initialized, | |||
| 1593 | * in which case its size and alignment have already been validate */ | |||
| 1594 | int LZ4_compress_HC_extStateHC_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) | |||
| 1595 | { | |||
| 1596 | LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse; | |||
| 1597 | if (!LZ4_isAligned(state, LZ4_streamHC_t_alignment())) return 0; | |||
| 1598 | LZ4_resetStreamHC_fast((LZ4_streamHC_t*)state, compressionLevel); | |||
| 1599 | LZ4HC_init_internal (ctx, (const BYTE*)src); | |||
| 1600 | if (dstCapacity < LZ4_compressBound(srcSize)) | |||
| 1601 | return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, limitedOutput); | |||
| 1602 | else | |||
| 1603 | return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, notLimited); | |||
| 1604 | } | |||
| 1605 | ||||
| 1606 | int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) | |||
| 1607 | { | |||
| 1608 | LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); | |||
| 1609 | if (ctx==NULL((void*)0)) return 0; /* init failure */ | |||
| 1610 | return LZ4_compress_HC_extStateHC_fastReset(state, src, dst, srcSize, dstCapacity, compressionLevel); | |||
| 1611 | } | |||
| 1612 | ||||
| 1613 | int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) | |||
| 1614 | { | |||
| 1615 | int cSize; | |||
| 1616 | DEBUGLOG(5, "LZ4_compress_HC"){} | |||
| 1617 | #if defined(LZ4HC_HEAPMODE1) && LZ4HC_HEAPMODE1==1 | |||
| 1618 | { /* extended state : a single allocation, including the temporary buffer of levels >= 10 */ | |||
| 1619 | LZ4_streamHC_t* const statePtr = LZ4_initStreamHC(ALLOC(LZ4_STREAMHC_EXTSIZE)malloc((262200 + 65584)), LZ4_STREAMHC_EXTSIZE(262200 + 65584)); | |||
| 1620 | if (statePtr==NULL((void*)0)) return 0; | |||
| 1621 | cSize = LZ4_compress_HC_extStateHC_fastReset(statePtr, src, dst, srcSize, dstCapacity, compressionLevel); | |||
| 1622 | FREEMEM(statePtr)free(statePtr); | |||
| 1623 | } | |||
| 1624 | #else | |||
| 1625 | { LZ4_streamHC_t state; | |||
| 1626 | cSize = LZ4_compress_HC_extStateHC(&state, src, dst, srcSize, dstCapacity, compressionLevel); | |||
| 1627 | } | |||
| 1628 | #endif | |||
| 1629 | return cSize; | |||
| 1630 | } | |||
| 1631 | ||||
| 1632 | /* state is presumed sized correctly (>= sizeof(LZ4_streamHC_t)) */ | |||
| 1633 | int LZ4_compress_HC_destSize(void* state, const char* source, char* dest, int* sourceSizePtr, int targetDestSize, int cLevel) | |||
| 1634 | { | |||
| 1635 | LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); | |||
| 1636 | if (ctx==NULL((void*)0)) return 0; /* init failure */ | |||
| 1637 | LZ4HC_init_internal(&ctx->internal_donotuse, (const BYTE*) source); | |||
| 1638 | LZ4_setCompressionLevel(ctx, cLevel); | |||
| 1639 | return LZ4HC_compress_generic(&ctx->internal_donotuse, source, dest, sourceSizePtr, targetDestSize, cLevel, fillOutput); | |||
| 1640 | } | |||
| 1641 | ||||
| 1642 | ||||
| 1643 | ||||
| 1644 | /************************************** | |||
| 1645 | * Streaming Functions | |||
| 1646 | **************************************/ | |||
| 1647 | /* allocation */ | |||
| 1648 | #if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) | |||
| 1649 | LZ4_streamHC_t* LZ4_createStreamHC(void) | |||
| 1650 | { | |||
| 1651 | LZ4_streamHC_t* const state = | |||
| 1652 | (LZ4_streamHC_t*)ALLOC_AND_ZERO(LZ4_STREAMHC_EXTSIZE)calloc(1,(262200 + 65584)); /* zeroed : initialized */ | |||
| 1653 | if (state == NULL((void*)0)) return NULL((void*)0); | |||
| 1654 | state->internal_donotuse.hasWorkspace = 1; | |||
| 1655 | LZ4_setCompressionLevel(state, LZ4HC_CLEVEL_DEFAULT9); | |||
| 1656 | return state; | |||
| 1657 | } | |||
| 1658 | ||||
| 1659 | int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr) | |||
| 1660 | { | |||
| 1661 | DEBUGLOG(4, "LZ4_freeStreamHC(%p)", LZ4_streamHCPtr){}; | |||
| 1662 | if (!LZ4_streamHCPtr) return 0; /* support free on NULL */ | |||
| 1663 | FREEMEM(LZ4_streamHCPtr)free(LZ4_streamHCPtr); | |||
| 1664 | return 0; | |||
| 1665 | } | |||
| 1666 | #endif | |||
| 1667 | ||||
| 1668 | ||||
| 1669 | LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size) | |||
| 1670 | { | |||
| 1671 | LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer; | |||
| 1672 | DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", buffer, (unsigned)size){}; | |||
| 1673 | /* check conditions */ | |||
| 1674 | if (buffer == NULL((void*)0)) return NULL((void*)0); | |||
| 1675 | if (size < sizeof(LZ4_streamHC_t)) return NULL((void*)0); | |||
| 1676 | if (!LZ4_isAligned(buffer, LZ4_streamHC_t_alignment())) return NULL((void*)0); | |||
| 1677 | /* init */ | |||
| 1678 | { LZ4HC_CCtx_internal* const hcstate = &(LZ4_streamHCPtr->internal_donotuse); | |||
| 1679 | MEM_INIT(hcstate, 0, sizeof(*hcstate))memset(((hcstate)),((0)),((sizeof(*hcstate)))); | |||
| 1680 | hcstate->hasWorkspace = (size >= LZ4_STREAMHC_EXTSIZE(262200 + 65584)); } | |||
| 1681 | LZ4_setCompressionLevel(LZ4_streamHCPtr, LZ4HC_CLEVEL_DEFAULT9); | |||
| 1682 | return LZ4_streamHCPtr; | |||
| 1683 | } | |||
| 1684 | ||||
| 1685 | /* size of an initialized state, to initialize it again : extended states are followed by their buffer */ | |||
| 1686 | static size_t LZ4HC_sizeofState(const LZ4_streamHC_t* s) | |||
| 1687 | { | |||
| 1688 | return s->internal_donotuse.hasWorkspace ? LZ4_STREAMHC_EXTSIZE(262200 + 65584) : sizeof(*s); | |||
| 1689 | } | |||
| 1690 | ||||
| 1691 | /* just a stub */ | |||
| 1692 | void LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) | |||
| 1693 | { | |||
| 1694 | LZ4_initStreamHC(LZ4_streamHCPtr, LZ4HC_sizeofState(LZ4_streamHCPtr)); | |||
| 1695 | LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); | |||
| 1696 | } | |||
| 1697 | ||||
| 1698 | void LZ4_resetStreamHC_fast (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) | |||
| 1699 | { | |||
| 1700 | LZ4HC_CCtx_internal* const s = &LZ4_streamHCPtr->internal_donotuse; | |||
| 1701 | DEBUGLOG(5, "LZ4_resetStreamHC_fast(%p, %d)", LZ4_streamHCPtr, compressionLevel){}; | |||
| 1702 | /* also after a failed compression : its positions are below @end, hence out of reach of the next stream */ | |||
| 1703 | assert(s->end >= s->prefixStart)((void)0); | |||
| 1704 | s->dictLimit += (U32)(s->end - s->prefixStart); | |||
| 1705 | s->prefixStart = NULL((void*)0); | |||
| 1706 | s->end = NULL((void*)0); | |||
| 1707 | s->dictCtx = NULL((void*)0); | |||
| 1708 | LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); | |||
| 1709 | } | |||
| 1710 | ||||
| 1711 | void LZ4_setCompressionLevel(LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) | |||
| 1712 | { | |||
| 1713 | DEBUGLOG(5, "LZ4_setCompressionLevel(%p, %d)", LZ4_streamHCPtr, compressionLevel){}; | |||
| 1714 | if (compressionLevel < 1) compressionLevel = LZ4HC_CLEVEL_DEFAULT9; | |||
| 1715 | if (compressionLevel > LZ4HC_CLEVEL_MAX12) compressionLevel = LZ4HC_CLEVEL_MAX12; | |||
| 1716 | LZ4_streamHCPtr->internal_donotuse.compressionLevel = (short)compressionLevel; | |||
| 1717 | } | |||
| 1718 | ||||
| 1719 | void LZ4_favorDecompressionSpeed(LZ4_streamHC_t* LZ4_streamHCPtr, int favor) | |||
| 1720 | { | |||
| 1721 | LZ4_streamHCPtr->internal_donotuse.favorDecSpeed = (favor!=0); | |||
| 1722 | } | |||
| 1723 | ||||
| 1724 | /* LZ4_loadDictHC() : | |||
| 1725 | * LZ4_streamHCPtr is presumed properly initialized */ | |||
| 1726 | int LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, | |||
| 1727 | const char* dictionary, int dictSize) | |||
| 1728 | { | |||
| 1729 | LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; | |||
| 1730 | cParams_t cp; | |||
| 1731 | DEBUGLOG(4, "LZ4_loadDictHC(ctx:%p, dict:%p, dictSize:%d, clevel=%d)", LZ4_streamHCPtr, dictionary, dictSize, ctxPtr->compressionLevel){}; | |||
| 1732 | assert(LZ4_streamHCPtr != NULL)((void)0); | |||
| 1733 | if (dictSize < 0) dictSize = 0; /* invalid size : no dictionary */ | |||
| 1734 | if (dictSize > 64 KB*(1 <<10)) { | |||
| 1735 | dictionary += (size_t)dictSize - 64 KB*(1 <<10); | |||
| 1736 | dictSize = 64 KB*(1 <<10); | |||
| 1737 | } | |||
| 1738 | /* need a full initialization, there are bad side-effects when using resetFast(), | |||
| 1739 | * but preserve parameters */ | |||
| 1740 | { int const cLevel = ctxPtr->compressionLevel; | |||
| 1741 | LZ4_i8 const favorDecSpeed = ctxPtr->favorDecSpeed; | |||
| 1742 | LZ4_initStreamHC(LZ4_streamHCPtr, LZ4HC_sizeofState(LZ4_streamHCPtr)); | |||
| 1743 | LZ4_setCompressionLevel(LZ4_streamHCPtr, cLevel); | |||
| 1744 | ctxPtr->favorDecSpeed = favorDecSpeed; | |||
| 1745 | cp = LZ4HC_getCLevelParams(cLevel); | |||
| 1746 | } | |||
| 1747 | LZ4HC_init_internal (ctxPtr, (const BYTE*)dictionary); | |||
| 1748 | ctxPtr->end = (const BYTE*)dictionary + dictSize; | |||
| 1749 | if (cp.strat == lz4mid) { | |||
| 1750 | LZ4MID_fillHTable (ctxPtr, dictionary, (size_t)dictSize); | |||
| 1751 | } else { | |||
| 1752 | if (dictSize >= LZ4HC_HASHSIZE4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); | |||
| 1753 | } | |||
| 1754 | return dictSize; | |||
| 1755 | } | |||
| 1756 | ||||
| 1757 | void LZ4_attach_HC_dictionary(LZ4_streamHC_t *working_stream, const LZ4_streamHC_t *dictionary_stream) { | |||
| 1758 | working_stream->internal_donotuse.dictCtx = dictionary_stream != NULL((void*)0) ? &(dictionary_stream->internal_donotuse) : NULL((void*)0); | |||
| 1759 | } | |||
| 1760 | ||||
| 1761 | /* compression */ | |||
| 1762 | ||||
| 1763 | static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock) | |||
| 1764 | { | |||
| 1765 | DEBUGLOG(4, "LZ4HC_setExternalDict(%p, %p)", ctxPtr, newBlock){}; | |||
| 1766 | if (LZ4HC_getCLevelParams(ctxPtr->compressionLevel).strat != lz4mid) { | |||
| 1767 | U32 const endIdx = ctxPtr->dictLimit + (U32)(ctxPtr->end - ctxPtr->prefixStart); | |||
| 1768 | U32 idx; | |||
| 1769 | if (ctxPtr->end >= ctxPtr->prefixStart + 4) | |||
| 1770 | LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */ | |||
| 1771 | /* last positions can't be inserted : don't leave stale chains from previous history */ | |||
| 1772 | for (idx = ctxPtr->nextToUpdate; idx < endIdx; idx++) | |||
| 1773 | DELTANEXTU16(ctxPtr->chainTable, idx)ctxPtr->chainTable[(U16)(idx)] = 0; | |||
| 1774 | } | |||
| 1775 | ||||
| 1776 | /* Only one memory segment for extDict, so any previous extDict is lost at this stage */ | |||
| 1777 | ctxPtr->lowLimit = ctxPtr->dictLimit; | |||
| 1778 | ctxPtr->dictStart = ctxPtr->prefixStart; | |||
| 1779 | ctxPtr->dictLimit += (U32)(ctxPtr->end - ctxPtr->prefixStart); | |||
| 1780 | ctxPtr->prefixStart = newBlock; | |||
| 1781 | ctxPtr->end = newBlock; | |||
| 1782 | ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */ | |||
| 1783 | ||||
| 1784 | /* cannot reference an extDict and a dictCtx at the same time */ | |||
| 1785 | ctxPtr->dictCtx = NULL((void*)0); | |||
| 1786 | } | |||
| 1787 | ||||
| 1788 | static int | |||
| 1789 | LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr, | |||
| 1790 | const char* src, char* dst, | |||
| 1791 | int* srcSizePtr, int dstCapacity, | |||
| 1792 | limitedOutput_directive limit) | |||
| 1793 | { | |||
| 1794 | LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; | |||
| 1795 | DEBUGLOG(5, "LZ4_compressHC_continue_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)",{} | |||
| 1796 | LZ4_streamHCPtr, src, *srcSizePtr, limit){}; | |||
| 1797 | assert(ctxPtr != NULL)((void)0); | |||
| 1798 | /* auto-init if forgotten */ | |||
| 1799 | if (ctxPtr->prefixStart == NULL((void*)0)) | |||
| 1800 | LZ4HC_init_internal (ctxPtr, (const BYTE*) src); | |||
| 1801 | ||||
| 1802 | /* Check overflow */ | |||
| 1803 | if ((size_t)(ctxPtr->end - ctxPtr->prefixStart) + ctxPtr->dictLimit > 2 GB*(1U<<30)) { | |||
| 1804 | size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->prefixStart); | |||
| 1805 | if (dictSize > 64 KB*(1 <<10)) dictSize = 64 KB*(1 <<10); | |||
| 1806 | LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize); | |||
| 1807 | } | |||
| 1808 | ||||
| 1809 | /* Check if blocks follow each other */ | |||
| 1810 | if ((const BYTE*)src != ctxPtr->end) | |||
| 1811 | LZ4HC_setExternalDict(ctxPtr, (const BYTE*)src); | |||
| 1812 | ||||
| 1813 | /* Check overlapping input/dictionary space */ | |||
| 1814 | { const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr; | |||
| 1815 | const BYTE* const dictBegin = ctxPtr->dictStart; | |||
| 1816 | const BYTE* const dictEnd = ctxPtr->dictStart + (ctxPtr->dictLimit - ctxPtr->lowLimit); | |||
| 1817 | if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) { | |||
| 1818 | if (sourceEnd > dictEnd) sourceEnd = dictEnd; | |||
| 1819 | ctxPtr->lowLimit += (U32)(sourceEnd - ctxPtr->dictStart); | |||
| 1820 | ctxPtr->dictStart += (U32)(sourceEnd - ctxPtr->dictStart); | |||
| 1821 | /* invalidate dictionary is it's too small */ | |||
| 1822 | if (ctxPtr->dictLimit - ctxPtr->lowLimit < LZ4HC_HASHSIZE4) { | |||
| 1823 | ctxPtr->lowLimit = ctxPtr->dictLimit; | |||
| 1824 | ctxPtr->dictStart = ctxPtr->prefixStart; | |||
| 1825 | } } } | |||
| 1826 | ||||
| 1827 | return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit); | |||
| 1828 | } | |||
| 1829 | ||||
| 1830 | int LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int srcSize, int dstCapacity) | |||
| 1831 | { | |||
| 1832 | DEBUGLOG(5, "LZ4_compress_HC_continue"){}; | |||
| 1833 | if (dstCapacity < LZ4_compressBound(srcSize)) | |||
| 1834 | return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, limitedOutput); | |||
| 1835 | else | |||
| 1836 | return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, notLimited); | |||
| 1837 | } | |||
| 1838 | ||||
| 1839 | int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int* srcSizePtr, int targetDestSize) | |||
| 1840 | { | |||
| 1841 | return LZ4_compressHC_continue_generic(LZ4_streamHCPtr, src, dst, srcSizePtr, targetDestSize, fillOutput); | |||
| 1842 | } | |||
| 1843 | ||||
| 1844 | ||||
| 1845 | /* LZ4_saveDictHC : | |||
| 1846 | * save history content | |||
| 1847 | * into a user-provided buffer | |||
| 1848 | * which is then used to continue compression | |||
| 1849 | */ | |||
| 1850 | int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize) | |||
| 1851 | { | |||
| 1852 | LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; | |||
| 1853 | int const prefixSize = (int)(streamPtr->end - streamPtr->prefixStart); | |||
| 1854 | DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize){}; | |||
| 1855 | /* nothing compressed yet (new or reset stream) : nothing to save, | |||
| 1856 | * the stream remains to be initialized by next compression (attached dictionary included) */ | |||
| 1857 | if (streamPtr->prefixStart == NULL((void*)0)) return 0; | |||
| ||||
| 1858 | assert(prefixSize >= 0)((void)0); | |||
| 1859 | if (dictSize > 64 KB*(1 <<10)) dictSize = 64 KB*(1 <<10); | |||
| 1860 | if (dictSize < 4) dictSize = 0; | |||
| 1861 | if (dictSize > prefixSize) dictSize = prefixSize; | |||
| 1862 | if (safeBuffer == NULL((void*)0)) assert(dictSize == 0)((void)0); /* a NULL buffer with !0 size is invalid */ | |||
| 1863 | if (dictSize
| |||
| 1864 | LZ4_memmove__builtin_memmove(safeBuffer, streamPtr->end - dictSize, (size_t)dictSize); | |||
| ||||
| 1865 | { U32 const endIndex = (U32)(streamPtr->end - streamPtr->prefixStart) + streamPtr->dictLimit; | |||
| 1866 | streamPtr->end = (safeBuffer == NULL((void*)0)) ? NULL((void*)0) : (const BYTE*)safeBuffer + dictSize; | |||
| 1867 | streamPtr->prefixStart = (const BYTE*)safeBuffer; | |||
| 1868 | streamPtr->dictLimit = endIndex - (U32)dictSize; | |||
| 1869 | streamPtr->lowLimit = endIndex - (U32)dictSize; | |||
| 1870 | streamPtr->dictStart = streamPtr->prefixStart; | |||
| 1871 | if (streamPtr->nextToUpdate < streamPtr->dictLimit) | |||
| 1872 | streamPtr->nextToUpdate = streamPtr->dictLimit; | |||
| 1873 | } | |||
| 1874 | return dictSize; | |||
| 1875 | } | |||
| 1876 | ||||
| 1877 | ||||
| 1878 | /* ================================================ | |||
| 1879 | * LZ4 Optimal parser (levels [LZ4HC_CLEVEL_OPT_MIN - LZ4HC_CLEVEL_MAX]) | |||
| 1880 | * ===============================================*/ | |||
| 1881 | typedef struct { | |||
| 1882 | int price; | |||
| 1883 | int off; | |||
| 1884 | int mlen; /* 1 means literal */ | |||
| 1885 | int litlen; | |||
| 1886 | } LZ4HC_optimal_t; | |||
| 1887 | ||||
| 1888 | #define TRAILING_LITERALS3 3 | |||
| 1889 | ||||
| 1890 | /* Prices are in 1/LZ4HC_PRICE_SCALE byte, | |||
| 1891 | * leaving room for a price per sequence, lower than a byte. */ | |||
| 1892 | #define LZ4HC_PRICE_SCALE4 4 | |||
| 1893 | ||||
| 1894 | /* favorDecSpeed : each sequence costs decoding time, so it also costs LZ4HC_SEQ_PRICE_FDS. | |||
| 1895 | * Removing a sequence almost always costs at least one byte, | |||
| 1896 | * so this price, lower than a byte, mostly acts as a tie-breaker : | |||
| 1897 | * among solutions of the same size, the one with fewer sequences is preferred. | |||
| 1898 | * They are found at positions only worth searching to save a sequence, | |||
| 1899 | * which are searched if at most LZ4HC_SEQ_SEARCH_DIST before the end of the furthest match. */ | |||
| 1900 | #define LZ4HC_SEQ_PRICE_FDS1 1 | |||
| 1901 | #define LZ4HC_SEQ_SEARCH_DIST4 4 | |||
| 1902 | ||||
| 1903 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) int LZ4HC_literalsPrice(int const litlen) | |||
| 1904 | { | |||
| 1905 | int price = litlen; | |||
| 1906 | assert(litlen >= 0)((void)0); | |||
| 1907 | if (litlen >= (int)RUN_MASK((1U<<(8-4))-1)) | |||
| 1908 | price += 1 + ((litlen-(int)RUN_MASK((1U<<(8-4))-1)) / 255); | |||
| 1909 | return price * LZ4HC_PRICE_SCALE4; | |||
| 1910 | } | |||
| 1911 | ||||
| 1912 | /* requires mlen >= MINMATCH */ | |||
| 1913 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) int LZ4HC_sequencePrice(int litlen, int mlen) | |||
| 1914 | { | |||
| 1915 | int price = 1 + 2 ; /* token + 16-bit offset */ | |||
| 1916 | assert(litlen >= 0)((void)0); | |||
| 1917 | assert(mlen >= MINMATCH)((void)0); | |||
| 1918 | ||||
| 1919 | if (mlen >= (int)(ML_MASK((1U<<4)-1)+MINMATCH4)) | |||
| 1920 | price += 1 + ((mlen-(int)(ML_MASK((1U<<4)-1)+MINMATCH4)) / 255); | |||
| 1921 | ||||
| 1922 | return price * LZ4HC_PRICE_SCALE4 + LZ4HC_literalsPrice(litlen); | |||
| 1923 | } | |||
| 1924 | ||||
| 1925 | /* LZ4HC_pricedLitlen() : | |||
| 1926 | * All solutions of a window start with the same run of @llen literals, | |||
| 1927 | * so only price differences matter, and they depend on @llen | |||
| 1928 | * only through its position relative to the 255-steps of the literal length field. | |||
| 1929 | * Reduce it accordingly : scaled prices of long runs would overflow. */ | |||
| 1930 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) int LZ4HC_pricedLitlen(int const llen) | |||
| 1931 | { | |||
| 1932 | int const reduceFrom = (int)RUN_MASK((1U<<(8-4))-1) + 255; | |||
| 1933 | assert(llen >= 0)((void)0); | |||
| 1934 | if (llen < reduceFrom) return llen; | |||
| 1935 | return reduceFrom + ((llen - (int)RUN_MASK((1U<<(8-4))-1)) % 255); | |||
| 1936 | } | |||
| 1937 | ||||
| 1938 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) LZ4HC_match_t | |||
| 1939 | LZ4HC_FindLongestMatch(LZ4HC_CCtx_internal* const ctx, | |||
| 1940 | const BYTE* ip, const BYTE* const iHighLimit, | |||
| 1941 | int nbSearches, | |||
| 1942 | const dictCtx_directive dict, | |||
| 1943 | const HCfavor_e favorDecSpeed) | |||
| 1944 | { | |||
| 1945 | LZ4HC_match_t const match0 = { 0 , 0, 0 }; | |||
| 1946 | /* note : LZ4HC_InsertAndGetWiderMatch() can extend a match backward (md.back < 0), | |||
| 1947 | * but not here, since iLowLimit == ip */ | |||
| 1948 | LZ4HC_match_t md = LZ4HC_InsertAndGetWiderMatch(ctx, ip, ip, iHighLimit, MINMATCH4-1, nbSearches, 1 /*patternAnalysis*/, 1 /*chainSwap*/, dict, favorDecSpeed); | |||
| 1949 | assert(md.back == 0)((void)0); | |||
| 1950 | if (md.len < MINMATCH4) return match0; | |||
| 1951 | if (favorDecSpeed) { | |||
| 1952 | if ((md.len>18) & (md.len<=36)) md.len=18; /* favor dec.speed (shortcut) */ | |||
| 1953 | } | |||
| 1954 | return md; | |||
| 1955 | } | |||
| 1956 | ||||
| 1957 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) void | |||
| 1958 | LZ4HC_setLiteral(LZ4HC_optimal_t* opt, int pos, int litlen, int price) | |||
| 1959 | { | |||
| 1960 | opt[pos].mlen = 1; | |||
| 1961 | opt[pos].off = 0; | |||
| 1962 | opt[pos].litlen = litlen; | |||
| 1963 | opt[pos].price = price; | |||
| 1964 | DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", pos, price, litlen){}; | |||
| 1965 | } | |||
| 1966 | ||||
| 1967 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) void | |||
| 1968 | LZ4HC_setMatch(LZ4HC_optimal_t* opt, int pos, int mlen, int off, int price) | |||
| 1969 | { | |||
| 1970 | opt[pos].mlen = mlen; | |||
| 1971 | opt[pos].off = off; | |||
| 1972 | opt[pos].litlen = 0; | |||
| 1973 | opt[pos].price = price; | |||
| 1974 | DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i)", pos, price, mlen){}; | |||
| 1975 | } | |||
| 1976 | ||||
| 1977 | /* price at the start of the literal run ending at @pos | |||
| 1978 | * (0 if this run started before position 0, i.e. it includes literals since anchor) */ | |||
| 1979 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) int LZ4HC_runStartPrice(const LZ4HC_optimal_t* opt, int pos) | |||
| 1980 | { | |||
| 1981 | int const ll = opt[pos].litlen; | |||
| 1982 | return (ll <= pos) ? opt[pos - ll].price : 0; | |||
| 1983 | } | |||
| 1984 | ||||
| 1985 | /* LZ4HC_addMatch() : | |||
| 1986 | * Update prices of positions reachable from @cur using @match. | |||
| 1987 | * Positions beyond @lastPos + TRAILING_LITERALS are not reached yet. | |||
| 1988 | * @return : updated @lastPos (end of the furthest match) */ | |||
| 1989 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) int | |||
| 1990 | LZ4HC_addMatch(LZ4HC_optimal_t* opt, int cur, LZ4HC_match_t match, | |||
| 1991 | int lastPos, const HCfavor_e favorDecSpeed) | |||
| 1992 | { | |||
| 1993 | int const ll = opt[cur].litlen; | |||
| 1994 | int const basePrice = LZ4HC_runStartPrice(opt, cur) + (favorDecSpeed ? LZ4HC_SEQ_PRICE_FDS1 : 0); | |||
| 1995 | int ml; | |||
| 1996 | assert(cur + match.len < LZ4_OPT_NUM)((void)0); | |||
| 1997 | assert((U32)favorDecSpeed <= 1)((void)0); | |||
| 1998 | for (ml = MINMATCH4; ml <= match.len; ml++) { | |||
| 1999 | int const pos = cur + ml; | |||
| 2000 | int const price = basePrice + LZ4HC_sequencePrice(ll, ml); | |||
| 2001 | /* on equal price, favorDecSpeed keeps the existing solution */ | |||
| 2002 | if ( pos > lastPos + TRAILING_LITERALS3 | |||
| 2003 | || price <= opt[pos].price - (int)favorDecSpeed) { | |||
| 2004 | LZ4HC_setMatch(opt, pos, ml, match.off, price); | |||
| 2005 | if ((ml == match.len) && (lastPos < pos)) lastPos = pos; | |||
| 2006 | } } | |||
| 2007 | ||||
| 2008 | /* complete following positions with literals */ | |||
| 2009 | { int addLit; | |||
| 2010 | assert(opt[lastPos].mlen >= MINMATCH)((void)0); | |||
| 2011 | for (addLit = 1; addLit <= TRAILING_LITERALS3; addLit++) | |||
| 2012 | LZ4HC_setLiteral(opt, lastPos + addLit, addLit, | |||
| 2013 | opt[lastPos].price + LZ4HC_literalsPrice(addLit)); | |||
| 2014 | } | |||
| 2015 | return lastPos; | |||
| 2016 | } | |||
| 2017 | ||||
| 2018 | ||||
| 2019 | /* preconditions: | |||
| 2020 | * - *srcSizePtr within [1, LZ4_MAX_INPUT_SIZE] | |||
| 2021 | * - src is valid | |||
| 2022 | * - maxOutputSize >= 1 | |||
| 2023 | * - dst is valid | |||
| 2024 | * - @opt : table of LZ4_OPT_NUM + TRAILING_LITERALS cells | |||
| 2025 | */ | |||
| 2026 | LZ4_FORCE_INLINEstatic inline __attribute__((always_inline)) int | |||
| 2027 | LZ4HC_compress_optimal_internal(LZ4HC_CCtx_internal* ctx, | |||
| 2028 | const char* const source, | |||
| 2029 | char* dst, | |||
| 2030 | int* srcSizePtr, | |||
| 2031 | int dstCapacity, | |||
| 2032 | int const nbSearches, | |||
| 2033 | int const nbSearchesSeq, | |||
| 2034 | int const targetLength, | |||
| 2035 | const limitedOutput_directive limit, | |||
| 2036 | const dictCtx_directive dict, | |||
| 2037 | const HCfavor_e favorDecSpeed, | |||
| 2038 | LZ4HC_optimal_t* const opt) | |||
| 2039 | { | |||
| 2040 | const BYTE* ip = (const BYTE*) source; | |||
| 2041 | const BYTE* anchor = ip; | |||
| 2042 | const int inputSize = *srcSizePtr; | |||
| 2043 | const BYTE* const iend = ip + inputSize; | |||
| 2044 | const BYTE* mflimit; /* set after the size check : would precede a small input */ | |||
| 2045 | const BYTE* matchlimit; | |||
| 2046 | BYTE* op = (BYTE*) dst; | |||
| 2047 | /* fillOutput : keep room for the final run of literals (LZ4 format restriction) */ | |||
| 2048 | BYTE* const oend = op + dstCapacity - ((limit == fillOutput) ? LASTLITERALS5 : 0); | |||
| 2049 | int const sufficientLen = MIN(targetLength, LZ4_OPT_NUM-1)( (targetLength) < ((1<<12)-1) ? (targetLength) : (( 1<<12)-1) ); | |||
| 2050 | ||||
| 2051 | DEBUGLOG(5, "LZ4HC_compress_optimal(dst=%p, dstCapa=%u)", dst, (unsigned)dstCapacity){}; | |||
| 2052 | ||||
| 2053 | /* preconditions verifications */ | |||
| 2054 | assert(dstCapacity > 0)((void)0); | |||
| 2055 | assert(dst != NULL)((void)0); | |||
| 2056 | assert(*srcSizePtr > 0)((void)0); | |||
| 2057 | assert(source != NULL)((void)0); | |||
| 2058 | assert(opt != NULL)((void)0); | |||
| 2059 | assert(nbSearchesSeq >= 1)((void)0); | |||
| 2060 | ||||
| 2061 | *srcSizePtr = 0; | |||
| 2062 | if (inputSize < LZ4_minLength) goto _last_literals; | |||
| 2063 | mflimit = iend - MFLIMIT12; | |||
| 2064 | matchlimit = iend - LASTLITERALS5; | |||
| 2065 | ||||
| 2066 | /* Main Loop */ | |||
| 2067 | while (ip <= mflimit) { | |||
| 2068 | int const llen = LZ4HC_pricedLitlen((int)(ip - anchor)); | |||
| 2069 | int cur, lastPos; | |||
| 2070 | int lastSeqPos; /* position of the last sequence to encode */ | |||
| 2071 | LZ4HC_match_t lastSeq; | |||
| 2072 | ||||
| 2073 | LZ4HC_match_t const firstMatch = LZ4HC_FindLongestMatch(ctx, ip, matchlimit, nbSearches, dict, favorDecSpeed); | |||
| 2074 | if (firstMatch.len==0) { ip++; continue; } | |||
| 2075 | ||||
| 2076 | if (firstMatch.len > sufficientLen) { | |||
| 2077 | /* good enough solution : immediate encoding */ | |||
| 2078 | lastSeqPos = 0; | |||
| 2079 | lastSeq = firstMatch; | |||
| 2080 | goto encode; | |||
| 2081 | } | |||
| 2082 | ||||
| 2083 | /* set prices for first positions (literals) */ | |||
| 2084 | { int rPos; | |||
| 2085 | for (rPos = 0 ; rPos < MINMATCH4 ; rPos++) | |||
| 2086 | LZ4HC_setLiteral(opt, rPos, llen + rPos, LZ4HC_literalsPrice(llen + rPos)); | |||
| 2087 | } | |||
| 2088 | lastPos = LZ4HC_addMatch(opt, 0, firstMatch, 0, favorDecSpeed); | |||
| 2089 | ||||
| 2090 | /* check further positions */ | |||
| 2091 | for (cur = 1; cur < lastPos; cur++) { | |||
| 2092 | const BYTE* const curPtr = ip + cur; | |||
| 2093 | LZ4HC_match_t newMatch; | |||
| 2094 | int nbS = nbSearches; | |||
| 2095 | ||||
| 2096 | if (curPtr > mflimit) break; | |||
| 2097 | DEBUGLOG(7, "rPos:%u[%u] vs [%u]%u",{} | |||
| 2098 | cur, opt[cur].price, opt[cur+1].price, cur+1){}; | |||
| 2099 | /* not useful to search here if next position has same (or lower) cost */ | |||
| 2100 | if (opt[cur+1].price <= opt[cur].price) continue; | |||
| 2101 | /* favorDecSpeed : next position costs less than one more byte, the difference comes from sequences. | |||
| 2102 | * A match starting here might save some : worth a cheaper search, if close to lastPos */ | |||
| 2103 | if (favorDecSpeed && (opt[cur+1].price < opt[cur].price + LZ4HC_PRICE_SCALE4)) { | |||
| 2104 | if (lastPos - cur > LZ4HC_SEQ_SEARCH_DIST4) continue; | |||
| 2105 | nbS = nbSearchesSeq; | |||
| 2106 | } | |||
| 2107 | ||||
| 2108 | newMatch = LZ4HC_FindLongestMatch(ctx, curPtr, matchlimit, nbS, dict, favorDecSpeed); | |||
| 2109 | if (!newMatch.len) continue; | |||
| 2110 | ||||
| 2111 | if ( (newMatch.len > sufficientLen) | |||
| 2112 | || (newMatch.len + cur >= LZ4_OPT_NUM(1<<12)) ) { | |||
| 2113 | /* immediate encoding */ | |||
| 2114 | lastSeqPos = cur; | |||
| 2115 | lastSeq = newMatch; | |||
| 2116 | goto encode; | |||
| 2117 | } | |||
| 2118 | ||||
| 2119 | lastPos = LZ4HC_addMatch(opt, cur, newMatch, lastPos, favorDecSpeed); | |||
| 2120 | } /* for (cur = 1; cur < lastPos; cur++) */ | |||
| 2121 | ||||
| 2122 | assert(lastPos < LZ4_OPT_NUM + TRAILING_LITERALS)((void)0); | |||
| 2123 | lastSeq.len = opt[lastPos].mlen; | |||
| 2124 | lastSeq.off = opt[lastPos].off; | |||
| 2125 | lastSeqPos = lastPos - lastSeq.len; | |||
| 2126 | ||||
| 2127 | encode: /* lastSeqPos and lastSeq must be set */ | |||
| 2128 | assert(0 <= lastSeqPos && lastSeqPos < LZ4_OPT_NUM)((void)0); | |||
| 2129 | DEBUGLOG(6, "reverse traversal, looking for shortest path (lastSeqPos=%i)", lastSeqPos){}; | |||
| 2130 | /* opt[pos] describes the step ending at pos. | |||
| 2131 | * Walk the path backward, moving each step to its starting position instead */ | |||
| 2132 | { int pos = lastSeqPos; | |||
| 2133 | int mlen = lastSeq.len; | |||
| 2134 | int off = lastSeq.off; | |||
| 2135 | while (pos > 0) { | |||
| 2136 | int const prevMlen = opt[pos].mlen; | |||
| 2137 | int const prevOff = opt[pos].off; | |||
| 2138 | opt[pos].mlen = mlen; | |||
| 2139 | opt[pos].off = off; | |||
| 2140 | mlen = prevMlen; | |||
| 2141 | off = prevOff; | |||
| 2142 | assert(0 < mlen && mlen <= pos)((void)0); | |||
| 2143 | pos -= mlen; | |||
| 2144 | } | |||
| 2145 | /* first step of the path; note : opt[0] isn't necessarily initialized */ | |||
| 2146 | opt[0].mlen = mlen; | |||
| 2147 | opt[0].off = off; | |||
| 2148 | } | |||
| 2149 | ||||
| 2150 | /* encode all recorded sequences in order */ | |||
| 2151 | { int rPos = 0; /* relative position (to ip) */ | |||
| 2152 | while (rPos <= lastSeqPos) { | |||
| 2153 | int const ml = opt[rPos].mlen; | |||
| 2154 | int const offset = opt[rPos].off; | |||
| 2155 | if (ml == 1) { ip++; rPos++; continue; } /* literal */ | |||
| 2156 | rPos += ml; | |||
| 2157 | assert(ml >= MINMATCH)((void)0); | |||
| 2158 | assert((offset >= 1) && (offset <= LZ4_DISTANCE_MAX))((void)0); | |||
| 2159 | if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, ml, offset, limit, oend) ) { /* updates ip, op and anchor */ | |||
| 2160 | if (limit != fillOutput) return 0; /* compression failed */ | |||
| 2161 | LZ4HC_encodeLastSequence_fillOutput(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, ml, offset, oend); | |||
| 2162 | goto _last_literals; | |||
| 2163 | } } } | |||
| 2164 | } /* while (ip <= mflimit) */ | |||
| 2165 | ||||
| 2166 | _last_literals: | |||
| 2167 | if (LZ4HC_encodeLastLiterals(UPDATABLE(ip, op, anchor)&ip, &op, &anchor, iend, limit, oend)) | |||
| 2168 | return 0; /* not enough space in @dst */ | |||
| 2169 | ||||
| 2170 | /* End */ | |||
| 2171 | *srcSizePtr = (int) (((const char*)ip) - source); | |||
| 2172 | return (int) ((char*)op-dst); | |||
| 2173 | } | |||
| 2174 | ||||
| 2175 | static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx, | |||
| 2176 | const char* const source, | |||
| 2177 | char* dst, | |||
| 2178 | int* srcSizePtr, | |||
| 2179 | int dstCapacity, | |||
| 2180 | int const nbSearches, | |||
| 2181 | int const nbSearchesSeq, | |||
| 2182 | int const targetLength, | |||
| 2183 | const limitedOutput_directive limit, | |||
| 2184 | const dictCtx_directive dict, | |||
| 2185 | const HCfavor_e favorDecSpeed) | |||
| 2186 | { | |||
| 2187 | LZ4_STATIC_ASSERT(LZ4_STREAMHC_EXTSIZE == sizeof(LZ4_streamHC_t) + sizeof(LZ4HC_optimal_t) * (LZ4_OPT_NUM + TRAILING_LITERALS)){ enum { LZ4_static_assert = 1/(int)(!!((262200 + 65584) == sizeof (LZ4_streamHC_t) + sizeof(LZ4HC_optimal_t) * ((1<<12) + 3))) }; }; | |||
| 2188 | if (ctx->hasWorkspace) { /* extended state : the buffer follows it */ | |||
| 2189 | LZ4HC_optimal_t* const opt = (LZ4HC_optimal_t*)(void*)((BYTE*)ctx + sizeof(LZ4_streamHC_t)); | |||
| 2190 | return LZ4HC_compress_optimal_internal(ctx, source, dst, srcSizePtr, dstCapacity, | |||
| 2191 | nbSearches, nbSearchesSeq, targetLength, limit, dict, favorDecSpeed, opt); | |||
| 2192 | } | |||
| 2193 | { /* plain state */ | |||
| 2194 | #if defined(LZ4HC_HEAPMODE1) && LZ4HC_HEAPMODE1==1 | |||
| 2195 | LZ4HC_optimal_t* const opt = (LZ4HC_optimal_t*)ALLOC(sizeof(LZ4HC_optimal_t) * (LZ4_OPT_NUM + TRAILING_LITERALS))malloc(sizeof(LZ4HC_optimal_t) * ((1<<12) + 3)); | |||
| 2196 | int result; | |||
| 2197 | if (opt == NULL((void*)0)) return 0; | |||
| 2198 | result = LZ4HC_compress_optimal_internal(ctx, source, dst, srcSizePtr, dstCapacity, | |||
| 2199 | nbSearches, nbSearchesSeq, targetLength, limit, dict, favorDecSpeed, opt); | |||
| 2200 | FREEMEM(opt)free(opt); | |||
| 2201 | return result; | |||
| 2202 | #else | |||
| 2203 | LZ4HC_optimal_t opt[LZ4_OPT_NUM(1<<12) + TRAILING_LITERALS3]; /* ~64 KB, which can be a bit large for some stacks... */ | |||
| 2204 | return LZ4HC_compress_optimal_internal(ctx, source, dst, srcSizePtr, dstCapacity, | |||
| 2205 | nbSearches, nbSearchesSeq, targetLength, limit, dict, favorDecSpeed, opt); | |||
| 2206 | #endif | |||
| 2207 | } | |||
| 2208 | } | |||
| 2209 | ||||
| 2210 | ||||
| 2211 | /*************************************************** | |||
| 2212 | * Obsolete functions, removed from lz4hc.h (v1.11.0) | |||
| 2213 | * still exported, for programs linked to previous versions | |||
| 2214 | ***************************************************/ | |||
| 2215 | #if defined (__cplusplus) | |||
| 2216 | extern "C" { | |||
| 2217 | #endif | |||
| 2218 | ||||
| 2219 | LZ4LIB_API int LZ4_compressHC (const char* source, char* dest, int inputSize); | |||
| 2220 | LZ4LIB_API int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize); | |||
| 2221 | LZ4LIB_API int LZ4_compressHC2 (const char* source, char* dest, int inputSize, int compressionLevel); | |||
| 2222 | LZ4LIB_API int LZ4_compressHC2_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); | |||
| 2223 | LZ4LIB_API int LZ4_compressHC_withStateHC (void* state, const char* source, char* dest, int inputSize); | |||
| 2224 | LZ4LIB_API int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); | |||
| 2225 | LZ4LIB_API int LZ4_compressHC2_withStateHC (void* state, const char* source, char* dest, int inputSize, int compressionLevel); | |||
| 2226 | LZ4LIB_API int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); | |||
| 2227 | LZ4LIB_API int LZ4_compressHC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize); | |||
| 2228 | LZ4LIB_API int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize); | |||
| 2229 | LZ4LIB_API void* LZ4_createHC (const char* inputBuffer); | |||
| 2230 | LZ4LIB_API int LZ4_freeHC (void* LZ4HC_Data); | |||
| 2231 | LZ4LIB_API char* LZ4_slideInputBufferHC (void* LZ4HC_Data); | |||
| 2232 | LZ4LIB_API int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel); | |||
| 2233 | LZ4LIB_API int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); | |||
| 2234 | LZ4LIB_API int LZ4_sizeofStreamStateHC(void); | |||
| 2235 | LZ4LIB_API int LZ4_resetStreamStateHC(void* state, char* inputBuffer); | |||
| 2236 | ||||
| 2237 | #if defined (__cplusplus) | |||
| 2238 | } | |||
| 2239 | #endif | |||
| 2240 | ||||
| 2241 | /* Wrappers for deprecated compression functions */ | |||
| 2242 | int LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); } | |||
| 2243 | int LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); } | |||
| 2244 | int LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } | |||
| 2245 | int LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); } | |||
| 2246 | int LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); } | |||
| 2247 | int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); } | |||
| 2248 | int LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } | |||
| 2249 | int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); } | |||
| 2250 | int LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); } | |||
| 2251 | int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); } | |||
| 2252 | ||||
| 2253 | ||||
| 2254 | /* Deprecated streaming functions */ | |||
| 2255 | int LZ4_sizeofStreamStateHC(void) { return sizeof(LZ4_streamHC_t); } | |||
| 2256 | ||||
| 2257 | /* state is presumed correctly sized, aka >= sizeof(LZ4_streamHC_t) | |||
| 2258 | * @return : 0 on success, !=0 if error */ | |||
| 2259 | int LZ4_resetStreamStateHC(void* state, char* inputBuffer) | |||
| 2260 | { | |||
| 2261 | LZ4_streamHC_t* const hc4 = LZ4_initStreamHC(state, sizeof(*hc4)); | |||
| 2262 | if (hc4 == NULL((void*)0)) return 1; /* init failed */ | |||
| 2263 | LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer); | |||
| 2264 | return 0; | |||
| 2265 | } | |||
| 2266 | ||||
| 2267 | #if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) | |||
| 2268 | void* LZ4_createHC (const char* inputBuffer) | |||
| 2269 | { | |||
| 2270 | LZ4_streamHC_t* const hc4 = LZ4_createStreamHC(); | |||
| 2271 | if (hc4 == NULL((void*)0)) return NULL((void*)0); /* not enough memory */ | |||
| 2272 | LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer); | |||
| 2273 | return hc4; | |||
| 2274 | } | |||
| 2275 | ||||
| 2276 | int LZ4_freeHC (void* LZ4HC_Data) | |||
| 2277 | { | |||
| 2278 | if (!LZ4HC_Data) return 0; /* support free on NULL */ | |||
| 2279 | FREEMEM(LZ4HC_Data)free(LZ4HC_Data); | |||
| 2280 | return 0; | |||
| 2281 | } | |||
| 2282 | #endif | |||
| 2283 | ||||
| 2284 | /* LZ4_compress_HC_continue() at level @cLevel : notably, restarts the stream after LZ4_slideInputBufferHC() */ | |||
| 2285 | int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel) | |||
| 2286 | { | |||
| 2287 | LZ4_setCompressionLevel((LZ4_streamHC_t*)LZ4HC_Data, cLevel); | |||
| 2288 | return LZ4_compress_HC_continue((LZ4_streamHC_t*)LZ4HC_Data, src, dst, srcSize, LZ4_compressBound(srcSize)); | |||
| 2289 | } | |||
| 2290 | ||||
| 2291 | int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int dstCapacity, int cLevel) | |||
| 2292 | { | |||
| 2293 | LZ4_setCompressionLevel((LZ4_streamHC_t*)LZ4HC_Data, cLevel); | |||
| 2294 | return LZ4_compress_HC_continue((LZ4_streamHC_t*)LZ4HC_Data, src, dst, srcSize, dstCapacity); | |||
| 2295 | } | |||
| 2296 | ||||
| 2297 | char* LZ4_slideInputBufferHC(void* LZ4HC_Data) | |||
| 2298 | { | |||
| 2299 | LZ4HC_CCtx_internal* const s = &((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse; | |||
| 2300 | const BYTE* const bufferStart = s->prefixStart - s->dictLimit + s->lowLimit; | |||
| 2301 | LZ4_resetStreamHC_fast((LZ4_streamHC_t*)LZ4HC_Data, s->compressionLevel); | |||
| 2302 | /* ugly conversion trick, required to evade (const char*) -> (char*) cast-qual warning :( */ | |||
| 2303 | return (char*)(uptrval)bufferStart; | |||
| 2304 | } |