Coverage Report

Created: 2025-07-11 06:34

/src/zstd/lib/dictBuilder/fastcover.c
Line
Count
Source (jump to first uncovered line)
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
    ctx->displayLevel = displayLevel;
321
322
    /* Checks */
323
0
    if (totalSamplesSize < MAX(d, sizeof(U64)) ||
324
0
        totalSamplesSize >= (size_t)FASTCOVER_MAX_SAMPLES_SIZE) {
325
0
        DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n",
326
0
                    (unsigned)(totalSamplesSize >> 20), (FASTCOVER_MAX_SAMPLES_SIZE >> 20));
327
0
        return ERROR(srcSize_wrong);
328
0
    }
329
330
    /* Check if there are at least 5 training samples */
331
0
    if (nbTrainSamples < 5) {
332
0
        DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid\n", nbTrainSamples);
333
0
        return ERROR(srcSize_wrong);
334
0
    }
335
336
    /* Check if there's testing sample */
337
0
    if (nbTestSamples < 1) {
338
0
        DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.\n", nbTestSamples);
339
0
        return ERROR(srcSize_wrong);
340
0
    }
341
342
    /* Zero the context */
343
0
    memset(ctx, 0, sizeof(*ctx));
344
0
    DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbTrainSamples,
345
0
                    (unsigned)trainingSamplesSize);
346
0
    DISPLAYLEVEL(2, "Testing on %u samples of total size %u\n", nbTestSamples,
347
0
                    (unsigned)testSamplesSize);
348
349
0
    ctx->samples = samples;
350
0
    ctx->samplesSizes = samplesSizes;
351
0
    ctx->nbSamples = nbSamples;
352
0
    ctx->nbTrainSamples = nbTrainSamples;
353
0
    ctx->nbTestSamples = nbTestSamples;
354
0
    ctx->nbDmers = trainingSamplesSize - MAX(d, sizeof(U64)) + 1;
355
0
    ctx->d = d;
356
0
    ctx->f = f;
357
0
    ctx->accelParams = accelParams;
358
359
    /* The offsets of each file */
360
0
    ctx->offsets = (size_t*)calloc((nbSamples + 1), sizeof(size_t));
361
0
    if (ctx->offsets == NULL) {
362
0
        DISPLAYLEVEL(1, "Failed to allocate scratch buffers \n");
363
0
        FASTCOVER_ctx_destroy(ctx);
364
0
        return ERROR(memory_allocation);
365
0
    }
366
367
    /* Fill offsets from the samplesSizes */
368
0
    {   U32 i;
369
0
        ctx->offsets[0] = 0;
370
0
        assert(nbSamples >= 5);
371
0
        for (i = 1; i <= nbSamples; ++i) {
372
0
            ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1];
373
0
        }
374
0
    }
375
376
    /* Initialize frequency array of size 2^f */
377
0
    ctx->freqs = (U32*)calloc(((U64)1 << f), sizeof(U32));
378
0
    if (ctx->freqs == NULL) {
379
0
        DISPLAYLEVEL(1, "Failed to allocate frequency table \n");
380
0
        FASTCOVER_ctx_destroy(ctx);
381
0
        return ERROR(memory_allocation);
382
0
    }
383
384
0
    DISPLAYLEVEL(2, "Computing frequencies\n");
385
0
    FASTCOVER_computeFrequency(ctx->freqs, ctx);
386
387
0
    return 0;
388
0
}
389
390
391
/**
392
 * Given the prepared context build the dictionary.
393
 */
394
static size_t
395
FASTCOVER_buildDictionary(const FASTCOVER_ctx_t* ctx,
396
                          U32* freqs,
397
                          void* dictBuffer, size_t dictBufferCapacity,
398
                          ZDICT_cover_params_t parameters,
399
                          U16* segmentFreqs)
400
0
{
401
0
  BYTE *const dict = (BYTE *)dictBuffer;
402
0
  size_t tail = dictBufferCapacity;
403
  /* Divide the data into epochs. We will select one segment from each epoch. */
404
0
  const COVER_epoch_info_t epochs = COVER_computeEpochs(
405
0
      (U32)dictBufferCapacity, (U32)ctx->nbDmers, parameters.k, 1);
406
0
  const size_t maxZeroScoreRun = 10;
407
0
  const int displayLevel = ctx->displayLevel;
408
0
  size_t zeroScoreRun = 0;
409
0
  clock_t lastUpdateTime = 0;
410
0
  size_t epoch;
411
0
  DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n",
412
0
                (U32)epochs.num, (U32)epochs.size);
