Coverage Report

Created: 2026-11-12 06:25

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