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