413
  /* Loop through the epochs until there are no more segments or the dictionary
414
   * is full.
415
   */
416
0
  for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs.num) {
417
0
    const U32 epochBegin = (U32)(epoch * epochs.size);
418
0
    const U32 epochEnd = epochBegin + epochs.size;
419
0
    size_t segmentSize;
420
    /* Select a segment */
421
0
    COVER_segment_t segment = FASTCOVER_selectSegment(
422
0
        ctx, freqs, epochBegin, epochEnd, parameters, segmentFreqs);
423
424
    /* If the segment covers no dmers, then we are out of content.
425
     * There may be new content in other epochs, for continue for some time.
426
     */
427
0
    if (segment.score == 0) {
428
0
      if (++zeroScoreRun >= maxZeroScoreRun) {
429
0
          break;
430
0
      }
431
0
      continue;
432
0
    }
433
0
    zeroScoreRun = 0;
434
435
    /* Trim the segment if necessary and if it is too small then we are done */
436
0
    segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail);
437
0
    if (segmentSize < parameters.d) {
438
0
      break;
439
0
    }
440
441
    /* We fill the dictionary from the back to allow the best segments to be
442
     * referenced with the smallest offsets.
443
     */
444
0
    tail -= segmentSize;
445
0
    memcpy(dict + tail, ctx->samples + segment.begin, segmentSize);
446
0
    DISPLAYUPDATE(
447
0
        lastUpdateTime,
448
0
        2, "\r%u%%       ",
449
0
        (unsigned)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity));
450
0
  }
451
0
  DISPLAYLEVEL(2, "\r%79s\r", "");
452
0
  return tail;
453
0
}
454
455
/**
456
 * Parameters for FASTCOVER_tryParameters().
457
 */
458
typedef struct FASTCOVER_tryParameters_data_s {
459
    const FASTCOVER_ctx_t* ctx;
460
    COVER_best_t* best;
461
    size_t dictBufferCapacity;
462
    ZDICT_cover_params_t parameters;
463
} FASTCOVER_tryParameters_data_t;
464
465
466
/**
467
 * Tries a set of parameters and updates the COVER_best_t with the results.
468
 * This function is thread safe if zstd is compiled with multithreaded support.
469
 * It takes its parameters as an *OWNING* opaque pointer to support threading.
470
 */
471
static void FASTCOVER_tryParameters(void* opaque)
472
0
{
473
  /* Save parameters as local variables */
474
0
  FASTCOVER_tryParameters_data_t *const data = (FASTCOVER_tryParameters_data_t*)opaque;
475
0
  const FASTCOVER_ctx_t *const ctx = data->ctx;
476
0
  const ZDICT_cover_params_t parameters = data->parameters;
477
0
  size_t dictBufferCapacity = data->dictBufferCapacity;
478
0
  size_t totalCompressedSize = ERROR(GENERIC);
479
  /* Initialize array to keep track of frequency of dmer within activeSegment */
480
0
  U16* segmentFreqs = (U16*)calloc(((U64)1 << ctx->f), sizeof(U16));
481
  /* Allocate space for hash table, dict, and freqs */
482
0
  BYTE *const dict = (BYTE*)malloc(dictBufferCapacity);
483
0
  COVER_dictSelection_t selection = COVER_dictSelectionError(ERROR(GENERIC));
484
0
  U32* freqs = (U32*) malloc(((U64)1 << ctx->f) * sizeof(U32));
485
0
  const int displayLevel = ctx->displayLevel;
486
0
  if (!segmentFreqs || !dict || !freqs) {
487
0
    DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n");
488
0
    goto _cleanup;
489
0
  }
490
  /* Copy the frequencies because we need to modify them */
491
0
  memcpy(freqs, ctx->freqs, ((U64)1 << ctx->f) * sizeof(U32));
492
  /* Build the dictionary */
493
0
  { const size_t tail = FASTCOVER_buildDictionary(ctx, freqs, dict, dictBufferCapacity,
494
0
                                                    parameters, segmentFreqs);
495
496
0
    const unsigned nbFinalizeSamples = (unsigned)(ctx->nbTrainSamples * ctx->accelParams.finalize / 100);
497
0
    selection = COVER_selectDict(dict + tail, dictBufferCapacity, dictBufferCapacity - tail,
498
0
         ctx->samples, ctx->samplesSizes, nbFinalizeSamples, ctx->nbTrainSamples, ctx->nbSamples, parameters, ctx->offsets,
499
0
         totalCompressedSize);
500
501
0
    if (COVER_dictSelectionIsError(selection)) {
502
0
      DISPLAYLEVEL(1, "Failed to select dictionary\n");
503
0
      goto _cleanup;
504
0
    }
505
0
  }
506
0
_cleanup:
507
0
  free(dict);
508
0
  COVER_best_finish(data->best, parameters, selection);
509
0
  free(data);
510
0
  free(segmentFreqs);
511
0
  COVER_dictSelectionFree(selection);
512
0
  free(freqs);
513
0
}
514
515
516
static void
517
FASTCOVER_convertToCoverParams(ZDICT_fastCover_params_t fastCoverParams,
518
                               ZDICT_cover_params_t* coverParams)
