zstd-ruby 1.3.8.0 → 1.4.5.0
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 +4 -4
- data/.travis.yml +6 -5
- data/README.md +1 -1
- data/ext/zstdruby/libzstd/Makefile +133 -61
- data/ext/zstdruby/libzstd/README.md +51 -18
- data/ext/zstdruby/libzstd/common/bitstream.h +38 -39
- data/ext/zstdruby/libzstd/common/compiler.h +41 -6
- data/ext/zstdruby/libzstd/common/cpu.h +1 -1
- data/ext/zstdruby/libzstd/common/debug.c +11 -31
- data/ext/zstdruby/libzstd/common/debug.h +11 -31
- data/ext/zstdruby/libzstd/common/entropy_common.c +13 -33
- data/ext/zstdruby/libzstd/common/error_private.c +2 -1
- data/ext/zstdruby/libzstd/common/error_private.h +6 -2
- data/ext/zstdruby/libzstd/common/fse.h +13 -33
- data/ext/zstdruby/libzstd/common/fse_decompress.c +12 -35
- data/ext/zstdruby/libzstd/common/huf.h +15 -33
- data/ext/zstdruby/libzstd/common/mem.h +75 -2
- data/ext/zstdruby/libzstd/common/pool.c +8 -4
- data/ext/zstdruby/libzstd/common/pool.h +2 -2
- data/ext/zstdruby/libzstd/common/threading.c +52 -6
- data/ext/zstdruby/libzstd/common/threading.h +36 -4
- data/ext/zstdruby/libzstd/common/xxhash.c +25 -37
- data/ext/zstdruby/libzstd/common/xxhash.h +11 -31
- data/ext/zstdruby/libzstd/common/zstd_common.c +1 -1
- data/ext/zstdruby/libzstd/common/zstd_errors.h +2 -1
- data/ext/zstdruby/libzstd/common/zstd_internal.h +203 -22
- data/ext/zstdruby/libzstd/compress/fse_compress.c +19 -42
- data/ext/zstdruby/libzstd/compress/hist.c +15 -35
- data/ext/zstdruby/libzstd/compress/hist.h +12 -32
- data/ext/zstdruby/libzstd/compress/huf_compress.c +92 -92
- data/ext/zstdruby/libzstd/compress/zstd_compress.c +1460 -1472
- data/ext/zstdruby/libzstd/compress/zstd_compress_internal.h +330 -65
- data/ext/zstdruby/libzstd/compress/zstd_compress_literals.c +158 -0
- data/ext/zstdruby/libzstd/compress/zstd_compress_literals.h +29 -0
- data/ext/zstdruby/libzstd/compress/zstd_compress_sequences.c +419 -0
- data/ext/zstdruby/libzstd/compress/zstd_compress_sequences.h +54 -0
- data/ext/zstdruby/libzstd/compress/zstd_compress_superblock.c +845 -0
- data/ext/zstdruby/libzstd/compress/zstd_compress_superblock.h +32 -0
- data/ext/zstdruby/libzstd/compress/zstd_cwksp.h +525 -0
- data/ext/zstdruby/libzstd/compress/zstd_double_fast.c +65 -43
- data/ext/zstdruby/libzstd/compress/zstd_double_fast.h +2 -2
- data/ext/zstdruby/libzstd/compress/zstd_fast.c +264 -159
- data/ext/zstdruby/libzstd/compress/zstd_fast.h +2 -2
- data/ext/zstdruby/libzstd/compress/zstd_lazy.c +74 -42
- data/ext/zstdruby/libzstd/compress/zstd_lazy.h +2 -2
- data/ext/zstdruby/libzstd/compress/zstd_ldm.c +33 -11
- data/ext/zstdruby/libzstd/compress/zstd_ldm.h +7 -2
- data/ext/zstdruby/libzstd/compress/zstd_opt.c +108 -125
- data/ext/zstdruby/libzstd/compress/zstd_opt.h +1 -1
- data/ext/zstdruby/libzstd/compress/zstdmt_compress.c +129 -93
- data/ext/zstdruby/libzstd/compress/zstdmt_compress.h +46 -28
- data/ext/zstdruby/libzstd/decompress/huf_decompress.c +76 -60
- data/ext/zstdruby/libzstd/decompress/zstd_ddict.c +14 -10
- data/ext/zstdruby/libzstd/decompress/zstd_ddict.h +2 -2
- data/ext/zstdruby/libzstd/decompress/zstd_decompress.c +471 -258
- data/ext/zstdruby/libzstd/decompress/zstd_decompress_block.c +471 -346
- data/ext/zstdruby/libzstd/decompress/zstd_decompress_block.h +3 -3
- data/ext/zstdruby/libzstd/decompress/zstd_decompress_internal.h +25 -4
- data/ext/zstdruby/libzstd/deprecated/zbuff.h +9 -8
- data/ext/zstdruby/libzstd/deprecated/zbuff_common.c +2 -2
- data/ext/zstdruby/libzstd/deprecated/zbuff_compress.c +1 -1
- data/ext/zstdruby/libzstd/deprecated/zbuff_decompress.c +1 -1
- data/ext/zstdruby/libzstd/dictBuilder/cover.c +220 -65
- data/ext/zstdruby/libzstd/dictBuilder/cover.h +81 -7
- data/ext/zstdruby/libzstd/dictBuilder/fastcover.c +85 -56
- data/ext/zstdruby/libzstd/dictBuilder/zdict.c +43 -19
- data/ext/zstdruby/libzstd/dictBuilder/zdict.h +73 -35
- data/ext/zstdruby/libzstd/dll/example/Makefile +2 -1
- data/ext/zstdruby/libzstd/dll/example/build_package.bat +3 -2
- data/ext/zstdruby/libzstd/legacy/zstd_legacy.h +49 -15
- data/ext/zstdruby/libzstd/legacy/zstd_v01.c +142 -117
- data/ext/zstdruby/libzstd/legacy/zstd_v01.h +13 -8
- data/ext/zstdruby/libzstd/legacy/zstd_v02.c +54 -25
- data/ext/zstdruby/libzstd/legacy/zstd_v02.h +13 -8
- data/ext/zstdruby/libzstd/legacy/zstd_v03.c +55 -25
- data/ext/zstdruby/libzstd/legacy/zstd_v03.h +13 -8
- data/ext/zstdruby/libzstd/legacy/zstd_v04.c +62 -29
- data/ext/zstdruby/libzstd/legacy/zstd_v04.h +13 -8
- data/ext/zstdruby/libzstd/legacy/zstd_v05.c +145 -109
- data/ext/zstdruby/libzstd/legacy/zstd_v05.h +14 -9
- data/ext/zstdruby/libzstd/legacy/zstd_v06.c +56 -26
- data/ext/zstdruby/libzstd/legacy/zstd_v06.h +11 -6
- data/ext/zstdruby/libzstd/legacy/zstd_v07.c +65 -28
- data/ext/zstdruby/libzstd/legacy/zstd_v07.h +11 -6
- data/ext/zstdruby/libzstd/libzstd.pc.in +3 -2
- data/ext/zstdruby/libzstd/zstd.h +921 -597
- data/lib/zstd-ruby/version.rb +1 -1
- data/zstd-ruby.gemspec +2 -2
- metadata +19 -14
- data/ext/zstdruby/libzstd/dll/libzstd.def +0 -87
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c) 2016-
|
|
2
|
+
* Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
|
|
3
3
|
* All rights reserved.
|
|
4
4
|
*
|
|
5
5
|
* This source code is licensed under both the BSD-style license (found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
|
7
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.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
#ifndef ZSTD_LDM_H
|
|
@@ -15,7 +16,7 @@ extern "C" {
|
|
|
15
16
|
#endif
|
|
16
17
|
|
|
17
18
|
#include "zstd_compress_internal.h" /* ldmParams_t, U32 */
|
|
18
|
-
#include "zstd.h" /* ZSTD_CCtx, size_t */
|
|
19
|
+
#include "../zstd.h" /* ZSTD_CCtx, size_t */
|
|
19
20
|
|
|
20
21
|
/*-*************************************
|
|
21
22
|
* Long distance matching
|
|
@@ -23,6 +24,10 @@ extern "C" {
|
|
|
23
24
|
|
|
24
25
|
#define ZSTD_LDM_DEFAULT_WINDOW_LOG ZSTD_WINDOWLOG_LIMIT_DEFAULT
|
|
25
26
|
|
|
27
|
+
void ZSTD_ldm_fillHashTable(
|
|
28
|
+
ldmState_t* state, const BYTE* ip,
|
|
29
|
+
const BYTE* iend, ldmParams_t const* params);
|
|
30
|
+
|
|
26
31
|
/**
|
|
27
32
|
* ZSTD_ldm_generateSequences():
|
|
28
33
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c) 2016-
|
|
2
|
+
* Copyright (c) 2016-2020, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
|
|
3
3
|
* All rights reserved.
|
|
4
4
|
*
|
|
5
5
|
* This source code is licensed under both the BSD-style license (found in the
|
|
@@ -64,9 +64,15 @@ MEM_STATIC double ZSTD_fCost(U32 price)
|
|
|
64
64
|
}
|
|
65
65
|
#endif
|
|
66
66
|
|
|
67
|
+
static int ZSTD_compressedLiterals(optState_t const* const optPtr)
|
|
68
|
+
{
|
|
69
|
+
return optPtr->literalCompressionMode != ZSTD_lcm_uncompressed;
|
|
70
|
+
}
|
|
71
|
+
|
|
67
72
|
static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel)
|
|
68
73
|
{
|
|
69
|
-
|
|
74
|
+
if (ZSTD_compressedLiterals(optPtr))
|
|
75
|
+
optPtr->litSumBasePrice = WEIGHT(optPtr->litSum, optLevel);
|
|
70
76
|
optPtr->litLengthSumBasePrice = WEIGHT(optPtr->litLengthSum, optLevel);
|
|
71
77
|
optPtr->matchLengthSumBasePrice = WEIGHT(optPtr->matchLengthSum, optLevel);
|
|
72
78
|
optPtr->offCodeSumBasePrice = WEIGHT(optPtr->offCodeSum, optLevel);
|
|
@@ -99,6 +105,7 @@ ZSTD_rescaleFreqs(optState_t* const optPtr,
|
|
|
99
105
|
const BYTE* const src, size_t const srcSize,
|
|
100
106
|
int const optLevel)
|
|
101
107
|
{
|
|
108
|
+
int const compressedLiterals = ZSTD_compressedLiterals(optPtr);
|
|
102
109
|
DEBUGLOG(5, "ZSTD_rescaleFreqs (srcSize=%u)", (unsigned)srcSize);
|
|
103
110
|
optPtr->priceType = zop_dynamic;
|
|
104
111
|
|
|
@@ -113,9 +120,10 @@ ZSTD_rescaleFreqs(optState_t* const optPtr,
|
|
|
113
120
|
/* huffman table presumed generated by dictionary */
|
|
114
121
|
optPtr->priceType = zop_dynamic;
|
|
115
122
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
if (compressedLiterals) {
|
|
124
|
+
unsigned lit;
|
|
125
|
+
assert(optPtr->litFreq != NULL);
|
|
126
|
+
optPtr->litSum = 0;
|
|
119
127
|
for (lit=0; lit<=MaxLit; lit++) {
|
|
120
128
|
U32 const scaleLog = 11; /* scale to 2K */
|
|
121
129
|
U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->huf.CTable, lit);
|
|
@@ -163,10 +171,11 @@ ZSTD_rescaleFreqs(optState_t* const optPtr,
|
|
|
163
171
|
} else { /* not a dictionary */
|
|
164
172
|
|
|
165
173
|
assert(optPtr->litFreq != NULL);
|
|
166
|
-
|
|
174
|
+
if (compressedLiterals) {
|
|
175
|
+
unsigned lit = MaxLit;
|
|
167
176
|
HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */
|
|
177
|
+
optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1);
|
|
168
178
|
}
|
|
169
|
-
optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1);
|
|
170
179
|
|
|
171
180
|
{ unsigned ll;
|
|
172
181
|
for (ll=0; ll<=MaxLL; ll++)
|
|
@@ -190,7 +199,8 @@ ZSTD_rescaleFreqs(optState_t* const optPtr,
|
|
|
190
199
|
|
|
191
200
|
} else { /* new block : re-use previous statistics, scaled down */
|
|
192
201
|
|
|
193
|
-
|
|
202
|
+
if (compressedLiterals)
|
|
203
|
+
optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1);
|
|
194
204
|
optPtr->litLengthSum = ZSTD_downscaleStat(optPtr->litLengthFreq, MaxLL, 0);
|
|
195
205
|
optPtr->matchLengthSum = ZSTD_downscaleStat(optPtr->matchLengthFreq, MaxML, 0);
|
|
196
206
|
optPtr->offCodeSum = ZSTD_downscaleStat(optPtr->offCodeFreq, MaxOff, 0);
|
|
@@ -207,6 +217,10 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength,
|
|
|
207
217
|
int optLevel)
|
|
208
218
|
{
|
|
209
219
|
if (litLength == 0) return 0;
|
|
220
|
+
|
|
221
|
+
if (!ZSTD_compressedLiterals(optPtr))
|
|
222
|
+
return (litLength << 3) * BITCOST_MULTIPLIER; /* Uncompressed - 8 bytes per literal. */
|
|
223
|
+
|
|
210
224
|
if (optPtr->priceType == zop_predef)
|
|
211
225
|
return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */
|
|
212
226
|
|
|
@@ -235,40 +249,6 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP
|
|
|
235
249
|
}
|
|
236
250
|
}
|
|
237
251
|
|
|
238
|
-
/* ZSTD_litLengthContribution() :
|
|
239
|
-
* @return ( cost(litlength) - cost(0) )
|
|
240
|
-
* this value can then be added to rawLiteralsCost()
|
|
241
|
-
* to provide a cost which is directly comparable to a match ending at same position */
|
|
242
|
-
static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr, int optLevel)
|
|
243
|
-
{
|
|
244
|
-
if (optPtr->priceType >= zop_predef) return WEIGHT(litLength, optLevel);
|
|
245
|
-
|
|
246
|
-
/* dynamic statistics */
|
|
247
|
-
{ U32 const llCode = ZSTD_LLcode(litLength);
|
|
248
|
-
int const contribution = (LL_bits[llCode] * BITCOST_MULTIPLIER)
|
|
249
|
-
+ WEIGHT(optPtr->litLengthFreq[0], optLevel) /* note: log2litLengthSum cancel out */
|
|
250
|
-
- WEIGHT(optPtr->litLengthFreq[llCode], optLevel);
|
|
251
|
-
#if 1
|
|
252
|
-
return contribution;
|
|
253
|
-
#else
|
|
254
|
-
return MAX(0, contribution); /* sometimes better, sometimes not ... */
|
|
255
|
-
#endif
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/* ZSTD_literalsContribution() :
|
|
260
|
-
* creates a fake cost for the literals part of a sequence
|
|
261
|
-
* which can be compared to the ending cost of a match
|
|
262
|
-
* should a new match start at this position */
|
|
263
|
-
static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLength,
|
|
264
|
-
const optState_t* const optPtr,
|
|
265
|
-
int optLevel)
|
|
266
|
-
{
|
|
267
|
-
int const contribution = ZSTD_rawLiteralsCost(literals, litLength, optPtr, optLevel)
|
|
268
|
-
+ ZSTD_litLengthContribution(litLength, optPtr, optLevel);
|
|
269
|
-
return contribution;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
252
|
/* ZSTD_getMatchPrice() :
|
|
273
253
|
* Provides the cost of the match part (offset + matchLength) of a sequence
|
|
274
254
|
* Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence.
|
|
@@ -310,7 +290,8 @@ static void ZSTD_updateStats(optState_t* const optPtr,
|
|
|
310
290
|
U32 offsetCode, U32 matchLength)
|
|
311
291
|
{
|
|
312
292
|
/* literals */
|
|
313
|
-
{
|
|
293
|
+
if (ZSTD_compressedLiterals(optPtr)) {
|
|
294
|
+
U32 u;
|
|
314
295
|
for (u=0; u < litLength; u++)
|
|
315
296
|
optPtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD;
|
|
316
297
|
optPtr->litSum += litLength*ZSTD_LITFREQ_ADD;
|
|
@@ -357,13 +338,15 @@ MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length)
|
|
|
357
338
|
|
|
358
339
|
/* Update hashTable3 up to ip (excluded)
|
|
359
340
|
Assumption : always within prefix (i.e. not within extDict) */
|
|
360
|
-
static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms,
|
|
341
|
+
static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms,
|
|
342
|
+
U32* nextToUpdate3,
|
|
343
|
+
const BYTE* const ip)
|
|
361
344
|
{
|
|
362
345
|
U32* const hashTable3 = ms->hashTable3;
|
|
363
346
|
U32 const hashLog3 = ms->hashLog3;
|
|
364
347
|
const BYTE* const base = ms->window.base;
|
|
365
|
-
U32 idx =
|
|
366
|
-
U32 const target =
|
|
348
|
+
U32 idx = *nextToUpdate3;
|
|
349
|
+
U32 const target = (U32)(ip - base);
|
|
367
350
|
size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3);
|
|
368
351
|
assert(hashLog3 > 0);
|
|
369
352
|
|
|
@@ -372,6 +355,7 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE*
|
|
|
372
355
|
idx++;
|
|
373
356
|
}
|
|
374
357
|
|
|
358
|
+
*nextToUpdate3 = target;
|
|
375
359
|
return hashTable3[hash3];
|
|
376
360
|
}
|
|
377
361
|
|
|
@@ -488,9 +472,11 @@ static U32 ZSTD_insertBt1(
|
|
|
488
472
|
} }
|
|
489
473
|
|
|
490
474
|
*smallerPtr = *largerPtr = 0;
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
475
|
+
{ U32 positions = 0;
|
|
476
|
+
if (bestLength > 384) positions = MIN(192, (U32)(bestLength - 384)); /* speed optimization */
|
|
477
|
+
assert(matchEndIdx > current + 8);
|
|
478
|
+
return MAX(positions, matchEndIdx - (current + 8));
|
|
479
|
+
}
|
|
494
480
|
}
|
|
495
481
|
|
|
496
482
|
FORCE_INLINE_TEMPLATE
|
|
@@ -505,8 +491,13 @@ void ZSTD_updateTree_internal(
|
|
|
505
491
|
DEBUGLOG(6, "ZSTD_updateTree_internal, from %u to %u (dictMode:%u)",
|
|
506
492
|
idx, target, dictMode);
|
|
507
493
|
|
|
508
|
-
while(idx < target)
|
|
509
|
-
|
|
494
|
+
while(idx < target) {
|
|
495
|
+
U32 const forward = ZSTD_insertBt1(ms, base+idx, iend, mls, dictMode == ZSTD_extDict);
|
|
496
|
+
assert(idx < (U32)(idx + forward));
|
|
497
|
+
idx += forward;
|
|
498
|
+
}
|
|
499
|
+
assert((size_t)(ip - base) <= (size_t)(U32)(-1));
|
|
500
|
+
assert((size_t)(iend - base) <= (size_t)(U32)(-1));
|
|
510
501
|
ms->nextToUpdate = target;
|
|
511
502
|
}
|
|
512
503
|
|
|
@@ -516,11 +507,12 @@ void ZSTD_updateTree(ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iend) {
|
|
|
516
507
|
|
|
517
508
|
FORCE_INLINE_TEMPLATE
|
|
518
509
|
U32 ZSTD_insertBtAndGetAllMatches (
|
|
510
|
+
ZSTD_match_t* matches, /* store result (found matches) in this table (presumed large enough) */
|
|
519
511
|
ZSTD_matchState_t* ms,
|
|
512
|
+
U32* nextToUpdate3,
|
|
520
513
|
const BYTE* const ip, const BYTE* const iLimit, const ZSTD_dictMode_e dictMode,
|
|
521
|
-
U32 rep[ZSTD_REP_NUM],
|
|
514
|
+
const U32 rep[ZSTD_REP_NUM],
|
|
522
515
|
U32 const ll0, /* tells if associated literal length is 0 or not. This value must be 0 or 1 */
|
|
523
|
-
ZSTD_match_t* matches,
|
|
524
516
|
const U32 lengthToBeat,
|
|
525
517
|
U32 const mls /* template */)
|
|
526
518
|
{
|
|
@@ -541,8 +533,8 @@ U32 ZSTD_insertBtAndGetAllMatches (
|
|
|
541
533
|
U32 const dictLimit = ms->window.dictLimit;
|
|
542
534
|
const BYTE* const dictEnd = dictBase + dictLimit;
|
|
543
535
|
const BYTE* const prefixStart = base + dictLimit;
|
|
544
|
-
U32 const btLow = btMask >= current ? 0 : current - btMask;
|
|
545
|
-
U32 const windowLow = ms->
|
|
536
|
+
U32 const btLow = (btMask >= current) ? 0 : current - btMask;
|
|
537
|
+
U32 const windowLow = ZSTD_getLowestMatchIndex(ms, current, cParams->windowLog);
|
|
546
538
|
U32 const matchLow = windowLow ? windowLow : 1;
|
|
547
539
|
U32* smallerPtr = bt + 2*(current&btMask);
|
|
548
540
|
U32* largerPtr = bt + 2*(current&btMask) + 1;
|
|
@@ -577,7 +569,10 @@ U32 ZSTD_insertBtAndGetAllMatches (
|
|
|
577
569
|
U32 repLen = 0;
|
|
578
570
|
assert(current >= dictLimit);
|
|
579
571
|
if (repOffset-1 /* intentional overflow, discards 0 and -1 */ < current-dictLimit) { /* equivalent to `current > repIndex >= dictLimit` */
|
|
580
|
-
|
|
572
|
+
/* We must validate the repcode offset because when we're using a dictionary the
|
|
573
|
+
* valid offset range shrinks when the dictionary goes out of bounds.
|
|
574
|
+
*/
|
|
575
|
+
if ((repIndex >= windowLow) & (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repOffset, minMatch))) {
|
|
581
576
|
repLen = (U32)ZSTD_count(ip+minMatch, ip+minMatch-repOffset, iLimit) + minMatch;
|
|
582
577
|
}
|
|
583
578
|
} else { /* repIndex < dictLimit || repIndex >= current */
|
|
@@ -612,7 +607,7 @@ U32 ZSTD_insertBtAndGetAllMatches (
|
|
|
612
607
|
|
|
613
608
|
/* HC3 match finder */
|
|
614
609
|
if ((mls == 3) /*static*/ && (bestLength < mls)) {
|
|
615
|
-
U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip);
|
|
610
|
+
U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, nextToUpdate3, ip);
|
|
616
611
|
if ((matchIndex3 >= matchLow)
|
|
617
612
|
& (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) {
|
|
618
613
|
size_t mlen;
|
|
@@ -638,9 +633,7 @@ U32 ZSTD_insertBtAndGetAllMatches (
|
|
|
638
633
|
(ip+mlen == iLimit) ) { /* best possible length */
|
|
639
634
|
ms->nextToUpdate = current+1; /* skip insertion */
|
|
640
635
|
return 1;
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
}
|
|
636
|
+
} } }
|
|
644
637
|
/* no dictMatchState lookup: dicts don't have a populated HC3 table */
|
|
645
638
|
}
|
|
646
639
|
|
|
@@ -648,19 +641,21 @@ U32 ZSTD_insertBtAndGetAllMatches (
|
|
|
648
641
|
|
|
649
642
|
while (nbCompares-- && (matchIndex >= matchLow)) {
|
|
650
643
|
U32* const nextPtr = bt + 2*(matchIndex & btMask);
|
|
651
|
-
size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
|
|
652
644
|
const BYTE* match;
|
|
645
|
+
size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
|
|
653
646
|
assert(current > matchIndex);
|
|
654
647
|
|
|
655
648
|
if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) {
|
|
656
649
|
assert(matchIndex+matchLength >= dictLimit); /* ensure the condition is correct when !extDict */
|
|
657
650
|
match = base + matchIndex;
|
|
651
|
+
if (matchIndex >= dictLimit) assert(memcmp(match, ip, matchLength) == 0); /* ensure early section of match is equal as expected */
|
|
658
652
|
matchLength += ZSTD_count(ip+matchLength, match+matchLength, iLimit);
|
|
659
653
|
} else {
|
|
660
654
|
match = dictBase + matchIndex;
|
|
655
|
+
assert(memcmp(match, ip, matchLength) == 0); /* ensure early section of match is equal as expected */
|
|
661
656
|
matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dictEnd, prefixStart);
|
|
662
657
|
if (matchIndex+matchLength >= dictLimit)
|
|
663
|
-
match = base + matchIndex; /* prepare for match[matchLength] */
|
|
658
|
+
match = base + matchIndex; /* prepare for match[matchLength] read */
|
|
664
659
|
}
|
|
665
660
|
|
|
666
661
|
if (matchLength > bestLength) {
|
|
@@ -745,10 +740,13 @@ U32 ZSTD_insertBtAndGetAllMatches (
|
|
|
745
740
|
|
|
746
741
|
|
|
747
742
|
FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches (
|
|
743
|
+
ZSTD_match_t* matches, /* store result (match found, increasing size) in this table */
|
|
748
744
|
ZSTD_matchState_t* ms,
|
|
745
|
+
U32* nextToUpdate3,
|
|
749
746
|
const BYTE* ip, const BYTE* const iHighLimit, const ZSTD_dictMode_e dictMode,
|
|
750
|
-
U32 rep[ZSTD_REP_NUM],
|
|
751
|
-
|
|
747
|
+
const U32 rep[ZSTD_REP_NUM],
|
|
748
|
+
U32 const ll0,
|
|
749
|
+
U32 const lengthToBeat)
|
|
752
750
|
{
|
|
753
751
|
const ZSTD_compressionParameters* const cParams = &ms->cParams;
|
|
754
752
|
U32 const matchLengthSearch = cParams->minMatch;
|
|
@@ -757,12 +755,12 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches (
|
|
|
757
755
|
ZSTD_updateTree_internal(ms, ip, iHighLimit, matchLengthSearch, dictMode);
|
|
758
756
|
switch(matchLengthSearch)
|
|
759
757
|
{
|
|
760
|
-
case 3 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0,
|
|
758
|
+
case 3 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 3);
|
|
761
759
|
default :
|
|
762
|
-
case 4 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0,
|
|
763
|
-
case 5 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0,
|
|
760
|
+
case 4 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 4);
|
|
761
|
+
case 5 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 5);
|
|
764
762
|
case 7 :
|
|
765
|
-
case 6 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0,
|
|
763
|
+
case 6 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 6);
|
|
766
764
|
}
|
|
767
765
|
}
|
|
768
766
|
|
|
@@ -770,30 +768,6 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches (
|
|
|
770
768
|
/*-*******************************
|
|
771
769
|
* Optimal parser
|
|
772
770
|
*********************************/
|
|
773
|
-
typedef struct repcodes_s {
|
|
774
|
-
U32 rep[3];
|
|
775
|
-
} repcodes_t;
|
|
776
|
-
|
|
777
|
-
static repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0)
|
|
778
|
-
{
|
|
779
|
-
repcodes_t newReps;
|
|
780
|
-
if (offset >= ZSTD_REP_NUM) { /* full offset */
|
|
781
|
-
newReps.rep[2] = rep[1];
|
|
782
|
-
newReps.rep[1] = rep[0];
|
|
783
|
-
newReps.rep[0] = offset - ZSTD_REP_MOVE;
|
|
784
|
-
} else { /* repcode */
|
|
785
|
-
U32 const repCode = offset + ll0;
|
|
786
|
-
if (repCode > 0) { /* note : if repCode==0, no change */
|
|
787
|
-
U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
|
|
788
|
-
newReps.rep[2] = (repCode >= 2) ? rep[1] : rep[2];
|
|
789
|
-
newReps.rep[1] = rep[0];
|
|
790
|
-
newReps.rep[0] = currentOffset;
|
|
791
|
-
} else { /* repCode == 0 */
|
|
792
|
-
memcpy(&newReps, rep, sizeof(newReps));
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
return newReps;
|
|
796
|
-
}
|
|
797
771
|
|
|
798
772
|
|
|
799
773
|
static U32 ZSTD_totalLen(ZSTD_optimal_t sol)
|
|
@@ -810,7 +784,7 @@ listStats(const U32* table, int lastEltID)
|
|
|
810
784
|
int enb;
|
|
811
785
|
for (enb=0; enb < nbElts; enb++) {
|
|
812
786
|
(void)table;
|
|
813
|
-
|
|
787
|
+
/* RAWLOG(2, "%3i:%3i, ", enb, table[enb]); */
|
|
814
788
|
RAWLOG(2, "%4i,", table[enb]);
|
|
815
789
|
}
|
|
816
790
|
RAWLOG(2, " \n");
|
|
@@ -838,6 +812,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
838
812
|
|
|
839
813
|
U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
|
|
840
814
|
U32 const minMatch = (cParams->minMatch == 3) ? 3 : 4;
|
|
815
|
+
U32 nextToUpdate3 = ms->nextToUpdate;
|
|
841
816
|
|
|
842
817
|
ZSTD_optimal_t* const opt = optStatePtr->priceTable;
|
|
843
818
|
ZSTD_match_t* const matches = optStatePtr->matchTable;
|
|
@@ -847,7 +822,6 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
847
822
|
DEBUGLOG(5, "ZSTD_compressBlock_opt_generic: current=%u, prefix=%u, nextToUpdate=%u",
|
|
848
823
|
(U32)(ip - base), ms->window.dictLimit, ms->nextToUpdate);
|
|
849
824
|
assert(optLevel <= 2);
|
|
850
|
-
ms->nextToUpdate3 = ms->nextToUpdate;
|
|
851
825
|
ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize, optLevel);
|
|
852
826
|
ip += (ip==prefixStart);
|
|
853
827
|
|
|
@@ -858,19 +832,24 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
858
832
|
/* find first match */
|
|
859
833
|
{ U32 const litlen = (U32)(ip - anchor);
|
|
860
834
|
U32 const ll0 = !litlen;
|
|
861
|
-
U32 const nbMatches = ZSTD_BtGetAllMatches(ms, ip, iend, dictMode, rep, ll0,
|
|
835
|
+
U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, ip, iend, dictMode, rep, ll0, minMatch);
|
|
862
836
|
if (!nbMatches) { ip++; continue; }
|
|
863
837
|
|
|
864
838
|
/* initialize opt[0] */
|
|
865
839
|
{ U32 i ; for (i=0; i<ZSTD_REP_NUM; i++) opt[0].rep[i] = rep[i]; }
|
|
866
840
|
opt[0].mlen = 0; /* means is_a_literal */
|
|
867
841
|
opt[0].litlen = litlen;
|
|
868
|
-
|
|
842
|
+
/* We don't need to include the actual price of the literals because
|
|
843
|
+
* it is static for the duration of the forward pass, and is included
|
|
844
|
+
* in every price. We include the literal length to avoid negative
|
|
845
|
+
* prices when we subtract the previous literal length.
|
|
846
|
+
*/
|
|
847
|
+
opt[0].price = ZSTD_litLengthPrice(litlen, optStatePtr, optLevel);
|
|
869
848
|
|
|
870
849
|
/* large match -> immediate encoding */
|
|
871
850
|
{ U32 const maxML = matches[nbMatches-1].len;
|
|
872
851
|
U32 const maxOffset = matches[nbMatches-1].off;
|
|
873
|
-
DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new
|
|
852
|
+
DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new series",
|
|
874
853
|
nbMatches, maxML, maxOffset, (U32)(ip-prefixStart));
|
|
875
854
|
|
|
876
855
|
if (maxML > sufficient_len) {
|
|
@@ -894,7 +873,6 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
894
873
|
for (matchNb = 0; matchNb < nbMatches; matchNb++) {
|
|
895
874
|
U32 const offset = matches[matchNb].off;
|
|
896
875
|
U32 const end = matches[matchNb].len;
|
|
897
|
-
repcodes_t const repHistory = ZSTD_updateRep(rep, offset, ll0);
|
|
898
876
|
for ( ; pos <= end ; pos++ ) {
|
|
899
877
|
U32 const matchPrice = ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel);
|
|
900
878
|
U32 const sequencePrice = literalsPrice + matchPrice;
|
|
@@ -904,8 +882,6 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
904
882
|
opt[pos].off = offset;
|
|
905
883
|
opt[pos].litlen = litlen;
|
|
906
884
|
opt[pos].price = sequencePrice;
|
|
907
|
-
ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory));
|
|
908
|
-
memcpy(opt[pos].rep, &repHistory, sizeof(repHistory));
|
|
909
885
|
} }
|
|
910
886
|
last_pos = pos-1;
|
|
911
887
|
}
|
|
@@ -932,7 +908,6 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
932
908
|
opt[cur].off = 0;
|
|
933
909
|
opt[cur].litlen = litlen;
|
|
934
910
|
opt[cur].price = price;
|
|
935
|
-
memcpy(opt[cur].rep, opt[cur-1].rep, sizeof(opt[cur].rep));
|
|
936
911
|
} else {
|
|
937
912
|
DEBUGLOG(7, "cPos:%zi==rPos:%u : literal would cost more (%.2f>%.2f) (hist:%u,%u,%u)",
|
|
938
913
|
inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price),
|
|
@@ -940,6 +915,21 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
940
915
|
}
|
|
941
916
|
}
|
|
942
917
|
|
|
918
|
+
/* Set the repcodes of the current position. We must do it here
|
|
919
|
+
* because we rely on the repcodes of the 2nd to last sequence being
|
|
920
|
+
* correct to set the next chunks repcodes during the backward
|
|
921
|
+
* traversal.
|
|
922
|
+
*/
|
|
923
|
+
ZSTD_STATIC_ASSERT(sizeof(opt[cur].rep) == sizeof(repcodes_t));
|
|
924
|
+
assert(cur >= opt[cur].mlen);
|
|
925
|
+
if (opt[cur].mlen != 0) {
|
|
926
|
+
U32 const prev = cur - opt[cur].mlen;
|
|
927
|
+
repcodes_t newReps = ZSTD_updateRep(opt[prev].rep, opt[cur].off, opt[cur].litlen==0);
|
|
928
|
+
memcpy(opt[cur].rep, &newReps, sizeof(repcodes_t));
|
|
929
|
+
} else {
|
|
930
|
+
memcpy(opt[cur].rep, opt[cur - 1].rep, sizeof(repcodes_t));
|
|
931
|
+
}
|
|
932
|
+
|
|
943
933
|
/* last match must start at a minimum distance of 8 from oend */
|
|
944
934
|
if (inr > ilimit) continue;
|
|
945
935
|
|
|
@@ -955,7 +945,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
955
945
|
U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0;
|
|
956
946
|
U32 const previousPrice = opt[cur].price;
|
|
957
947
|
U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel);
|
|
958
|
-
U32 const nbMatches = ZSTD_BtGetAllMatches(ms, inr, iend, dictMode, opt[cur].rep, ll0,
|
|
948
|
+
U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, inr, iend, dictMode, opt[cur].rep, ll0, minMatch);
|
|
959
949
|
U32 matchNb;
|
|
960
950
|
if (!nbMatches) {
|
|
961
951
|
DEBUGLOG(7, "rPos:%u : no match found", cur);
|
|
@@ -980,7 +970,6 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
980
970
|
/* set prices using matches found at position == cur */
|
|
981
971
|
for (matchNb = 0; matchNb < nbMatches; matchNb++) {
|
|
982
972
|
U32 const offset = matches[matchNb].off;
|
|
983
|
-
repcodes_t const repHistory = ZSTD_updateRep(opt[cur].rep, offset, ll0);
|
|
984
973
|
U32 const lastML = matches[matchNb].len;
|
|
985
974
|
U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch;
|
|
986
975
|
U32 mlen;
|
|
@@ -1000,8 +989,6 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
1000
989
|
opt[pos].off = offset;
|
|
1001
990
|
opt[pos].litlen = litlen;
|
|
1002
991
|
opt[pos].price = price;
|
|
1003
|
-
ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory));
|
|
1004
|
-
memcpy(opt[pos].rep, &repHistory, sizeof(repHistory));
|
|
1005
992
|
} else {
|
|
1006
993
|
DEBUGLOG(7, "rPos:%u (ml=%2u) => new price is worse (%.2f>=%.2f)",
|
|
1007
994
|
pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price));
|
|
@@ -1017,6 +1004,17 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
|
|
|
1017
1004
|
_shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */
|
|
1018
1005
|
assert(opt[0].mlen == 0);
|
|
1019
1006
|
|
|
1007
|
+
/* Set the next chunk's repcodes based on the repcodes of the beginning
|
|
1008
|
+
* of the last match, and the last sequence. This avoids us having to
|
|
1009
|
+
* update them while traversing the sequences.
|
|
1010
|
+
*/
|
|
1011
|
+
if (lastSequence.mlen != 0) {
|
|
1012
|
+
repcodes_t reps = ZSTD_updateRep(opt[cur].rep, lastSequence.off, lastSequence.litlen==0);
|
|
1013
|
+
memcpy(rep, &reps, sizeof(reps));
|
|
1014
|
+
} else {
|
|
1015
|
+
memcpy(rep, opt[cur].rep, sizeof(repcodes_t));
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1020
1018
|
{ U32 const storeEnd = cur + 1;
|
|
1021
1019
|
U32 storeStart = storeEnd;
|
|
1022
1020
|
U32 seqPos = cur;
|
|
@@ -1053,33 +1051,18 @@ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */
|
|
|
1053
1051
|
continue; /* will finish */
|
|
1054
1052
|
}
|
|
1055
1053
|
|
|
1056
|
-
/* repcodes update : like ZSTD_updateRep(), but update in place */
|
|
1057
|
-
if (offCode >= ZSTD_REP_NUM) { /* full offset */
|
|
1058
|
-
rep[2] = rep[1];
|
|
1059
|
-
rep[1] = rep[0];
|
|
1060
|
-
rep[0] = offCode - ZSTD_REP_MOVE;
|
|
1061
|
-
} else { /* repcode */
|
|
1062
|
-
U32 const repCode = offCode + (llen==0);
|
|
1063
|
-
if (repCode) { /* note : if repCode==0, no change */
|
|
1064
|
-
U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
|
|
1065
|
-
if (repCode >= 2) rep[2] = rep[1];
|
|
1066
|
-
rep[1] = rep[0];
|
|
1067
|
-
rep[0] = currentOffset;
|
|
1068
|
-
} }
|
|
1069
|
-
|
|
1070
1054
|
assert(anchor + llen <= iend);
|
|
1071
1055
|
ZSTD_updateStats(optStatePtr, llen, anchor, offCode, mlen);
|
|
1072
|
-
ZSTD_storeSeq(seqStore, llen, anchor, offCode, mlen-MINMATCH);
|
|
1056
|
+
ZSTD_storeSeq(seqStore, llen, anchor, iend, offCode, mlen-MINMATCH);
|
|
1073
1057
|
anchor += advance;
|
|
1074
1058
|
ip = anchor;
|
|
1075
1059
|
} }
|
|
1076
1060
|
ZSTD_setBasePrices(optStatePtr, optLevel);
|
|
1077
1061
|
}
|
|
1078
|
-
|
|
1079
1062
|
} /* while (ip < ilimit) */
|
|
1080
1063
|
|
|
1081
1064
|
/* Return the last literals size */
|
|
1082
|
-
return iend - anchor;
|
|
1065
|
+
return (size_t)(iend - anchor);
|
|
1083
1066
|
}
|
|
1084
1067
|
|
|
1085
1068
|
|
|
@@ -1108,7 +1091,8 @@ static U32 ZSTD_upscaleStat(unsigned* table, U32 lastEltIndex, int bonus)
|
|
|
1108
1091
|
/* used in 2-pass strategy */
|
|
1109
1092
|
MEM_STATIC void ZSTD_upscaleStats(optState_t* optPtr)
|
|
1110
1093
|
{
|
|
1111
|
-
|
|
1094
|
+
if (ZSTD_compressedLiterals(optPtr))
|
|
1095
|
+
optPtr->litSum = ZSTD_upscaleStat(optPtr->litFreq, MaxLit, 0);
|
|
1112
1096
|
optPtr->litLengthSum = ZSTD_upscaleStat(optPtr->litLengthFreq, MaxLL, 0);
|
|
1113
1097
|
optPtr->matchLengthSum = ZSTD_upscaleStat(optPtr->matchLengthFreq, MaxML, 0);
|
|
1114
1098
|
optPtr->offCodeSum = ZSTD_upscaleStat(optPtr->offCodeFreq, MaxOff, 0);
|
|
@@ -1117,7 +1101,7 @@ MEM_STATIC void ZSTD_upscaleStats(optState_t* optPtr)
|
|
|
1117
1101
|
/* ZSTD_initStats_ultra():
|
|
1118
1102
|
* make a first compression pass, just to seed stats with more accurate starting values.
|
|
1119
1103
|
* only works on first block, with no dictionary and no ldm.
|
|
1120
|
-
* this function cannot error, hence its
|
|
1104
|
+
* this function cannot error, hence its contract must be respected.
|
|
1121
1105
|
*/
|
|
1122
1106
|
static void
|
|
1123
1107
|
ZSTD_initStats_ultra(ZSTD_matchState_t* ms,
|
|
@@ -1142,7 +1126,6 @@ ZSTD_initStats_ultra(ZSTD_matchState_t* ms,
|
|
|
1142
1126
|
ms->window.dictLimit += (U32)srcSize;
|
|
1143
1127
|
ms->window.lowLimit = ms->window.dictLimit;
|
|
1144
1128
|
ms->nextToUpdate = ms->window.dictLimit;
|
|
1145
|
-
ms->nextToUpdate3 = ms->window.dictLimit;
|
|
1146
1129
|
|
|
1147
1130
|
/* re-inforce weight of collected statistics */
|
|
1148
1131
|
ZSTD_upscaleStats(&ms->opt);
|