xref: /netbsd-src/external/bsd/zstd/dist/lib/dictBuilder/fastcover.c (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
1*3117ece4Schristos /*
2*3117ece4Schristos  * Copyright (c) Meta Platforms, Inc. and affiliates.
3*3117ece4Schristos  * All rights reserved.
4*3117ece4Schristos  *
5*3117ece4Schristos  * This source code is licensed under both the BSD-style license (found in the
6*3117ece4Schristos  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7*3117ece4Schristos  * in the COPYING file in the root directory of this source tree).
8*3117ece4Schristos  * You may select, at your option, one of the above-listed licenses.
9*3117ece4Schristos  */
10*3117ece4Schristos 
11*3117ece4Schristos /*-*************************************
12*3117ece4Schristos *  Dependencies
13*3117ece4Schristos ***************************************/
14*3117ece4Schristos #include <stdio.h>  /* fprintf */
15*3117ece4Schristos #include <stdlib.h> /* malloc, free, qsort */
16*3117ece4Schristos #include <string.h> /* memset */
17*3117ece4Schristos #include <time.h>   /* clock */
18*3117ece4Schristos 
19*3117ece4Schristos #ifndef ZDICT_STATIC_LINKING_ONLY
20*3117ece4Schristos #  define ZDICT_STATIC_LINKING_ONLY
21*3117ece4Schristos #endif
22*3117ece4Schristos 
23*3117ece4Schristos #include "../common/mem.h" /* read */
24*3117ece4Schristos #include "../common/pool.h"
25*3117ece4Schristos #include "../common/threading.h"
26*3117ece4Schristos #include "../common/zstd_internal.h" /* includes zstd.h */
27*3117ece4Schristos #include "../compress/zstd_compress_internal.h" /* ZSTD_hash*() */
28*3117ece4Schristos #include "../zdict.h"
29*3117ece4Schristos #include "cover.h"
30*3117ece4Schristos 
31*3117ece4Schristos 
32*3117ece4Schristos /*-*************************************
33*3117ece4Schristos *  Constants
34*3117ece4Schristos ***************************************/
35*3117ece4Schristos /**
36*3117ece4Schristos * There are 32bit indexes used to ref samples, so limit samples size to 4GB
37*3117ece4Schristos * on 64bit builds.
38*3117ece4Schristos * For 32bit builds we choose 1 GB.
39*3117ece4Schristos * Most 32bit platforms have 2GB user-mode addressable space and we allocate a large
40*3117ece4Schristos * contiguous buffer, so 1GB is already a high limit.
41*3117ece4Schristos */
42*3117ece4Schristos #define FASTCOVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB))
43*3117ece4Schristos #define FASTCOVER_MAX_F 31
44*3117ece4Schristos #define FASTCOVER_MAX_ACCEL 10
45*3117ece4Schristos #define FASTCOVER_DEFAULT_SPLITPOINT 0.75
46*3117ece4Schristos #define DEFAULT_F 20
47*3117ece4Schristos #define DEFAULT_ACCEL 1
48*3117ece4Schristos 
49*3117ece4Schristos 
50*3117ece4Schristos /*-*************************************
51*3117ece4Schristos *  Console display
52*3117ece4Schristos ***************************************/
53*3117ece4Schristos #ifndef LOCALDISPLAYLEVEL
54*3117ece4Schristos static int g_displayLevel = 0;
55*3117ece4Schristos #endif
56*3117ece4Schristos #undef  DISPLAY
57*3117ece4Schristos #define DISPLAY(...)                                                           \
58*3117ece4Schristos   {                                                                            \
59*3117ece4Schristos     fprintf(stderr, __VA_ARGS__);                                              \
60*3117ece4Schristos     fflush(stderr);                                                            \
61*3117ece4Schristos   }
62*3117ece4Schristos #undef  LOCALDISPLAYLEVEL
63*3117ece4Schristos #define LOCALDISPLAYLEVEL(displayLevel, l, ...)                                \
64*3117ece4Schristos   if (displayLevel >= l) {                                                     \
65*3117ece4Schristos     DISPLAY(__VA_ARGS__);                                                      \
66*3117ece4Schristos   } /* 0 : no display;   1: errors;   2: default;  3: details;  4: debug */
67*3117ece4Schristos #undef  DISPLAYLEVEL
68*3117ece4Schristos #define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__)
69*3117ece4Schristos 
70*3117ece4Schristos #ifndef LOCALDISPLAYUPDATE
71*3117ece4Schristos static const clock_t g_refreshRate = CLOCKS_PER_SEC * 15 / 100;
72*3117ece4Schristos static clock_t g_time = 0;
73*3117ece4Schristos #endif
74*3117ece4Schristos #undef  LOCALDISPLAYUPDATE
75*3117ece4Schristos #define LOCALDISPLAYUPDATE(displayLevel, l, ...)                               \
76*3117ece4Schristos   if (displayLevel >= l) {                                                     \
77*3117ece4Schristos     if ((clock() - g_time > g_refreshRate) || (displayLevel >= 4)) {             \
78*3117ece4Schristos       g_time = clock();                                                        \
79*3117ece4Schristos       DISPLAY(__VA_ARGS__);                                                    \
80*3117ece4Schristos     }                                                                          \
81*3117ece4Schristos   }
82*3117ece4Schristos #undef  DISPLAYUPDATE
83*3117ece4Schristos #define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__)
84*3117ece4Schristos 
85*3117ece4Schristos 
86*3117ece4Schristos /*-*************************************
87*3117ece4Schristos * Hash Functions
88*3117ece4Schristos ***************************************/
89*3117ece4Schristos /**
90*3117ece4Schristos  * Hash the d-byte value pointed to by p and mod 2^f into the frequency vector
91*3117ece4Schristos  */
92*3117ece4Schristos static size_t FASTCOVER_hashPtrToIndex(const void* p, U32 f, unsigned d) {
93*3117ece4Schristos   if (d == 6) {
94*3117ece4Schristos     return ZSTD_hash6Ptr(p, f);
95*3117ece4Schristos   }
96*3117ece4Schristos   return ZSTD_hash8Ptr(p, f);
97*3117ece4Schristos }
98*3117ece4Schristos 
99*3117ece4Schristos 
100*3117ece4Schristos /*-*************************************
101*3117ece4Schristos * Acceleration
102*3117ece4Schristos ***************************************/
103*3117ece4Schristos typedef struct {
104*3117ece4Schristos   unsigned finalize;    /* Percentage of training samples used for ZDICT_finalizeDictionary */
105*3117ece4Schristos   unsigned skip;        /* Number of dmer skipped between each dmer counted in computeFrequency */
106*3117ece4Schristos } FASTCOVER_accel_t;
107*3117ece4Schristos 
108*3117ece4Schristos 
109*3117ece4Schristos static const FASTCOVER_accel_t FASTCOVER_defaultAccelParameters[FASTCOVER_MAX_ACCEL+1] = {
110*3117ece4Schristos   { 100, 0 },   /* accel = 0, should not happen because accel = 0 defaults to accel = 1 */
111*3117ece4Schristos   { 100, 0 },   /* accel = 1 */
112*3117ece4Schristos   { 50, 1 },   /* accel = 2 */
113*3117ece4Schristos   { 34, 2 },   /* accel = 3 */
114*3117ece4Schristos   { 25, 3 },   /* accel = 4 */
115*3117ece4Schristos   { 20, 4 },   /* accel = 5 */
116*3117ece4Schristos   { 17, 5 },   /* accel = 6 */
117*3117ece4Schristos   { 14, 6 },   /* accel = 7 */
118*3117ece4Schristos   { 13, 7 },   /* accel = 8 */
119*3117ece4Schristos   { 11, 8 },   /* accel = 9 */
120*3117ece4Schristos   { 10, 9 },   /* accel = 10 */
121*3117ece4Schristos };
122*3117ece4Schristos 
123*3117ece4Schristos 
124*3117ece4Schristos /*-*************************************
125*3117ece4Schristos * Context
126*3117ece4Schristos ***************************************/
127*3117ece4Schristos typedef struct {
128*3117ece4Schristos   const BYTE *samples;
129*3117ece4Schristos   size_t *offsets;
130*3117ece4Schristos   const size_t *samplesSizes;
131*3117ece4Schristos   size_t nbSamples;
132*3117ece4Schristos   size_t nbTrainSamples;
133*3117ece4Schristos   size_t nbTestSamples;
134*3117ece4Schristos   size_t nbDmers;
135*3117ece4Schristos   U32 *freqs;
136*3117ece4Schristos   unsigned d;
137*3117ece4Schristos   unsigned f;
138*3117ece4Schristos   FASTCOVER_accel_t accelParams;
139*3117ece4Schristos } FASTCOVER_ctx_t;
140*3117ece4Schristos 
141*3117ece4Schristos 
142*3117ece4Schristos /*-*************************************
143*3117ece4Schristos *  Helper functions
144*3117ece4Schristos ***************************************/
145*3117ece4Schristos /**
146*3117ece4Schristos  * Selects the best segment in an epoch.
147*3117ece4Schristos  * Segments of are scored according to the function:
148*3117ece4Schristos  *
149*3117ece4Schristos  * Let F(d) be the frequency of all dmers with hash value d.
150*3117ece4Schristos  * Let S_i be hash value of the dmer at position i of segment S which has length k.
151*3117ece4Schristos  *
152*3117ece4Schristos  *     Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1})
153*3117ece4Schristos  *
154*3117ece4Schristos  * Once the dmer with hash value d is in the dictionary we set F(d) = 0.
155*3117ece4Schristos  */
156*3117ece4Schristos static COVER_segment_t FASTCOVER_selectSegment(const FASTCOVER_ctx_t *ctx,
157*3117ece4Schristos                                               U32 *freqs, U32 begin, U32 end,
158*3117ece4Schristos                                               ZDICT_cover_params_t parameters,
159*3117ece4Schristos                                               U16* segmentFreqs) {
160*3117ece4Schristos   /* Constants */
161*3117ece4Schristos   const U32 k = parameters.k;
162*3117ece4Schristos   const U32 d = parameters.d;
163*3117ece4Schristos   const U32 f = ctx->f;
164*3117ece4Schristos   const U32 dmersInK = k - d + 1;
165*3117ece4Schristos 
166*3117ece4Schristos   /* Try each segment (activeSegment) and save the best (bestSegment) */
167*3117ece4Schristos   COVER_segment_t bestSegment = {0, 0, 0};
168*3117ece4Schristos   COVER_segment_t activeSegment;
169*3117ece4Schristos 
170*3117ece4Schristos   /* Reset the activeDmers in the segment */
171*3117ece4Schristos   /* The activeSegment starts at the beginning of the epoch. */
172*3117ece4Schristos   activeSegment.begin = begin;
173*3117ece4Schristos   activeSegment.end = begin;
174*3117ece4Schristos   activeSegment.score = 0;
175*3117ece4Schristos 
176*3117ece4Schristos   /* Slide the activeSegment through the whole epoch.
177*3117ece4Schristos    * Save the best segment in bestSegment.
178*3117ece4Schristos    */
179*3117ece4Schristos   while (activeSegment.end < end) {
180*3117ece4Schristos     /* Get hash value of current dmer */
181*3117ece4Schristos     const size_t idx = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.end, f, d);
182*3117ece4Schristos 
183*3117ece4Schristos     /* Add frequency of this index to score if this is the first occurrence of index in active segment */
184*3117ece4Schristos     if (segmentFreqs[idx] == 0) {
185*3117ece4Schristos       activeSegment.score += freqs[idx];
186*3117ece4Schristos     }
187*3117ece4Schristos     /* Increment end of segment and segmentFreqs*/
188*3117ece4Schristos     activeSegment.end += 1;
189*3117ece4Schristos     segmentFreqs[idx] += 1;
190*3117ece4Schristos     /* If the window is now too large, drop the first position */
191*3117ece4Schristos     if (activeSegment.end - activeSegment.begin == dmersInK + 1) {
192*3117ece4Schristos       /* Get hash value of the dmer to be eliminated from active segment */
193*3117ece4Schristos       const size_t delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d);
194*3117ece4Schristos       segmentFreqs[delIndex] -= 1;
195*3117ece4Schristos       /* Subtract frequency of this index from score if this is the last occurrence of this index in active segment */
196*3117ece4Schristos       if (segmentFreqs[delIndex] == 0) {
197*3117ece4Schristos         activeSegment.score -= freqs[delIndex];
198*3117ece4Schristos       }
199*3117ece4Schristos       /* Increment start of segment */
200*3117ece4Schristos       activeSegment.begin += 1;
201*3117ece4Schristos     }
202*3117ece4Schristos 
203*3117ece4Schristos     /* If this segment is the best so far save it */
204*3117ece4Schristos     if (activeSegment.score > bestSegment.score) {
205*3117ece4Schristos       bestSegment = activeSegment;
206*3117ece4Schristos     }
207*3117ece4Schristos   }
208*3117ece4Schristos 
209*3117ece4Schristos   /* Zero out rest of segmentFreqs array */
210*3117ece4Schristos   while (activeSegment.begin < end) {
211*3117ece4Schristos     const size_t delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d);
212*3117ece4Schristos     segmentFreqs[delIndex] -= 1;
213*3117ece4Schristos     activeSegment.begin += 1;
214*3117ece4Schristos   }
215*3117ece4Schristos 
216*3117ece4Schristos   {
217*3117ece4Schristos     /*  Zero the frequency of hash value of each dmer covered by the chosen segment. */
218*3117ece4Schristos     U32 pos;
219*3117ece4Schristos     for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) {
220*3117ece4Schristos       const size_t i = FASTCOVER_hashPtrToIndex(ctx->samples + pos, f, d);
221*3117ece4Schristos       freqs[i] = 0;
222*3117ece4Schristos     }
223*3117ece4Schristos   }
224*3117ece4Schristos 
225*3117ece4Schristos   return bestSegment;
226*3117ece4Schristos }
227*3117ece4Schristos 
228*3117ece4Schristos 
229*3117ece4Schristos static int FASTCOVER_checkParameters(ZDICT_cover_params_t parameters,
230*3117ece4Schristos                                      size_t maxDictSize, unsigned f,
231*3117ece4Schristos                                      unsigned accel) {
232*3117ece4Schristos   /* k, d, and f are required parameters */
233*3117ece4Schristos   if (parameters.d == 0 || parameters.k == 0) {
234*3117ece4Schristos     return 0;
235*3117ece4Schristos   }
236*3117ece4Schristos   /* d has to be 6 or 8 */
237*3117ece4Schristos   if (parameters.d != 6 && parameters.d != 8) {
238*3117ece4Schristos     return 0;
239*3117ece4Schristos   }
240*3117ece4Schristos   /* k <= maxDictSize */
241*3117ece4Schristos   if (parameters.k > maxDictSize) {
242*3117ece4Schristos     return 0;
243*3117ece4Schristos   }
244*3117ece4Schristos   /* d <= k */
245*3117ece4Schristos   if (parameters.d > parameters.k) {
246*3117ece4Schristos     return 0;
247*3117ece4Schristos   }
248*3117ece4Schristos   /* 0 < f <= FASTCOVER_MAX_F*/
249*3117ece4Schristos   if (f > FASTCOVER_MAX_F || f == 0) {
250*3117ece4Schristos     return 0;
251*3117ece4Schristos   }
252*3117ece4Schristos   /* 0 < splitPoint <= 1 */
253*3117ece4Schristos   if (parameters.splitPoint <= 0 || parameters.splitPoint > 1) {
254*3117ece4Schristos     return 0;
255*3117ece4Schristos   }
256*3117ece4Schristos   /* 0 < accel <= 10 */
257*3117ece4Schristos   if (accel > 10 || accel == 0) {
258*3117ece4Schristos     return 0;
259*3117ece4Schristos   }
260*3117ece4Schristos   return 1;
261*3117ece4Schristos }
262*3117ece4Schristos 
263*3117ece4Schristos 
264*3117ece4Schristos /**
265*3117ece4Schristos  * Clean up a context initialized with `FASTCOVER_ctx_init()`.
266*3117ece4Schristos  */
267*3117ece4Schristos static void
268*3117ece4Schristos FASTCOVER_ctx_destroy(FASTCOVER_ctx_t* ctx)
269*3117ece4Schristos {
270*3117ece4Schristos     if (!ctx) return;
271*3117ece4Schristos 
272*3117ece4Schristos     free(ctx->freqs);
273*3117ece4Schristos     ctx->freqs = NULL;
274*3117ece4Schristos 
275*3117ece4Schristos     free(ctx->offsets);
276*3117ece4Schristos     ctx->offsets = NULL;
277*3117ece4Schristos }
278*3117ece4Schristos 
279*3117ece4Schristos 
280*3117ece4Schristos /**
281*3117ece4Schristos  * Calculate for frequency of hash value of each dmer in ctx->samples
282*3117ece4Schristos  */
283*3117ece4Schristos static void
284*3117ece4Schristos FASTCOVER_computeFrequency(U32* freqs, const FASTCOVER_ctx_t* ctx)
285*3117ece4Schristos {
286*3117ece4Schristos     const unsigned f = ctx->f;
287*3117ece4Schristos     const unsigned d = ctx->d;
288*3117ece4Schristos     const unsigned skip = ctx->accelParams.skip;
289*3117ece4Schristos     const unsigned readLength = MAX(d, 8);
290*3117ece4Schristos     size_t i;
291*3117ece4Schristos     assert(ctx->nbTrainSamples >= 5);
292*3117ece4Schristos     assert(ctx->nbTrainSamples <= ctx->nbSamples);
293*3117ece4Schristos     for (i = 0; i < ctx->nbTrainSamples; i++) {
294*3117ece4Schristos         size_t start = ctx->offsets[i];  /* start of current dmer */
295*3117ece4Schristos         size_t const currSampleEnd = ctx->offsets[i+1];
296*3117ece4Schristos         while (start + readLength <= currSampleEnd) {
297*3117ece4Schristos             const size_t dmerIndex = FASTCOVER_hashPtrToIndex(ctx->samples + start, f, d);
298*3117ece4Schristos             freqs[dmerIndex]++;
299*3117ece4Schristos             start = start + skip + 1;
300*3117ece4Schristos         }
301*3117ece4Schristos     }
302*3117ece4Schristos }
303*3117ece4Schristos 
304*3117ece4Schristos 
305*3117ece4Schristos /**
306*3117ece4Schristos  * Prepare a context for dictionary building.
307*3117ece4Schristos  * The context is only dependent on the parameter `d` and can be used multiple
308*3117ece4Schristos  * times.
309*3117ece4Schristos  * Returns 0 on success or error code on error.
310*3117ece4Schristos  * The context must be destroyed with `FASTCOVER_ctx_destroy()`.
311*3117ece4Schristos  */
312*3117ece4Schristos static size_t
313*3117ece4Schristos FASTCOVER_ctx_init(FASTCOVER_ctx_t* ctx,
314*3117ece4Schristos                    const void* samplesBuffer,
315*3117ece4Schristos                    const size_t* samplesSizes, unsigned nbSamples,
316*3117ece4Schristos                    unsigned d, double splitPoint, unsigned f,
317*3117ece4Schristos                    FASTCOVER_accel_t accelParams)
318*3117ece4Schristos {
319*3117ece4Schristos     const BYTE* const samples = (const BYTE*)samplesBuffer;
320*3117ece4Schristos     const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples);
321*3117ece4Schristos     /* Split samples into testing and training sets */
322*3117ece4Schristos     const unsigned nbTrainSamples = splitPoint < 1.0 ? (unsigned)((double)nbSamples * splitPoint) : nbSamples;
323*3117ece4Schristos     const unsigned nbTestSamples = splitPoint < 1.0 ? nbSamples - nbTrainSamples : nbSamples;
324*3117ece4Schristos     const size_t trainingSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes, nbTrainSamples) : totalSamplesSize;
325*3117ece4Schristos     const size_t testSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes + nbTrainSamples, nbTestSamples) : totalSamplesSize;
326*3117ece4Schristos 
327*3117ece4Schristos     /* Checks */
328*3117ece4Schristos     if (totalSamplesSize < MAX(d, sizeof(U64)) ||
329*3117ece4Schristos         totalSamplesSize >= (size_t)FASTCOVER_MAX_SAMPLES_SIZE) {
330*3117ece4Schristos         DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n",
331*3117ece4Schristos                     (unsigned)(totalSamplesSize >> 20), (FASTCOVER_MAX_SAMPLES_SIZE >> 20));
332*3117ece4Schristos         return ERROR(srcSize_wrong);
333*3117ece4Schristos     }
334*3117ece4Schristos 
335*3117ece4Schristos     /* Check if there are at least 5 training samples */
336*3117ece4Schristos     if (nbTrainSamples < 5) {
337*3117ece4Schristos         DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid\n", nbTrainSamples);
338*3117ece4Schristos         return ERROR(srcSize_wrong);
339*3117ece4Schristos     }
340*3117ece4Schristos 
341*3117ece4Schristos     /* Check if there's testing sample */
342*3117ece4Schristos     if (nbTestSamples < 1) {
343*3117ece4Schristos         DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.\n", nbTestSamples);
344*3117ece4Schristos         return ERROR(srcSize_wrong);
345*3117ece4Schristos     }
346*3117ece4Schristos 
347*3117ece4Schristos     /* Zero the context */
348*3117ece4Schristos     memset(ctx, 0, sizeof(*ctx));
349*3117ece4Schristos     DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbTrainSamples,
350*3117ece4Schristos                     (unsigned)trainingSamplesSize);
351*3117ece4Schristos     DISPLAYLEVEL(2, "Testing on %u samples of total size %u\n", nbTestSamples,
352*3117ece4Schristos                     (unsigned)testSamplesSize);
353*3117ece4Schristos 
354*3117ece4Schristos     ctx->samples = samples;
355*3117ece4Schristos     ctx->samplesSizes = samplesSizes;
356*3117ece4Schristos     ctx->nbSamples = nbSamples;
357*3117ece4Schristos     ctx->nbTrainSamples = nbTrainSamples;
358*3117ece4Schristos     ctx->nbTestSamples = nbTestSamples;
359*3117ece4Schristos     ctx->nbDmers = trainingSamplesSize - MAX(d, sizeof(U64)) + 1;
360*3117ece4Schristos     ctx->d = d;
361*3117ece4Schristos     ctx->f = f;
362*3117ece4Schristos     ctx->accelParams = accelParams;
363*3117ece4Schristos 
364*3117ece4Schristos     /* The offsets of each file */
365*3117ece4Schristos     ctx->offsets = (size_t*)calloc((nbSamples + 1), sizeof(size_t));
366*3117ece4Schristos     if (ctx->offsets == NULL) {
367*3117ece4Schristos         DISPLAYLEVEL(1, "Failed to allocate scratch buffers \n");
368*3117ece4Schristos         FASTCOVER_ctx_destroy(ctx);
369*3117ece4Schristos         return ERROR(memory_allocation);
370*3117ece4Schristos     }
371*3117ece4Schristos 
372*3117ece4Schristos     /* Fill offsets from the samplesSizes */
373*3117ece4Schristos     {   U32 i;
374*3117ece4Schristos         ctx->offsets[0] = 0;
375*3117ece4Schristos         assert(nbSamples >= 5);
376*3117ece4Schristos         for (i = 1; i <= nbSamples; ++i) {
377*3117ece4Schristos             ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1];
378*3117ece4Schristos         }
379*3117ece4Schristos     }
380*3117ece4Schristos 
381*3117ece4Schristos     /* Initialize frequency array of size 2^f */
382*3117ece4Schristos     ctx->freqs = (U32*)calloc(((U64)1 << f), sizeof(U32));
383*3117ece4Schristos     if (ctx->freqs == NULL) {
384*3117ece4Schristos         DISPLAYLEVEL(1, "Failed to allocate frequency table \n");
385*3117ece4Schristos         FASTCOVER_ctx_destroy(ctx);
386*3117ece4Schristos         return ERROR(memory_allocation);
387*3117ece4Schristos     }
388*3117ece4Schristos 
389*3117ece4Schristos     DISPLAYLEVEL(2, "Computing frequencies\n");
390*3117ece4Schristos     FASTCOVER_computeFrequency(ctx->freqs, ctx);
391*3117ece4Schristos 
392*3117ece4Schristos     return 0;
393*3117ece4Schristos }
394*3117ece4Schristos 
395*3117ece4Schristos 
396*3117ece4Schristos /**
397*3117ece4Schristos  * Given the prepared context build the dictionary.
398*3117ece4Schristos  */
399*3117ece4Schristos static size_t
400*3117ece4Schristos FASTCOVER_buildDictionary(const FASTCOVER_ctx_t* ctx,
401*3117ece4Schristos                           U32* freqs,
402*3117ece4Schristos                           void* dictBuffer, size_t dictBufferCapacity,
403*3117ece4Schristos                           ZDICT_cover_params_t parameters,
404*3117ece4Schristos                           U16* segmentFreqs)
405*3117ece4Schristos {
406*3117ece4Schristos   BYTE *const dict = (BYTE *)dictBuffer;
407*3117ece4Schristos   size_t tail = dictBufferCapacity;
408*3117ece4Schristos   /* Divide the data into epochs. We will select one segment from each epoch. */
409*3117ece4Schristos   const COVER_epoch_info_t epochs = COVER_computeEpochs(
410*3117ece4Schristos       (U32)dictBufferCapacity, (U32)ctx->nbDmers, parameters.k, 1);
411*3117ece4Schristos   const size_t maxZeroScoreRun = 10;
412*3117ece4Schristos   size_t zeroScoreRun = 0;
413*3117ece4Schristos   size_t epoch;
414*3117ece4Schristos   DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n",
415*3117ece4Schristos                 (U32)epochs.num, (U32)epochs.size);
416*3117ece4Schristos   /* Loop through the epochs until there are no more segments or the dictionary
417*3117ece4Schristos    * is full.
418*3117ece4Schristos    */
419*3117ece4Schristos   for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs.num) {
420*3117ece4Schristos     const U32 epochBegin = (U32)(epoch * epochs.size);
421*3117ece4Schristos     const U32 epochEnd = epochBegin + epochs.size;
422*3117ece4Schristos     size_t segmentSize;
423*3117ece4Schristos     /* Select a segment */
424*3117ece4Schristos     COVER_segment_t segment = FASTCOVER_selectSegment(
425*3117ece4Schristos         ctx, freqs, epochBegin, epochEnd, parameters, segmentFreqs);
426*3117ece4Schristos 
427*3117ece4Schristos     /* If the segment covers no dmers, then we are out of content.
428*3117ece4Schristos      * There may be new content in other epochs, for continue for some time.
429*3117ece4Schristos      */
430*3117ece4Schristos     if (segment.score == 0) {
431*3117ece4Schristos       if (++zeroScoreRun >= maxZeroScoreRun) {
432*3117ece4Schristos           break;
433*3117ece4Schristos       }
434*3117ece4Schristos       continue;
435*3117ece4Schristos     }
436*3117ece4Schristos     zeroScoreRun = 0;
437*3117ece4Schristos 
438*3117ece4Schristos     /* Trim the segment if necessary and if it is too small then we are done */
439*3117ece4Schristos     segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail);
440*3117ece4Schristos     if (segmentSize < parameters.d) {
441*3117ece4Schristos       break;
442*3117ece4Schristos     }
443*3117ece4Schristos 
444*3117ece4Schristos     /* We fill the dictionary from the back to allow the best segments to be
445*3117ece4Schristos      * referenced with the smallest offsets.
446*3117ece4Schristos      */
447*3117ece4Schristos     tail -= segmentSize;
448*3117ece4Schristos     memcpy(dict + tail, ctx->samples + segment.begin, segmentSize);
449*3117ece4Schristos     DISPLAYUPDATE(
450*3117ece4Schristos         2, "\r%u%%       ",
451*3117ece4Schristos         (unsigned)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity));
452*3117ece4Schristos   }
453*3117ece4Schristos   DISPLAYLEVEL(2, "\r%79s\r", "");
454*3117ece4Schristos   return tail;
455*3117ece4Schristos }
456*3117ece4Schristos 
457*3117ece4Schristos /**
458*3117ece4Schristos  * Parameters for FASTCOVER_tryParameters().
459*3117ece4Schristos  */
460*3117ece4Schristos typedef struct FASTCOVER_tryParameters_data_s {
461*3117ece4Schristos     const FASTCOVER_ctx_t* ctx;
462*3117ece4Schristos     COVER_best_t* best;
463*3117ece4Schristos     size_t dictBufferCapacity;
464*3117ece4Schristos     ZDICT_cover_params_t parameters;
465*3117ece4Schristos } FASTCOVER_tryParameters_data_t;
466*3117ece4Schristos 
467*3117ece4Schristos 
468*3117ece4Schristos /**
469*3117ece4Schristos  * Tries a set of parameters and updates the COVER_best_t with the results.
470*3117ece4Schristos  * This function is thread safe if zstd is compiled with multithreaded support.
471*3117ece4Schristos  * It takes its parameters as an *OWNING* opaque pointer to support threading.
472*3117ece4Schristos  */
473*3117ece4Schristos static void FASTCOVER_tryParameters(void* opaque)
474*3117ece4Schristos {
475*3117ece4Schristos   /* Save parameters as local variables */
476*3117ece4Schristos   FASTCOVER_tryParameters_data_t *const data = (FASTCOVER_tryParameters_data_t*)opaque;
477*3117ece4Schristos   const FASTCOVER_ctx_t *const ctx = data->ctx;
478*3117ece4Schristos   const ZDICT_cover_params_t parameters = data->parameters;
479*3117ece4Schristos   size_t dictBufferCapacity = data->dictBufferCapacity;
480*3117ece4Schristos   size_t totalCompressedSize = ERROR(GENERIC);
481*3117ece4Schristos   /* Initialize array to keep track of frequency of dmer within activeSegment */
482*3117ece4Schristos   U16* segmentFreqs = (U16*)calloc(((U64)1 << ctx->f), sizeof(U16));
483*3117ece4Schristos   /* Allocate space for hash table, dict, and freqs */
484*3117ece4Schristos   BYTE *const dict = (BYTE*)malloc(dictBufferCapacity);
485*3117ece4Schristos   COVER_dictSelection_t selection = COVER_dictSelectionError(ERROR(GENERIC));
486*3117ece4Schristos   U32* freqs = (U32*) malloc(((U64)1 << ctx->f) * sizeof(U32));
487*3117ece4Schristos   if (!segmentFreqs || !dict || !freqs) {
488*3117ece4Schristos     DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n");
489*3117ece4Schristos     goto _cleanup;
490*3117ece4Schristos   }
491*3117ece4Schristos   /* Copy the frequencies because we need to modify them */
492*3117ece4Schristos   memcpy(freqs, ctx->freqs, ((U64)1 << ctx->f) * sizeof(U32));
493*3117ece4Schristos   /* Build the dictionary */
494*3117ece4Schristos   { const size_t tail = FASTCOVER_buildDictionary(ctx, freqs, dict, dictBufferCapacity,
495*3117ece4Schristos                                                     parameters, segmentFreqs);
496*3117ece4Schristos 
497*3117ece4Schristos     const unsigned nbFinalizeSamples = (unsigned)(ctx->nbTrainSamples * ctx->accelParams.finalize / 100);
498*3117ece4Schristos     selection = COVER_selectDict(dict + tail, dictBufferCapacity, dictBufferCapacity - tail,
499*3117ece4Schristos          ctx->samples, ctx->samplesSizes, nbFinalizeSamples, ctx->nbTrainSamples, ctx->nbSamples, parameters, ctx->offsets,
500*3117ece4Schristos          totalCompressedSize);
501*3117ece4Schristos 
502*3117ece4Schristos     if (COVER_dictSelectionIsError(selection)) {
503*3117ece4Schristos       DISPLAYLEVEL(1, "Failed to select dictionary\n");
504*3117ece4Schristos       goto _cleanup;
505*3117ece4Schristos     }
506*3117ece4Schristos   }
507*3117ece4Schristos _cleanup:
508*3117ece4Schristos   free(dict);
509*3117ece4Schristos   COVER_best_finish(data->best, parameters, selection);
510*3117ece4Schristos   free(data);
511*3117ece4Schristos   free(segmentFreqs);
512*3117ece4Schristos   COVER_dictSelectionFree(selection);
513*3117ece4Schristos   free(freqs);
514*3117ece4Schristos }
515*3117ece4Schristos 
516*3117ece4Schristos 
517*3117ece4Schristos static void
518*3117ece4Schristos FASTCOVER_convertToCoverParams(ZDICT_fastCover_params_t fastCoverParams,
519*3117ece4Schristos                                ZDICT_cover_params_t* coverParams)
520*3117ece4Schristos {
521*3117ece4Schristos     coverParams->k = fastCoverParams.k;
522*3117ece4Schristos     coverParams->d = fastCoverParams.d;
523*3117ece4Schristos     coverParams->steps = fastCoverParams.steps;
524*3117ece4Schristos     coverParams->nbThreads = fastCoverParams.nbThreads;
525*3117ece4Schristos     coverParams->splitPoint = fastCoverParams.splitPoint;
526*3117ece4Schristos     coverParams->zParams = fastCoverParams.zParams;
527*3117ece4Schristos     coverParams->shrinkDict = fastCoverParams.shrinkDict;
528*3117ece4Schristos }
529*3117ece4Schristos 
530*3117ece4Schristos 
531*3117ece4Schristos static void
532*3117ece4Schristos FASTCOVER_convertToFastCoverParams(ZDICT_cover_params_t coverParams,
533*3117ece4Schristos                                    ZDICT_fastCover_params_t* fastCoverParams,
534*3117ece4Schristos                                    unsigned f, unsigned accel)
535*3117ece4Schristos {
536*3117ece4Schristos     fastCoverParams->k = coverParams.k;
537*3117ece4Schristos     fastCoverParams->d = coverParams.d;
538*3117ece4Schristos     fastCoverParams->steps = coverParams.steps;
539*3117ece4Schristos     fastCoverParams->nbThreads = coverParams.nbThreads;
540*3117ece4Schristos     fastCoverParams->splitPoint = coverParams.splitPoint;
541*3117ece4Schristos     fastCoverParams->f = f;
542*3117ece4Schristos     fastCoverParams->accel = accel;
543*3117ece4Schristos     fastCoverParams->zParams = coverParams.zParams;
544*3117ece4Schristos     fastCoverParams->shrinkDict = coverParams.shrinkDict;
545*3117ece4Schristos }
546*3117ece4Schristos 
547*3117ece4Schristos 
548*3117ece4Schristos ZDICTLIB_STATIC_API size_t
549*3117ece4Schristos ZDICT_trainFromBuffer_fastCover(void* dictBuffer, size_t dictBufferCapacity,
550*3117ece4Schristos                                 const void* samplesBuffer,
551*3117ece4Schristos                                 const size_t* samplesSizes, unsigned nbSamples,
552*3117ece4Schristos                                 ZDICT_fastCover_params_t parameters)
553*3117ece4Schristos {
554*3117ece4Schristos     BYTE* const dict = (BYTE*)dictBuffer;
555*3117ece4Schristos     FASTCOVER_ctx_t ctx;
556*3117ece4Schristos     ZDICT_cover_params_t coverParams;
557*3117ece4Schristos     FASTCOVER_accel_t accelParams;
558*3117ece4Schristos     /* Initialize global data */
559*3117ece4Schristos     g_displayLevel = (int)parameters.zParams.notificationLevel;
560*3117ece4Schristos     /* Assign splitPoint and f if not provided */
561*3117ece4Schristos     parameters.splitPoint = 1.0;
562*3117ece4Schristos     parameters.f = parameters.f == 0 ? DEFAULT_F : parameters.f;
563*3117ece4Schristos     parameters.accel = parameters.accel == 0 ? DEFAULT_ACCEL : parameters.accel;
564*3117ece4Schristos     /* Convert to cover parameter */
565*3117ece4Schristos     memset(&coverParams, 0 , sizeof(coverParams));
566*3117ece4Schristos     FASTCOVER_convertToCoverParams(parameters, &coverParams);
567*3117ece4Schristos     /* Checks */
568*3117ece4Schristos     if (!FASTCOVER_checkParameters(coverParams, dictBufferCapacity, parameters.f,
569*3117ece4Schristos                                    parameters.accel)) {
570*3117ece4Schristos       DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n");
571*3117ece4Schristos       return ERROR(parameter_outOfBound);
572*3117ece4Schristos     }
573*3117ece4Schristos     if (nbSamples == 0) {
574*3117ece4Schristos       DISPLAYLEVEL(1, "FASTCOVER must have at least one input file\n");
575*3117ece4Schristos       return ERROR(srcSize_wrong);
576*3117ece4Schristos     }
577*3117ece4Schristos     if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
578*3117ece4Schristos       DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",
579*3117ece4Schristos                    ZDICT_DICTSIZE_MIN);
580*3117ece4Schristos       return ERROR(dstSize_tooSmall);
581*3117ece4Schristos     }
582*3117ece4Schristos     /* Assign corresponding FASTCOVER_accel_t to accelParams*/
583*3117ece4Schristos     accelParams = FASTCOVER_defaultAccelParameters[parameters.accel];
584*3117ece4Schristos     /* Initialize context */
585*3117ece4Schristos     {
586*3117ece4Schristos       size_t const initVal = FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples,
587*3117ece4Schristos                             coverParams.d, parameters.splitPoint, parameters.f,
588*3117ece4Schristos                             accelParams);
589*3117ece4Schristos       if (ZSTD_isError(initVal)) {
590*3117ece4Schristos         DISPLAYLEVEL(1, "Failed to initialize context\n");
591*3117ece4Schristos         return initVal;
592*3117ece4Schristos       }
593*3117ece4Schristos     }
594*3117ece4Schristos     COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, g_displayLevel);
595*3117ece4Schristos     /* Build the dictionary */
596*3117ece4Schristos     DISPLAYLEVEL(2, "Building dictionary\n");
597*3117ece4Schristos     {
598*3117ece4Schristos       /* Initialize array to keep track of frequency of dmer within activeSegment */
599*3117ece4Schristos       U16* segmentFreqs = (U16 *)calloc(((U64)1 << parameters.f), sizeof(U16));
600*3117ece4Schristos       const size_t tail = FASTCOVER_buildDictionary(&ctx, ctx.freqs, dictBuffer,
601*3117ece4Schristos                                                 dictBufferCapacity, coverParams, segmentFreqs);
602*3117ece4Schristos       const unsigned nbFinalizeSamples = (unsigned)(ctx.nbTrainSamples * ctx.accelParams.finalize / 100);
603*3117ece4Schristos       const size_t dictionarySize = ZDICT_finalizeDictionary(
604*3117ece4Schristos           dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,
605*3117ece4Schristos           samplesBuffer, samplesSizes, nbFinalizeSamples, coverParams.zParams);
606*3117ece4Schristos       if (!ZSTD_isError(dictionarySize)) {
607*3117ece4Schristos           DISPLAYLEVEL(2, "Constructed dictionary of size %u\n",
608*3117ece4Schristos                       (unsigned)dictionarySize);
609*3117ece4Schristos       }
610*3117ece4Schristos       FASTCOVER_ctx_destroy(&ctx);
611*3117ece4Schristos       free(segmentFreqs);
612*3117ece4Schristos       return dictionarySize;
613*3117ece4Schristos     }
614*3117ece4Schristos }
615*3117ece4Schristos 
616*3117ece4Schristos 
617*3117ece4Schristos ZDICTLIB_STATIC_API size_t
618*3117ece4Schristos ZDICT_optimizeTrainFromBuffer_fastCover(
619*3117ece4Schristos                     void* dictBuffer, size_t dictBufferCapacity,
620*3117ece4Schristos                     const void* samplesBuffer,
621*3117ece4Schristos                     const size_t* samplesSizes, unsigned nbSamples,
622*3117ece4Schristos                     ZDICT_fastCover_params_t* parameters)
623*3117ece4Schristos {
624*3117ece4Schristos     ZDICT_cover_params_t coverParams;
625*3117ece4Schristos     FASTCOVER_accel_t accelParams;
626*3117ece4Schristos     /* constants */
627*3117ece4Schristos     const unsigned nbThreads = parameters->nbThreads;
628*3117ece4Schristos     const double splitPoint =
629*3117ece4Schristos         parameters->splitPoint <= 0.0 ? FASTCOVER_DEFAULT_SPLITPOINT : parameters->splitPoint;
630*3117ece4Schristos     const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d;
631*3117ece4Schristos     const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d;
632*3117ece4Schristos     const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k;
633*3117ece4Schristos     const unsigned kMaxK = parameters->k == 0 ? 2000 : parameters->k;
634*3117ece4Schristos     const unsigned kSteps = parameters->steps == 0 ? 40 : parameters->steps;
635*3117ece4Schristos     const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1);
636*3117ece4Schristos     const unsigned kIterations =
637*3117ece4Schristos         (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize);
638*3117ece4Schristos     const unsigned f = parameters->f == 0 ? DEFAULT_F : parameters->f;
639*3117ece4Schristos     const unsigned accel = parameters->accel == 0 ? DEFAULT_ACCEL : parameters->accel;
640*3117ece4Schristos     const unsigned shrinkDict = 0;
641*3117ece4Schristos     /* Local variables */
642*3117ece4Schristos     const int displayLevel = (int)parameters->zParams.notificationLevel;
643*3117ece4Schristos     unsigned iteration = 1;
644*3117ece4Schristos     unsigned d;
645*3117ece4Schristos     unsigned k;
646*3117ece4Schristos     COVER_best_t best;
647*3117ece4Schristos     POOL_ctx *pool = NULL;
648*3117ece4Schristos     int warned = 0;
649*3117ece4Schristos     /* Checks */
650*3117ece4Schristos     if (splitPoint <= 0 || splitPoint > 1) {
651*3117ece4Schristos       LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect splitPoint\n");
652*3117ece4Schristos       return ERROR(parameter_outOfBound);
653*3117ece4Schristos     }
654*3117ece4Schristos     if (accel == 0 || accel > FASTCOVER_MAX_ACCEL) {
655*3117ece4Schristos       LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect accel\n");
656*3117ece4Schristos       return ERROR(parameter_outOfBound);
657*3117ece4Schristos     }
658*3117ece4Schristos     if (kMinK < kMaxD || kMaxK < kMinK) {
659*3117ece4Schristos       LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect k\n");
660*3117ece4Schristos       return ERROR(parameter_outOfBound);
661*3117ece4Schristos     }
662*3117ece4Schristos     if (nbSamples == 0) {
663*3117ece4Schristos       LOCALDISPLAYLEVEL(displayLevel, 1, "FASTCOVER must have at least one input file\n");
664*3117ece4Schristos       return ERROR(srcSize_wrong);
665*3117ece4Schristos     }
666*3117ece4Schristos     if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
667*3117ece4Schristos       LOCALDISPLAYLEVEL(displayLevel, 1, "dictBufferCapacity must be at least %u\n",
668*3117ece4Schristos                    ZDICT_DICTSIZE_MIN);
669*3117ece4Schristos       return ERROR(dstSize_tooSmall);
670*3117ece4Schristos     }
671*3117ece4Schristos     if (nbThreads > 1) {
672*3117ece4Schristos       pool = POOL_create(nbThreads, 1);
673*3117ece4Schristos       if (!pool) {
674*3117ece4Schristos         return ERROR(memory_allocation);
675*3117ece4Schristos       }
676*3117ece4Schristos     }
677*3117ece4Schristos     /* Initialization */
678*3117ece4Schristos     COVER_best_init(&best);
679*3117ece4Schristos     memset(&coverParams, 0 , sizeof(coverParams));
680*3117ece4Schristos     FASTCOVER_convertToCoverParams(*parameters, &coverParams);
681*3117ece4Schristos     accelParams = FASTCOVER_defaultAccelParameters[accel];
682*3117ece4Schristos     /* Turn down global display level to clean up display at level 2 and below */
683*3117ece4Schristos     g_displayLevel = displayLevel == 0 ? 0 : displayLevel - 1;
684*3117ece4Schristos     /* Loop through d first because each new value needs a new context */
685*3117ece4Schristos     LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n",
686*3117ece4Schristos                       kIterations);
687*3117ece4Schristos     for (d = kMinD; d <= kMaxD; d += 2) {
688*3117ece4Schristos       /* Initialize the context for this value of d */
689*3117ece4Schristos       FASTCOVER_ctx_t ctx;
690*3117ece4Schristos       LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d);
691*3117ece4Schristos       {
692*3117ece4Schristos         size_t const initVal = FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint, f, accelParams);
693*3117ece4Schristos         if (ZSTD_isError(initVal)) {
694*3117ece4Schristos           LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n");
695*3117ece4Schristos           COVER_best_destroy(&best);
696*3117ece4Schristos           POOL_free(pool);
697*3117ece4Schristos           return initVal;
698*3117ece4Schristos         }
699*3117ece4Schristos       }
700*3117ece4Schristos       if (!warned) {
701*3117ece4Schristos         COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, displayLevel);
702*3117ece4Schristos         warned = 1;
703*3117ece4Schristos       }
704*3117ece4Schristos       /* Loop through k reusing the same context */
705*3117ece4Schristos       for (k = kMinK; k <= kMaxK; k += kStepSize) {
706*3117ece4Schristos         /* Prepare the arguments */
707*3117ece4Schristos         FASTCOVER_tryParameters_data_t *data = (FASTCOVER_tryParameters_data_t *)malloc(
708*3117ece4Schristos             sizeof(FASTCOVER_tryParameters_data_t));
709*3117ece4Schristos         LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k);
710*3117ece4Schristos         if (!data) {
711*3117ece4Schristos           LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n");
712*3117ece4Schristos           COVER_best_destroy(&best);
713*3117ece4Schristos           FASTCOVER_ctx_destroy(&ctx);
714*3117ece4Schristos           POOL_free(pool);
715*3117ece4Schristos           return ERROR(memory_allocation);
716*3117ece4Schristos         }
717*3117ece4Schristos         data->ctx = &ctx;
718*3117ece4Schristos         data->best = &best;
719*3117ece4Schristos         data->dictBufferCapacity = dictBufferCapacity;
720*3117ece4Schristos         data->parameters = coverParams;
721*3117ece4Schristos         data->parameters.k = k;
722*3117ece4Schristos         data->parameters.d = d;
723*3117ece4Schristos         data->parameters.splitPoint = splitPoint;
724*3117ece4Schristos         data->parameters.steps = kSteps;
725*3117ece4Schristos         data->parameters.shrinkDict = shrinkDict;
726*3117ece4Schristos         data->parameters.zParams.notificationLevel = (unsigned)g_displayLevel;
727*3117ece4Schristos         /* Check the parameters */
728*3117ece4Schristos         if (!FASTCOVER_checkParameters(data->parameters, dictBufferCapacity,
729*3117ece4Schristos                                        data->ctx->f, accel)) {
730*3117ece4Schristos           DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n");
731*3117ece4Schristos           free(data);
732*3117ece4Schristos           continue;
733*3117ece4Schristos         }
734*3117ece4Schristos         /* Call the function and pass ownership of data to it */
735*3117ece4Schristos         COVER_best_start(&best);
736*3117ece4Schristos         if (pool) {
737*3117ece4Schristos           POOL_add(pool, &FASTCOVER_tryParameters, data);
738*3117ece4Schristos         } else {
739*3117ece4Schristos           FASTCOVER_tryParameters(data);
740*3117ece4Schristos         }
741*3117ece4Schristos         /* Print status */
742*3117ece4Schristos         LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%%       ",
743*3117ece4Schristos                            (unsigned)((iteration * 100) / kIterations));
744*3117ece4Schristos         ++iteration;
745*3117ece4Schristos       }
746*3117ece4Schristos       COVER_best_wait(&best);
747*3117ece4Schristos       FASTCOVER_ctx_destroy(&ctx);
748*3117ece4Schristos     }
749*3117ece4Schristos     LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", "");
750*3117ece4Schristos     /* Fill the output buffer and parameters with output of the best parameters */
751*3117ece4Schristos     {
752*3117ece4Schristos       const size_t dictSize = best.dictSize;
753*3117ece4Schristos       if (ZSTD_isError(best.compressedSize)) {
754*3117ece4Schristos         const size_t compressedSize = best.compressedSize;
755*3117ece4Schristos         COVER_best_destroy(&best);
756*3117ece4Schristos         POOL_free(pool);
757*3117ece4Schristos         return compressedSize;
758*3117ece4Schristos       }
759*3117ece4Schristos       FASTCOVER_convertToFastCoverParams(best.parameters, parameters, f, accel);
760*3117ece4Schristos       memcpy(dictBuffer, best.dict, dictSize);
761*3117ece4Schristos       COVER_best_destroy(&best);
762*3117ece4Schristos       POOL_free(pool);
763*3117ece4Schristos       return dictSize;
764*3117ece4Schristos     }
765*3117ece4Schristos 
766*3117ece4Schristos }
767