519
0
{
520
0
    coverParams->k = fastCoverParams.k;
521
0
    coverParams->d = fastCoverParams.d;
522
0
    coverParams->steps = fastCoverParams.steps;
523
0
    coverParams->nbThreads = fastCoverParams.nbThreads;
524
0
    coverParams->splitPoint = fastCoverParams.splitPoint;
525
0
    coverParams->zParams = fastCoverParams.zParams;
526
0
    coverParams->shrinkDict = fastCoverParams.shrinkDict;
527
0
}
528
529
530
static void
531
FASTCOVER_convertToFastCoverParams(ZDICT_cover_params_t coverParams,
532
                                   ZDICT_fastCover_params_t* fastCoverParams,
533
                                   unsigned f, unsigned accel)
534
0
{
535
0
    fastCoverParams->k = coverParams.k;
536
0
    fastCoverParams->d = coverParams.d;
537
0
    fastCoverParams->steps = coverParams.steps;
538
0
    fastCoverParams->nbThreads = coverParams.nbThreads;
539
0
    fastCoverParams->splitPoint = coverParams.splitPoint;
540
0
    fastCoverParams->f = f;
541
0
    fastCoverParams->accel = accel;
542
0
    fastCoverParams->zParams = coverParams.zParams;
543
0
    fastCoverParams->shrinkDict = coverParams.shrinkDict;
544
0
}
545
546
547
ZDICTLIB_STATIC_API size_t
548
ZDICT_trainFromBuffer_fastCover(void* dictBuffer, size_t dictBufferCapacity,
549
                                const void* samplesBuffer,
550
                                const size_t* samplesSizes, unsigned nbSamples,
551
                                ZDICT_fastCover_params_t parameters)
552
0
{
553
0
    BYTE* const dict = (BYTE*)dictBuffer;
554
0
    FASTCOVER_ctx_t ctx;
555
0
    ZDICT_cover_params_t coverParams;
556
0
    FASTCOVER_accel_t accelParams;
557
0
    const int displayLevel = (int)parameters.zParams.notificationLevel;
558
    /* Assign splitPoint and f if not provided */
559
0
    parameters.splitPoint = 1.0;
560
0
    parameters.f = parameters.f == 0 ? DEFAULT_F : parameters.f;
561
0
    parameters.accel = parameters.accel == 0 ? DEFAULT_ACCEL : parameters.accel;
562
    /* Convert to cover parameter */
563
0
    memset(&coverParams, 0 , sizeof(coverParams));
564
0
    FASTCOVER_convertToCoverParams(parameters, &coverParams);
565
    /* Checks */
566
0
    if (!FASTCOVER_checkParameters(coverParams, dictBufferCapacity, parameters.f,
567
0
                                   parameters.accel)) {
568
0
      DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n");
569
0
      return ERROR(parameter_outOfBound);
570
0
    }
571
0
    if (nbSamples == 0) {
572
0
      DISPLAYLEVEL(1, "FASTCOVER must have at least one input file\n");
573
0
      return ERROR(srcSize_wrong);
574
0
    }
575
0
    if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
576
0
      DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",
577
0
                   ZDICT_DICTSIZE_MIN);
578
0
      return ERROR(dstSize_tooSmall);
579
0
    }
580
    /* Assign corresponding FASTCOVER_accel_t to accelParams*/
581
0
    accelParams = FASTCOVER_defaultAccelParameters[parameters.accel];
582
    /* Initialize context */
583
0
    {
584
0
      size_t const initVal = FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples,
585
0
                            coverParams.d, parameters.splitPoint, parameters.f,
586
0
                            accelParams, displayLevel);
587
0
      if (ZSTD_isError(initVal)) {
588
0
        DISPLAYLEVEL(1, "Failed to initialize context\n");
589
0
        return initVal;
590
0
      }
