lz4-native-ruby 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +48 -0
- data/ext/lz4/extconf.rb +12 -0
- data/ext/lz4/lz4_ext.c +230 -0
- data/lib/lz4/lz4_ext.so +0 -0
- data/lib/lz4/version.rb +3 -0
- data/lib/lz4.rb +60 -0
- data/vendor/lz4/lib/LICENSE +24 -0
- data/vendor/lz4/lib/Makefile +244 -0
- data/vendor/lz4/lib/README.md +193 -0
- data/vendor/lz4/lib/dll/example/Makefile +63 -0
- data/vendor/lz4/lib/dll/example/README.md +69 -0
- data/vendor/lz4/lib/dll/example/fullbench-dll.sln +25 -0
- data/vendor/lz4/lib/dll/example/fullbench-dll.vcxproj +182 -0
- data/vendor/lz4/lib/liblz4-dll.rc.in +35 -0
- data/vendor/lz4/lib/liblz4.pc.in +14 -0
- data/vendor/lz4/lib/lz4.c +2829 -0
- data/vendor/lz4/lib/lz4.h +884 -0
- data/vendor/lz4/lib/lz4file.c +341 -0
- data/vendor/lz4/lib/lz4file.h +93 -0
- data/vendor/lz4/lib/lz4frame.c +2136 -0
- data/vendor/lz4/lib/lz4frame.h +751 -0
- data/vendor/lz4/lib/lz4frame_static.h +47 -0
- data/vendor/lz4/lib/lz4hc.c +2192 -0
- data/vendor/lz4/lib/lz4hc.h +414 -0
- data/vendor/lz4/lib/xxhash.c +1030 -0
- data/vendor/lz4/lib/xxhash.h +328 -0
- metadata +115 -0
|
@@ -0,0 +1,2136 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* LZ4 auto-framing library
|
|
3
|
+
* Copyright (C) 2011-2016, Yann Collet.
|
|
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 homepage : http://www.lz4.org
|
|
32
|
+
* - LZ4 source repository : https://github.com/lz4/lz4
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/* LZ4F is a stand-alone API to create LZ4-compressed Frames
|
|
36
|
+
* in full conformance with specification v1.6.1 .
|
|
37
|
+
* This library rely upon memory management capabilities (malloc, free)
|
|
38
|
+
* provided either by <stdlib.h>,
|
|
39
|
+
* or redirected towards another library of user's choice
|
|
40
|
+
* (see Memory Routines below).
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
/*-************************************
|
|
45
|
+
* Compiler Options
|
|
46
|
+
**************************************/
|
|
47
|
+
#include <limits.h>
|
|
48
|
+
#ifdef _MSC_VER /* Visual Studio */
|
|
49
|
+
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
|
50
|
+
#endif
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/*-************************************
|
|
54
|
+
* Tuning parameters
|
|
55
|
+
**************************************/
|
|
56
|
+
/*
|
|
57
|
+
* LZ4F_HEAPMODE :
|
|
58
|
+
* Control how LZ4F_compressFrame allocates the Compression State,
|
|
59
|
+
* either on stack (0:default, fastest), or in memory heap (1:requires malloc()).
|
|
60
|
+
*/
|
|
61
|
+
#ifndef LZ4F_HEAPMODE
|
|
62
|
+
# define LZ4F_HEAPMODE 0
|
|
63
|
+
#endif
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
/*-************************************
|
|
67
|
+
* Library declarations
|
|
68
|
+
**************************************/
|
|
69
|
+
#define LZ4F_STATIC_LINKING_ONLY
|
|
70
|
+
#include "lz4frame.h"
|
|
71
|
+
#define LZ4_STATIC_LINKING_ONLY
|
|
72
|
+
#include "lz4.h"
|
|
73
|
+
#define LZ4_HC_STATIC_LINKING_ONLY
|
|
74
|
+
#include "lz4hc.h"
|
|
75
|
+
#define XXH_STATIC_LINKING_ONLY
|
|
76
|
+
#include "xxhash.h"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/*-************************************
|
|
80
|
+
* Memory routines
|
|
81
|
+
**************************************/
|
|
82
|
+
/*
|
|
83
|
+
* User may redirect invocations of
|
|
84
|
+
* malloc(), calloc() and free()
|
|
85
|
+
* towards another library or solution of their choice
|
|
86
|
+
* by modifying below section.
|
|
87
|
+
**/
|
|
88
|
+
|
|
89
|
+
#include <string.h> /* memset, memcpy, memmove */
|
|
90
|
+
#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */
|
|
91
|
+
# define MEM_INIT(p,v,s) memset((p),(v),(s))
|
|
92
|
+
#endif
|
|
93
|
+
|
|
94
|
+
#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */
|
|
95
|
+
# include <stdlib.h> /* malloc, calloc, free */
|
|
96
|
+
# define ALLOC(s) malloc(s)
|
|
97
|
+
# define ALLOC_AND_ZERO(s) calloc(1,(s))
|
|
98
|
+
# define FREEMEM(p) free(p)
|
|
99
|
+
#endif
|
|
100
|
+
|
|
101
|
+
static void* LZ4F_calloc(size_t s, LZ4F_CustomMem cmem)
|
|
102
|
+
{
|
|
103
|
+
/* custom calloc defined : use it */
|
|
104
|
+
if (cmem.customCalloc != NULL) {
|
|
105
|
+
return cmem.customCalloc(cmem.opaqueState, s);
|
|
106
|
+
}
|
|
107
|
+
/* nothing defined : use default <stdlib.h>'s calloc() */
|
|
108
|
+
if (cmem.customAlloc == NULL) {
|
|
109
|
+
return ALLOC_AND_ZERO(s);
|
|
110
|
+
}
|
|
111
|
+
/* only custom alloc defined : use it, and combine it with memset() */
|
|
112
|
+
{ void* const p = cmem.customAlloc(cmem.opaqueState, s);
|
|
113
|
+
if (p != NULL) MEM_INIT(p, 0, s);
|
|
114
|
+
return p;
|
|
115
|
+
} }
|
|
116
|
+
|
|
117
|
+
static void* LZ4F_malloc(size_t s, LZ4F_CustomMem cmem)
|
|
118
|
+
{
|
|
119
|
+
/* custom malloc defined : use it */
|
|
120
|
+
if (cmem.customAlloc != NULL) {
|
|
121
|
+
return cmem.customAlloc(cmem.opaqueState, s);
|
|
122
|
+
}
|
|
123
|
+
/* nothing defined : use default <stdlib.h>'s malloc() */
|
|
124
|
+
return ALLOC(s);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static void LZ4F_free(void* p, LZ4F_CustomMem cmem)
|
|
128
|
+
{
|
|
129
|
+
if (p == NULL) return;
|
|
130
|
+
if (cmem.customFree != NULL) {
|
|
131
|
+
/* custom allocation defined : use it */
|
|
132
|
+
cmem.customFree(cmem.opaqueState, p);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
/* nothing defined : use default <stdlib.h>'s free() */
|
|
136
|
+
FREEMEM(p);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
/*-************************************
|
|
141
|
+
* Debug
|
|
142
|
+
**************************************/
|
|
143
|
+
#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)
|
|
144
|
+
# include <assert.h>
|
|
145
|
+
#else
|
|
146
|
+
# ifndef assert
|
|
147
|
+
# define assert(condition) ((void)0)
|
|
148
|
+
# endif
|
|
149
|
+
#endif
|
|
150
|
+
|
|
151
|
+
#define LZ4F_STATIC_ASSERT(c) { enum { LZ4F_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
|
|
152
|
+
|
|
153
|
+
#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) && !defined(DEBUGLOG)
|
|
154
|
+
# include <stdio.h>
|
|
155
|
+
static int g_debuglog_enable = 1;
|
|
156
|
+
# define DEBUGLOG(l, ...) { \
|
|
157
|
+
if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \
|
|
158
|
+
fprintf(stderr, __FILE__ " (%i): ", __LINE__ ); \
|
|
159
|
+
fprintf(stderr, __VA_ARGS__); \
|
|
160
|
+
fprintf(stderr, " \n"); \
|
|
161
|
+
} }
|
|
162
|
+
#else
|
|
163
|
+
# define DEBUGLOG(l, ...) {} /* disabled */
|
|
164
|
+
#endif
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
/*-************************************
|
|
168
|
+
* Basic Types
|
|
169
|
+
**************************************/
|
|
170
|
+
#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
|
|
171
|
+
# include <stdint.h>
|
|
172
|
+
typedef uint8_t BYTE;
|
|
173
|
+
typedef uint16_t U16;
|
|
174
|
+
typedef uint32_t U32;
|
|
175
|
+
typedef int32_t S32;
|
|
176
|
+
typedef uint64_t U64;
|
|
177
|
+
#else
|
|
178
|
+
typedef unsigned char BYTE;
|
|
179
|
+
typedef unsigned short U16;
|
|
180
|
+
typedef unsigned int U32;
|
|
181
|
+
typedef signed int S32;
|
|
182
|
+
typedef unsigned long long U64;
|
|
183
|
+
#endif
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
/* unoptimized version; solves endianness & alignment issues */
|
|
187
|
+
static U32 LZ4F_readLE32 (const void* src)
|
|
188
|
+
{
|
|
189
|
+
const BYTE* const srcPtr = (const BYTE*)src;
|
|
190
|
+
U32 value32 = srcPtr[0];
|
|
191
|
+
value32 |= ((U32)srcPtr[1])<< 8;
|
|
192
|
+
value32 |= ((U32)srcPtr[2])<<16;
|
|
193
|
+
value32 |= ((U32)srcPtr[3])<<24;
|
|
194
|
+
return value32;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static void LZ4F_writeLE32 (void* dst, U32 value32)
|
|
198
|
+
{
|
|
199
|
+
BYTE* const dstPtr = (BYTE*)dst;
|
|
200
|
+
dstPtr[0] = (BYTE)value32;
|
|
201
|
+
dstPtr[1] = (BYTE)(value32 >> 8);
|
|
202
|
+
dstPtr[2] = (BYTE)(value32 >> 16);
|
|
203
|
+
dstPtr[3] = (BYTE)(value32 >> 24);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static U64 LZ4F_readLE64 (const void* src)
|
|
207
|
+
{
|
|
208
|
+
const BYTE* const srcPtr = (const BYTE*)src;
|
|
209
|
+
U64 value64 = srcPtr[0];
|
|
210
|
+
value64 |= ((U64)srcPtr[1]<<8);
|
|
211
|
+
value64 |= ((U64)srcPtr[2]<<16);
|
|
212
|
+
value64 |= ((U64)srcPtr[3]<<24);
|
|
213
|
+
value64 |= ((U64)srcPtr[4]<<32);
|
|
214
|
+
value64 |= ((U64)srcPtr[5]<<40);
|
|
215
|
+
value64 |= ((U64)srcPtr[6]<<48);
|
|
216
|
+
value64 |= ((U64)srcPtr[7]<<56);
|
|
217
|
+
return value64;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
static void LZ4F_writeLE64 (void* dst, U64 value64)
|
|
221
|
+
{
|
|
222
|
+
BYTE* const dstPtr = (BYTE*)dst;
|
|
223
|
+
dstPtr[0] = (BYTE)value64;
|
|
224
|
+
dstPtr[1] = (BYTE)(value64 >> 8);
|
|
225
|
+
dstPtr[2] = (BYTE)(value64 >> 16);
|
|
226
|
+
dstPtr[3] = (BYTE)(value64 >> 24);
|
|
227
|
+
dstPtr[4] = (BYTE)(value64 >> 32);
|
|
228
|
+
dstPtr[5] = (BYTE)(value64 >> 40);
|
|
229
|
+
dstPtr[6] = (BYTE)(value64 >> 48);
|
|
230
|
+
dstPtr[7] = (BYTE)(value64 >> 56);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
/*-************************************
|
|
235
|
+
* Constants
|
|
236
|
+
**************************************/
|
|
237
|
+
#ifndef LZ4_SRC_INCLUDED /* avoid double definition */
|
|
238
|
+
# define KB *(1<<10)
|
|
239
|
+
# define MB *(1<<20)
|
|
240
|
+
# define GB *(1<<30)
|
|
241
|
+
#endif
|
|
242
|
+
|
|
243
|
+
#define _1BIT 0x01
|
|
244
|
+
#define _2BITS 0x03
|
|
245
|
+
#define _3BITS 0x07
|
|
246
|
+
#define _4BITS 0x0F
|
|
247
|
+
#define _8BITS 0xFF
|
|
248
|
+
|
|
249
|
+
#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U
|
|
250
|
+
#define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB
|
|
251
|
+
|
|
252
|
+
static const size_t minFHSize = LZ4F_HEADER_SIZE_MIN; /* 7 */
|
|
253
|
+
static const size_t maxFHSize = LZ4F_HEADER_SIZE_MAX; /* 19 */
|
|
254
|
+
static const size_t BHSize = LZ4F_BLOCK_HEADER_SIZE; /* block header : size, and compress flag */
|
|
255
|
+
static const size_t BFSize = LZ4F_BLOCK_CHECKSUM_SIZE; /* block footer : checksum (optional) */
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
/*-************************************
|
|
259
|
+
* Structures and local types
|
|
260
|
+
**************************************/
|
|
261
|
+
|
|
262
|
+
typedef enum { LZ4B_COMPRESSED, LZ4B_UNCOMPRESSED} LZ4F_BlockCompressMode_e;
|
|
263
|
+
typedef enum { ctxNone, ctxFast, ctxHC } LZ4F_CtxType_e;
|
|
264
|
+
|
|
265
|
+
typedef struct LZ4F_cctx_s
|
|
266
|
+
{
|
|
267
|
+
LZ4F_CustomMem cmem;
|
|
268
|
+
LZ4F_preferences_t prefs;
|
|
269
|
+
U32 version;
|
|
270
|
+
U32 cStage; /* 0 : compression uninitialized ; 1 : initialized, can compress */
|
|
271
|
+
const LZ4F_CDict* cdict;
|
|
272
|
+
size_t maxBlockSize;
|
|
273
|
+
size_t maxBufferSize;
|
|
274
|
+
BYTE* tmpBuff; /* internal buffer, for streaming */
|
|
275
|
+
BYTE* tmpIn; /* starting position of data compress within internal buffer (>= tmpBuff) */
|
|
276
|
+
size_t tmpInSize; /* amount of data to compress after tmpIn */
|
|
277
|
+
U64 totalInSize;
|
|
278
|
+
XXH32_state_t xxh;
|
|
279
|
+
void* lz4CtxPtr;
|
|
280
|
+
U16 lz4CtxAlloc; /* sized for: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */
|
|
281
|
+
U16 lz4CtxType; /* in use as: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */
|
|
282
|
+
LZ4F_BlockCompressMode_e blockCompressMode;
|
|
283
|
+
} LZ4F_cctx_t;
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
/*-************************************
|
|
287
|
+
* Error management
|
|
288
|
+
**************************************/
|
|
289
|
+
#define LZ4F_GENERATE_STRING(STRING) #STRING,
|
|
290
|
+
static const char* LZ4F_errorStrings[] = { LZ4F_LIST_ERRORS(LZ4F_GENERATE_STRING) };
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
unsigned LZ4F_isError(LZ4F_errorCode_t code)
|
|
294
|
+
{
|
|
295
|
+
return (code > (LZ4F_errorCode_t)(-LZ4F_ERROR_maxCode));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const char* LZ4F_getErrorName(LZ4F_errorCode_t code)
|
|
299
|
+
{
|
|
300
|
+
static const char* codeError = "Unspecified error code";
|
|
301
|
+
if (LZ4F_isError(code)) return LZ4F_errorStrings[-(int)(code)];
|
|
302
|
+
return codeError;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult)
|
|
306
|
+
{
|
|
307
|
+
if (!LZ4F_isError(functionResult)) return LZ4F_OK_NoError;
|
|
308
|
+
return (LZ4F_errorCodes)(-(ptrdiff_t)functionResult);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
static LZ4F_errorCode_t LZ4F_returnErrorCode(LZ4F_errorCodes code)
|
|
312
|
+
{
|
|
313
|
+
/* A compilation error here means sizeof(ptrdiff_t) is not large enough */
|
|
314
|
+
LZ4F_STATIC_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t));
|
|
315
|
+
return (LZ4F_errorCode_t)-(ptrdiff_t)code;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
#define RETURN_ERROR(e) return LZ4F_returnErrorCode(LZ4F_ERROR_ ## e)
|
|
319
|
+
|
|
320
|
+
#define RETURN_ERROR_IF(c,e) do { \
|
|
321
|
+
if (c) { \
|
|
322
|
+
DEBUGLOG(3, "Error: " #c); \
|
|
323
|
+
RETURN_ERROR(e); \
|
|
324
|
+
} \
|
|
325
|
+
} while (0)
|
|
326
|
+
|
|
327
|
+
#define FORWARD_IF_ERROR(r) do { if (LZ4F_isError(r)) return (r); } while (0)
|
|
328
|
+
|
|
329
|
+
unsigned LZ4F_getVersion(void) { return LZ4F_VERSION; }
|
|
330
|
+
|
|
331
|
+
int LZ4F_compressionLevel_max(void) { return LZ4HC_CLEVEL_MAX; }
|
|
332
|
+
|
|
333
|
+
size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID)
|
|
334
|
+
{
|
|
335
|
+
static const size_t blockSizes[4] = { 64 KB, 256 KB, 1 MB, 4 MB };
|
|
336
|
+
|
|
337
|
+
if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;
|
|
338
|
+
if (blockSizeID < LZ4F_max64KB || blockSizeID > LZ4F_max4MB)
|
|
339
|
+
RETURN_ERROR(maxBlockSize_invalid);
|
|
340
|
+
{ int const blockSizeIdx = (int)blockSizeID - (int)LZ4F_max64KB;
|
|
341
|
+
return blockSizes[blockSizeIdx];
|
|
342
|
+
} }
|
|
343
|
+
|
|
344
|
+
/*-************************************
|
|
345
|
+
* Private functions
|
|
346
|
+
**************************************/
|
|
347
|
+
#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
|
|
348
|
+
|
|
349
|
+
static BYTE LZ4F_headerChecksum (const void* header, size_t length)
|
|
350
|
+
{
|
|
351
|
+
U32 const xxh = XXH32(header, length, 0);
|
|
352
|
+
return (BYTE)(xxh >> 8);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
/*-************************************
|
|
357
|
+
* Simple-pass compression functions
|
|
358
|
+
**************************************/
|
|
359
|
+
static LZ4F_blockSizeID_t LZ4F_optimalBSID(const LZ4F_blockSizeID_t requestedBSID,
|
|
360
|
+
const size_t srcSize)
|
|
361
|
+
{
|
|
362
|
+
LZ4F_blockSizeID_t proposedBSID = LZ4F_max64KB;
|
|
363
|
+
size_t maxBlockSize = 64 KB;
|
|
364
|
+
while (requestedBSID > proposedBSID) {
|
|
365
|
+
if (srcSize <= maxBlockSize)
|
|
366
|
+
return proposedBSID;
|
|
367
|
+
proposedBSID = (LZ4F_blockSizeID_t)((int)proposedBSID + 1);
|
|
368
|
+
maxBlockSize <<= 2;
|
|
369
|
+
}
|
|
370
|
+
return requestedBSID;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/*! LZ4F_compressBound_internal() :
|
|
374
|
+
* Provides dstCapacity given a srcSize to guarantee operation success in worst case situations.
|
|
375
|
+
* prefsPtr is optional : if NULL is provided, preferences will be set to cover worst case scenario.
|
|
376
|
+
* @return is always the same for a srcSize and prefsPtr, so it can be relied upon to size reusable buffers.
|
|
377
|
+
* When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() operations.
|
|
378
|
+
*/
|
|
379
|
+
static size_t LZ4F_compressBound_internal(size_t srcSize,
|
|
380
|
+
const LZ4F_preferences_t* preferencesPtr,
|
|
381
|
+
size_t alreadyBuffered)
|
|
382
|
+
{
|
|
383
|
+
LZ4F_preferences_t prefsNull = LZ4F_INIT_PREFERENCES;
|
|
384
|
+
prefsNull.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled; /* worst case */
|
|
385
|
+
prefsNull.frameInfo.blockChecksumFlag = LZ4F_blockChecksumEnabled; /* worst case */
|
|
386
|
+
{ const LZ4F_preferences_t* const prefsPtr = (preferencesPtr==NULL) ? &prefsNull : preferencesPtr;
|
|
387
|
+
U32 const flush = prefsPtr->autoFlush | (srcSize==0);
|
|
388
|
+
LZ4F_blockSizeID_t const blockID = prefsPtr->frameInfo.blockSizeID;
|
|
389
|
+
size_t const blockSize = LZ4F_getBlockSize(blockID);
|
|
390
|
+
size_t const maxBuffered = blockSize - 1;
|
|
391
|
+
size_t const bufferedSize = MIN(alreadyBuffered, maxBuffered);
|
|
392
|
+
size_t const maxSrcSize = srcSize + bufferedSize;
|
|
393
|
+
unsigned const nbFullBlocks = (unsigned)(maxSrcSize / blockSize);
|
|
394
|
+
size_t const partialBlockSize = maxSrcSize & (blockSize-1);
|
|
395
|
+
size_t const lastBlockSize = flush ? partialBlockSize : 0;
|
|
396
|
+
unsigned const nbBlocks = nbFullBlocks + (lastBlockSize>0);
|
|
397
|
+
|
|
398
|
+
size_t const blockCRCSize = BFSize * prefsPtr->frameInfo.blockChecksumFlag;
|
|
399
|
+
size_t const frameEnd = BHSize + (prefsPtr->frameInfo.contentChecksumFlag*BFSize);
|
|
400
|
+
|
|
401
|
+
return ((BHSize + blockCRCSize) * nbBlocks) +
|
|
402
|
+
(blockSize * nbFullBlocks) + lastBlockSize + frameEnd;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
|
|
407
|
+
{
|
|
408
|
+
LZ4F_preferences_t prefs;
|
|
409
|
+
size_t const headerSize = maxFHSize; /* max header size, including optional fields */
|
|
410
|
+
|
|
411
|
+
if (preferencesPtr!=NULL) prefs = *preferencesPtr;
|
|
412
|
+
else MEM_INIT(&prefs, 0, sizeof(prefs));
|
|
413
|
+
prefs.autoFlush = 1;
|
|
414
|
+
|
|
415
|
+
return headerSize + LZ4F_compressBound_internal(srcSize, &prefs, 0);;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
/*! LZ4F_compressFrame_usingCDict() :
|
|
420
|
+
* Compress srcBuffer using a dictionary, in a single step.
|
|
421
|
+
* cdict can be NULL, in which case, no dictionary is used.
|
|
422
|
+
* dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).
|
|
423
|
+
* The LZ4F_preferences_t structure is optional : you may provide NULL as argument,
|
|
424
|
+
* however, it's the only way to provide a dictID, so it's not recommended.
|
|
425
|
+
* @return : number of bytes written into dstBuffer,
|
|
426
|
+
* or an error code if it fails (can be tested using LZ4F_isError())
|
|
427
|
+
*/
|
|
428
|
+
size_t LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx,
|
|
429
|
+
void* dstBuffer, size_t dstCapacity,
|
|
430
|
+
const void* srcBuffer, size_t srcSize,
|
|
431
|
+
const LZ4F_CDict* cdict,
|
|
432
|
+
const LZ4F_preferences_t* preferencesPtr)
|
|
433
|
+
{
|
|
434
|
+
LZ4F_preferences_t prefs;
|
|
435
|
+
LZ4F_compressOptions_t options;
|
|
436
|
+
BYTE* const dstStart = (BYTE*) dstBuffer;
|
|
437
|
+
BYTE* dstPtr = dstStart;
|
|
438
|
+
BYTE* const dstEnd = dstStart + dstCapacity;
|
|
439
|
+
|
|
440
|
+
DEBUGLOG(4, "LZ4F_compressFrame_usingCDict (srcSize=%u)", (unsigned)srcSize);
|
|
441
|
+
if (preferencesPtr!=NULL)
|
|
442
|
+
prefs = *preferencesPtr;
|
|
443
|
+
else
|
|
444
|
+
MEM_INIT(&prefs, 0, sizeof(prefs));
|
|
445
|
+
if (prefs.frameInfo.contentSize != 0)
|
|
446
|
+
prefs.frameInfo.contentSize = (U64)srcSize; /* auto-correct content size if selected (!=0) */
|
|
447
|
+
|
|
448
|
+
prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);
|
|
449
|
+
prefs.autoFlush = 1;
|
|
450
|
+
if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID))
|
|
451
|
+
prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* only one block => no need for inter-block link */
|
|
452
|
+
|
|
453
|
+
MEM_INIT(&options, 0, sizeof(options));
|
|
454
|
+
options.stableSrc = 1;
|
|
455
|
+
|
|
456
|
+
RETURN_ERROR_IF(dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs), dstMaxSize_tooSmall);
|
|
457
|
+
|
|
458
|
+
{ size_t const headerSize = LZ4F_compressBegin_usingCDict(cctx, dstBuffer, dstCapacity, cdict, &prefs); /* write header */
|
|
459
|
+
FORWARD_IF_ERROR(headerSize);
|
|
460
|
+
dstPtr += headerSize; /* header size */ }
|
|
461
|
+
|
|
462
|
+
assert(dstEnd >= dstPtr);
|
|
463
|
+
{ size_t const cSize = LZ4F_compressUpdate(cctx, dstPtr, (size_t)(dstEnd-dstPtr), srcBuffer, srcSize, &options);
|
|
464
|
+
FORWARD_IF_ERROR(cSize);
|
|
465
|
+
dstPtr += cSize; }
|
|
466
|
+
|
|
467
|
+
assert(dstEnd >= dstPtr);
|
|
468
|
+
{ size_t const tailSize = LZ4F_compressEnd(cctx, dstPtr, (size_t)(dstEnd-dstPtr), &options); /* flush last block, and generate suffix */
|
|
469
|
+
FORWARD_IF_ERROR(tailSize);
|
|
470
|
+
dstPtr += tailSize; }
|
|
471
|
+
|
|
472
|
+
assert(dstEnd >= dstStart);
|
|
473
|
+
return (size_t)(dstPtr - dstStart);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
/*! LZ4F_compressFrame() :
|
|
478
|
+
* Compress an entire srcBuffer into a valid LZ4 frame, in a single step.
|
|
479
|
+
* dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).
|
|
480
|
+
* The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default.
|
|
481
|
+
* @return : number of bytes written into dstBuffer.
|
|
482
|
+
* or an error code if it fails (can be tested using LZ4F_isError())
|
|
483
|
+
*/
|
|
484
|
+
size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity,
|
|
485
|
+
const void* srcBuffer, size_t srcSize,
|
|
486
|
+
const LZ4F_preferences_t* preferencesPtr)
|
|
487
|
+
{
|
|
488
|
+
size_t result;
|
|
489
|
+
#if (LZ4F_HEAPMODE)
|
|
490
|
+
LZ4F_cctx_t* cctxPtr;
|
|
491
|
+
result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION);
|
|
492
|
+
FORWARD_IF_ERROR(result);
|
|
493
|
+
#else
|
|
494
|
+
LZ4F_cctx_t cctx;
|
|
495
|
+
LZ4_stream_t lz4ctx;
|
|
496
|
+
LZ4F_cctx_t* const cctxPtr = &cctx;
|
|
497
|
+
|
|
498
|
+
MEM_INIT(&cctx, 0, sizeof(cctx));
|
|
499
|
+
cctx.version = LZ4F_VERSION;
|
|
500
|
+
cctx.maxBufferSize = 5 MB; /* mess with real buffer size to prevent dynamic allocation; works only because autoflush==1 & stableSrc==1 */
|
|
501
|
+
if ( preferencesPtr == NULL
|
|
502
|
+
|| preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN ) {
|
|
503
|
+
LZ4_initStream(&lz4ctx, sizeof(lz4ctx));
|
|
504
|
+
cctxPtr->lz4CtxPtr = &lz4ctx;
|
|
505
|
+
cctxPtr->lz4CtxAlloc = 1;
|
|
506
|
+
cctxPtr->lz4CtxType = ctxFast;
|
|
507
|
+
}
|
|
508
|
+
#endif
|
|
509
|
+
DEBUGLOG(4, "LZ4F_compressFrame");
|
|
510
|
+
|
|
511
|
+
result = LZ4F_compressFrame_usingCDict(cctxPtr, dstBuffer, dstCapacity,
|
|
512
|
+
srcBuffer, srcSize,
|
|
513
|
+
NULL, preferencesPtr);
|
|
514
|
+
|
|
515
|
+
#if (LZ4F_HEAPMODE)
|
|
516
|
+
LZ4F_freeCompressionContext(cctxPtr);
|
|
517
|
+
#else
|
|
518
|
+
if ( preferencesPtr != NULL
|
|
519
|
+
&& preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN ) {
|
|
520
|
+
LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);
|
|
521
|
+
}
|
|
522
|
+
#endif
|
|
523
|
+
return result;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
/*-***************************************************
|
|
528
|
+
* Dictionary compression
|
|
529
|
+
*****************************************************/
|
|
530
|
+
|
|
531
|
+
struct LZ4F_CDict_s {
|
|
532
|
+
LZ4F_CustomMem cmem;
|
|
533
|
+
void* dictContent;
|
|
534
|
+
LZ4_stream_t* fastCtx;
|
|
535
|
+
LZ4_streamHC_t* HCCtx;
|
|
536
|
+
}; /* typedef'd to LZ4F_CDict within lz4frame_static.h */
|
|
537
|
+
|
|
538
|
+
LZ4F_CDict*
|
|
539
|
+
LZ4F_createCDict_advanced(LZ4F_CustomMem cmem, const void* dictBuffer, size_t dictSize)
|
|
540
|
+
{
|
|
541
|
+
const char* dictStart = (const char*)dictBuffer;
|
|
542
|
+
LZ4F_CDict* const cdict = (LZ4F_CDict*)LZ4F_malloc(sizeof(*cdict), cmem);
|
|
543
|
+
DEBUGLOG(4, "LZ4F_createCDict_advanced");
|
|
544
|
+
if (!cdict) return NULL;
|
|
545
|
+
cdict->cmem = cmem;
|
|
546
|
+
if (dictSize > 64 KB) {
|
|
547
|
+
dictStart += dictSize - 64 KB;
|
|
548
|
+
dictSize = 64 KB;
|
|
549
|
+
}
|
|
550
|
+
cdict->dictContent = LZ4F_malloc(dictSize, cmem);
|
|
551
|
+
/* note: using @cmem to allocate => can't use default create */
|
|
552
|
+
cdict->fastCtx = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cmem);
|
|
553
|
+
cdict->HCCtx = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cmem);
|
|
554
|
+
if (!cdict->dictContent || !cdict->fastCtx || !cdict->HCCtx) {
|
|
555
|
+
LZ4F_freeCDict(cdict);
|
|
556
|
+
return NULL;
|
|
557
|
+
}
|
|
558
|
+
memcpy(cdict->dictContent, dictStart, dictSize);
|
|
559
|
+
LZ4_initStream(cdict->fastCtx, sizeof(LZ4_stream_t));
|
|
560
|
+
LZ4_loadDictSlow(cdict->fastCtx, (const char*)cdict->dictContent, (int)dictSize);
|
|
561
|
+
LZ4_initStreamHC(cdict->HCCtx, sizeof(LZ4_streamHC_t));
|
|
562
|
+
/* note: we don't know at this point which compression level is going to be used
|
|
563
|
+
* as a consequence, HCCtx is created for the more common HC mode */
|
|
564
|
+
LZ4_setCompressionLevel(cdict->HCCtx, LZ4HC_CLEVEL_DEFAULT);
|
|
565
|
+
LZ4_loadDictHC(cdict->HCCtx, (const char*)cdict->dictContent, (int)dictSize);
|
|
566
|
+
return cdict;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/*! LZ4F_createCDict() :
|
|
570
|
+
* When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once.
|
|
571
|
+
* LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.
|
|
572
|
+
* LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.
|
|
573
|
+
* @dictBuffer can be released after LZ4F_CDict creation, since its content is copied within CDict
|
|
574
|
+
* @return : digested dictionary for compression, or NULL if failed */
|
|
575
|
+
LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize)
|
|
576
|
+
{
|
|
577
|
+
DEBUGLOG(4, "LZ4F_createCDict");
|
|
578
|
+
return LZ4F_createCDict_advanced(LZ4F_defaultCMem, dictBuffer, dictSize);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
void LZ4F_freeCDict(LZ4F_CDict* cdict)
|
|
582
|
+
{
|
|
583
|
+
if (cdict==NULL) return; /* support free on NULL */
|
|
584
|
+
LZ4F_free(cdict->dictContent, cdict->cmem);
|
|
585
|
+
LZ4F_free(cdict->fastCtx, cdict->cmem);
|
|
586
|
+
LZ4F_free(cdict->HCCtx, cdict->cmem);
|
|
587
|
+
LZ4F_free(cdict, cdict->cmem);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
/*-*********************************
|
|
592
|
+
* Advanced compression functions
|
|
593
|
+
***********************************/
|
|
594
|
+
|
|
595
|
+
LZ4F_cctx*
|
|
596
|
+
LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)
|
|
597
|
+
{
|
|
598
|
+
LZ4F_cctx* const cctxPtr =
|
|
599
|
+
(LZ4F_cctx*)LZ4F_calloc(sizeof(LZ4F_cctx), customMem);
|
|
600
|
+
if (cctxPtr==NULL) return NULL;
|
|
601
|
+
|
|
602
|
+
cctxPtr->cmem = customMem;
|
|
603
|
+
cctxPtr->version = version;
|
|
604
|
+
cctxPtr->cStage = 0; /* Uninitialized. Next stage : init cctx */
|
|
605
|
+
|
|
606
|
+
return cctxPtr;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/*! LZ4F_createCompressionContext() :
|
|
610
|
+
* The first thing to do is to create a compressionContext object, which will be used in all compression operations.
|
|
611
|
+
* This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure.
|
|
612
|
+
* The version provided MUST be LZ4F_VERSION. It is intended to track potential incompatible differences between different binaries.
|
|
613
|
+
* The function will provide a pointer to an allocated LZ4F_compressionContext_t object.
|
|
614
|
+
* If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation.
|
|
615
|
+
* Object can release its memory using LZ4F_freeCompressionContext();
|
|
616
|
+
**/
|
|
617
|
+
LZ4F_errorCode_t
|
|
618
|
+
LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version)
|
|
619
|
+
{
|
|
620
|
+
assert(LZ4F_compressionContextPtr != NULL); /* considered a violation of narrow contract */
|
|
621
|
+
/* in case it nonetheless happen in production */
|
|
622
|
+
RETURN_ERROR_IF(LZ4F_compressionContextPtr == NULL, parameter_null);
|
|
623
|
+
|
|
624
|
+
*LZ4F_compressionContextPtr = LZ4F_createCompressionContext_advanced(LZ4F_defaultCMem, version);
|
|
625
|
+
RETURN_ERROR_IF(*LZ4F_compressionContextPtr==NULL, allocation_failed);
|
|
626
|
+
return LZ4F_OK_NoError;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr)
|
|
630
|
+
{
|
|
631
|
+
if (cctxPtr != NULL) { /* support free on NULL */
|
|
632
|
+
LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */
|
|
633
|
+
LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem);
|
|
634
|
+
LZ4F_free(cctxPtr, cctxPtr->cmem);
|
|
635
|
+
}
|
|
636
|
+
return LZ4F_OK_NoError;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* This function prepares the internal LZ4(HC) stream for a new compression,
|
|
642
|
+
* resetting the context and attaching the dictionary, if there is one.
|
|
643
|
+
*
|
|
644
|
+
* It needs to be called at the beginning of each independent compression
|
|
645
|
+
* stream (i.e., at the beginning of a frame in blockLinked mode, or at the
|
|
646
|
+
* beginning of each block in blockIndependent mode).
|
|
647
|
+
*/
|
|
648
|
+
static void LZ4F_initStream(void* ctx,
|
|
649
|
+
const LZ4F_CDict* cdict,
|
|
650
|
+
int level,
|
|
651
|
+
LZ4F_blockMode_t blockMode) {
|
|
652
|
+
if (level < LZ4HC_CLEVEL_MIN) {
|
|
653
|
+
if (cdict || blockMode == LZ4F_blockLinked) {
|
|
654
|
+
/* In these cases, we will call LZ4_compress_fast_continue(),
|
|
655
|
+
* which needs an already reset context. Otherwise, we'll call a
|
|
656
|
+
* one-shot API. The non-continued APIs internally perform their own
|
|
657
|
+
* resets at the beginning of their calls, where they know what
|
|
658
|
+
* tableType they need the context to be in. So in that case this
|
|
659
|
+
* would be misguided / wasted work. */
|
|
660
|
+
LZ4_resetStream_fast((LZ4_stream_t*)ctx);
|
|
661
|
+
if (cdict)
|
|
662
|
+
LZ4_attach_dictionary((LZ4_stream_t*)ctx, cdict->fastCtx);
|
|
663
|
+
}
|
|
664
|
+
/* In these cases, we'll call a one-shot API.
|
|
665
|
+
* The non-continued APIs internally perform their own resets
|
|
666
|
+
* at the beginning of their calls, where they know
|
|
667
|
+
* which tableType they need the context to be in.
|
|
668
|
+
* Therefore, a reset here would be wasted work. */
|
|
669
|
+
} else {
|
|
670
|
+
LZ4_resetStreamHC_fast((LZ4_streamHC_t*)ctx, level);
|
|
671
|
+
if (cdict)
|
|
672
|
+
LZ4_attach_HC_dictionary((LZ4_streamHC_t*)ctx, cdict->HCCtx);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
static int ctxTypeID_to_size(int ctxTypeID) {
|
|
677
|
+
switch(ctxTypeID) {
|
|
678
|
+
case 1:
|
|
679
|
+
return LZ4_sizeofState();
|
|
680
|
+
case 2:
|
|
681
|
+
return LZ4_sizeofStateHC();
|
|
682
|
+
default:
|
|
683
|
+
return 0;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/* LZ4F_compressBegin_internal()
|
|
688
|
+
* Note: only accepts @cdict _or_ @dictBuffer as non NULL.
|
|
689
|
+
*/
|
|
690
|
+
size_t LZ4F_compressBegin_internal(LZ4F_cctx* cctx,
|
|
691
|
+
void* dstBuffer, size_t dstCapacity,
|
|
692
|
+
const void* dictBuffer, size_t dictSize,
|
|
693
|
+
const LZ4F_CDict* cdict,
|
|
694
|
+
const LZ4F_preferences_t* preferencesPtr)
|
|
695
|
+
{
|
|
696
|
+
LZ4F_preferences_t const prefNull = LZ4F_INIT_PREFERENCES;
|
|
697
|
+
BYTE* const dstStart = (BYTE*)dstBuffer;
|
|
698
|
+
BYTE* dstPtr = dstStart;
|
|
699
|
+
|
|
700
|
+
RETURN_ERROR_IF(dstCapacity < maxFHSize, dstMaxSize_tooSmall);
|
|
701
|
+
if (preferencesPtr == NULL) preferencesPtr = &prefNull;
|
|
702
|
+
cctx->prefs = *preferencesPtr;
|
|
703
|
+
|
|
704
|
+
/* cctx Management */
|
|
705
|
+
{ U16 const ctxTypeID = (cctx->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) ? 1 : 2;
|
|
706
|
+
int requiredSize = ctxTypeID_to_size(ctxTypeID);
|
|
707
|
+
int allocatedSize = ctxTypeID_to_size(cctx->lz4CtxAlloc);
|
|
708
|
+
if (allocatedSize < requiredSize) {
|
|
709
|
+
/* not enough space allocated */
|
|
710
|
+
LZ4F_free(cctx->lz4CtxPtr, cctx->cmem);
|
|
711
|
+
if (cctx->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) {
|
|
712
|
+
/* must take ownership of memory allocation,
|
|
713
|
+
* in order to respect custom allocator contract */
|
|
714
|
+
cctx->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_stream_t), cctx->cmem);
|
|
715
|
+
if (cctx->lz4CtxPtr)
|
|
716
|
+
LZ4_initStream(cctx->lz4CtxPtr, sizeof(LZ4_stream_t));
|
|
717
|
+
} else {
|
|
718
|
+
cctx->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_streamHC_t), cctx->cmem);
|
|
719
|
+
if (cctx->lz4CtxPtr)
|
|
720
|
+
LZ4_initStreamHC(cctx->lz4CtxPtr, sizeof(LZ4_streamHC_t));
|
|
721
|
+
}
|
|
722
|
+
RETURN_ERROR_IF(cctx->lz4CtxPtr == NULL, allocation_failed);
|
|
723
|
+
cctx->lz4CtxAlloc = ctxTypeID;
|
|
724
|
+
cctx->lz4CtxType = ctxTypeID;
|
|
725
|
+
} else if (cctx->lz4CtxType != ctxTypeID) {
|
|
726
|
+
/* otherwise, a sufficient buffer is already allocated,
|
|
727
|
+
* but we need to reset it to the correct context type */
|
|
728
|
+
if (cctx->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) {
|
|
729
|
+
LZ4_initStream((LZ4_stream_t*)cctx->lz4CtxPtr, sizeof(LZ4_stream_t));
|
|
730
|
+
} else {
|
|
731
|
+
LZ4_initStreamHC((LZ4_streamHC_t*)cctx->lz4CtxPtr, sizeof(LZ4_streamHC_t));
|
|
732
|
+
LZ4_setCompressionLevel((LZ4_streamHC_t*)cctx->lz4CtxPtr, cctx->prefs.compressionLevel);
|
|
733
|
+
}
|
|
734
|
+
cctx->lz4CtxType = ctxTypeID;
|
|
735
|
+
} }
|
|
736
|
+
|
|
737
|
+
/* Buffer Management */
|
|
738
|
+
if (cctx->prefs.frameInfo.blockSizeID == 0)
|
|
739
|
+
cctx->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;
|
|
740
|
+
cctx->maxBlockSize = LZ4F_getBlockSize(cctx->prefs.frameInfo.blockSizeID);
|
|
741
|
+
|
|
742
|
+
{ size_t const requiredBuffSize = preferencesPtr->autoFlush ?
|
|
743
|
+
((cctx->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 64 KB : 0) : /* only needs past data up to window size */
|
|
744
|
+
cctx->maxBlockSize + ((cctx->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 128 KB : 0);
|
|
745
|
+
|
|
746
|
+
if (cctx->maxBufferSize < requiredBuffSize) {
|
|
747
|
+
cctx->maxBufferSize = 0;
|
|
748
|
+
LZ4F_free(cctx->tmpBuff, cctx->cmem);
|
|
749
|
+
cctx->tmpBuff = (BYTE*)LZ4F_malloc(requiredBuffSize, cctx->cmem);
|
|
750
|
+
RETURN_ERROR_IF(cctx->tmpBuff == NULL, allocation_failed);
|
|
751
|
+
cctx->maxBufferSize = requiredBuffSize;
|
|
752
|
+
} }
|
|
753
|
+
cctx->tmpIn = cctx->tmpBuff;
|
|
754
|
+
cctx->tmpInSize = 0;
|
|
755
|
+
(void)XXH32_reset(&(cctx->xxh), 0);
|
|
756
|
+
|
|
757
|
+
/* context init */
|
|
758
|
+
cctx->cdict = cdict;
|
|
759
|
+
if (cctx->prefs.frameInfo.blockMode == LZ4F_blockLinked) {
|
|
760
|
+
/* frame init only for blockLinked : blockIndependent will be init at each block */
|
|
761
|
+
LZ4F_initStream(cctx->lz4CtxPtr, cdict, cctx->prefs.compressionLevel, LZ4F_blockLinked);
|
|
762
|
+
}
|
|
763
|
+
if (preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN) {
|
|
764
|
+
LZ4_favorDecompressionSpeed((LZ4_streamHC_t*)cctx->lz4CtxPtr, (int)preferencesPtr->favorDecSpeed);
|
|
765
|
+
}
|
|
766
|
+
if (dictBuffer) {
|
|
767
|
+
assert(cdict == NULL);
|
|
768
|
+
RETURN_ERROR_IF(dictSize > INT_MAX, parameter_invalid);
|
|
769
|
+
if (cctx->lz4CtxType == ctxFast) {
|
|
770
|
+
/* lz4 fast*/
|
|
771
|
+
LZ4_loadDict((LZ4_stream_t*)cctx->lz4CtxPtr, (const char*)dictBuffer, (int)dictSize);
|
|
772
|
+
} else {
|
|
773
|
+
/* lz4hc */
|
|
774
|
+
assert(cctx->lz4CtxType == ctxHC);
|
|
775
|
+
LZ4_loadDictHC((LZ4_streamHC_t*)cctx->lz4CtxPtr, (const char*)dictBuffer, (int)dictSize);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/* Stage 2 : Write Frame Header */
|
|
780
|
+
|
|
781
|
+
/* Magic Number */
|
|
782
|
+
LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER);
|
|
783
|
+
dstPtr += 4;
|
|
784
|
+
{ BYTE* const headerStart = dstPtr;
|
|
785
|
+
|
|
786
|
+
/* FLG Byte */
|
|
787
|
+
*dstPtr++ = (BYTE)(((1 & _2BITS) << 6) /* Version('01') */
|
|
788
|
+
+ ((cctx->prefs.frameInfo.blockMode & _1BIT ) << 5)
|
|
789
|
+
+ ((cctx->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4)
|
|
790
|
+
+ ((unsigned)(cctx->prefs.frameInfo.contentSize > 0) << 3)
|
|
791
|
+
+ ((cctx->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2)
|
|
792
|
+
+ (cctx->prefs.frameInfo.dictID > 0) );
|
|
793
|
+
/* BD Byte */
|
|
794
|
+
*dstPtr++ = (BYTE)((cctx->prefs.frameInfo.blockSizeID & _3BITS) << 4);
|
|
795
|
+
/* Optional Frame content size field */
|
|
796
|
+
if (cctx->prefs.frameInfo.contentSize) {
|
|
797
|
+
LZ4F_writeLE64(dstPtr, cctx->prefs.frameInfo.contentSize);
|
|
798
|
+
dstPtr += 8;
|
|
799
|
+
cctx->totalInSize = 0;
|
|
800
|
+
}
|
|
801
|
+
/* Optional dictionary ID field */
|
|
802
|
+
if (cctx->prefs.frameInfo.dictID) {
|
|
803
|
+
LZ4F_writeLE32(dstPtr, cctx->prefs.frameInfo.dictID);
|
|
804
|
+
dstPtr += 4;
|
|
805
|
+
}
|
|
806
|
+
/* Header CRC Byte */
|
|
807
|
+
*dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart));
|
|
808
|
+
dstPtr++;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
cctx->cStage = 1; /* header written, now request input data block */
|
|
812
|
+
return (size_t)(dstPtr - dstStart);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
size_t LZ4F_compressBegin(LZ4F_cctx* cctx,
|
|
816
|
+
void* dstBuffer, size_t dstCapacity,
|
|
817
|
+
const LZ4F_preferences_t* preferencesPtr)
|
|
818
|
+
{
|
|
819
|
+
return LZ4F_compressBegin_internal(cctx, dstBuffer, dstCapacity,
|
|
820
|
+
NULL, 0,
|
|
821
|
+
NULL, preferencesPtr);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/* LZ4F_compressBegin_usingDictOnce:
|
|
825
|
+
* Hidden implementation,
|
|
826
|
+
* employed for multi-threaded compression
|
|
827
|
+
* when frame defines linked blocks */
|
|
828
|
+
size_t LZ4F_compressBegin_usingDictOnce(LZ4F_cctx* cctx,
|
|
829
|
+
void* dstBuffer, size_t dstCapacity,
|
|
830
|
+
const void* dict, size_t dictSize,
|
|
831
|
+
const LZ4F_preferences_t* preferencesPtr)
|
|
832
|
+
{
|
|
833
|
+
return LZ4F_compressBegin_internal(cctx, dstBuffer, dstCapacity,
|
|
834
|
+
dict, dictSize,
|
|
835
|
+
NULL, preferencesPtr);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
size_t LZ4F_compressBegin_usingDict(LZ4F_cctx* cctx,
|
|
839
|
+
void* dstBuffer, size_t dstCapacity,
|
|
840
|
+
const void* dict, size_t dictSize,
|
|
841
|
+
const LZ4F_preferences_t* preferencesPtr)
|
|
842
|
+
{
|
|
843
|
+
/* note : incorrect implementation :
|
|
844
|
+
* this will only use the dictionary once,
|
|
845
|
+
* instead of once *per* block when frames defines independent blocks */
|
|
846
|
+
return LZ4F_compressBegin_usingDictOnce(cctx, dstBuffer, dstCapacity,
|
|
847
|
+
dict, dictSize,
|
|
848
|
+
preferencesPtr);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctx,
|
|
852
|
+
void* dstBuffer, size_t dstCapacity,
|
|
853
|
+
const LZ4F_CDict* cdict,
|
|
854
|
+
const LZ4F_preferences_t* preferencesPtr)
|
|
855
|
+
{
|
|
856
|
+
return LZ4F_compressBegin_internal(cctx, dstBuffer, dstCapacity,
|
|
857
|
+
NULL, 0,
|
|
858
|
+
cdict, preferencesPtr);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
/* LZ4F_compressBound() :
|
|
863
|
+
* @return minimum capacity of dstBuffer for a given srcSize to handle worst case scenario.
|
|
864
|
+
* LZ4F_preferences_t structure is optional : if NULL, preferences will be set to cover worst case scenario.
|
|
865
|
+
* This function cannot fail.
|
|
866
|
+
*/
|
|
867
|
+
size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
|
|
868
|
+
{
|
|
869
|
+
if (preferencesPtr && preferencesPtr->autoFlush) {
|
|
870
|
+
return LZ4F_compressBound_internal(srcSize, preferencesPtr, 0);
|
|
871
|
+
}
|
|
872
|
+
return LZ4F_compressBound_internal(srcSize, preferencesPtr, (size_t)-1);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
typedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level, const LZ4F_CDict* cdict);
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
/*! LZ4F_makeBlock():
|
|
880
|
+
* compress a single block, add header and optional checksum.
|
|
881
|
+
* assumption : dst buffer capacity is >= BHSize + srcSize + crcSize
|
|
882
|
+
*/
|
|
883
|
+
static size_t LZ4F_makeBlock(void* dst,
|
|
884
|
+
const void* src, size_t srcSize,
|
|
885
|
+
compressFunc_t compress, void* lz4ctx, int level,
|
|
886
|
+
const LZ4F_CDict* cdict,
|
|
887
|
+
LZ4F_blockChecksum_t crcFlag)
|
|
888
|
+
{
|
|
889
|
+
BYTE* const cSizePtr = (BYTE*)dst;
|
|
890
|
+
U32 cSize;
|
|
891
|
+
assert(compress != NULL);
|
|
892
|
+
cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize),
|
|
893
|
+
(int)(srcSize), (int)(srcSize-1),
|
|
894
|
+
level, cdict);
|
|
895
|
+
|
|
896
|
+
if (cSize == 0 || cSize >= srcSize) {
|
|
897
|
+
cSize = (U32)srcSize;
|
|
898
|
+
LZ4F_writeLE32(cSizePtr, cSize | LZ4F_BLOCKUNCOMPRESSED_FLAG);
|
|
899
|
+
memcpy(cSizePtr+BHSize, src, srcSize);
|
|
900
|
+
} else {
|
|
901
|
+
LZ4F_writeLE32(cSizePtr, cSize);
|
|
902
|
+
}
|
|
903
|
+
if (crcFlag) {
|
|
904
|
+
U32 const crc32 = XXH32(cSizePtr+BHSize, cSize, 0); /* checksum of compressed data */
|
|
905
|
+
LZ4F_writeLE32(cSizePtr+BHSize+cSize, crc32);
|
|
906
|
+
}
|
|
907
|
+
return BHSize + cSize + ((U32)crcFlag)*BFSize;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
static int LZ4F_compressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
|
|
912
|
+
{
|
|
913
|
+
int const acceleration = (level < 0) ? -level + 1 : 1;
|
|
914
|
+
DEBUGLOG(5, "LZ4F_compressBlock (srcSize=%i)", srcSize);
|
|
915
|
+
LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);
|
|
916
|
+
if (cdict) {
|
|
917
|
+
return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration);
|
|
918
|
+
} else {
|
|
919
|
+
return LZ4_compress_fast_extState_fastReset(ctx, src, dst, srcSize, dstCapacity, acceleration);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
static int LZ4F_compressBlock_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
|
|
924
|
+
{
|
|
925
|
+
int const acceleration = (level < 0) ? -level + 1 : 1;
|
|
926
|
+
(void)cdict; /* init once at beginning of frame */
|
|
927
|
+
DEBUGLOG(5, "LZ4F_compressBlock_continue (srcSize=%i)", srcSize);
|
|
928
|
+
return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
static int LZ4F_compressBlockHC(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
|
|
932
|
+
{
|
|
933
|
+
LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);
|
|
934
|
+
if (cdict) {
|
|
935
|
+
return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);
|
|
936
|
+
}
|
|
937
|
+
return LZ4_compress_HC_extStateHC_fastReset(ctx, src, dst, srcSize, dstCapacity, level);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
static int LZ4F_compressBlockHC_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
|
|
941
|
+
{
|
|
942
|
+
(void)level; (void)cdict; /* init once at beginning of frame */
|
|
943
|
+
return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
static int LZ4F_doNotCompressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
|
|
947
|
+
{
|
|
948
|
+
(void)ctx; (void)src; (void)dst; (void)srcSize; (void)dstCapacity; (void)level; (void)cdict;
|
|
949
|
+
return 0;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level, LZ4F_BlockCompressMode_e compressMode)
|
|
953
|
+
{
|
|
954
|
+
if (compressMode == LZ4B_UNCOMPRESSED)
|
|
955
|
+
return LZ4F_doNotCompressBlock;
|
|
956
|
+
if (level < LZ4HC_CLEVEL_MIN) {
|
|
957
|
+
if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlock;
|
|
958
|
+
return LZ4F_compressBlock_continue;
|
|
959
|
+
}
|
|
960
|
+
if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlockHC;
|
|
961
|
+
return LZ4F_compressBlockHC_continue;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/* Save history (up to 64KB) into @tmpBuff */
|
|
965
|
+
static int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr)
|
|
966
|
+
{
|
|
967
|
+
if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN)
|
|
968
|
+
return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);
|
|
969
|
+
return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus;
|
|
973
|
+
|
|
974
|
+
static const LZ4F_compressOptions_t k_cOptionsNull = { 0, { 0, 0, 0 } };
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
/*! LZ4F_compressUpdateImpl() :
|
|
978
|
+
* LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
|
|
979
|
+
* When successful, the function always entirely consumes @srcBuffer.
|
|
980
|
+
* src data is either buffered or compressed into @dstBuffer.
|
|
981
|
+
* If the block compression does not match the compression of the previous block, the old data is flushed
|
|
982
|
+
* and operations continue with the new compression mode.
|
|
983
|
+
* @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr) when block compression is turned on.
|
|
984
|
+
* @compressOptionsPtr is optional : provide NULL to mean "default".
|
|
985
|
+
* @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.
|
|
986
|
+
* or an error code if it fails (which can be tested using LZ4F_isError())
|
|
987
|
+
* After an error, the state is left in a UB state, and must be re-initialized.
|
|
988
|
+
*/
|
|
989
|
+
static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr,
|
|
990
|
+
void* dstBuffer, size_t dstCapacity,
|
|
991
|
+
const void* srcBuffer, size_t srcSize,
|
|
992
|
+
const LZ4F_compressOptions_t* compressOptionsPtr,
|
|
993
|
+
LZ4F_BlockCompressMode_e blockCompression)
|
|
994
|
+
{
|
|
995
|
+
size_t const blockSize = cctxPtr->maxBlockSize;
|
|
996
|
+
const BYTE* srcPtr = (const BYTE*)srcBuffer;
|
|
997
|
+
const BYTE* const srcEnd = srcPtr + srcSize;
|
|
998
|
+
BYTE* const dstStart = (BYTE*)dstBuffer;
|
|
999
|
+
BYTE* dstPtr = dstStart;
|
|
1000
|
+
LZ4F_lastBlockStatus lastBlockCompressed = notDone;
|
|
1001
|
+
compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, blockCompression);
|
|
1002
|
+
size_t bytesWritten;
|
|
1003
|
+
DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize);
|
|
1004
|
+
|
|
1005
|
+
RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized); /* state must be initialized and waiting for next block */
|
|
1006
|
+
if (dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize))
|
|
1007
|
+
RETURN_ERROR(dstMaxSize_tooSmall);
|
|
1008
|
+
|
|
1009
|
+
if (blockCompression == LZ4B_UNCOMPRESSED && dstCapacity < srcSize)
|
|
1010
|
+
RETURN_ERROR(dstMaxSize_tooSmall);
|
|
1011
|
+
|
|
1012
|
+
/* flush currently written block, to continue with new block compression */
|
|
1013
|
+
if (cctxPtr->blockCompressMode != blockCompression) {
|
|
1014
|
+
bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);
|
|
1015
|
+
dstPtr += bytesWritten;
|
|
1016
|
+
cctxPtr->blockCompressMode = blockCompression;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (compressOptionsPtr == NULL) compressOptionsPtr = &k_cOptionsNull;
|
|
1020
|
+
|
|
1021
|
+
/* complete tmp buffer */
|
|
1022
|
+
if (cctxPtr->tmpInSize > 0) { /* some data already within tmp buffer */
|
|
1023
|
+
size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize;
|
|
1024
|
+
assert(blockSize > cctxPtr->tmpInSize);
|
|
1025
|
+
if (sizeToCopy > srcSize) {
|
|
1026
|
+
/* add src to tmpIn buffer */
|
|
1027
|
+
memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize);
|
|
1028
|
+
srcPtr = srcEnd;
|
|
1029
|
+
cctxPtr->tmpInSize += srcSize;
|
|
1030
|
+
/* still needs some CRC */
|
|
1031
|
+
} else {
|
|
1032
|
+
/* complete tmpIn block and then compress it */
|
|
1033
|
+
lastBlockCompressed = fromTmpBuffer;
|
|
1034
|
+
memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy);
|
|
1035
|
+
srcPtr += sizeToCopy;
|
|
1036
|
+
|
|
1037
|
+
dstPtr += LZ4F_makeBlock(dstPtr,
|
|
1038
|
+
cctxPtr->tmpIn, blockSize,
|
|
1039
|
+
compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
|
|
1040
|
+
cctxPtr->cdict,
|
|
1041
|
+
cctxPtr->prefs.frameInfo.blockChecksumFlag);
|
|
1042
|
+
if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize;
|
|
1043
|
+
cctxPtr->tmpInSize = 0;
|
|
1044
|
+
} }
|
|
1045
|
+
|
|
1046
|
+
while ((size_t)(srcEnd - srcPtr) >= blockSize) {
|
|
1047
|
+
/* compress full blocks */
|
|
1048
|
+
lastBlockCompressed = fromSrcBuffer;
|
|
1049
|
+
dstPtr += LZ4F_makeBlock(dstPtr,
|
|
1050
|
+
srcPtr, blockSize,
|
|
1051
|
+
compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
|
|
1052
|
+
cctxPtr->cdict,
|
|
1053
|
+
cctxPtr->prefs.frameInfo.blockChecksumFlag);
|
|
1054
|
+
srcPtr += blockSize;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) {
|
|
1058
|
+
/* autoFlush : remaining input (< blockSize) is compressed */
|
|
1059
|
+
lastBlockCompressed = fromSrcBuffer;
|
|
1060
|
+
dstPtr += LZ4F_makeBlock(dstPtr,
|
|
1061
|
+
srcPtr, (size_t)(srcEnd - srcPtr),
|
|
1062
|
+
compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
|
|
1063
|
+
cctxPtr->cdict,
|
|
1064
|
+
cctxPtr->prefs.frameInfo.blockChecksumFlag);
|
|
1065
|
+
srcPtr = srcEnd;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/* preserve dictionary within @tmpBuff whenever necessary */
|
|
1069
|
+
if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) {
|
|
1070
|
+
/* linked blocks are only supported in compressed mode, see LZ4F_uncompressedUpdate */
|
|
1071
|
+
assert(blockCompression == LZ4B_COMPRESSED);
|
|
1072
|
+
if (compressOptionsPtr->stableSrc) {
|
|
1073
|
+
cctxPtr->tmpIn = cctxPtr->tmpBuff; /* src is stable : dictionary remains in src across invocations */
|
|
1074
|
+
} else {
|
|
1075
|
+
int const realDictSize = LZ4F_localSaveDict(cctxPtr);
|
|
1076
|
+
assert(0 <= realDictSize && realDictSize <= 64 KB);
|
|
1077
|
+
cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/* keep tmpIn within limits */
|
|
1082
|
+
if (!(cctxPtr->prefs.autoFlush) /* no autoflush : there may be some data left within internal buffer */
|
|
1083
|
+
&& (cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) ) /* not enough room to store next block */
|
|
1084
|
+
{
|
|
1085
|
+
/* only preserve 64KB within internal buffer. Ensures there is enough room for next block.
|
|
1086
|
+
* note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */
|
|
1087
|
+
int const realDictSize = LZ4F_localSaveDict(cctxPtr);
|
|
1088
|
+
cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
|
|
1089
|
+
assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->maxBufferSize));
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/* some input data left, necessarily < blockSize */
|
|
1093
|
+
if (srcPtr < srcEnd) {
|
|
1094
|
+
/* fill tmp buffer */
|
|
1095
|
+
size_t const sizeToCopy = (size_t)(srcEnd - srcPtr);
|
|
1096
|
+
memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy);
|
|
1097
|
+
cctxPtr->tmpInSize = sizeToCopy;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled)
|
|
1101
|
+
(void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize);
|
|
1102
|
+
|
|
1103
|
+
cctxPtr->totalInSize += srcSize;
|
|
1104
|
+
return (size_t)(dstPtr - dstStart);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/*! LZ4F_compressUpdate() :
|
|
1108
|
+
* LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
|
|
1109
|
+
* When successful, the function always entirely consumes @srcBuffer.
|
|
1110
|
+
* src data is either buffered or compressed into @dstBuffer.
|
|
1111
|
+
* If previously an uncompressed block was written, buffered data is flushed
|
|
1112
|
+
* before appending compressed data is continued.
|
|
1113
|
+
* @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr).
|
|
1114
|
+
* @compressOptionsPtr is optional : provide NULL to mean "default".
|
|
1115
|
+
* @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.
|
|
1116
|
+
* or an error code if it fails (which can be tested using LZ4F_isError())
|
|
1117
|
+
* After an error, the state is left in a UB state, and must be re-initialized.
|
|
1118
|
+
*/
|
|
1119
|
+
size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr,
|
|
1120
|
+
void* dstBuffer, size_t dstCapacity,
|
|
1121
|
+
const void* srcBuffer, size_t srcSize,
|
|
1122
|
+
const LZ4F_compressOptions_t* compressOptionsPtr)
|
|
1123
|
+
{
|
|
1124
|
+
return LZ4F_compressUpdateImpl(cctxPtr,
|
|
1125
|
+
dstBuffer, dstCapacity,
|
|
1126
|
+
srcBuffer, srcSize,
|
|
1127
|
+
compressOptionsPtr, LZ4B_COMPRESSED);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/*! LZ4F_uncompressedUpdate() :
|
|
1131
|
+
* Same as LZ4F_compressUpdate(), but requests blocks to be sent uncompressed.
|
|
1132
|
+
* This symbol is only supported when LZ4F_blockIndependent is used
|
|
1133
|
+
* @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr).
|
|
1134
|
+
* @compressOptionsPtr is optional : provide NULL to mean "default".
|
|
1135
|
+
* @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.
|
|
1136
|
+
* or an error code if it fails (which can be tested using LZ4F_isError())
|
|
1137
|
+
* After an error, the state is left in a UB state, and must be re-initialized.
|
|
1138
|
+
*/
|
|
1139
|
+
size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr,
|
|
1140
|
+
void* dstBuffer, size_t dstCapacity,
|
|
1141
|
+
const void* srcBuffer, size_t srcSize,
|
|
1142
|
+
const LZ4F_compressOptions_t* compressOptionsPtr)
|
|
1143
|
+
{
|
|
1144
|
+
return LZ4F_compressUpdateImpl(cctxPtr,
|
|
1145
|
+
dstBuffer, dstCapacity,
|
|
1146
|
+
srcBuffer, srcSize,
|
|
1147
|
+
compressOptionsPtr, LZ4B_UNCOMPRESSED);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
/*! LZ4F_flush() :
|
|
1152
|
+
* When compressed data must be sent immediately, without waiting for a block to be filled,
|
|
1153
|
+
* invoke LZ4_flush(), which will immediately compress any remaining data stored within LZ4F_cctx.
|
|
1154
|
+
* The result of the function is the number of bytes written into dstBuffer.
|
|
1155
|
+
* It can be zero, this means there was no data left within LZ4F_cctx.
|
|
1156
|
+
* The function outputs an error code if it fails (can be tested using LZ4F_isError())
|
|
1157
|
+
* LZ4F_compressOptions_t* is optional. NULL is a valid argument.
|
|
1158
|
+
*/
|
|
1159
|
+
size_t LZ4F_flush(LZ4F_cctx* cctxPtr,
|
|
1160
|
+
void* dstBuffer, size_t dstCapacity,
|
|
1161
|
+
const LZ4F_compressOptions_t* compressOptionsPtr)
|
|
1162
|
+
{
|
|
1163
|
+
BYTE* const dstStart = (BYTE*)dstBuffer;
|
|
1164
|
+
BYTE* dstPtr = dstStart;
|
|
1165
|
+
compressFunc_t compress;
|
|
1166
|
+
|
|
1167
|
+
if (cctxPtr->tmpInSize == 0) return 0; /* nothing to flush */
|
|
1168
|
+
RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized);
|
|
1169
|
+
RETURN_ERROR_IF(dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize), dstMaxSize_tooSmall);
|
|
1170
|
+
(void)compressOptionsPtr; /* not useful (yet) */
|
|
1171
|
+
|
|
1172
|
+
/* select compression function */
|
|
1173
|
+
compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, cctxPtr->blockCompressMode);
|
|
1174
|
+
|
|
1175
|
+
/* compress tmp buffer */
|
|
1176
|
+
dstPtr += LZ4F_makeBlock(dstPtr,
|
|
1177
|
+
cctxPtr->tmpIn, cctxPtr->tmpInSize,
|
|
1178
|
+
compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
|
|
1179
|
+
cctxPtr->cdict,
|
|
1180
|
+
cctxPtr->prefs.frameInfo.blockChecksumFlag);
|
|
1181
|
+
assert(((void)"flush overflows dstBuffer!", (size_t)(dstPtr - dstStart) <= dstCapacity));
|
|
1182
|
+
|
|
1183
|
+
if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked)
|
|
1184
|
+
cctxPtr->tmpIn += cctxPtr->tmpInSize;
|
|
1185
|
+
cctxPtr->tmpInSize = 0;
|
|
1186
|
+
|
|
1187
|
+
/* keep tmpIn within limits */
|
|
1188
|
+
if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)) { /* necessarily LZ4F_blockLinked */
|
|
1189
|
+
int const realDictSize = LZ4F_localSaveDict(cctxPtr);
|
|
1190
|
+
cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
return (size_t)(dstPtr - dstStart);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
/*! LZ4F_compressEnd() :
|
|
1198
|
+
* When you want to properly finish the compressed frame, just call LZ4F_compressEnd().
|
|
1199
|
+
* It will flush whatever data remained within compressionContext (like LZ4_flush())
|
|
1200
|
+
* but also properly finalize the frame, with an endMark and an (optional) checksum.
|
|
1201
|
+
* LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
|
|
1202
|
+
* @return: the number of bytes written into dstBuffer (necessarily >= 4 (endMark size))
|
|
1203
|
+
* or an error code if it fails (can be tested using LZ4F_isError())
|
|
1204
|
+
* The context can then be used again to compress a new frame, starting with LZ4F_compressBegin().
|
|
1205
|
+
*/
|
|
1206
|
+
size_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr,
|
|
1207
|
+
void* dstBuffer, size_t dstCapacity,
|
|
1208
|
+
const LZ4F_compressOptions_t* compressOptionsPtr)
|
|
1209
|
+
{
|
|
1210
|
+
BYTE* const dstStart = (BYTE*)dstBuffer;
|
|
1211
|
+
BYTE* dstPtr = dstStart;
|
|
1212
|
+
|
|
1213
|
+
size_t const flushSize = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);
|
|
1214
|
+
DEBUGLOG(5,"LZ4F_compressEnd: dstCapacity=%u", (unsigned)dstCapacity);
|
|
1215
|
+
FORWARD_IF_ERROR(flushSize);
|
|
1216
|
+
dstPtr += flushSize;
|
|
1217
|
+
|
|
1218
|
+
assert(flushSize <= dstCapacity);
|
|
1219
|
+
dstCapacity -= flushSize;
|
|
1220
|
+
|
|
1221
|
+
RETURN_ERROR_IF(dstCapacity < 4, dstMaxSize_tooSmall);
|
|
1222
|
+
LZ4F_writeLE32(dstPtr, 0);
|
|
1223
|
+
dstPtr += 4; /* endMark */
|
|
1224
|
+
|
|
1225
|
+
if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) {
|
|
1226
|
+
U32 const xxh = XXH32_digest(&(cctxPtr->xxh));
|
|
1227
|
+
RETURN_ERROR_IF(dstCapacity < 8, dstMaxSize_tooSmall);
|
|
1228
|
+
DEBUGLOG(5,"Writing 32-bit content checksum (0x%0X)", xxh);
|
|
1229
|
+
LZ4F_writeLE32(dstPtr, xxh);
|
|
1230
|
+
dstPtr+=4; /* content Checksum */
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
cctxPtr->cStage = 0; /* state is now re-usable (with identical preferences) */
|
|
1234
|
+
|
|
1235
|
+
if (cctxPtr->prefs.frameInfo.contentSize) {
|
|
1236
|
+
if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize)
|
|
1237
|
+
RETURN_ERROR(frameSize_wrong);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
return (size_t)(dstPtr - dstStart);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
/*-***************************************************
|
|
1245
|
+
* Frame Decompression
|
|
1246
|
+
*****************************************************/
|
|
1247
|
+
|
|
1248
|
+
typedef enum {
|
|
1249
|
+
dstage_getFrameHeader=0, dstage_storeFrameHeader,
|
|
1250
|
+
dstage_init,
|
|
1251
|
+
dstage_getBlockHeader, dstage_storeBlockHeader,
|
|
1252
|
+
dstage_copyDirect, dstage_getBlockChecksum,
|
|
1253
|
+
dstage_getCBlock, dstage_storeCBlock,
|
|
1254
|
+
dstage_flushOut,
|
|
1255
|
+
dstage_getSuffix, dstage_storeSuffix,
|
|
1256
|
+
dstage_getSFrameSize, dstage_storeSFrameSize,
|
|
1257
|
+
dstage_skipSkippable
|
|
1258
|
+
} dStage_t;
|
|
1259
|
+
|
|
1260
|
+
struct LZ4F_dctx_s {
|
|
1261
|
+
LZ4F_CustomMem cmem;
|
|
1262
|
+
LZ4F_frameInfo_t frameInfo;
|
|
1263
|
+
U32 version;
|
|
1264
|
+
dStage_t dStage;
|
|
1265
|
+
U64 frameRemainingSize;
|
|
1266
|
+
size_t maxBlockSize;
|
|
1267
|
+
size_t maxBufferSize;
|
|
1268
|
+
BYTE* tmpIn;
|
|
1269
|
+
size_t tmpInSize;
|
|
1270
|
+
size_t tmpInTarget;
|
|
1271
|
+
BYTE* tmpOutBuffer;
|
|
1272
|
+
const BYTE* dict;
|
|
1273
|
+
size_t dictSize;
|
|
1274
|
+
BYTE* tmpOut;
|
|
1275
|
+
size_t tmpOutSize;
|
|
1276
|
+
size_t tmpOutStart;
|
|
1277
|
+
XXH32_state_t xxh;
|
|
1278
|
+
XXH32_state_t blockChecksum;
|
|
1279
|
+
int skipChecksum;
|
|
1280
|
+
BYTE header[LZ4F_HEADER_SIZE_MAX];
|
|
1281
|
+
}; /* typedef'd to LZ4F_dctx in lz4frame.h */
|
|
1282
|
+
|
|
1283
|
+
|
|
1284
|
+
LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)
|
|
1285
|
+
{
|
|
1286
|
+
LZ4F_dctx* const dctx = (LZ4F_dctx*)LZ4F_calloc(sizeof(LZ4F_dctx), customMem);
|
|
1287
|
+
if (dctx == NULL) return NULL;
|
|
1288
|
+
|
|
1289
|
+
dctx->cmem = customMem;
|
|
1290
|
+
dctx->version = version;
|
|
1291
|
+
return dctx;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/*! LZ4F_createDecompressionContext() :
|
|
1295
|
+
* Create a decompressionContext object, which will track all decompression operations.
|
|
1296
|
+
* Provides a pointer to a fully allocated and initialized LZ4F_decompressionContext object.
|
|
1297
|
+
* Object can later be released using LZ4F_freeDecompressionContext().
|
|
1298
|
+
* @return : if != 0, there was an error during context creation.
|
|
1299
|
+
*/
|
|
1300
|
+
LZ4F_errorCode_t
|
|
1301
|
+
LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber)
|
|
1302
|
+
{
|
|
1303
|
+
assert(LZ4F_decompressionContextPtr != NULL); /* violation of narrow contract */
|
|
1304
|
+
RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null); /* in case it nonetheless happen in production */
|
|
1305
|
+
|
|
1306
|
+
*LZ4F_decompressionContextPtr = LZ4F_createDecompressionContext_advanced(LZ4F_defaultCMem, versionNumber);
|
|
1307
|
+
if (*LZ4F_decompressionContextPtr == NULL) { /* failed allocation */
|
|
1308
|
+
RETURN_ERROR(allocation_failed);
|
|
1309
|
+
}
|
|
1310
|
+
return LZ4F_OK_NoError;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx)
|
|
1314
|
+
{
|
|
1315
|
+
LZ4F_errorCode_t result = LZ4F_OK_NoError;
|
|
1316
|
+
if (dctx != NULL) { /* can accept NULL input, like free() */
|
|
1317
|
+
result = (LZ4F_errorCode_t)dctx->dStage;
|
|
1318
|
+
LZ4F_free(dctx->tmpIn, dctx->cmem);
|
|
1319
|
+
LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);
|
|
1320
|
+
LZ4F_free(dctx, dctx->cmem);
|
|
1321
|
+
}
|
|
1322
|
+
return result;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
/*==--- Streaming Decompression operations ---==*/
|
|
1327
|
+
void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx)
|
|
1328
|
+
{
|
|
1329
|
+
DEBUGLOG(5, "LZ4F_resetDecompressionContext");
|
|
1330
|
+
dctx->dStage = dstage_getFrameHeader;
|
|
1331
|
+
dctx->dict = NULL;
|
|
1332
|
+
dctx->dictSize = 0;
|
|
1333
|
+
dctx->skipChecksum = 0;
|
|
1334
|
+
dctx->frameRemainingSize = 0;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
|
|
1338
|
+
/*! LZ4F_decodeHeader() :
|
|
1339
|
+
* input : `src` points at the **beginning of the frame**
|
|
1340
|
+
* output : set internal values of dctx, such as
|
|
1341
|
+
* dctx->frameInfo and dctx->dStage.
|
|
1342
|
+
* Also allocates internal buffers.
|
|
1343
|
+
* @return : nb Bytes read from src (necessarily <= srcSize)
|
|
1344
|
+
* or an error code (testable with LZ4F_isError())
|
|
1345
|
+
*/
|
|
1346
|
+
static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize)
|
|
1347
|
+
{
|
|
1348
|
+
unsigned blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, dictIDFlag, blockSizeID;
|
|
1349
|
+
size_t frameHeaderSize;
|
|
1350
|
+
const BYTE* srcPtr = (const BYTE*)src;
|
|
1351
|
+
|
|
1352
|
+
DEBUGLOG(5, "LZ4F_decodeHeader");
|
|
1353
|
+
/* need to decode header to get frameInfo */
|
|
1354
|
+
RETURN_ERROR_IF(srcSize < minFHSize, frameHeader_incomplete); /* minimal frame header size */
|
|
1355
|
+
MEM_INIT(&(dctx->frameInfo), 0, sizeof(dctx->frameInfo));
|
|
1356
|
+
|
|
1357
|
+
/* special case : skippable frames */
|
|
1358
|
+
if ((LZ4F_readLE32(srcPtr) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) {
|
|
1359
|
+
dctx->frameInfo.frameType = LZ4F_skippableFrame;
|
|
1360
|
+
if (src == (void*)(dctx->header)) {
|
|
1361
|
+
dctx->tmpInSize = srcSize;
|
|
1362
|
+
dctx->tmpInTarget = 8;
|
|
1363
|
+
dctx->dStage = dstage_storeSFrameSize;
|
|
1364
|
+
return srcSize;
|
|
1365
|
+
} else {
|
|
1366
|
+
dctx->dStage = dstage_getSFrameSize;
|
|
1367
|
+
return 4;
|
|
1368
|
+
} }
|
|
1369
|
+
|
|
1370
|
+
/* control magic number */
|
|
1371
|
+
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
1372
|
+
if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER) {
|
|
1373
|
+
DEBUGLOG(4, "frame header error : unknown magic number");
|
|
1374
|
+
RETURN_ERROR(frameType_unknown);
|
|
1375
|
+
}
|
|
1376
|
+
#endif
|
|
1377
|
+
dctx->frameInfo.frameType = LZ4F_frame;
|
|
1378
|
+
|
|
1379
|
+
/* Flags */
|
|
1380
|
+
{ U32 const FLG = srcPtr[4];
|
|
1381
|
+
U32 const version = (FLG>>6) & _2BITS;
|
|
1382
|
+
blockChecksumFlag = (FLG>>4) & _1BIT;
|
|
1383
|
+
blockMode = (FLG>>5) & _1BIT;
|
|
1384
|
+
contentSizeFlag = (FLG>>3) & _1BIT;
|
|
1385
|
+
contentChecksumFlag = (FLG>>2) & _1BIT;
|
|
1386
|
+
dictIDFlag = FLG & _1BIT;
|
|
1387
|
+
/* validate */
|
|
1388
|
+
if (((FLG>>1)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */
|
|
1389
|
+
if (version != 1) RETURN_ERROR(headerVersion_wrong); /* Version Number, only supported value */
|
|
1390
|
+
}
|
|
1391
|
+
DEBUGLOG(6, "contentSizeFlag: %u", contentSizeFlag);
|
|
1392
|
+
|
|
1393
|
+
/* Frame Header Size */
|
|
1394
|
+
frameHeaderSize = minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);
|
|
1395
|
+
|
|
1396
|
+
if (srcSize < frameHeaderSize) {
|
|
1397
|
+
/* not enough input to fully decode frame header */
|
|
1398
|
+
if (srcPtr != dctx->header)
|
|
1399
|
+
memcpy(dctx->header, srcPtr, srcSize);
|
|
1400
|
+
dctx->tmpInSize = srcSize;
|
|
1401
|
+
dctx->tmpInTarget = frameHeaderSize;
|
|
1402
|
+
dctx->dStage = dstage_storeFrameHeader;
|
|
1403
|
+
return srcSize;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
{ U32 const BD = srcPtr[5];
|
|
1407
|
+
blockSizeID = (BD>>4) & _3BITS;
|
|
1408
|
+
/* validate */
|
|
1409
|
+
if (((BD>>7)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */
|
|
1410
|
+
if (blockSizeID < 4) RETURN_ERROR(maxBlockSize_invalid); /* 4-7 only supported values for the time being */
|
|
1411
|
+
if (((BD>>0)&_4BITS) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bits */
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/* check header */
|
|
1415
|
+
assert(frameHeaderSize > 5);
|
|
1416
|
+
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
1417
|
+
{ BYTE const HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5);
|
|
1418
|
+
RETURN_ERROR_IF(HC != srcPtr[frameHeaderSize-1], headerChecksum_invalid);
|
|
1419
|
+
}
|
|
1420
|
+
#endif
|
|
1421
|
+
|
|
1422
|
+
/* save */
|
|
1423
|
+
dctx->frameInfo.blockMode = (LZ4F_blockMode_t)blockMode;
|
|
1424
|
+
dctx->frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)blockChecksumFlag;
|
|
1425
|
+
dctx->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag;
|
|
1426
|
+
dctx->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID;
|
|
1427
|
+
dctx->maxBlockSize = LZ4F_getBlockSize((LZ4F_blockSizeID_t)blockSizeID);
|
|
1428
|
+
if (contentSizeFlag) {
|
|
1429
|
+
dctx->frameRemainingSize = dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6);
|
|
1430
|
+
}
|
|
1431
|
+
if (dictIDFlag)
|
|
1432
|
+
dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5);
|
|
1433
|
+
|
|
1434
|
+
dctx->dStage = dstage_init;
|
|
1435
|
+
|
|
1436
|
+
return frameHeaderSize;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
/*! LZ4F_headerSize() :
|
|
1441
|
+
* @return : size of frame header
|
|
1442
|
+
* or an error code, which can be tested using LZ4F_isError()
|
|
1443
|
+
*/
|
|
1444
|
+
size_t LZ4F_headerSize(const void* src, size_t srcSize)
|
|
1445
|
+
{
|
|
1446
|
+
RETURN_ERROR_IF(src == NULL, srcPtr_wrong);
|
|
1447
|
+
|
|
1448
|
+
/* minimal srcSize to determine header size */
|
|
1449
|
+
if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH)
|
|
1450
|
+
RETURN_ERROR(frameHeader_incomplete);
|
|
1451
|
+
|
|
1452
|
+
/* special case : skippable frames */
|
|
1453
|
+
if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START)
|
|
1454
|
+
return 8;
|
|
1455
|
+
|
|
1456
|
+
/* control magic number */
|
|
1457
|
+
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
1458
|
+
if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER)
|
|
1459
|
+
RETURN_ERROR(frameType_unknown);
|
|
1460
|
+
#endif
|
|
1461
|
+
|
|
1462
|
+
/* Frame Header Size */
|
|
1463
|
+
{ BYTE const FLG = ((const BYTE*)src)[4];
|
|
1464
|
+
U32 const contentSizeFlag = (FLG>>3) & _1BIT;
|
|
1465
|
+
U32 const dictIDFlag = FLG & _1BIT;
|
|
1466
|
+
return minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/*! LZ4F_getFrameInfo() :
|
|
1471
|
+
* This function extracts frame parameters (max blockSize, frame checksum, etc.).
|
|
1472
|
+
* Usage is optional. Objective is to provide relevant information for allocation purposes.
|
|
1473
|
+
* This function works in 2 situations :
|
|
1474
|
+
* - At the beginning of a new frame, in which case it will decode this information from `srcBuffer`, and start the decoding process.
|
|
1475
|
+
* Amount of input data provided must be large enough to successfully decode the frame header.
|
|
1476
|
+
* A header size is variable, but is guaranteed to be <= LZ4F_HEADER_SIZE_MAX bytes. It's possible to provide more input data than this minimum.
|
|
1477
|
+
* - After decoding has been started. In which case, no input is read, frame parameters are extracted from dctx.
|
|
1478
|
+
* The number of bytes consumed from srcBuffer will be updated within *srcSizePtr (necessarily <= original value).
|
|
1479
|
+
* Decompression must resume from (srcBuffer + *srcSizePtr).
|
|
1480
|
+
* @return : an hint about how many srcSize bytes LZ4F_decompress() expects for next call,
|
|
1481
|
+
* or an error code which can be tested using LZ4F_isError()
|
|
1482
|
+
* note 1 : in case of error, dctx is not modified. Decoding operations can resume from where they stopped.
|
|
1483
|
+
* note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure.
|
|
1484
|
+
*/
|
|
1485
|
+
LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx,
|
|
1486
|
+
LZ4F_frameInfo_t* frameInfoPtr,
|
|
1487
|
+
const void* srcBuffer, size_t* srcSizePtr)
|
|
1488
|
+
{
|
|
1489
|
+
LZ4F_STATIC_ASSERT(dstage_getFrameHeader < dstage_storeFrameHeader);
|
|
1490
|
+
if (dctx->dStage > dstage_storeFrameHeader) {
|
|
1491
|
+
/* frameInfo already decoded */
|
|
1492
|
+
size_t o=0, i=0;
|
|
1493
|
+
*srcSizePtr = 0;
|
|
1494
|
+
*frameInfoPtr = dctx->frameInfo;
|
|
1495
|
+
/* returns : recommended nb of bytes for LZ4F_decompress() */
|
|
1496
|
+
return LZ4F_decompress(dctx, NULL, &o, NULL, &i, NULL);
|
|
1497
|
+
} else {
|
|
1498
|
+
if (dctx->dStage == dstage_storeFrameHeader) {
|
|
1499
|
+
/* frame decoding already started, in the middle of header => automatic fail */
|
|
1500
|
+
*srcSizePtr = 0;
|
|
1501
|
+
RETURN_ERROR(frameDecoding_alreadyStarted);
|
|
1502
|
+
} else {
|
|
1503
|
+
size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr);
|
|
1504
|
+
if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; }
|
|
1505
|
+
if (*srcSizePtr < hSize) {
|
|
1506
|
+
*srcSizePtr=0;
|
|
1507
|
+
RETURN_ERROR(frameHeader_incomplete);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
{ size_t decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize);
|
|
1511
|
+
if (LZ4F_isError(decodeResult)) {
|
|
1512
|
+
*srcSizePtr = 0;
|
|
1513
|
+
} else {
|
|
1514
|
+
*srcSizePtr = decodeResult;
|
|
1515
|
+
decodeResult = BHSize; /* block header size */
|
|
1516
|
+
}
|
|
1517
|
+
*frameInfoPtr = dctx->frameInfo;
|
|
1518
|
+
return decodeResult;
|
|
1519
|
+
} } }
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
|
|
1523
|
+
/* LZ4F_updateDict() :
|
|
1524
|
+
* only used for LZ4F_blockLinked mode
|
|
1525
|
+
* Condition : @dstPtr != NULL
|
|
1526
|
+
*/
|
|
1527
|
+
static void LZ4F_updateDict(LZ4F_dctx* dctx,
|
|
1528
|
+
const BYTE* dstPtr, size_t dstSize, const BYTE* dstBufferStart,
|
|
1529
|
+
unsigned withinTmp)
|
|
1530
|
+
{
|
|
1531
|
+
assert(dstPtr != NULL);
|
|
1532
|
+
if (dctx->dictSize==0) dctx->dict = (const BYTE*)dstPtr; /* will lead to prefix mode */
|
|
1533
|
+
assert(dctx->dict != NULL);
|
|
1534
|
+
|
|
1535
|
+
if (dctx->dict + dctx->dictSize == dstPtr) { /* prefix mode, everything within dstBuffer */
|
|
1536
|
+
dctx->dictSize += dstSize;
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
assert(dstPtr >= dstBufferStart);
|
|
1541
|
+
if ((size_t)(dstPtr - dstBufferStart) + dstSize >= 64 KB) { /* history in dstBuffer becomes large enough to become dictionary */
|
|
1542
|
+
dctx->dict = (const BYTE*)dstBufferStart;
|
|
1543
|
+
dctx->dictSize = (size_t)(dstPtr - dstBufferStart) + dstSize;
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
assert(dstSize < 64 KB); /* if dstSize >= 64 KB, dictionary would be set into dstBuffer directly */
|
|
1548
|
+
|
|
1549
|
+
/* dstBuffer does not contain whole useful history (64 KB), so it must be saved within tmpOutBuffer */
|
|
1550
|
+
assert(dctx->tmpOutBuffer != NULL);
|
|
1551
|
+
|
|
1552
|
+
if (withinTmp && (dctx->dict == dctx->tmpOutBuffer)) { /* continue history within tmpOutBuffer */
|
|
1553
|
+
/* withinTmp expectation : content of [dstPtr,dstSize] is same as [dict+dictSize,dstSize], so we just extend it */
|
|
1554
|
+
assert(dctx->dict + dctx->dictSize == dctx->tmpOut + dctx->tmpOutStart);
|
|
1555
|
+
dctx->dictSize += dstSize;
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
if (withinTmp) { /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */
|
|
1560
|
+
size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);
|
|
1561
|
+
size_t copySize = 64 KB - dctx->tmpOutSize;
|
|
1562
|
+
const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart;
|
|
1563
|
+
if (dctx->tmpOutSize > 64 KB) copySize = 0;
|
|
1564
|
+
if (copySize > preserveSize) copySize = preserveSize;
|
|
1565
|
+
|
|
1566
|
+
memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
|
|
1567
|
+
|
|
1568
|
+
dctx->dict = dctx->tmpOutBuffer;
|
|
1569
|
+
dctx->dictSize = preserveSize + dctx->tmpOutStart + dstSize;
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
if (dctx->dict == dctx->tmpOutBuffer) { /* copy dst into tmp to complete dict */
|
|
1574
|
+
if (dctx->dictSize + dstSize > dctx->maxBufferSize) { /* tmp buffer not large enough */
|
|
1575
|
+
size_t const preserveSize = 64 KB - dstSize;
|
|
1576
|
+
memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);
|
|
1577
|
+
dctx->dictSize = preserveSize;
|
|
1578
|
+
}
|
|
1579
|
+
memcpy(dctx->tmpOutBuffer + dctx->dictSize, dstPtr, dstSize);
|
|
1580
|
+
dctx->dictSize += dstSize;
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
/* join dict & dest into tmp */
|
|
1585
|
+
{ size_t preserveSize = 64 KB - dstSize;
|
|
1586
|
+
if (preserveSize > dctx->dictSize) preserveSize = dctx->dictSize;
|
|
1587
|
+
memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);
|
|
1588
|
+
memcpy(dctx->tmpOutBuffer + preserveSize, dstPtr, dstSize);
|
|
1589
|
+
dctx->dict = dctx->tmpOutBuffer;
|
|
1590
|
+
dctx->dictSize = preserveSize + dstSize;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
/*! LZ4F_decompress() :
|
|
1596
|
+
* Call this function repetitively to regenerate compressed data in srcBuffer.
|
|
1597
|
+
* The function will attempt to decode up to *srcSizePtr bytes from srcBuffer
|
|
1598
|
+
* into dstBuffer of capacity *dstSizePtr.
|
|
1599
|
+
*
|
|
1600
|
+
* The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value).
|
|
1601
|
+
*
|
|
1602
|
+
* The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
|
|
1603
|
+
* If number of bytes read is < number of bytes provided, then decompression operation is not complete.
|
|
1604
|
+
* Remaining data will have to be presented again in a subsequent invocation.
|
|
1605
|
+
*
|
|
1606
|
+
* The function result is an hint of the better srcSize to use for next call to LZ4F_decompress.
|
|
1607
|
+
* Schematically, it's the size of the current (or remaining) compressed block + header of next block.
|
|
1608
|
+
* Respecting the hint provides a small boost to performance, since it allows less buffer shuffling.
|
|
1609
|
+
* Note that this is just a hint, and it's always possible to any srcSize value.
|
|
1610
|
+
* When a frame is fully decoded, @return will be 0.
|
|
1611
|
+
* If decompression failed, @return is an error code which can be tested using LZ4F_isError().
|
|
1612
|
+
*/
|
|
1613
|
+
size_t LZ4F_decompress(LZ4F_dctx* dctx,
|
|
1614
|
+
void* dstBuffer, size_t* dstSizePtr,
|
|
1615
|
+
const void* srcBuffer, size_t* srcSizePtr,
|
|
1616
|
+
const LZ4F_decompressOptions_t* decompressOptionsPtr)
|
|
1617
|
+
{
|
|
1618
|
+
LZ4F_decompressOptions_t optionsNull;
|
|
1619
|
+
const BYTE* const srcStart = (const BYTE*)srcBuffer;
|
|
1620
|
+
const BYTE* const srcEnd = srcStart + *srcSizePtr;
|
|
1621
|
+
const BYTE* srcPtr = srcStart;
|
|
1622
|
+
BYTE* const dstStart = (BYTE*)dstBuffer;
|
|
1623
|
+
BYTE* const dstEnd = dstStart ? dstStart + *dstSizePtr : NULL;
|
|
1624
|
+
BYTE* dstPtr = dstStart;
|
|
1625
|
+
const BYTE* selectedIn = NULL;
|
|
1626
|
+
unsigned doAnotherStage = 1;
|
|
1627
|
+
size_t nextSrcSizeHint = 1;
|
|
1628
|
+
|
|
1629
|
+
|
|
1630
|
+
DEBUGLOG(5, "LZ4F_decompress: src[%p](%u) => dst[%p](%u)",
|
|
1631
|
+
srcBuffer, (unsigned)*srcSizePtr, dstBuffer, (unsigned)*dstSizePtr);
|
|
1632
|
+
if (dstBuffer == NULL) assert(*dstSizePtr == 0);
|
|
1633
|
+
MEM_INIT(&optionsNull, 0, sizeof(optionsNull));
|
|
1634
|
+
if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull;
|
|
1635
|
+
*srcSizePtr = 0;
|
|
1636
|
+
*dstSizePtr = 0;
|
|
1637
|
+
assert(dctx != NULL);
|
|
1638
|
+
dctx->skipChecksum |= (decompressOptionsPtr->skipChecksums != 0); /* once set, disable for the remainder of the frame */
|
|
1639
|
+
|
|
1640
|
+
/* behaves as a state machine */
|
|
1641
|
+
|
|
1642
|
+
while (doAnotherStage) {
|
|
1643
|
+
|
|
1644
|
+
switch(dctx->dStage)
|
|
1645
|
+
{
|
|
1646
|
+
|
|
1647
|
+
case dstage_getFrameHeader:
|
|
1648
|
+
DEBUGLOG(6, "dstage_getFrameHeader");
|
|
1649
|
+
if ((size_t)(srcEnd-srcPtr) >= maxFHSize) { /* enough to decode - shortcut */
|
|
1650
|
+
size_t const hSize = LZ4F_decodeHeader(dctx, srcPtr, (size_t)(srcEnd-srcPtr)); /* will update dStage appropriately */
|
|
1651
|
+
FORWARD_IF_ERROR(hSize);
|
|
1652
|
+
srcPtr += hSize;
|
|
1653
|
+
break;
|
|
1654
|
+
}
|
|
1655
|
+
dctx->tmpInSize = 0;
|
|
1656
|
+
if (srcEnd-srcPtr == 0) return minFHSize; /* 0-size input */
|
|
1657
|
+
dctx->tmpInTarget = minFHSize; /* minimum size to decode header */
|
|
1658
|
+
dctx->dStage = dstage_storeFrameHeader;
|
|
1659
|
+
/* fall-through */
|
|
1660
|
+
|
|
1661
|
+
case dstage_storeFrameHeader:
|
|
1662
|
+
DEBUGLOG(6, "dstage_storeFrameHeader");
|
|
1663
|
+
{ size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, (size_t)(srcEnd - srcPtr));
|
|
1664
|
+
memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
|
|
1665
|
+
dctx->tmpInSize += sizeToCopy;
|
|
1666
|
+
srcPtr += sizeToCopy;
|
|
1667
|
+
}
|
|
1668
|
+
if (dctx->tmpInSize < dctx->tmpInTarget) {
|
|
1669
|
+
nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + BHSize; /* rest of header + nextBlockHeader */
|
|
1670
|
+
doAnotherStage = 0; /* not enough src data, ask for some more */
|
|
1671
|
+
break;
|
|
1672
|
+
}
|
|
1673
|
+
FORWARD_IF_ERROR( LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget) ); /* will update dStage appropriately */
|
|
1674
|
+
break;
|
|
1675
|
+
|
|
1676
|
+
case dstage_init:
|
|
1677
|
+
DEBUGLOG(6, "dstage_init");
|
|
1678
|
+
if (dctx->frameInfo.contentChecksumFlag) (void)XXH32_reset(&(dctx->xxh), 0);
|
|
1679
|
+
/* internal buffers allocation */
|
|
1680
|
+
{ size_t const bufferNeeded = dctx->maxBlockSize
|
|
1681
|
+
+ ((dctx->frameInfo.blockMode==LZ4F_blockLinked) ? 128 KB : 0);
|
|
1682
|
+
if (bufferNeeded > dctx->maxBufferSize) { /* tmp buffers too small */
|
|
1683
|
+
dctx->maxBufferSize = 0; /* ensure allocation will be re-attempted on next entry*/
|
|
1684
|
+
LZ4F_free(dctx->tmpIn, dctx->cmem);
|
|
1685
|
+
dctx->tmpIn = (BYTE*)LZ4F_malloc(dctx->maxBlockSize + BFSize /* block checksum */, dctx->cmem);
|
|
1686
|
+
RETURN_ERROR_IF(dctx->tmpIn == NULL, allocation_failed);
|
|
1687
|
+
LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);
|
|
1688
|
+
dctx->tmpOutBuffer= (BYTE*)LZ4F_malloc(bufferNeeded, dctx->cmem);
|
|
1689
|
+
RETURN_ERROR_IF(dctx->tmpOutBuffer== NULL, allocation_failed);
|
|
1690
|
+
dctx->maxBufferSize = bufferNeeded;
|
|
1691
|
+
} }
|
|
1692
|
+
dctx->tmpInSize = 0;
|
|
1693
|
+
dctx->tmpInTarget = 0;
|
|
1694
|
+
dctx->tmpOut = dctx->tmpOutBuffer;
|
|
1695
|
+
dctx->tmpOutStart = 0;
|
|
1696
|
+
dctx->tmpOutSize = 0;
|
|
1697
|
+
|
|
1698
|
+
dctx->dStage = dstage_getBlockHeader;
|
|
1699
|
+
/* fall-through */
|
|
1700
|
+
|
|
1701
|
+
case dstage_getBlockHeader:
|
|
1702
|
+
if ((size_t)(srcEnd - srcPtr) >= BHSize) {
|
|
1703
|
+
selectedIn = srcPtr;
|
|
1704
|
+
srcPtr += BHSize;
|
|
1705
|
+
} else {
|
|
1706
|
+
/* not enough input to read cBlockSize field */
|
|
1707
|
+
dctx->tmpInSize = 0;
|
|
1708
|
+
dctx->dStage = dstage_storeBlockHeader;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
if (dctx->dStage == dstage_storeBlockHeader) /* can be skipped */
|
|
1712
|
+
case dstage_storeBlockHeader:
|
|
1713
|
+
{ size_t const remainingInput = (size_t)(srcEnd - srcPtr);
|
|
1714
|
+
size_t const wantedData = BHSize - dctx->tmpInSize;
|
|
1715
|
+
size_t const sizeToCopy = MIN(wantedData, remainingInput);
|
|
1716
|
+
memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);
|
|
1717
|
+
srcPtr += sizeToCopy;
|
|
1718
|
+
dctx->tmpInSize += sizeToCopy;
|
|
1719
|
+
|
|
1720
|
+
if (dctx->tmpInSize < BHSize) { /* not enough input for cBlockSize */
|
|
1721
|
+
nextSrcSizeHint = BHSize - dctx->tmpInSize;
|
|
1722
|
+
doAnotherStage = 0;
|
|
1723
|
+
break;
|
|
1724
|
+
}
|
|
1725
|
+
selectedIn = dctx->tmpIn;
|
|
1726
|
+
} /* if (dctx->dStage == dstage_storeBlockHeader) */
|
|
1727
|
+
|
|
1728
|
+
/* decode block header */
|
|
1729
|
+
{ U32 const blockHeader = LZ4F_readLE32(selectedIn);
|
|
1730
|
+
size_t const nextCBlockSize = blockHeader & 0x7FFFFFFFU;
|
|
1731
|
+
size_t const crcSize = dctx->frameInfo.blockChecksumFlag * BFSize;
|
|
1732
|
+
if (blockHeader==0) { /* frameEnd signal, no more block */
|
|
1733
|
+
DEBUGLOG(5, "end of frame");
|
|
1734
|
+
dctx->dStage = dstage_getSuffix;
|
|
1735
|
+
break;
|
|
1736
|
+
}
|
|
1737
|
+
if (nextCBlockSize > dctx->maxBlockSize) {
|
|
1738
|
+
RETURN_ERROR(maxBlockSize_invalid);
|
|
1739
|
+
}
|
|
1740
|
+
if (blockHeader & LZ4F_BLOCKUNCOMPRESSED_FLAG) {
|
|
1741
|
+
/* next block is uncompressed */
|
|
1742
|
+
dctx->tmpInTarget = nextCBlockSize;
|
|
1743
|
+
DEBUGLOG(5, "next block is uncompressed (size %u)", (U32)nextCBlockSize);
|
|
1744
|
+
if (dctx->frameInfo.blockChecksumFlag) {
|
|
1745
|
+
(void)XXH32_reset(&dctx->blockChecksum, 0);
|
|
1746
|
+
}
|
|
1747
|
+
dctx->dStage = dstage_copyDirect;
|
|
1748
|
+
break;
|
|
1749
|
+
}
|
|
1750
|
+
/* next block is a compressed block */
|
|
1751
|
+
dctx->tmpInTarget = nextCBlockSize + crcSize;
|
|
1752
|
+
dctx->dStage = dstage_getCBlock;
|
|
1753
|
+
if (dstPtr==dstEnd || srcPtr==srcEnd) {
|
|
1754
|
+
nextSrcSizeHint = BHSize + nextCBlockSize + crcSize;
|
|
1755
|
+
doAnotherStage = 0;
|
|
1756
|
+
}
|
|
1757
|
+
break;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
case dstage_copyDirect: /* uncompressed block */
|
|
1761
|
+
DEBUGLOG(6, "dstage_copyDirect");
|
|
1762
|
+
{ size_t sizeToCopy;
|
|
1763
|
+
if (dstPtr == NULL) {
|
|
1764
|
+
sizeToCopy = 0;
|
|
1765
|
+
} else {
|
|
1766
|
+
size_t const minBuffSize = MIN((size_t)(srcEnd-srcPtr), (size_t)(dstEnd-dstPtr));
|
|
1767
|
+
sizeToCopy = MIN(dctx->tmpInTarget, minBuffSize);
|
|
1768
|
+
memcpy(dstPtr, srcPtr, sizeToCopy);
|
|
1769
|
+
if (!dctx->skipChecksum) {
|
|
1770
|
+
if (dctx->frameInfo.blockChecksumFlag) {
|
|
1771
|
+
(void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy);
|
|
1772
|
+
}
|
|
1773
|
+
if (dctx->frameInfo.contentChecksumFlag)
|
|
1774
|
+
(void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy);
|
|
1775
|
+
}
|
|
1776
|
+
if (dctx->frameInfo.contentSize)
|
|
1777
|
+
dctx->frameRemainingSize -= sizeToCopy;
|
|
1778
|
+
|
|
1779
|
+
/* history management (linked blocks only)*/
|
|
1780
|
+
if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {
|
|
1781
|
+
LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 0);
|
|
1782
|
+
}
|
|
1783
|
+
srcPtr += sizeToCopy;
|
|
1784
|
+
dstPtr += sizeToCopy;
|
|
1785
|
+
}
|
|
1786
|
+
if (sizeToCopy == dctx->tmpInTarget) { /* all done */
|
|
1787
|
+
if (dctx->frameInfo.blockChecksumFlag) {
|
|
1788
|
+
dctx->tmpInSize = 0;
|
|
1789
|
+
dctx->dStage = dstage_getBlockChecksum;
|
|
1790
|
+
} else
|
|
1791
|
+
dctx->dStage = dstage_getBlockHeader; /* new block */
|
|
1792
|
+
break;
|
|
1793
|
+
}
|
|
1794
|
+
dctx->tmpInTarget -= sizeToCopy; /* need to copy more */
|
|
1795
|
+
}
|
|
1796
|
+
nextSrcSizeHint = dctx->tmpInTarget +
|
|
1797
|
+
+(dctx->frameInfo.blockChecksumFlag ? BFSize : 0)
|
|
1798
|
+
+ BHSize /* next header size */;
|
|
1799
|
+
doAnotherStage = 0;
|
|
1800
|
+
break;
|
|
1801
|
+
|
|
1802
|
+
/* check block checksum for recently transferred uncompressed block */
|
|
1803
|
+
case dstage_getBlockChecksum:
|
|
1804
|
+
DEBUGLOG(6, "dstage_getBlockChecksum");
|
|
1805
|
+
{ const void* crcSrc;
|
|
1806
|
+
if ((srcEnd-srcPtr >= 4) && (dctx->tmpInSize==0)) {
|
|
1807
|
+
crcSrc = srcPtr;
|
|
1808
|
+
srcPtr += 4;
|
|
1809
|
+
} else {
|
|
1810
|
+
size_t const stillToCopy = 4 - dctx->tmpInSize;
|
|
1811
|
+
size_t const sizeToCopy = MIN(stillToCopy, (size_t)(srcEnd-srcPtr));
|
|
1812
|
+
memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
|
|
1813
|
+
dctx->tmpInSize += sizeToCopy;
|
|
1814
|
+
srcPtr += sizeToCopy;
|
|
1815
|
+
if (dctx->tmpInSize < 4) { /* all input consumed */
|
|
1816
|
+
doAnotherStage = 0;
|
|
1817
|
+
break;
|
|
1818
|
+
}
|
|
1819
|
+
crcSrc = dctx->header;
|
|
1820
|
+
}
|
|
1821
|
+
if (!dctx->skipChecksum) {
|
|
1822
|
+
U32 const readCRC = LZ4F_readLE32(crcSrc);
|
|
1823
|
+
U32 const calcCRC = XXH32_digest(&dctx->blockChecksum);
|
|
1824
|
+
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
1825
|
+
DEBUGLOG(6, "compare block checksum");
|
|
1826
|
+
if (readCRC != calcCRC) {
|
|
1827
|
+
DEBUGLOG(4, "incorrect block checksum: %08X != %08X",
|
|
1828
|
+
readCRC, calcCRC);
|
|
1829
|
+
RETURN_ERROR(blockChecksum_invalid);
|
|
1830
|
+
}
|
|
1831
|
+
#else
|
|
1832
|
+
(void)readCRC;
|
|
1833
|
+
(void)calcCRC;
|
|
1834
|
+
#endif
|
|
1835
|
+
} }
|
|
1836
|
+
dctx->dStage = dstage_getBlockHeader; /* new block */
|
|
1837
|
+
break;
|
|
1838
|
+
|
|
1839
|
+
case dstage_getCBlock:
|
|
1840
|
+
DEBUGLOG(6, "dstage_getCBlock");
|
|
1841
|
+
if ((size_t)(srcEnd-srcPtr) < dctx->tmpInTarget) {
|
|
1842
|
+
dctx->tmpInSize = 0;
|
|
1843
|
+
dctx->dStage = dstage_storeCBlock;
|
|
1844
|
+
break;
|
|
1845
|
+
}
|
|
1846
|
+
/* input large enough to read full block directly */
|
|
1847
|
+
selectedIn = srcPtr;
|
|
1848
|
+
srcPtr += dctx->tmpInTarget;
|
|
1849
|
+
|
|
1850
|
+
if (0) /* always jump over next block */
|
|
1851
|
+
case dstage_storeCBlock:
|
|
1852
|
+
{ size_t const wantedData = dctx->tmpInTarget - dctx->tmpInSize;
|
|
1853
|
+
size_t const inputLeft = (size_t)(srcEnd-srcPtr);
|
|
1854
|
+
size_t const sizeToCopy = MIN(wantedData, inputLeft);
|
|
1855
|
+
memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);
|
|
1856
|
+
dctx->tmpInSize += sizeToCopy;
|
|
1857
|
+
srcPtr += sizeToCopy;
|
|
1858
|
+
if (dctx->tmpInSize < dctx->tmpInTarget) { /* need more input */
|
|
1859
|
+
nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize)
|
|
1860
|
+
+ (dctx->frameInfo.blockChecksumFlag ? BFSize : 0)
|
|
1861
|
+
+ BHSize /* next header size */;
|
|
1862
|
+
doAnotherStage = 0;
|
|
1863
|
+
break;
|
|
1864
|
+
}
|
|
1865
|
+
selectedIn = dctx->tmpIn;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
/* At this stage, input is large enough to decode a block */
|
|
1869
|
+
|
|
1870
|
+
/* First, decode and control block checksum if it exists */
|
|
1871
|
+
if (dctx->frameInfo.blockChecksumFlag) {
|
|
1872
|
+
assert(dctx->tmpInTarget >= 4);
|
|
1873
|
+
dctx->tmpInTarget -= 4;
|
|
1874
|
+
assert(selectedIn != NULL); /* selectedIn is defined at this stage (either srcPtr, or dctx->tmpIn) */
|
|
1875
|
+
{ U32 const readBlockCrc = LZ4F_readLE32(selectedIn + dctx->tmpInTarget);
|
|
1876
|
+
U32 const calcBlockCrc = XXH32(selectedIn, dctx->tmpInTarget, 0);
|
|
1877
|
+
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
1878
|
+
RETURN_ERROR_IF(readBlockCrc != calcBlockCrc, blockChecksum_invalid);
|
|
1879
|
+
#else
|
|
1880
|
+
(void)readBlockCrc;
|
|
1881
|
+
(void)calcBlockCrc;
|
|
1882
|
+
#endif
|
|
1883
|
+
} }
|
|
1884
|
+
|
|
1885
|
+
/* decode directly into destination buffer if there is enough room */
|
|
1886
|
+
if ( ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize)
|
|
1887
|
+
/* unless the dictionary is stored in tmpOut:
|
|
1888
|
+
* in which case it's faster to decode within tmpOut
|
|
1889
|
+
* to benefit from prefix speedup */
|
|
1890
|
+
&& !(dctx->dict!= NULL && (const BYTE*)dctx->dict + dctx->dictSize == dctx->tmpOut) )
|
|
1891
|
+
{
|
|
1892
|
+
const char* dict = (const char*)dctx->dict;
|
|
1893
|
+
size_t dictSize = dctx->dictSize;
|
|
1894
|
+
int decodedSize;
|
|
1895
|
+
assert(dstPtr != NULL);
|
|
1896
|
+
if (dict && dictSize > 1 GB) {
|
|
1897
|
+
/* overflow control : dctx->dictSize is an int, avoid truncation / sign issues */
|
|
1898
|
+
dict += dictSize - 64 KB;
|
|
1899
|
+
dictSize = 64 KB;
|
|
1900
|
+
}
|
|
1901
|
+
decodedSize = LZ4_decompress_safe_usingDict(
|
|
1902
|
+
(const char*)selectedIn, (char*)dstPtr,
|
|
1903
|
+
(int)dctx->tmpInTarget, (int)dctx->maxBlockSize,
|
|
1904
|
+
dict, (int)dictSize);
|
|
1905
|
+
RETURN_ERROR_IF(decodedSize < 0, decompressionFailed);
|
|
1906
|
+
if ((dctx->frameInfo.contentChecksumFlag) && (!dctx->skipChecksum))
|
|
1907
|
+
XXH32_update(&(dctx->xxh), dstPtr, (size_t)decodedSize);
|
|
1908
|
+
if (dctx->frameInfo.contentSize)
|
|
1909
|
+
dctx->frameRemainingSize -= (size_t)decodedSize;
|
|
1910
|
+
|
|
1911
|
+
/* dictionary management */
|
|
1912
|
+
if (dctx->frameInfo.blockMode==LZ4F_blockLinked) {
|
|
1913
|
+
LZ4F_updateDict(dctx, dstPtr, (size_t)decodedSize, dstStart, 0);
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
dstPtr += decodedSize;
|
|
1917
|
+
dctx->dStage = dstage_getBlockHeader; /* end of block, let's get another one */
|
|
1918
|
+
break;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
/* not enough place into dst : decode into tmpOut */
|
|
1922
|
+
|
|
1923
|
+
/* manage dictionary */
|
|
1924
|
+
if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {
|
|
1925
|
+
if (dctx->dict == dctx->tmpOutBuffer) {
|
|
1926
|
+
/* truncate dictionary to 64 KB if too big */
|
|
1927
|
+
if (dctx->dictSize > 128 KB) {
|
|
1928
|
+
memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - 64 KB, 64 KB);
|
|
1929
|
+
dctx->dictSize = 64 KB;
|
|
1930
|
+
}
|
|
1931
|
+
dctx->tmpOut = dctx->tmpOutBuffer + dctx->dictSize;
|
|
1932
|
+
} else { /* dict not within tmpOut */
|
|
1933
|
+
size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB);
|
|
1934
|
+
dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace;
|
|
1935
|
+
} }
|
|
1936
|
+
|
|
1937
|
+
/* Decode block into tmpOut */
|
|
1938
|
+
{ const char* dict = (const char*)dctx->dict;
|
|
1939
|
+
size_t dictSize = dctx->dictSize;
|
|
1940
|
+
int decodedSize;
|
|
1941
|
+
if (dict && dictSize > 1 GB) {
|
|
1942
|
+
/* the dictSize param is an int, avoid truncation / sign issues */
|
|
1943
|
+
dict += dictSize - 64 KB;
|
|
1944
|
+
dictSize = 64 KB;
|
|
1945
|
+
}
|
|
1946
|
+
decodedSize = LZ4_decompress_safe_usingDict(
|
|
1947
|
+
(const char*)selectedIn, (char*)dctx->tmpOut,
|
|
1948
|
+
(int)dctx->tmpInTarget, (int)dctx->maxBlockSize,
|
|
1949
|
+
dict, (int)dictSize);
|
|
1950
|
+
RETURN_ERROR_IF(decodedSize < 0, decompressionFailed);
|
|
1951
|
+
if (dctx->frameInfo.contentChecksumFlag && !dctx->skipChecksum)
|
|
1952
|
+
XXH32_update(&(dctx->xxh), dctx->tmpOut, (size_t)decodedSize);
|
|
1953
|
+
if (dctx->frameInfo.contentSize)
|
|
1954
|
+
dctx->frameRemainingSize -= (size_t)decodedSize;
|
|
1955
|
+
dctx->tmpOutSize = (size_t)decodedSize;
|
|
1956
|
+
dctx->tmpOutStart = 0;
|
|
1957
|
+
dctx->dStage = dstage_flushOut;
|
|
1958
|
+
}
|
|
1959
|
+
/* fall-through */
|
|
1960
|
+
|
|
1961
|
+
case dstage_flushOut: /* flush decoded data from tmpOut to dstBuffer */
|
|
1962
|
+
DEBUGLOG(6, "dstage_flushOut");
|
|
1963
|
+
if (dstPtr != NULL) {
|
|
1964
|
+
size_t const sizeToCopy = MIN(dctx->tmpOutSize - dctx->tmpOutStart, (size_t)(dstEnd-dstPtr));
|
|
1965
|
+
memcpy(dstPtr, dctx->tmpOut + dctx->tmpOutStart, sizeToCopy);
|
|
1966
|
+
|
|
1967
|
+
/* dictionary management */
|
|
1968
|
+
if (dctx->frameInfo.blockMode == LZ4F_blockLinked)
|
|
1969
|
+
LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 1 /*withinTmp*/);
|
|
1970
|
+
|
|
1971
|
+
dctx->tmpOutStart += sizeToCopy;
|
|
1972
|
+
dstPtr += sizeToCopy;
|
|
1973
|
+
}
|
|
1974
|
+
if (dctx->tmpOutStart == dctx->tmpOutSize) { /* all flushed */
|
|
1975
|
+
dctx->dStage = dstage_getBlockHeader; /* get next block */
|
|
1976
|
+
break;
|
|
1977
|
+
}
|
|
1978
|
+
/* could not flush everything : stop there, just request a block header */
|
|
1979
|
+
doAnotherStage = 0;
|
|
1980
|
+
nextSrcSizeHint = BHSize;
|
|
1981
|
+
break;
|
|
1982
|
+
|
|
1983
|
+
case dstage_getSuffix:
|
|
1984
|
+
RETURN_ERROR_IF(dctx->frameRemainingSize, frameSize_wrong); /* incorrect frame size decoded */
|
|
1985
|
+
if (!dctx->frameInfo.contentChecksumFlag) { /* no checksum, frame is completed */
|
|
1986
|
+
nextSrcSizeHint = 0;
|
|
1987
|
+
LZ4F_resetDecompressionContext(dctx);
|
|
1988
|
+
doAnotherStage = 0;
|
|
1989
|
+
break;
|
|
1990
|
+
}
|
|
1991
|
+
if ((srcEnd - srcPtr) < 4) { /* not enough size for entire CRC */
|
|
1992
|
+
dctx->tmpInSize = 0;
|
|
1993
|
+
dctx->dStage = dstage_storeSuffix;
|
|
1994
|
+
} else {
|
|
1995
|
+
selectedIn = srcPtr;
|
|
1996
|
+
srcPtr += 4;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
if (dctx->dStage == dstage_storeSuffix) /* can be skipped */
|
|
2000
|
+
case dstage_storeSuffix:
|
|
2001
|
+
{ size_t const remainingInput = (size_t)(srcEnd - srcPtr);
|
|
2002
|
+
size_t const wantedData = 4 - dctx->tmpInSize;
|
|
2003
|
+
size_t const sizeToCopy = MIN(wantedData, remainingInput);
|
|
2004
|
+
memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);
|
|
2005
|
+
srcPtr += sizeToCopy;
|
|
2006
|
+
dctx->tmpInSize += sizeToCopy;
|
|
2007
|
+
if (dctx->tmpInSize < 4) { /* not enough input to read complete suffix */
|
|
2008
|
+
nextSrcSizeHint = 4 - dctx->tmpInSize;
|
|
2009
|
+
doAnotherStage=0;
|
|
2010
|
+
break;
|
|
2011
|
+
}
|
|
2012
|
+
selectedIn = dctx->tmpIn;
|
|
2013
|
+
} /* if (dctx->dStage == dstage_storeSuffix) */
|
|
2014
|
+
|
|
2015
|
+
/* case dstage_checkSuffix: */ /* no direct entry, avoid initialization risks */
|
|
2016
|
+
if (!dctx->skipChecksum) {
|
|
2017
|
+
U32 const readCRC = LZ4F_readLE32(selectedIn);
|
|
2018
|
+
U32 const resultCRC = XXH32_digest(&(dctx->xxh));
|
|
2019
|
+
DEBUGLOG(4, "frame checksum: stored 0x%0X vs 0x%0X processed", readCRC, resultCRC);
|
|
2020
|
+
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
2021
|
+
RETURN_ERROR_IF(readCRC != resultCRC, contentChecksum_invalid);
|
|
2022
|
+
#else
|
|
2023
|
+
(void)readCRC;
|
|
2024
|
+
(void)resultCRC;
|
|
2025
|
+
#endif
|
|
2026
|
+
}
|
|
2027
|
+
nextSrcSizeHint = 0;
|
|
2028
|
+
LZ4F_resetDecompressionContext(dctx);
|
|
2029
|
+
doAnotherStage = 0;
|
|
2030
|
+
break;
|
|
2031
|
+
|
|
2032
|
+
case dstage_getSFrameSize:
|
|
2033
|
+
if ((srcEnd - srcPtr) >= 4) {
|
|
2034
|
+
selectedIn = srcPtr;
|
|
2035
|
+
srcPtr += 4;
|
|
2036
|
+
} else {
|
|
2037
|
+
/* not enough input to read cBlockSize field */
|
|
2038
|
+
dctx->tmpInSize = 4;
|
|
2039
|
+
dctx->tmpInTarget = 8;
|
|
2040
|
+
dctx->dStage = dstage_storeSFrameSize;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
if (dctx->dStage == dstage_storeSFrameSize)
|
|
2044
|
+
case dstage_storeSFrameSize:
|
|
2045
|
+
{ size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize,
|
|
2046
|
+
(size_t)(srcEnd - srcPtr) );
|
|
2047
|
+
memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
|
|
2048
|
+
srcPtr += sizeToCopy;
|
|
2049
|
+
dctx->tmpInSize += sizeToCopy;
|
|
2050
|
+
if (dctx->tmpInSize < dctx->tmpInTarget) {
|
|
2051
|
+
/* not enough input to get full sBlockSize; wait for more */
|
|
2052
|
+
nextSrcSizeHint = dctx->tmpInTarget - dctx->tmpInSize;
|
|
2053
|
+
doAnotherStage = 0;
|
|
2054
|
+
break;
|
|
2055
|
+
}
|
|
2056
|
+
selectedIn = dctx->header + 4;
|
|
2057
|
+
} /* if (dctx->dStage == dstage_storeSFrameSize) */
|
|
2058
|
+
|
|
2059
|
+
/* case dstage_decodeSFrameSize: */ /* no direct entry */
|
|
2060
|
+
{ size_t const SFrameSize = LZ4F_readLE32(selectedIn);
|
|
2061
|
+
dctx->frameInfo.contentSize = SFrameSize;
|
|
2062
|
+
dctx->tmpInTarget = SFrameSize;
|
|
2063
|
+
dctx->dStage = dstage_skipSkippable;
|
|
2064
|
+
break;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
case dstage_skipSkippable:
|
|
2068
|
+
{ size_t const skipSize = MIN(dctx->tmpInTarget, (size_t)(srcEnd-srcPtr));
|
|
2069
|
+
srcPtr += skipSize;
|
|
2070
|
+
dctx->tmpInTarget -= skipSize;
|
|
2071
|
+
doAnotherStage = 0;
|
|
2072
|
+
nextSrcSizeHint = dctx->tmpInTarget;
|
|
2073
|
+
if (nextSrcSizeHint) break; /* still more to skip */
|
|
2074
|
+
/* frame fully skipped : prepare context for a new frame */
|
|
2075
|
+
LZ4F_resetDecompressionContext(dctx);
|
|
2076
|
+
break;
|
|
2077
|
+
}
|
|
2078
|
+
} /* switch (dctx->dStage) */
|
|
2079
|
+
} /* while (doAnotherStage) */
|
|
2080
|
+
|
|
2081
|
+
/* preserve history within tmpOut whenever necessary */
|
|
2082
|
+
LZ4F_STATIC_ASSERT((unsigned)dstage_init == 2);
|
|
2083
|
+
if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked) /* next block will use up to 64KB from previous ones */
|
|
2084
|
+
&& (dctx->dict != dctx->tmpOutBuffer) /* dictionary is not already within tmp */
|
|
2085
|
+
&& (dctx->dict != NULL) /* dictionary exists */
|
|
2086
|
+
&& (!decompressOptionsPtr->stableDst) /* cannot rely on dst data to remain there for next call */
|
|
2087
|
+
&& ((unsigned)(dctx->dStage)-2 < (unsigned)(dstage_getSuffix)-2) ) /* valid stages : [init ... getSuffix[ */
|
|
2088
|
+
{
|
|
2089
|
+
if (dctx->dStage == dstage_flushOut) {
|
|
2090
|
+
size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);
|
|
2091
|
+
size_t copySize = 64 KB - dctx->tmpOutSize;
|
|
2092
|
+
const BYTE* oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart;
|
|
2093
|
+
if (dctx->tmpOutSize > 64 KB) copySize = 0;
|
|
2094
|
+
if (copySize > preserveSize) copySize = preserveSize;
|
|
2095
|
+
assert(dctx->tmpOutBuffer != NULL);
|
|
2096
|
+
|
|
2097
|
+
memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
|
|
2098
|
+
|
|
2099
|
+
dctx->dict = dctx->tmpOutBuffer;
|
|
2100
|
+
dctx->dictSize = preserveSize + dctx->tmpOutStart;
|
|
2101
|
+
} else {
|
|
2102
|
+
const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize;
|
|
2103
|
+
size_t const newDictSize = MIN(dctx->dictSize, 64 KB);
|
|
2104
|
+
|
|
2105
|
+
memcpy(dctx->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize);
|
|
2106
|
+
|
|
2107
|
+
dctx->dict = dctx->tmpOutBuffer;
|
|
2108
|
+
dctx->dictSize = newDictSize;
|
|
2109
|
+
dctx->tmpOut = dctx->tmpOutBuffer + newDictSize;
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
*srcSizePtr = (size_t)(srcPtr - srcStart);
|
|
2114
|
+
*dstSizePtr = (size_t)(dstPtr - dstStart);
|
|
2115
|
+
return nextSrcSizeHint;
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
/*! LZ4F_decompress_usingDict() :
|
|
2119
|
+
* Same as LZ4F_decompress(), using a predefined dictionary.
|
|
2120
|
+
* Dictionary is used "in place", without any preprocessing.
|
|
2121
|
+
* It must remain accessible throughout the entire frame decoding.
|
|
2122
|
+
*/
|
|
2123
|
+
size_t LZ4F_decompress_usingDict(LZ4F_dctx* dctx,
|
|
2124
|
+
void* dstBuffer, size_t* dstSizePtr,
|
|
2125
|
+
const void* srcBuffer, size_t* srcSizePtr,
|
|
2126
|
+
const void* dict, size_t dictSize,
|
|
2127
|
+
const LZ4F_decompressOptions_t* decompressOptionsPtr)
|
|
2128
|
+
{
|
|
2129
|
+
if (dctx->dStage <= dstage_init) {
|
|
2130
|
+
dctx->dict = (const BYTE*)dict;
|
|
2131
|
+
dctx->dictSize = dictSize;
|
|
2132
|
+
}
|
|
2133
|
+
return LZ4F_decompress(dctx, dstBuffer, dstSizePtr,
|
|
2134
|
+
srcBuffer, srcSizePtr,
|
|
2135
|
+
decompressOptionsPtr);
|
|
2136
|
+
}
|