Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmzstd/lib/compress/zstd_ldm.c
5030 views
1
/*
2
* Copyright (c) Meta Platforms, Inc. and affiliates.
3
* All rights reserved.
4
*
5
* This source code is licensed under both the BSD-style license (found in the
6
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
7
* in the COPYING file in the root directory of this source tree).
8
* You may select, at your option, one of the above-listed licenses.
9
*/
10
11
#include "zstd_ldm.h"
12
13
#include "../common/debug.h"
14
#include "../common/xxhash.h"
15
#include "zstd_fast.h" /* ZSTD_fillHashTable() */
16
#include "zstd_double_fast.h" /* ZSTD_fillDoubleHashTable() */
17
#include "zstd_ldm_geartab.h"
18
19
#define LDM_BUCKET_SIZE_LOG 4
20
#define LDM_MIN_MATCH_LENGTH 64
21
#define LDM_HASH_RLOG 7
22
23
typedef struct {
24
U64 rolling;
25
U64 stopMask;
26
} ldmRollingHashState_t;
27
28
/** ZSTD_ldm_gear_init():
29
*
30
* Initializes the rolling hash state such that it will honor the
31
* settings in params. */
32
static void ZSTD_ldm_gear_init(ldmRollingHashState_t* state, ldmParams_t const* params)
33
{
34
unsigned maxBitsInMask = MIN(params->minMatchLength, 64);
35
unsigned hashRateLog = params->hashRateLog;
36
37
state->rolling = ~(U32)0;
38
39
/* The choice of the splitting criterion is subject to two conditions:
40
* 1. it has to trigger on average every 2^(hashRateLog) bytes;
41
* 2. ideally, it has to depend on a window of minMatchLength bytes.
42
*
43
* In the gear hash algorithm, bit n depends on the last n bytes;
44
* so in order to obtain a good quality splitting criterion it is
45
* preferable to use bits with high weight.
46
*
47
* To match condition 1 we use a mask with hashRateLog bits set
48
* and, because of the previous remark, we make sure these bits
49
* have the highest possible weight while still respecting
50
* condition 2.
51
*/
52
if (hashRateLog > 0 && hashRateLog <= maxBitsInMask) {
53
state->stopMask = (((U64)1 << hashRateLog) - 1) << (maxBitsInMask - hashRateLog);
54
} else {
55
/* In this degenerate case we simply honor the hash rate. */
56
state->stopMask = ((U64)1 << hashRateLog) - 1;
57
}
58
}
59
60
/** ZSTD_ldm_gear_reset()
61
* Feeds [data, data + minMatchLength) into the hash without registering any
62
* splits. This effectively resets the hash state. This is used when skipping
63
* over data, either at the beginning of a block, or skipping sections.
64
*/
65
static void ZSTD_ldm_gear_reset(ldmRollingHashState_t* state,
66
BYTE const* data, size_t minMatchLength)
67
{
68
U64 hash = state->rolling;
69
size_t n = 0;
70
71
#define GEAR_ITER_ONCE() do { \
72
hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \
73
n += 1; \
74
} while (0)
75
while (n + 3 < minMatchLength) {
76
GEAR_ITER_ONCE();
77
GEAR_ITER_ONCE();
78
GEAR_ITER_ONCE();
79
GEAR_ITER_ONCE();
80
}
81
while (n < minMatchLength) {
82
GEAR_ITER_ONCE();
83
}
84
#undef GEAR_ITER_ONCE
85
}
86
87
/** ZSTD_ldm_gear_feed():
88
*
89
* Registers in the splits array all the split points found in the first
90
* size bytes following the data pointer. This function terminates when
91
* either all the data has been processed or LDM_BATCH_SIZE splits are
92
* present in the splits array.
93
*
94
* Precondition: The splits array must not be full.
95
* Returns: The number of bytes processed. */
96
static size_t ZSTD_ldm_gear_feed(ldmRollingHashState_t* state,
97
BYTE const* data, size_t size,
98
size_t* splits, unsigned* numSplits)
99
{
100
size_t n;
101
U64 hash, mask;
102
103
hash = state->rolling;
104
mask = state->stopMask;
105
n = 0;
106
107
#define GEAR_ITER_ONCE() do { \
108
hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \
109
n += 1; \
110
if (UNLIKELY((hash & mask) == 0)) { \
111
splits[*numSplits] = n; \
112
*numSplits += 1; \
113
if (*numSplits == LDM_BATCH_SIZE) \
114
goto done; \
115
} \
116
} while (0)
117
118
while (n + 3 < size) {
119
GEAR_ITER_ONCE();
120
GEAR_ITER_ONCE();
121
GEAR_ITER_ONCE();
122
GEAR_ITER_ONCE();
123
}
124
while (n < size) {
125
GEAR_ITER_ONCE();
126
}
127
128
#undef GEAR_ITER_ONCE
129
130
done:
131
state->rolling = hash;
132
return n;
133
}
134
135
void ZSTD_ldm_adjustParameters(ldmParams_t* params,
136
const ZSTD_compressionParameters* cParams)
137
{
138
params->windowLog = cParams->windowLog;
139
ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
140
DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
141
if (params->hashRateLog == 0) {
142
if (params->hashLog > 0) {
143
/* if params->hashLog is set, derive hashRateLog from it */
144
assert(params->hashLog <= ZSTD_HASHLOG_MAX);
145
if (params->windowLog > params->hashLog) {
146
params->hashRateLog = params->windowLog - params->hashLog;
147
}
148
} else {
149
assert(1 <= (int)cParams->strategy && (int)cParams->strategy <= 9);
150
/* mapping from [fast, rate7] to [btultra2, rate4] */
151
params->hashRateLog = 7 - (cParams->strategy/3);
152
}
153
}
154
if (params->hashLog == 0) {
155
params->hashLog = BOUNDED(ZSTD_HASHLOG_MIN, params->windowLog - params->hashRateLog, ZSTD_HASHLOG_MAX);
156
}
157
if (params->minMatchLength == 0) {
158
params->minMatchLength = LDM_MIN_MATCH_LENGTH;
159
if (cParams->strategy >= ZSTD_btultra)
160
params->minMatchLength /= 2;
161
}
162
if (params->bucketSizeLog==0) {
163
assert(1 <= (int)cParams->strategy && (int)cParams->strategy <= 9);
164
params->bucketSizeLog = BOUNDED(LDM_BUCKET_SIZE_LOG, (U32)cParams->strategy, ZSTD_LDM_BUCKETSIZELOG_MAX);
165
}
166
params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog);
167
}
168
169
size_t ZSTD_ldm_getTableSize(ldmParams_t params)
170
{
171
size_t const ldmHSize = ((size_t)1) << params.hashLog;
172
size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog);
173
size_t const ldmBucketSize = ((size_t)1) << (params.hashLog - ldmBucketSizeLog);
174
size_t const totalSize = ZSTD_cwksp_alloc_size(ldmBucketSize)
175
+ ZSTD_cwksp_alloc_size(ldmHSize * sizeof(ldmEntry_t));
176
return params.enableLdm == ZSTD_ps_enable ? totalSize : 0;
177
}
178
179
size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize)
180
{
181
return params.enableLdm == ZSTD_ps_enable ? (maxChunkSize / params.minMatchLength) : 0;
182
}
183
184
/** ZSTD_ldm_getBucket() :
185
* Returns a pointer to the start of the bucket associated with hash. */
186
static ldmEntry_t* ZSTD_ldm_getBucket(
187
const ldmState_t* ldmState, size_t hash, U32 const bucketSizeLog)
188
{
189
return ldmState->hashTable + (hash << bucketSizeLog);
190
}
191
192
/** ZSTD_ldm_insertEntry() :
193
* Insert the entry with corresponding hash into the hash table */
194
static void ZSTD_ldm_insertEntry(ldmState_t* ldmState,
195
size_t const hash, const ldmEntry_t entry,
196
U32 const bucketSizeLog)
197
{
198
BYTE* const pOffset = ldmState->bucketOffsets + hash;
199
unsigned const offset = *pOffset;
200
201
*(ZSTD_ldm_getBucket(ldmState, hash, bucketSizeLog) + offset) = entry;
202
*pOffset = (BYTE)((offset + 1) & ((1u << bucketSizeLog) - 1));
203
204
}
205
206
/** ZSTD_ldm_countBackwardsMatch() :
207
* Returns the number of bytes that match backwards before pIn and pMatch.
208
*
209
* We count only bytes where pMatch >= pBase and pIn >= pAnchor. */
210
static size_t ZSTD_ldm_countBackwardsMatch(
211
const BYTE* pIn, const BYTE* pAnchor,
212
const BYTE* pMatch, const BYTE* pMatchBase)
213
{
214
size_t matchLength = 0;
215
while (pIn > pAnchor && pMatch > pMatchBase && pIn[-1] == pMatch[-1]) {
216
pIn--;
217
pMatch--;
218
matchLength++;
219
}
220
return matchLength;
221
}
222
223
/** ZSTD_ldm_countBackwardsMatch_2segments() :
224
* Returns the number of bytes that match backwards from pMatch,
225
* even with the backwards match spanning 2 different segments.
226
*
227
* On reaching `pMatchBase`, start counting from mEnd */
228
static size_t ZSTD_ldm_countBackwardsMatch_2segments(
229
const BYTE* pIn, const BYTE* pAnchor,
230
const BYTE* pMatch, const BYTE* pMatchBase,
231
const BYTE* pExtDictStart, const BYTE* pExtDictEnd)
232
{
233
size_t matchLength = ZSTD_ldm_countBackwardsMatch(pIn, pAnchor, pMatch, pMatchBase);
234
if (pMatch - matchLength != pMatchBase || pMatchBase == pExtDictStart) {
235
/* If backwards match is entirely in the extDict or prefix, immediately return */
236
return matchLength;
237
}
238
DEBUGLOG(7, "ZSTD_ldm_countBackwardsMatch_2segments: found 2-parts backwards match (length in prefix==%zu)", matchLength);
239
matchLength += ZSTD_ldm_countBackwardsMatch(pIn - matchLength, pAnchor, pExtDictEnd, pExtDictStart);
240
DEBUGLOG(7, "final backwards match length = %zu", matchLength);
241
return matchLength;
242
}
243
244
/** ZSTD_ldm_fillFastTables() :
245
*
246
* Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies.
247
* This is similar to ZSTD_loadDictionaryContent.
248
*
249
* The tables for the other strategies are filled within their
250
* block compressors. */
251
static size_t ZSTD_ldm_fillFastTables(ZSTD_MatchState_t* ms,
252
void const* end)
253
{
254
const BYTE* const iend = (const BYTE*)end;
255
256
switch(ms->cParams.strategy)
257
{
258
case ZSTD_fast:
259
ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);
260
break;
261
262
case ZSTD_dfast:
263
#ifndef ZSTD_EXCLUDE_DFAST_BLOCK_COMPRESSOR
264
ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);
265
#else
266
assert(0); /* shouldn't be called: cparams should've been adjusted. */
267
#endif
268
break;
269
270
case ZSTD_greedy:
271
case ZSTD_lazy:
272
case ZSTD_lazy2:
273
case ZSTD_btlazy2:
274
case ZSTD_btopt:
275
case ZSTD_btultra:
276
case ZSTD_btultra2:
277
break;
278
default:
279
assert(0); /* not possible : not a valid strategy id */
280
}
281
282
return 0;
283
}
284
285
void ZSTD_ldm_fillHashTable(
286
ldmState_t* ldmState, const BYTE* ip,
287
const BYTE* iend, ldmParams_t const* params)
288
{
289
U32 const minMatchLength = params->minMatchLength;
290
U32 const bucketSizeLog = params->bucketSizeLog;
291
U32 const hBits = params->hashLog - bucketSizeLog;
292
BYTE const* const base = ldmState->window.base;
293
BYTE const* const istart = ip;
294
ldmRollingHashState_t hashState;
295
size_t* const splits = ldmState->splitIndices;
296
unsigned numSplits;
297
298
DEBUGLOG(5, "ZSTD_ldm_fillHashTable");
299
300
ZSTD_ldm_gear_init(&hashState, params);
301
while (ip < iend) {
302
size_t hashed;
303
unsigned n;
304
305
numSplits = 0;
306
hashed = ZSTD_ldm_gear_feed(&hashState, ip, (size_t)(iend - ip), splits, &numSplits);
307
308
for (n = 0; n < numSplits; n++) {
309
if (ip + splits[n] >= istart + minMatchLength) {
310
BYTE const* const split = ip + splits[n] - minMatchLength;
311
U64 const xxhash = XXH64(split, minMatchLength, 0);
312
U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));
313
ldmEntry_t entry;
314
315
entry.offset = (U32)(split - base);
316
entry.checksum = (U32)(xxhash >> 32);
317
ZSTD_ldm_insertEntry(ldmState, hash, entry, params->bucketSizeLog);
318
}
319
}
320
321
ip += hashed;
322
}
323
}
324
325
326
/** ZSTD_ldm_limitTableUpdate() :
327
*
328
* Sets cctx->nextToUpdate to a position corresponding closer to anchor
329
* if it is far way
330
* (after a long match, only update tables a limited amount). */
331
static void ZSTD_ldm_limitTableUpdate(ZSTD_MatchState_t* ms, const BYTE* anchor)
332
{
333
U32 const curr = (U32)(anchor - ms->window.base);
334
if (curr > ms->nextToUpdate + 1024) {
335
ms->nextToUpdate =
336
curr - MIN(512, curr - ms->nextToUpdate - 1024);
337
}
338
}
339
340
static
341
ZSTD_ALLOW_POINTER_OVERFLOW_ATTR
342
size_t ZSTD_ldm_generateSequences_internal(
343
ldmState_t* ldmState, RawSeqStore_t* rawSeqStore,
344
ldmParams_t const* params, void const* src, size_t srcSize)
345
{
346
/* LDM parameters */
347
int const extDict = ZSTD_window_hasExtDict(ldmState->window);
348
U32 const minMatchLength = params->minMatchLength;
349
U32 const entsPerBucket = 1U << params->bucketSizeLog;
350
U32 const hBits = params->hashLog - params->bucketSizeLog;
351
/* Prefix and extDict parameters */
352
U32 const dictLimit = ldmState->window.dictLimit;
353
U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit;
354
BYTE const* const base = ldmState->window.base;
355
BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL;
356
BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL;
357
BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL;
358
BYTE const* const lowPrefixPtr = base + dictLimit;
359
/* Input bounds */
360
BYTE const* const istart = (BYTE const*)src;
361
BYTE const* const iend = istart + srcSize;
362
BYTE const* const ilimit = iend - HASH_READ_SIZE;
363
/* Input positions */
364
BYTE const* anchor = istart;
365
BYTE const* ip = istart;
366
/* Rolling hash state */
367
ldmRollingHashState_t hashState;
368
/* Arrays for staged-processing */
369
size_t* const splits = ldmState->splitIndices;
370
ldmMatchCandidate_t* const candidates = ldmState->matchCandidates;
371
unsigned numSplits;
372
373
if (srcSize < minMatchLength)
374
return iend - anchor;
375
376
/* Initialize the rolling hash state with the first minMatchLength bytes */
377
ZSTD_ldm_gear_init(&hashState, params);
378
ZSTD_ldm_gear_reset(&hashState, ip, minMatchLength);
379
ip += minMatchLength;
380
381
while (ip < ilimit) {
382
size_t hashed;
383
unsigned n;
384
385
numSplits = 0;
386
hashed = ZSTD_ldm_gear_feed(&hashState, ip, ilimit - ip,
387
splits, &numSplits);
388
389
for (n = 0; n < numSplits; n++) {
390
BYTE const* const split = ip + splits[n] - minMatchLength;
391
U64 const xxhash = XXH64(split, minMatchLength, 0);
392
U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));
393
394
candidates[n].split = split;
395
candidates[n].hash = hash;
396
candidates[n].checksum = (U32)(xxhash >> 32);
397
candidates[n].bucket = ZSTD_ldm_getBucket(ldmState, hash, params->bucketSizeLog);
398
PREFETCH_L1(candidates[n].bucket);
399
}
400
401
for (n = 0; n < numSplits; n++) {
402
size_t forwardMatchLength = 0, backwardMatchLength = 0,
403
bestMatchLength = 0, mLength;
404
U32 offset;
405
BYTE const* const split = candidates[n].split;
406
U32 const checksum = candidates[n].checksum;
407
U32 const hash = candidates[n].hash;
408
ldmEntry_t* const bucket = candidates[n].bucket;
409
ldmEntry_t const* cur;
410
ldmEntry_t const* bestEntry = NULL;
411
ldmEntry_t newEntry;
412
413
newEntry.offset = (U32)(split - base);
414
newEntry.checksum = checksum;
415
416
/* If a split point would generate a sequence overlapping with
417
* the previous one, we merely register it in the hash table and
418
* move on */
419
if (split < anchor) {
420
ZSTD_ldm_insertEntry(ldmState, hash, newEntry, params->bucketSizeLog);
421
continue;
422
}
423
424
for (cur = bucket; cur < bucket + entsPerBucket; cur++) {
425
size_t curForwardMatchLength, curBackwardMatchLength,
426
curTotalMatchLength;
427
if (cur->checksum != checksum || cur->offset <= lowestIndex) {
428
continue;
429
}
430
if (extDict) {
431
BYTE const* const curMatchBase =
432
cur->offset < dictLimit ? dictBase : base;
433
BYTE const* const pMatch = curMatchBase + cur->offset;
434
BYTE const* const matchEnd =
435
cur->offset < dictLimit ? dictEnd : iend;
436
BYTE const* const lowMatchPtr =
437
cur->offset < dictLimit ? dictStart : lowPrefixPtr;
438
curForwardMatchLength =
439
ZSTD_count_2segments(split, pMatch, iend, matchEnd, lowPrefixPtr);
440
if (curForwardMatchLength < minMatchLength) {
441
continue;
442
}
443
curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch_2segments(
444
split, anchor, pMatch, lowMatchPtr, dictStart, dictEnd);
445
} else { /* !extDict */
446
BYTE const* const pMatch = base + cur->offset;
447
curForwardMatchLength = ZSTD_count(split, pMatch, iend);
448
if (curForwardMatchLength < minMatchLength) {
449
continue;
450
}
451
curBackwardMatchLength =
452
ZSTD_ldm_countBackwardsMatch(split, anchor, pMatch, lowPrefixPtr);
453
}
454
curTotalMatchLength = curForwardMatchLength + curBackwardMatchLength;
455
456
if (curTotalMatchLength > bestMatchLength) {
457
bestMatchLength = curTotalMatchLength;
458
forwardMatchLength = curForwardMatchLength;
459
backwardMatchLength = curBackwardMatchLength;
460
bestEntry = cur;
461
}
462
}
463
464
/* No match found -- insert an entry into the hash table
465
* and process the next candidate match */
466
if (bestEntry == NULL) {
467
ZSTD_ldm_insertEntry(ldmState, hash, newEntry, params->bucketSizeLog);
468
continue;
469
}
470
471
/* Match found */
472
offset = (U32)(split - base) - bestEntry->offset;
473
mLength = forwardMatchLength + backwardMatchLength;
474
{
475
rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
476
477
/* Out of sequence storage */
478
if (rawSeqStore->size == rawSeqStore->capacity)
479
return ERROR(dstSize_tooSmall);
480
seq->litLength = (U32)(split - backwardMatchLength - anchor);
481
seq->matchLength = (U32)mLength;
482
seq->offset = offset;
483
rawSeqStore->size++;
484
}
485
486
/* Insert the current entry into the hash table --- it must be
487
* done after the previous block to avoid clobbering bestEntry */
488
ZSTD_ldm_insertEntry(ldmState, hash, newEntry, params->bucketSizeLog);
489
490
anchor = split + forwardMatchLength;
491
492
/* If we find a match that ends after the data that we've hashed
493
* then we have a repeating, overlapping, pattern. E.g. all zeros.
494
* If one repetition of the pattern matches our `stopMask` then all
495
* repetitions will. We don't need to insert them all into out table,
496
* only the first one. So skip over overlapping matches.
497
* This is a major speed boost (20x) for compressing a single byte
498
* repeated, when that byte ends up in the table.
499
*/
500
if (anchor > ip + hashed) {
501
ZSTD_ldm_gear_reset(&hashState, anchor - minMatchLength, minMatchLength);
502
/* Continue the outer loop at anchor (ip + hashed == anchor). */
503
ip = anchor - hashed;
504
break;
505
}
506
}
507
508
ip += hashed;
509
}
510
511
return iend - anchor;
512
}
513
514
/*! ZSTD_ldm_reduceTable() :
515
* reduce table indexes by `reducerValue` */
516
static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,
517
U32 const reducerValue)
518
{
519
U32 u;
520
for (u = 0; u < size; u++) {
521
if (table[u].offset < reducerValue) table[u].offset = 0;
522
else table[u].offset -= reducerValue;
523
}
524
}
525
526
size_t ZSTD_ldm_generateSequences(
527
ldmState_t* ldmState, RawSeqStore_t* sequences,
528
ldmParams_t const* params, void const* src, size_t srcSize)
529
{
530
U32 const maxDist = 1U << params->windowLog;
531
BYTE const* const istart = (BYTE const*)src;
532
BYTE const* const iend = istart + srcSize;
533
size_t const kMaxChunkSize = 1 << 20;
534
size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);
535
size_t chunk;
536
size_t leftoverSize = 0;
537
538
assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize);
539
/* Check that ZSTD_window_update() has been called for this chunk prior
540
* to passing it to this function.
541
*/
542
assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);
543
/* The input could be very large (in zstdmt), so it must be broken up into
544
* chunks to enforce the maximum distance and handle overflow correction.
545
*/
546
assert(sequences->pos <= sequences->size);
547
assert(sequences->size <= sequences->capacity);
548
for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) {
549
BYTE const* const chunkStart = istart + chunk * kMaxChunkSize;
550
size_t const remaining = (size_t)(iend - chunkStart);
551
BYTE const *const chunkEnd =
552
(remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize;
553
size_t const chunkSize = chunkEnd - chunkStart;
554
size_t newLeftoverSize;
555
size_t const prevSize = sequences->size;
556
557
assert(chunkStart < iend);
558
/* 1. Perform overflow correction if necessary. */
559
if (ZSTD_window_needOverflowCorrection(ldmState->window, 0, maxDist, ldmState->loadedDictEnd, chunkStart, chunkEnd)) {
560
U32 const ldmHSize = 1U << params->hashLog;
561
U32 const correction = ZSTD_window_correctOverflow(
562
&ldmState->window, /* cycleLog */ 0, maxDist, chunkStart);
563
ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);
564
/* invalidate dictionaries on overflow correction */
565
ldmState->loadedDictEnd = 0;
566
}
567
/* 2. We enforce the maximum offset allowed.
568
*
569
* kMaxChunkSize should be small enough that we don't lose too much of
570
* the window through early invalidation.
571
* TODO: * Test the chunk size.
572
* * Try invalidation after the sequence generation and test the
573
* offset against maxDist directly.
574
*
575
* NOTE: Because of dictionaries + sequence splitting we MUST make sure
576
* that any offset used is valid at the END of the sequence, since it may
577
* be split into two sequences. This condition holds when using
578
* ZSTD_window_enforceMaxDist(), but if we move to checking offsets
579
* against maxDist directly, we'll have to carefully handle that case.
580
*/
581
ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, &ldmState->loadedDictEnd, NULL);
582
/* 3. Generate the sequences for the chunk, and get newLeftoverSize. */
583
newLeftoverSize = ZSTD_ldm_generateSequences_internal(
584
ldmState, sequences, params, chunkStart, chunkSize);
585
if (ZSTD_isError(newLeftoverSize))
586
return newLeftoverSize;
587
/* 4. We add the leftover literals from previous iterations to the first
588
* newly generated sequence, or add the `newLeftoverSize` if none are
589
* generated.
590
*/
591
/* Prepend the leftover literals from the last call */
592
if (prevSize < sequences->size) {
593
sequences->seq[prevSize].litLength += (U32)leftoverSize;
594
leftoverSize = newLeftoverSize;
595
} else {
596
assert(newLeftoverSize == chunkSize);
597
leftoverSize += chunkSize;
598
}
599
}
600
return 0;
601
}
602
603
void
604
ZSTD_ldm_skipSequences(RawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch)
605
{
606
while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) {
607
rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos;
608
if (srcSize <= seq->litLength) {
609
/* Skip past srcSize literals */
610
seq->litLength -= (U32)srcSize;
611
return;
612
}
613
srcSize -= seq->litLength;
614
seq->litLength = 0;
615
if (srcSize < seq->matchLength) {
616
/* Skip past the first srcSize of the match */
617
seq->matchLength -= (U32)srcSize;
618
if (seq->matchLength < minMatch) {
619
/* The match is too short, omit it */
620
if (rawSeqStore->pos + 1 < rawSeqStore->size) {
621
seq[1].litLength += seq[0].matchLength;
622
}
623
rawSeqStore->pos++;
624
}
625
return;
626
}
627
srcSize -= seq->matchLength;
628
seq->matchLength = 0;
629
rawSeqStore->pos++;
630
}
631
}
632
633
/**
634
* If the sequence length is longer than remaining then the sequence is split
635
* between this block and the next.
636
*
637
* Returns the current sequence to handle, or if the rest of the block should
638
* be literals, it returns a sequence with offset == 0.
639
*/
640
static rawSeq maybeSplitSequence(RawSeqStore_t* rawSeqStore,
641
U32 const remaining, U32 const minMatch)
642
{
643
rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos];
644
assert(sequence.offset > 0);
645
/* Likely: No partial sequence */
646
if (remaining >= sequence.litLength + sequence.matchLength) {
647
rawSeqStore->pos++;
648
return sequence;
649
}
650
/* Cut the sequence short (offset == 0 ==> rest is literals). */
651
if (remaining <= sequence.litLength) {
652
sequence.offset = 0;
653
} else if (remaining < sequence.litLength + sequence.matchLength) {
654
sequence.matchLength = remaining - sequence.litLength;
655
if (sequence.matchLength < minMatch) {
656
sequence.offset = 0;
657
}
658
}
659
/* Skip past `remaining` bytes for the future sequences. */
660
ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch);
661
return sequence;
662
}
663
664
void ZSTD_ldm_skipRawSeqStoreBytes(RawSeqStore_t* rawSeqStore, size_t nbBytes) {
665
U32 currPos = (U32)(rawSeqStore->posInSequence + nbBytes);
666
while (currPos && rawSeqStore->pos < rawSeqStore->size) {
667
rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos];
668
if (currPos >= currSeq.litLength + currSeq.matchLength) {
669
currPos -= currSeq.litLength + currSeq.matchLength;
670
rawSeqStore->pos++;
671
} else {
672
rawSeqStore->posInSequence = currPos;
673
break;
674
}
675
}
676
if (currPos == 0 || rawSeqStore->pos == rawSeqStore->size) {
677
rawSeqStore->posInSequence = 0;
678
}
679
}
680
681
size_t ZSTD_ldm_blockCompress(RawSeqStore_t* rawSeqStore,
682
ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
683
ZSTD_ParamSwitch_e useRowMatchFinder,
684
void const* src, size_t srcSize)
685
{
686
const ZSTD_compressionParameters* const cParams = &ms->cParams;
687
unsigned const minMatch = cParams->minMatch;
688
ZSTD_BlockCompressor_f const blockCompressor =
689
ZSTD_selectBlockCompressor(cParams->strategy, useRowMatchFinder, ZSTD_matchState_dictMode(ms));
690
/* Input bounds */
691
BYTE const* const istart = (BYTE const*)src;
692
BYTE const* const iend = istart + srcSize;
693
/* Input positions */
694
BYTE const* ip = istart;
695
696
DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize);
697
/* If using opt parser, use LDMs only as candidates rather than always accepting them */
698
if (cParams->strategy >= ZSTD_btopt) {
699
size_t lastLLSize;
700
ms->ldmSeqStore = rawSeqStore;
701
lastLLSize = blockCompressor(ms, seqStore, rep, src, srcSize);
702
ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore, srcSize);
703
return lastLLSize;
704
}
705
706
assert(rawSeqStore->pos <= rawSeqStore->size);
707
assert(rawSeqStore->size <= rawSeqStore->capacity);
708
/* Loop through each sequence and apply the block compressor to the literals */
709
while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {
710
/* maybeSplitSequence updates rawSeqStore->pos */
711
rawSeq const sequence = maybeSplitSequence(rawSeqStore,
712
(U32)(iend - ip), minMatch);
713
/* End signal */
714
if (sequence.offset == 0)
715
break;
716
717
assert(ip + sequence.litLength + sequence.matchLength <= iend);
718
719
/* Fill tables for block compressor */
720
ZSTD_ldm_limitTableUpdate(ms, ip);
721
ZSTD_ldm_fillFastTables(ms, ip);
722
/* Run the block compressor */
723
DEBUGLOG(5, "pos %u : calling block compressor on segment of size %u", (unsigned)(ip-istart), sequence.litLength);
724
{
725
int i;
726
size_t const newLitLength =
727
blockCompressor(ms, seqStore, rep, ip, sequence.litLength);
728
ip += sequence.litLength;
729
/* Update the repcodes */
730
for (i = ZSTD_REP_NUM - 1; i > 0; i--)
731
rep[i] = rep[i-1];
732
rep[0] = sequence.offset;
733
/* Store the sequence */
734
ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend,
735
OFFSET_TO_OFFBASE(sequence.offset),
736
sequence.matchLength);
737
ip += sequence.matchLength;
738
}
739
}
740
/* Fill the tables for the block compressor */
741
ZSTD_ldm_limitTableUpdate(ms, ip);
742
ZSTD_ldm_fillFastTables(ms, ip);
743
/* Compress the last literals */
744
return blockCompressor(ms, seqStore, rep, ip, iend - ip);
745
}
746
747