591
0
    }
592
0
    COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, displayLevel);
593
    /* Build the dictionary */
594
0
    DISPLAYLEVEL(2, "Building dictionary\n");
595
0
    {
596
      /* Initialize array to keep track of frequency of dmer within activeSegment */
597
0
      U16* segmentFreqs = (U16 *)calloc(((U64)1 << parameters.f), sizeof(U16));
598
0
      const size_t tail = FASTCOVER_buildDictionary(&ctx, ctx.freqs, dictBuffer,
599
0
                                                dictBufferCapacity, coverParams, segmentFreqs);
600
0
      const unsigned nbFinalizeSamples = (unsigned)(ctx.nbTrainSamples * ctx.accelParams.finalize / 100);
601
0
      const size_t dictionarySize = ZDICT_finalizeDictionary(
602
0
          dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,
603
0
          samplesBuffer, samplesSizes, nbFinalizeSamples, coverParams.zParams);
604
0
      if (!ZSTD_isError(dictionarySize)) {
605
0
          DISPLAYLEVEL(2, "Constructed dictionary of size %u\n",
606
0
                      (unsigned)dictionarySize);
607
0
      }
608
0
      FASTCOVER_ctx_destroy(&ctx);
609
0
      free(segmentFreqs);
610
0
      return dictionarySize;
611
0
    }
612
0
}
613
614
615
ZDICTLIB_STATIC_API size_t
616
ZDICT_optimizeTrainFromBuffer_fastCover(
617
                    void* dictBuffer, size_t dictBufferCapacity,
618
                    const void* samplesBuffer,
619
                    const size_t* samplesSizes, unsigned nbSamples,
620
                    ZDICT_fastCover_params_t* parameters)
621
0
{
622
0
    ZDICT_cover_params_t coverParams;
623
0
    FASTCOVER_accel_t accelParams;
624
    /* constants */
625
0
    const unsigned nbThreads = parameters->nbThreads;
626
0
    const double splitPoint =
627
0
        parameters->splitPoint <= 0.0 ? FASTCOVER_DEFAULT_SPLITPOINT : parameters->splitPoint;
628
0
    const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d;
629
0
    const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d;
630
0
    const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k;
631
0
    const unsigned kMaxK = parameters->k == 0 ? 2000 : parameters->k;
632
0
    const unsigned kSteps = parameters->steps == 0 ? 40 : parameters->steps;
633
0
    const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1);
634
0
    const unsigned kIterations =
635
0
        (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize);
636
0
    const unsigned f = parameters->f == 0 ? DEFAULT_F : parameters->f;
637
0
    const unsigned accel = parameters->accel == 0 ? DEFAULT_ACCEL : parameters->accel;
638
0
    const unsigned shrinkDict = 0;
639
    /* Local variables */
640
0
    const int displayLevel = (int)parameters->zParams.notificationLevel;
641
0
    unsigned iteration = 1;
642
0
    unsigned d;
643
0
    unsigned k;
644
0
    COVER_best_t best;
645
0
    POOL_ctx *pool = NULL;
646
0
    int warned = 0;
647
0
    clock_t lastUpdateTime = 0;
648
    /* Checks */
649
0
    if (splitPoint <= 0 || splitPoint > 1) {
650
0
      DISPLAYLEVEL(1, "Incorrect splitPoint\n");
651
0
      return ERROR(parameter_outOfBound);
652
0
    }
653
0
    if (accel == 0 || accel > FASTCOVER_MAX_ACCEL) {
654
0
      DISPLAYLEVEL(1, "Incorrect accel\n");
655
0
      return ERROR(parameter_outOfBound);
656
0
    }
657
0
    if (kMinK < kMaxD || kMaxK < kMinK) {
658
0
      DISPLAYLEVEL(1, "Incorrect k\n");
659
0
      return ERROR(parameter_outOfBound);
660
0
    }
661
0
    if (nbSamples == 0) {
662
0
      DISPLAYLEVEL(1, "FASTCOVER must have at least one input file\n");
663
0
      return ERROR(srcSize_wrong);
664
0
    }
665
0
    if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
666
0
      DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",
667
0
                   ZDICT_DICTSIZE_MIN);
668
0
      return ERROR(dstSize_tooSmall);
669
0
    }
670
0
    if (nbThreads > 1) {
671
0
      pool = POOL_create(nbThreads, 1);
672
0
      if (!pool) {
673
0
        return ERROR(memory_allocation);
674
0
      }
675
0
    }
676
    /* Initialization */
677
0
    COVER_best_init(&best);
678
0
    memset(&coverParams, 0 , sizeof(coverParams));
679
0
    FASTCOVER_convertToCoverParams(*parameters, &coverParams);
680
0
    accelParams = FASTCOVER_defaultAccelParameters[accel];
681
    /* Loop through d first because each new value needs a new context */
682
0
    DISPLAYLEVEL(2, "Trying %u different sets of parameters\n", kIterations);
683
0
    for (d = kMinD; d <= kMaxD; d += 2) {
684
      /* Initialize the context for this value of d */
685
0
      FASTCOVER_ctx_t ctx;
686
0
      DISPLAYLEVEL(3, "d=%u\n", d);
687
0
      {
688
        /* Turn down global display level to clean up display at level 2 and below */
689
0
        const int childDisplayLevel = displayLevel == 0 ? 0 : displayLevel - 1;
690
0
        size_t const initVal = FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint, f, accelParams, childDisplayLevel);
691
0
        if (ZSTD_isError(initVal)) {
692
0
          DISPLAYLEVEL(1, "Failed to initialize context\n");
693
0
          COVER_best_destroy(&best);
694
0
          POOL_free(pool);
695
0
          return initVal;
696
0
        }
697
0
      }
698
0
      if (!warned) {
699
0
        COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, displayLevel);
700
0
        warned = 1;
701
0
      }
702
      /* Loop through k reusing the same context */
703
0
      for (k = kMinK; k <= kMaxK; k += kStepSize) {
704
        /* Prepare the arguments */
705
0
        FASTCOVER_tryParameters_data_t *data = (FASTCOVER_tryParameters_data_t *)malloc(
706
0
            sizeof(FASTCOVER_tryParameters_data_t));
707
0
        DISPLAYLEVEL(3, "k=%u\n", k);
708
0
        if (!data) {
709
0
          DISPLAYLEVEL(1, "Failed to allocate parameters\n");
710
0
          COVER_best_destroy(&best);
711
0
          FASTCOVER_ctx_destroy(&ctx);
712
0
          POOL_free(pool);
713
0
          return ERROR(memory_allocation);
714
0
        }
715
0
        data->ctx = &ctx;
716
0
        data->best = &best;
717
0
        data->dictBufferCapacity = dictBufferCapacity;
718
0
        data->parameters = coverParams;
719
0
        data->parameters.k = k;
720
0
        data->parameters.d = d;
721
0
        data->parameters.splitPoint = splitPoint;
722
0
        data->parameters.steps = kSteps;
723
0
        data->parameters.shrinkDict = shrinkDict;
724
0
        data->parameters.zParams.notificationLevel = (unsigned)ctx.displayLevel;
725
        /* Check the parameters */
726
0
        if (!FASTCOVER_checkParameters(data->parameters, dictBufferCapacity,
727
0
                                       data->ctx->f, accel)) {
728
0
          DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n");
729
0
          free(data);
730
0
          continue;
731
0
        }
732
        /* Call the function and pass ownership of data to it */
733
0
        COVER_best_start(&best);
734
0
        if (pool) {
735
0
          POOL_add(pool, &FASTCOVER_tryParameters, data);
736
0
        } else {
737
0
          FASTCOVER_tryParameters(data);
738
0
        }
739
        /* Print status */
740
0
        DISPLAYUPDATE(lastUpdateTime,
741
0
                      2, "\r%u%%       ",
742
0
                      (unsigned)((iteration * 100) / kIterations));
743
0
        ++iteration;
744
0
      }
745
0
      COVER_best_wait(&best);
746
0
      FASTCOVER_ctx_destroy(&ctx);
747
0
    }
748
0
    DISPLAYLEVEL(2, "\r%79s\r", "");
749
    /* Fill the output buffer and parameters with output of the best parameters */
750
0
    {
751
0
      const size_t dictSize = best.dictSize;
752
0
      if (ZSTD_isError(best.compressedSize)) {
753
0
        const size_t compressedSize = best.compressedSize;
754
0
        COVER_best_destroy(&best);
755
0
        POOL_free(pool);
756
0
        return compressedSize;
757
0
      }
758
0
      FASTCOVER_convertToFastCoverParams(best.parameters, parameters, f, accel);
759
0
      memcpy(dictBuffer, best.dict, dictSize);
760
0
      COVER_best_destroy(&best);
761
0
      POOL_free(pool);
762
0
      return dictSize;
763
0
    }
764
765
0
}