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 * Constructs a dictionary using a heuristic based on the following paper: 13*3117ece4Schristos * 14*3117ece4Schristos * Liao, Petri, Moffat, Wirth 15*3117ece4Schristos * Effective Construction of Relative Lempel-Ziv Dictionaries 16*3117ece4Schristos * Published in WWW 2016. 17*3117ece4Schristos * 18*3117ece4Schristos * Adapted from code originally written by @ot (Giuseppe Ottaviano). 19*3117ece4Schristos ******************************************************************************/ 20*3117ece4Schristos 21*3117ece4Schristos /*-************************************* 22*3117ece4Schristos * Dependencies 23*3117ece4Schristos ***************************************/ 24*3117ece4Schristos #include <stdio.h> /* fprintf */ 25*3117ece4Schristos #include <stdlib.h> /* malloc, free, qsort */ 26*3117ece4Schristos #include <string.h> /* memset */ 27*3117ece4Schristos #include <time.h> /* clock */ 28*3117ece4Schristos 29*3117ece4Schristos #ifndef ZDICT_STATIC_LINKING_ONLY 30*3117ece4Schristos # define ZDICT_STATIC_LINKING_ONLY 31*3117ece4Schristos #endif 32*3117ece4Schristos 33*3117ece4Schristos #include "../common/mem.h" /* read */ 34*3117ece4Schristos #include "../common/pool.h" /* POOL_ctx */ 35*3117ece4Schristos #include "../common/threading.h" /* ZSTD_pthread_mutex_t */ 36*3117ece4Schristos #include "../common/zstd_internal.h" /* includes zstd.h */ 37*3117ece4Schristos #include "../common/bits.h" /* ZSTD_highbit32 */ 38*3117ece4Schristos #include "../zdict.h" 39*3117ece4Schristos #include "cover.h" 40*3117ece4Schristos 41*3117ece4Schristos /*-************************************* 42*3117ece4Schristos * Constants 43*3117ece4Schristos ***************************************/ 44*3117ece4Schristos /** 45*3117ece4Schristos * There are 32bit indexes used to ref samples, so limit samples size to 4GB 46*3117ece4Schristos * on 64bit builds. 47*3117ece4Schristos * For 32bit builds we choose 1 GB. 48*3117ece4Schristos * Most 32bit platforms have 2GB user-mode addressable space and we allocate a large 49*3117ece4Schristos * contiguous buffer, so 1GB is already a high limit. 50*3117ece4Schristos */ 51*3117ece4Schristos #define COVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB)) 52*3117ece4Schristos #define COVER_DEFAULT_SPLITPOINT 1.0 53*3117ece4Schristos 54*3117ece4Schristos /*-************************************* 55*3117ece4Schristos * Console display 56*3117ece4Schristos ***************************************/ 57*3117ece4Schristos #ifndef LOCALDISPLAYLEVEL 58*3117ece4Schristos static int g_displayLevel = 0; 59*3117ece4Schristos #endif 60*3117ece4Schristos #undef DISPLAY 61*3117ece4Schristos #define DISPLAY(...) \ 62*3117ece4Schristos { \ 63*3117ece4Schristos fprintf(stderr, __VA_ARGS__); \ 64*3117ece4Schristos fflush(stderr); \ 65*3117ece4Schristos } 66*3117ece4Schristos #undef LOCALDISPLAYLEVEL 67*3117ece4Schristos #define LOCALDISPLAYLEVEL(displayLevel, l, ...) \ 68*3117ece4Schristos if (displayLevel >= l) { \ 69*3117ece4Schristos DISPLAY(__VA_ARGS__); \ 70*3117ece4Schristos } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */ 71*3117ece4Schristos #undef DISPLAYLEVEL 72*3117ece4Schristos #define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__) 73*3117ece4Schristos 74*3117ece4Schristos #ifndef LOCALDISPLAYUPDATE 75*3117ece4Schristos static const clock_t g_refreshRate = CLOCKS_PER_SEC * 15 / 100; 76*3117ece4Schristos static clock_t g_time = 0; 77*3117ece4Schristos #endif 78*3117ece4Schristos #undef LOCALDISPLAYUPDATE 79*3117ece4Schristos #define LOCALDISPLAYUPDATE(displayLevel, l, ...) \ 80*3117ece4Schristos if (displayLevel >= l) { \ 81*3117ece4Schristos if ((clock() - g_time > g_refreshRate) || (displayLevel >= 4)) { \ 82*3117ece4Schristos g_time = clock(); \ 83*3117ece4Schristos DISPLAY(__VA_ARGS__); \ 84*3117ece4Schristos } \ 85*3117ece4Schristos } 86*3117ece4Schristos #undef DISPLAYUPDATE 87*3117ece4Schristos #define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__) 88*3117ece4Schristos 89*3117ece4Schristos /*-************************************* 90*3117ece4Schristos * Hash table 91*3117ece4Schristos *************************************** 92*3117ece4Schristos * A small specialized hash map for storing activeDmers. 93*3117ece4Schristos * The map does not resize, so if it becomes full it will loop forever. 94*3117ece4Schristos * Thus, the map must be large enough to store every value. 95*3117ece4Schristos * The map implements linear probing and keeps its load less than 0.5. 96*3117ece4Schristos */ 97*3117ece4Schristos 98*3117ece4Schristos #define MAP_EMPTY_VALUE ((U32)-1) 99*3117ece4Schristos typedef struct COVER_map_pair_t_s { 100*3117ece4Schristos U32 key; 101*3117ece4Schristos U32 value; 102*3117ece4Schristos } COVER_map_pair_t; 103*3117ece4Schristos 104*3117ece4Schristos typedef struct COVER_map_s { 105*3117ece4Schristos COVER_map_pair_t *data; 106*3117ece4Schristos U32 sizeLog; 107*3117ece4Schristos U32 size; 108*3117ece4Schristos U32 sizeMask; 109*3117ece4Schristos } COVER_map_t; 110*3117ece4Schristos 111*3117ece4Schristos /** 112*3117ece4Schristos * Clear the map. 113*3117ece4Schristos */ 114*3117ece4Schristos static void COVER_map_clear(COVER_map_t *map) { 115*3117ece4Schristos memset(map->data, MAP_EMPTY_VALUE, map->size * sizeof(COVER_map_pair_t)); 116*3117ece4Schristos } 117*3117ece4Schristos 118*3117ece4Schristos /** 119*3117ece4Schristos * Initializes a map of the given size. 120*3117ece4Schristos * Returns 1 on success and 0 on failure. 121*3117ece4Schristos * The map must be destroyed with COVER_map_destroy(). 122*3117ece4Schristos * The map is only guaranteed to be large enough to hold size elements. 123*3117ece4Schristos */ 124*3117ece4Schristos static int COVER_map_init(COVER_map_t *map, U32 size) { 125*3117ece4Schristos map->sizeLog = ZSTD_highbit32(size) + 2; 126*3117ece4Schristos map->size = (U32)1 << map->sizeLog; 127*3117ece4Schristos map->sizeMask = map->size - 1; 128*3117ece4Schristos map->data = (COVER_map_pair_t *)malloc(map->size * sizeof(COVER_map_pair_t)); 129*3117ece4Schristos if (!map->data) { 130*3117ece4Schristos map->sizeLog = 0; 131*3117ece4Schristos map->size = 0; 132*3117ece4Schristos return 0; 133*3117ece4Schristos } 134*3117ece4Schristos COVER_map_clear(map); 135*3117ece4Schristos return 1; 136*3117ece4Schristos } 137*3117ece4Schristos 138*3117ece4Schristos /** 139*3117ece4Schristos * Internal hash function 140*3117ece4Schristos */ 141*3117ece4Schristos static const U32 COVER_prime4bytes = 2654435761U; 142*3117ece4Schristos static U32 COVER_map_hash(COVER_map_t *map, U32 key) { 143*3117ece4Schristos return (key * COVER_prime4bytes) >> (32 - map->sizeLog); 144*3117ece4Schristos } 145*3117ece4Schristos 146*3117ece4Schristos /** 147*3117ece4Schristos * Helper function that returns the index that a key should be placed into. 148*3117ece4Schristos */ 149*3117ece4Schristos static U32 COVER_map_index(COVER_map_t *map, U32 key) { 150*3117ece4Schristos const U32 hash = COVER_map_hash(map, key); 151*3117ece4Schristos U32 i; 152*3117ece4Schristos for (i = hash;; i = (i + 1) & map->sizeMask) { 153*3117ece4Schristos COVER_map_pair_t *pos = &map->data[i]; 154*3117ece4Schristos if (pos->value == MAP_EMPTY_VALUE) { 155*3117ece4Schristos return i; 156*3117ece4Schristos } 157*3117ece4Schristos if (pos->key == key) { 158*3117ece4Schristos return i; 159*3117ece4Schristos } 160*3117ece4Schristos } 161*3117ece4Schristos } 162*3117ece4Schristos 163*3117ece4Schristos /** 164*3117ece4Schristos * Returns the pointer to the value for key. 165*3117ece4Schristos * If key is not in the map, it is inserted and the value is set to 0. 166*3117ece4Schristos * The map must not be full. 167*3117ece4Schristos */ 168*3117ece4Schristos static U32 *COVER_map_at(COVER_map_t *map, U32 key) { 169*3117ece4Schristos COVER_map_pair_t *pos = &map->data[COVER_map_index(map, key)]; 170*3117ece4Schristos if (pos->value == MAP_EMPTY_VALUE) { 171*3117ece4Schristos pos->key = key; 172*3117ece4Schristos pos->value = 0; 173*3117ece4Schristos } 174*3117ece4Schristos return &pos->value; 175*3117ece4Schristos } 176*3117ece4Schristos 177*3117ece4Schristos /** 178*3117ece4Schristos * Deletes key from the map if present. 179*3117ece4Schristos */ 180*3117ece4Schristos static void COVER_map_remove(COVER_map_t *map, U32 key) { 181*3117ece4Schristos U32 i = COVER_map_index(map, key); 182*3117ece4Schristos COVER_map_pair_t *del = &map->data[i]; 183*3117ece4Schristos U32 shift = 1; 184*3117ece4Schristos if (del->value == MAP_EMPTY_VALUE) { 185*3117ece4Schristos return; 186*3117ece4Schristos } 187*3117ece4Schristos for (i = (i + 1) & map->sizeMask;; i = (i + 1) & map->sizeMask) { 188*3117ece4Schristos COVER_map_pair_t *const pos = &map->data[i]; 189*3117ece4Schristos /* If the position is empty we are done */ 190*3117ece4Schristos if (pos->value == MAP_EMPTY_VALUE) { 191*3117ece4Schristos del->value = MAP_EMPTY_VALUE; 192*3117ece4Schristos return; 193*3117ece4Schristos } 194*3117ece4Schristos /* If pos can be moved to del do so */ 195*3117ece4Schristos if (((i - COVER_map_hash(map, pos->key)) & map->sizeMask) >= shift) { 196*3117ece4Schristos del->key = pos->key; 197*3117ece4Schristos del->value = pos->value; 198*3117ece4Schristos del = pos; 199*3117ece4Schristos shift = 1; 200*3117ece4Schristos } else { 201*3117ece4Schristos ++shift; 202*3117ece4Schristos } 203*3117ece4Schristos } 204*3117ece4Schristos } 205*3117ece4Schristos 206*3117ece4Schristos /** 207*3117ece4Schristos * Destroys a map that is inited with COVER_map_init(). 208*3117ece4Schristos */ 209*3117ece4Schristos static void COVER_map_destroy(COVER_map_t *map) { 210*3117ece4Schristos if (map->data) { 211*3117ece4Schristos free(map->data); 212*3117ece4Schristos } 213*3117ece4Schristos map->data = NULL; 214*3117ece4Schristos map->size = 0; 215*3117ece4Schristos } 216*3117ece4Schristos 217*3117ece4Schristos /*-************************************* 218*3117ece4Schristos * Context 219*3117ece4Schristos ***************************************/ 220*3117ece4Schristos 221*3117ece4Schristos typedef struct { 222*3117ece4Schristos const BYTE *samples; 223*3117ece4Schristos size_t *offsets; 224*3117ece4Schristos const size_t *samplesSizes; 225*3117ece4Schristos size_t nbSamples; 226*3117ece4Schristos size_t nbTrainSamples; 227*3117ece4Schristos size_t nbTestSamples; 228*3117ece4Schristos U32 *suffix; 229*3117ece4Schristos size_t suffixSize; 230*3117ece4Schristos U32 *freqs; 231*3117ece4Schristos U32 *dmerAt; 232*3117ece4Schristos unsigned d; 233*3117ece4Schristos } COVER_ctx_t; 234*3117ece4Schristos 235*3117ece4Schristos /* We need a global context for qsort... */ 236*3117ece4Schristos static COVER_ctx_t *g_coverCtx = NULL; 237*3117ece4Schristos 238*3117ece4Schristos /*-************************************* 239*3117ece4Schristos * Helper functions 240*3117ece4Schristos ***************************************/ 241*3117ece4Schristos 242*3117ece4Schristos /** 243*3117ece4Schristos * Returns the sum of the sample sizes. 244*3117ece4Schristos */ 245*3117ece4Schristos size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) { 246*3117ece4Schristos size_t sum = 0; 247*3117ece4Schristos unsigned i; 248*3117ece4Schristos for (i = 0; i < nbSamples; ++i) { 249*3117ece4Schristos sum += samplesSizes[i]; 250*3117ece4Schristos } 251*3117ece4Schristos return sum; 252*3117ece4Schristos } 253*3117ece4Schristos 254*3117ece4Schristos /** 255*3117ece4Schristos * Returns -1 if the dmer at lp is less than the dmer at rp. 256*3117ece4Schristos * Return 0 if the dmers at lp and rp are equal. 257*3117ece4Schristos * Returns 1 if the dmer at lp is greater than the dmer at rp. 258*3117ece4Schristos */ 259*3117ece4Schristos static int COVER_cmp(COVER_ctx_t *ctx, const void *lp, const void *rp) { 260*3117ece4Schristos U32 const lhs = *(U32 const *)lp; 261*3117ece4Schristos U32 const rhs = *(U32 const *)rp; 262*3117ece4Schristos return memcmp(ctx->samples + lhs, ctx->samples + rhs, ctx->d); 263*3117ece4Schristos } 264*3117ece4Schristos /** 265*3117ece4Schristos * Faster version for d <= 8. 266*3117ece4Schristos */ 267*3117ece4Schristos static int COVER_cmp8(COVER_ctx_t *ctx, const void *lp, const void *rp) { 268*3117ece4Schristos U64 const mask = (ctx->d == 8) ? (U64)-1 : (((U64)1 << (8 * ctx->d)) - 1); 269*3117ece4Schristos U64 const lhs = MEM_readLE64(ctx->samples + *(U32 const *)lp) & mask; 270*3117ece4Schristos U64 const rhs = MEM_readLE64(ctx->samples + *(U32 const *)rp) & mask; 271*3117ece4Schristos if (lhs < rhs) { 272*3117ece4Schristos return -1; 273*3117ece4Schristos } 274*3117ece4Schristos return (lhs > rhs); 275*3117ece4Schristos } 276*3117ece4Schristos 277*3117ece4Schristos /** 278*3117ece4Schristos * Same as COVER_cmp() except ties are broken by pointer value 279*3117ece4Schristos * NOTE: g_coverCtx must be set to call this function. A global is required because 280*3117ece4Schristos * qsort doesn't take an opaque pointer. 281*3117ece4Schristos */ 282*3117ece4Schristos static int WIN_CDECL COVER_strict_cmp(const void *lp, const void *rp) { 283*3117ece4Schristos int result = COVER_cmp(g_coverCtx, lp, rp); 284*3117ece4Schristos if (result == 0) { 285*3117ece4Schristos result = lp < rp ? -1 : 1; 286*3117ece4Schristos } 287*3117ece4Schristos return result; 288*3117ece4Schristos } 289*3117ece4Schristos /** 290*3117ece4Schristos * Faster version for d <= 8. 291*3117ece4Schristos */ 292*3117ece4Schristos static int WIN_CDECL COVER_strict_cmp8(const void *lp, const void *rp) { 293*3117ece4Schristos int result = COVER_cmp8(g_coverCtx, lp, rp); 294*3117ece4Schristos if (result == 0) { 295*3117ece4Schristos result = lp < rp ? -1 : 1; 296*3117ece4Schristos } 297*3117ece4Schristos return result; 298*3117ece4Schristos } 299*3117ece4Schristos 300*3117ece4Schristos /** 301*3117ece4Schristos * Returns the first pointer in [first, last) whose element does not compare 302*3117ece4Schristos * less than value. If no such element exists it returns last. 303*3117ece4Schristos */ 304*3117ece4Schristos static const size_t *COVER_lower_bound(const size_t* first, const size_t* last, 305*3117ece4Schristos size_t value) { 306*3117ece4Schristos size_t count = (size_t)(last - first); 307*3117ece4Schristos assert(last >= first); 308*3117ece4Schristos while (count != 0) { 309*3117ece4Schristos size_t step = count / 2; 310*3117ece4Schristos const size_t *ptr = first; 311*3117ece4Schristos ptr += step; 312*3117ece4Schristos if (*ptr < value) { 313*3117ece4Schristos first = ++ptr; 314*3117ece4Schristos count -= step + 1; 315*3117ece4Schristos } else { 316*3117ece4Schristos count = step; 317*3117ece4Schristos } 318*3117ece4Schristos } 319*3117ece4Schristos return first; 320*3117ece4Schristos } 321*3117ece4Schristos 322*3117ece4Schristos /** 323*3117ece4Schristos * Generic groupBy function. 324*3117ece4Schristos * Groups an array sorted by cmp into groups with equivalent values. 325*3117ece4Schristos * Calls grp for each group. 326*3117ece4Schristos */ 327*3117ece4Schristos static void 328*3117ece4Schristos COVER_groupBy(const void *data, size_t count, size_t size, COVER_ctx_t *ctx, 329*3117ece4Schristos int (*cmp)(COVER_ctx_t *, const void *, const void *), 330*3117ece4Schristos void (*grp)(COVER_ctx_t *, const void *, const void *)) { 331*3117ece4Schristos const BYTE *ptr = (const BYTE *)data; 332*3117ece4Schristos size_t num = 0; 333*3117ece4Schristos while (num < count) { 334*3117ece4Schristos const BYTE *grpEnd = ptr + size; 335*3117ece4Schristos ++num; 336*3117ece4Schristos while (num < count && cmp(ctx, ptr, grpEnd) == 0) { 337*3117ece4Schristos grpEnd += size; 338*3117ece4Schristos ++num; 339*3117ece4Schristos } 340*3117ece4Schristos grp(ctx, ptr, grpEnd); 341*3117ece4Schristos ptr = grpEnd; 342*3117ece4Schristos } 343*3117ece4Schristos } 344*3117ece4Schristos 345*3117ece4Schristos /*-************************************* 346*3117ece4Schristos * Cover functions 347*3117ece4Schristos ***************************************/ 348*3117ece4Schristos 349*3117ece4Schristos /** 350*3117ece4Schristos * Called on each group of positions with the same dmer. 351*3117ece4Schristos * Counts the frequency of each dmer and saves it in the suffix array. 352*3117ece4Schristos * Fills `ctx->dmerAt`. 353*3117ece4Schristos */ 354*3117ece4Schristos static void COVER_group(COVER_ctx_t *ctx, const void *group, 355*3117ece4Schristos const void *groupEnd) { 356*3117ece4Schristos /* The group consists of all the positions with the same first d bytes. */ 357*3117ece4Schristos const U32 *grpPtr = (const U32 *)group; 358*3117ece4Schristos const U32 *grpEnd = (const U32 *)groupEnd; 359*3117ece4Schristos /* The dmerId is how we will reference this dmer. 360*3117ece4Schristos * This allows us to map the whole dmer space to a much smaller space, the 361*3117ece4Schristos * size of the suffix array. 362*3117ece4Schristos */ 363*3117ece4Schristos const U32 dmerId = (U32)(grpPtr - ctx->suffix); 364*3117ece4Schristos /* Count the number of samples this dmer shows up in */ 365*3117ece4Schristos U32 freq = 0; 366*3117ece4Schristos /* Details */ 367*3117ece4Schristos const size_t *curOffsetPtr = ctx->offsets; 368*3117ece4Schristos const size_t *offsetsEnd = ctx->offsets + ctx->nbSamples; 369*3117ece4Schristos /* Once *grpPtr >= curSampleEnd this occurrence of the dmer is in a 370*3117ece4Schristos * different sample than the last. 371*3117ece4Schristos */ 372*3117ece4Schristos size_t curSampleEnd = ctx->offsets[0]; 373*3117ece4Schristos for (; grpPtr != grpEnd; ++grpPtr) { 374*3117ece4Schristos /* Save the dmerId for this position so we can get back to it. */ 375*3117ece4Schristos ctx->dmerAt[*grpPtr] = dmerId; 376*3117ece4Schristos /* Dictionaries only help for the first reference to the dmer. 377*3117ece4Schristos * After that zstd can reference the match from the previous reference. 378*3117ece4Schristos * So only count each dmer once for each sample it is in. 379*3117ece4Schristos */ 380*3117ece4Schristos if (*grpPtr < curSampleEnd) { 381*3117ece4Schristos continue; 382*3117ece4Schristos } 383*3117ece4Schristos freq += 1; 384*3117ece4Schristos /* Binary search to find the end of the sample *grpPtr is in. 385*3117ece4Schristos * In the common case that grpPtr + 1 == grpEnd we can skip the binary 386*3117ece4Schristos * search because the loop is over. 387*3117ece4Schristos */ 388*3117ece4Schristos if (grpPtr + 1 != grpEnd) { 389*3117ece4Schristos const size_t *sampleEndPtr = 390*3117ece4Schristos COVER_lower_bound(curOffsetPtr, offsetsEnd, *grpPtr); 391*3117ece4Schristos curSampleEnd = *sampleEndPtr; 392*3117ece4Schristos curOffsetPtr = sampleEndPtr + 1; 393*3117ece4Schristos } 394*3117ece4Schristos } 395*3117ece4Schristos /* At this point we are never going to look at this segment of the suffix 396*3117ece4Schristos * array again. We take advantage of this fact to save memory. 397*3117ece4Schristos * We store the frequency of the dmer in the first position of the group, 398*3117ece4Schristos * which is dmerId. 399*3117ece4Schristos */ 400*3117ece4Schristos ctx->suffix[dmerId] = freq; 401*3117ece4Schristos } 402*3117ece4Schristos 403*3117ece4Schristos 404*3117ece4Schristos /** 405*3117ece4Schristos * Selects the best segment in an epoch. 406*3117ece4Schristos * Segments of are scored according to the function: 407*3117ece4Schristos * 408*3117ece4Schristos * Let F(d) be the frequency of dmer d. 409*3117ece4Schristos * Let S_i be the dmer at position i of segment S which has length k. 410*3117ece4Schristos * 411*3117ece4Schristos * Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1}) 412*3117ece4Schristos * 413*3117ece4Schristos * Once the dmer d is in the dictionary we set F(d) = 0. 414*3117ece4Schristos */ 415*3117ece4Schristos static COVER_segment_t COVER_selectSegment(const COVER_ctx_t *ctx, U32 *freqs, 416*3117ece4Schristos COVER_map_t *activeDmers, U32 begin, 417*3117ece4Schristos U32 end, 418*3117ece4Schristos ZDICT_cover_params_t parameters) { 419*3117ece4Schristos /* Constants */ 420*3117ece4Schristos const U32 k = parameters.k; 421*3117ece4Schristos const U32 d = parameters.d; 422*3117ece4Schristos const U32 dmersInK = k - d + 1; 423*3117ece4Schristos /* Try each segment (activeSegment) and save the best (bestSegment) */ 424*3117ece4Schristos COVER_segment_t bestSegment = {0, 0, 0}; 425*3117ece4Schristos COVER_segment_t activeSegment; 426*3117ece4Schristos /* Reset the activeDmers in the segment */ 427*3117ece4Schristos COVER_map_clear(activeDmers); 428*3117ece4Schristos /* The activeSegment starts at the beginning of the epoch. */ 429*3117ece4Schristos activeSegment.begin = begin; 430*3117ece4Schristos activeSegment.end = begin; 431*3117ece4Schristos activeSegment.score = 0; 432*3117ece4Schristos /* Slide the activeSegment through the whole epoch. 433*3117ece4Schristos * Save the best segment in bestSegment. 434*3117ece4Schristos */ 435*3117ece4Schristos while (activeSegment.end < end) { 436*3117ece4Schristos /* The dmerId for the dmer at the next position */ 437*3117ece4Schristos U32 newDmer = ctx->dmerAt[activeSegment.end]; 438*3117ece4Schristos /* The entry in activeDmers for this dmerId */ 439*3117ece4Schristos U32 *newDmerOcc = COVER_map_at(activeDmers, newDmer); 440*3117ece4Schristos /* If the dmer isn't already present in the segment add its score. */ 441*3117ece4Schristos if (*newDmerOcc == 0) { 442*3117ece4Schristos /* The paper suggest using the L-0.5 norm, but experiments show that it 443*3117ece4Schristos * doesn't help. 444*3117ece4Schristos */ 445*3117ece4Schristos activeSegment.score += freqs[newDmer]; 446*3117ece4Schristos } 447*3117ece4Schristos /* Add the dmer to the segment */ 448*3117ece4Schristos activeSegment.end += 1; 449*3117ece4Schristos *newDmerOcc += 1; 450*3117ece4Schristos 451*3117ece4Schristos /* If the window is now too large, drop the first position */ 452*3117ece4Schristos if (activeSegment.end - activeSegment.begin == dmersInK + 1) { 453*3117ece4Schristos U32 delDmer = ctx->dmerAt[activeSegment.begin]; 454*3117ece4Schristos U32 *delDmerOcc = COVER_map_at(activeDmers, delDmer); 455*3117ece4Schristos activeSegment.begin += 1; 456*3117ece4Schristos *delDmerOcc -= 1; 457*3117ece4Schristos /* If this is the last occurrence of the dmer, subtract its score */ 458*3117ece4Schristos if (*delDmerOcc == 0) { 459*3117ece4Schristos COVER_map_remove(activeDmers, delDmer); 460*3117ece4Schristos activeSegment.score -= freqs[delDmer]; 461*3117ece4Schristos } 462*3117ece4Schristos } 463*3117ece4Schristos 464*3117ece4Schristos /* If this segment is the best so far save it */ 465*3117ece4Schristos if (activeSegment.score > bestSegment.score) { 466*3117ece4Schristos bestSegment = activeSegment; 467*3117ece4Schristos } 468*3117ece4Schristos } 469*3117ece4Schristos { 470*3117ece4Schristos /* Trim off the zero frequency head and tail from the segment. */ 471*3117ece4Schristos U32 newBegin = bestSegment.end; 472*3117ece4Schristos U32 newEnd = bestSegment.begin; 473*3117ece4Schristos U32 pos; 474*3117ece4Schristos for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { 475*3117ece4Schristos U32 freq = freqs[ctx->dmerAt[pos]]; 476*3117ece4Schristos if (freq != 0) { 477*3117ece4Schristos newBegin = MIN(newBegin, pos); 478*3117ece4Schristos newEnd = pos + 1; 479*3117ece4Schristos } 480*3117ece4Schristos } 481*3117ece4Schristos bestSegment.begin = newBegin; 482*3117ece4Schristos bestSegment.end = newEnd; 483*3117ece4Schristos } 484*3117ece4Schristos { 485*3117ece4Schristos /* Zero out the frequency of each dmer covered by the chosen segment. */ 486*3117ece4Schristos U32 pos; 487*3117ece4Schristos for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { 488*3117ece4Schristos freqs[ctx->dmerAt[pos]] = 0; 489*3117ece4Schristos } 490*3117ece4Schristos } 491*3117ece4Schristos return bestSegment; 492*3117ece4Schristos } 493*3117ece4Schristos 494*3117ece4Schristos /** 495*3117ece4Schristos * Check the validity of the parameters. 496*3117ece4Schristos * Returns non-zero if the parameters are valid and 0 otherwise. 497*3117ece4Schristos */ 498*3117ece4Schristos static int COVER_checkParameters(ZDICT_cover_params_t parameters, 499*3117ece4Schristos size_t maxDictSize) { 500*3117ece4Schristos /* k and d are required parameters */ 501*3117ece4Schristos if (parameters.d == 0 || parameters.k == 0) { 502*3117ece4Schristos return 0; 503*3117ece4Schristos } 504*3117ece4Schristos /* k <= maxDictSize */ 505*3117ece4Schristos if (parameters.k > maxDictSize) { 506*3117ece4Schristos return 0; 507*3117ece4Schristos } 508*3117ece4Schristos /* d <= k */ 509*3117ece4Schristos if (parameters.d > parameters.k) { 510*3117ece4Schristos return 0; 511*3117ece4Schristos } 512*3117ece4Schristos /* 0 < splitPoint <= 1 */ 513*3117ece4Schristos if (parameters.splitPoint <= 0 || parameters.splitPoint > 1){ 514*3117ece4Schristos return 0; 515*3117ece4Schristos } 516*3117ece4Schristos return 1; 517*3117ece4Schristos } 518*3117ece4Schristos 519*3117ece4Schristos /** 520*3117ece4Schristos * Clean up a context initialized with `COVER_ctx_init()`. 521*3117ece4Schristos */ 522*3117ece4Schristos static void COVER_ctx_destroy(COVER_ctx_t *ctx) { 523*3117ece4Schristos if (!ctx) { 524*3117ece4Schristos return; 525*3117ece4Schristos } 526*3117ece4Schristos if (ctx->suffix) { 527*3117ece4Schristos free(ctx->suffix); 528*3117ece4Schristos ctx->suffix = NULL; 529*3117ece4Schristos } 530*3117ece4Schristos if (ctx->freqs) { 531*3117ece4Schristos free(ctx->freqs); 532*3117ece4Schristos ctx->freqs = NULL; 533*3117ece4Schristos } 534*3117ece4Schristos if (ctx->dmerAt) { 535*3117ece4Schristos free(ctx->dmerAt); 536*3117ece4Schristos ctx->dmerAt = NULL; 537*3117ece4Schristos } 538*3117ece4Schristos if (ctx->offsets) { 539*3117ece4Schristos free(ctx->offsets); 540*3117ece4Schristos ctx->offsets = NULL; 541*3117ece4Schristos } 542*3117ece4Schristos } 543*3117ece4Schristos 544*3117ece4Schristos /** 545*3117ece4Schristos * Prepare a context for dictionary building. 546*3117ece4Schristos * The context is only dependent on the parameter `d` and can be used multiple 547*3117ece4Schristos * times. 548*3117ece4Schristos * Returns 0 on success or error code on error. 549*3117ece4Schristos * The context must be destroyed with `COVER_ctx_destroy()`. 550*3117ece4Schristos */ 551*3117ece4Schristos static size_t COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer, 552*3117ece4Schristos const size_t *samplesSizes, unsigned nbSamples, 553*3117ece4Schristos unsigned d, double splitPoint) 554*3117ece4Schristos { 555*3117ece4Schristos const BYTE *const samples = (const BYTE *)samplesBuffer; 556*3117ece4Schristos const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples); 557*3117ece4Schristos /* Split samples into testing and training sets */ 558*3117ece4Schristos const unsigned nbTrainSamples = splitPoint < 1.0 ? (unsigned)((double)nbSamples * splitPoint) : nbSamples; 559*3117ece4Schristos const unsigned nbTestSamples = splitPoint < 1.0 ? nbSamples - nbTrainSamples : nbSamples; 560*3117ece4Schristos const size_t trainingSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes, nbTrainSamples) : totalSamplesSize; 561*3117ece4Schristos const size_t testSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes + nbTrainSamples, nbTestSamples) : totalSamplesSize; 562*3117ece4Schristos /* Checks */ 563*3117ece4Schristos if (totalSamplesSize < MAX(d, sizeof(U64)) || 564*3117ece4Schristos totalSamplesSize >= (size_t)COVER_MAX_SAMPLES_SIZE) { 565*3117ece4Schristos DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n", 566*3117ece4Schristos (unsigned)(totalSamplesSize>>20), (COVER_MAX_SAMPLES_SIZE >> 20)); 567*3117ece4Schristos return ERROR(srcSize_wrong); 568*3117ece4Schristos } 569*3117ece4Schristos /* Check if there are at least 5 training samples */ 570*3117ece4Schristos if (nbTrainSamples < 5) { 571*3117ece4Schristos DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid.", nbTrainSamples); 572*3117ece4Schristos return ERROR(srcSize_wrong); 573*3117ece4Schristos } 574*3117ece4Schristos /* Check if there's testing sample */ 575*3117ece4Schristos if (nbTestSamples < 1) { 576*3117ece4Schristos DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.", nbTestSamples); 577*3117ece4Schristos return ERROR(srcSize_wrong); 578*3117ece4Schristos } 579*3117ece4Schristos /* Zero the context */ 580*3117ece4Schristos memset(ctx, 0, sizeof(*ctx)); 581*3117ece4Schristos DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbTrainSamples, 582*3117ece4Schristos (unsigned)trainingSamplesSize); 583*3117ece4Schristos DISPLAYLEVEL(2, "Testing on %u samples of total size %u\n", nbTestSamples, 584*3117ece4Schristos (unsigned)testSamplesSize); 585*3117ece4Schristos ctx->samples = samples; 586*3117ece4Schristos ctx->samplesSizes = samplesSizes; 587*3117ece4Schristos ctx->nbSamples = nbSamples; 588*3117ece4Schristos ctx->nbTrainSamples = nbTrainSamples; 589*3117ece4Schristos ctx->nbTestSamples = nbTestSamples; 590*3117ece4Schristos /* Partial suffix array */ 591*3117ece4Schristos ctx->suffixSize = trainingSamplesSize - MAX(d, sizeof(U64)) + 1; 592*3117ece4Schristos ctx->suffix = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); 593*3117ece4Schristos /* Maps index to the dmerID */ 594*3117ece4Schristos ctx->dmerAt = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); 595*3117ece4Schristos /* The offsets of each file */ 596*3117ece4Schristos ctx->offsets = (size_t *)malloc((nbSamples + 1) * sizeof(size_t)); 597*3117ece4Schristos if (!ctx->suffix || !ctx->dmerAt || !ctx->offsets) { 598*3117ece4Schristos DISPLAYLEVEL(1, "Failed to allocate scratch buffers\n"); 599*3117ece4Schristos COVER_ctx_destroy(ctx); 600*3117ece4Schristos return ERROR(memory_allocation); 601*3117ece4Schristos } 602*3117ece4Schristos ctx->freqs = NULL; 603*3117ece4Schristos ctx->d = d; 604*3117ece4Schristos 605*3117ece4Schristos /* Fill offsets from the samplesSizes */ 606*3117ece4Schristos { 607*3117ece4Schristos U32 i; 608*3117ece4Schristos ctx->offsets[0] = 0; 609*3117ece4Schristos for (i = 1; i <= nbSamples; ++i) { 610*3117ece4Schristos ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1]; 611*3117ece4Schristos } 612*3117ece4Schristos } 613*3117ece4Schristos DISPLAYLEVEL(2, "Constructing partial suffix array\n"); 614*3117ece4Schristos { 615*3117ece4Schristos /* suffix is a partial suffix array. 616*3117ece4Schristos * It only sorts suffixes by their first parameters.d bytes. 617*3117ece4Schristos * The sort is stable, so each dmer group is sorted by position in input. 618*3117ece4Schristos */ 619*3117ece4Schristos U32 i; 620*3117ece4Schristos for (i = 0; i < ctx->suffixSize; ++i) { 621*3117ece4Schristos ctx->suffix[i] = i; 622*3117ece4Schristos } 623*3117ece4Schristos /* qsort doesn't take an opaque pointer, so pass as a global. 624*3117ece4Schristos * On OpenBSD qsort() is not guaranteed to be stable, their mergesort() is. 625*3117ece4Schristos */ 626*3117ece4Schristos g_coverCtx = ctx; 627*3117ece4Schristos #if defined(__OpenBSD__) 628*3117ece4Schristos mergesort(ctx->suffix, ctx->suffixSize, sizeof(U32), 629*3117ece4Schristos (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); 630*3117ece4Schristos #else 631*3117ece4Schristos qsort(ctx->suffix, ctx->suffixSize, sizeof(U32), 632*3117ece4Schristos (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); 633*3117ece4Schristos #endif 634*3117ece4Schristos } 635*3117ece4Schristos DISPLAYLEVEL(2, "Computing frequencies\n"); 636*3117ece4Schristos /* For each dmer group (group of positions with the same first d bytes): 637*3117ece4Schristos * 1. For each position we set dmerAt[position] = dmerID. The dmerID is 638*3117ece4Schristos * (groupBeginPtr - suffix). This allows us to go from position to 639*3117ece4Schristos * dmerID so we can look up values in freq. 640*3117ece4Schristos * 2. We calculate how many samples the dmer occurs in and save it in 641*3117ece4Schristos * freqs[dmerId]. 642*3117ece4Schristos */ 643*3117ece4Schristos COVER_groupBy(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx, 644*3117ece4Schristos (ctx->d <= 8 ? &COVER_cmp8 : &COVER_cmp), &COVER_group); 645*3117ece4Schristos ctx->freqs = ctx->suffix; 646*3117ece4Schristos ctx->suffix = NULL; 647*3117ece4Schristos return 0; 648*3117ece4Schristos } 649*3117ece4Schristos 650*3117ece4Schristos void COVER_warnOnSmallCorpus(size_t maxDictSize, size_t nbDmers, int displayLevel) 651*3117ece4Schristos { 652*3117ece4Schristos const double ratio = (double)nbDmers / (double)maxDictSize; 653*3117ece4Schristos if (ratio >= 10) { 654*3117ece4Schristos return; 655*3117ece4Schristos } 656*3117ece4Schristos LOCALDISPLAYLEVEL(displayLevel, 1, 657*3117ece4Schristos "WARNING: The maximum dictionary size %u is too large " 658*3117ece4Schristos "compared to the source size %u! " 659*3117ece4Schristos "size(source)/size(dictionary) = %f, but it should be >= " 660*3117ece4Schristos "10! This may lead to a subpar dictionary! We recommend " 661*3117ece4Schristos "training on sources at least 10x, and preferably 100x " 662*3117ece4Schristos "the size of the dictionary! \n", (U32)maxDictSize, 663*3117ece4Schristos (U32)nbDmers, ratio); 664*3117ece4Schristos } 665*3117ece4Schristos 666*3117ece4Schristos COVER_epoch_info_t COVER_computeEpochs(U32 maxDictSize, 667*3117ece4Schristos U32 nbDmers, U32 k, U32 passes) 668*3117ece4Schristos { 669*3117ece4Schristos const U32 minEpochSize = k * 10; 670*3117ece4Schristos COVER_epoch_info_t epochs; 671*3117ece4Schristos epochs.num = MAX(1, maxDictSize / k / passes); 672*3117ece4Schristos epochs.size = nbDmers / epochs.num; 673*3117ece4Schristos if (epochs.size >= minEpochSize) { 674*3117ece4Schristos assert(epochs.size * epochs.num <= nbDmers); 675*3117ece4Schristos return epochs; 676*3117ece4Schristos } 677*3117ece4Schristos epochs.size = MIN(minEpochSize, nbDmers); 678*3117ece4Schristos epochs.num = nbDmers / epochs.size; 679*3117ece4Schristos assert(epochs.size * epochs.num <= nbDmers); 680*3117ece4Schristos return epochs; 681*3117ece4Schristos } 682*3117ece4Schristos 683*3117ece4Schristos /** 684*3117ece4Schristos * Given the prepared context build the dictionary. 685*3117ece4Schristos */ 686*3117ece4Schristos static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs, 687*3117ece4Schristos COVER_map_t *activeDmers, void *dictBuffer, 688*3117ece4Schristos size_t dictBufferCapacity, 689*3117ece4Schristos ZDICT_cover_params_t parameters) { 690*3117ece4Schristos BYTE *const dict = (BYTE *)dictBuffer; 691*3117ece4Schristos size_t tail = dictBufferCapacity; 692*3117ece4Schristos /* Divide the data into epochs. We will select one segment from each epoch. */ 693*3117ece4Schristos const COVER_epoch_info_t epochs = COVER_computeEpochs( 694*3117ece4Schristos (U32)dictBufferCapacity, (U32)ctx->suffixSize, parameters.k, 4); 695*3117ece4Schristos const size_t maxZeroScoreRun = MAX(10, MIN(100, epochs.num >> 3)); 696*3117ece4Schristos size_t zeroScoreRun = 0; 697*3117ece4Schristos size_t epoch; 698*3117ece4Schristos DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n", 699*3117ece4Schristos (U32)epochs.num, (U32)epochs.size); 700*3117ece4Schristos /* Loop through the epochs until there are no more segments or the dictionary 701*3117ece4Schristos * is full. 702*3117ece4Schristos */ 703*3117ece4Schristos for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs.num) { 704*3117ece4Schristos const U32 epochBegin = (U32)(epoch * epochs.size); 705*3117ece4Schristos const U32 epochEnd = epochBegin + epochs.size; 706*3117ece4Schristos size_t segmentSize; 707*3117ece4Schristos /* Select a segment */ 708*3117ece4Schristos COVER_segment_t segment = COVER_selectSegment( 709*3117ece4Schristos ctx, freqs, activeDmers, epochBegin, epochEnd, parameters); 710*3117ece4Schristos /* If the segment covers no dmers, then we are out of content. 711*3117ece4Schristos * There may be new content in other epochs, for continue for some time. 712*3117ece4Schristos */ 713*3117ece4Schristos if (segment.score == 0) { 714*3117ece4Schristos if (++zeroScoreRun >= maxZeroScoreRun) { 715*3117ece4Schristos break; 716*3117ece4Schristos } 717*3117ece4Schristos continue; 718*3117ece4Schristos } 719*3117ece4Schristos zeroScoreRun = 0; 720*3117ece4Schristos /* Trim the segment if necessary and if it is too small then we are done */ 721*3117ece4Schristos segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail); 722*3117ece4Schristos if (segmentSize < parameters.d) { 723*3117ece4Schristos break; 724*3117ece4Schristos } 725*3117ece4Schristos /* We fill the dictionary from the back to allow the best segments to be 726*3117ece4Schristos * referenced with the smallest offsets. 727*3117ece4Schristos */ 728*3117ece4Schristos tail -= segmentSize; 729*3117ece4Schristos memcpy(dict + tail, ctx->samples + segment.begin, segmentSize); 730*3117ece4Schristos DISPLAYUPDATE( 731*3117ece4Schristos 2, "\r%u%% ", 732*3117ece4Schristos (unsigned)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity)); 733*3117ece4Schristos } 734*3117ece4Schristos DISPLAYLEVEL(2, "\r%79s\r", ""); 735*3117ece4Schristos return tail; 736*3117ece4Schristos } 737*3117ece4Schristos 738*3117ece4Schristos ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_cover( 739*3117ece4Schristos void *dictBuffer, size_t dictBufferCapacity, 740*3117ece4Schristos const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, 741*3117ece4Schristos ZDICT_cover_params_t parameters) 742*3117ece4Schristos { 743*3117ece4Schristos BYTE* const dict = (BYTE*)dictBuffer; 744*3117ece4Schristos COVER_ctx_t ctx; 745*3117ece4Schristos COVER_map_t activeDmers; 746*3117ece4Schristos parameters.splitPoint = 1.0; 747*3117ece4Schristos /* Initialize global data */ 748*3117ece4Schristos g_displayLevel = (int)parameters.zParams.notificationLevel; 749*3117ece4Schristos /* Checks */ 750*3117ece4Schristos if (!COVER_checkParameters(parameters, dictBufferCapacity)) { 751*3117ece4Schristos DISPLAYLEVEL(1, "Cover parameters incorrect\n"); 752*3117ece4Schristos return ERROR(parameter_outOfBound); 753*3117ece4Schristos } 754*3117ece4Schristos if (nbSamples == 0) { 755*3117ece4Schristos DISPLAYLEVEL(1, "Cover must have at least one input file\n"); 756*3117ece4Schristos return ERROR(srcSize_wrong); 757*3117ece4Schristos } 758*3117ece4Schristos if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { 759*3117ece4Schristos DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", 760*3117ece4Schristos ZDICT_DICTSIZE_MIN); 761*3117ece4Schristos return ERROR(dstSize_tooSmall); 762*3117ece4Schristos } 763*3117ece4Schristos /* Initialize context and activeDmers */ 764*3117ece4Schristos { 765*3117ece4Schristos size_t const initVal = COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, 766*3117ece4Schristos parameters.d, parameters.splitPoint); 767*3117ece4Schristos if (ZSTD_isError(initVal)) { 768*3117ece4Schristos return initVal; 769*3117ece4Schristos } 770*3117ece4Schristos } 771*3117ece4Schristos COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.suffixSize, g_displayLevel); 772*3117ece4Schristos if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) { 773*3117ece4Schristos DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n"); 774*3117ece4Schristos COVER_ctx_destroy(&ctx); 775*3117ece4Schristos return ERROR(memory_allocation); 776*3117ece4Schristos } 777*3117ece4Schristos 778*3117ece4Schristos DISPLAYLEVEL(2, "Building dictionary\n"); 779*3117ece4Schristos { 780*3117ece4Schristos const size_t tail = 781*3117ece4Schristos COVER_buildDictionary(&ctx, ctx.freqs, &activeDmers, dictBuffer, 782*3117ece4Schristos dictBufferCapacity, parameters); 783*3117ece4Schristos const size_t dictionarySize = ZDICT_finalizeDictionary( 784*3117ece4Schristos dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, 785*3117ece4Schristos samplesBuffer, samplesSizes, nbSamples, parameters.zParams); 786*3117ece4Schristos if (!ZSTD_isError(dictionarySize)) { 787*3117ece4Schristos DISPLAYLEVEL(2, "Constructed dictionary of size %u\n", 788*3117ece4Schristos (unsigned)dictionarySize); 789*3117ece4Schristos } 790*3117ece4Schristos COVER_ctx_destroy(&ctx); 791*3117ece4Schristos COVER_map_destroy(&activeDmers); 792*3117ece4Schristos return dictionarySize; 793*3117ece4Schristos } 794*3117ece4Schristos } 795*3117ece4Schristos 796*3117ece4Schristos 797*3117ece4Schristos 798*3117ece4Schristos size_t COVER_checkTotalCompressedSize(const ZDICT_cover_params_t parameters, 799*3117ece4Schristos const size_t *samplesSizes, const BYTE *samples, 800*3117ece4Schristos size_t *offsets, 801*3117ece4Schristos size_t nbTrainSamples, size_t nbSamples, 802*3117ece4Schristos BYTE *const dict, size_t dictBufferCapacity) { 803*3117ece4Schristos size_t totalCompressedSize = ERROR(GENERIC); 804*3117ece4Schristos /* Pointers */ 805*3117ece4Schristos ZSTD_CCtx *cctx; 806*3117ece4Schristos ZSTD_CDict *cdict; 807*3117ece4Schristos void *dst; 808*3117ece4Schristos /* Local variables */ 809*3117ece4Schristos size_t dstCapacity; 810*3117ece4Schristos size_t i; 811*3117ece4Schristos /* Allocate dst with enough space to compress the maximum sized sample */ 812*3117ece4Schristos { 813*3117ece4Schristos size_t maxSampleSize = 0; 814*3117ece4Schristos i = parameters.splitPoint < 1.0 ? nbTrainSamples : 0; 815*3117ece4Schristos for (; i < nbSamples; ++i) { 816*3117ece4Schristos maxSampleSize = MAX(samplesSizes[i], maxSampleSize); 817*3117ece4Schristos } 818*3117ece4Schristos dstCapacity = ZSTD_compressBound(maxSampleSize); 819*3117ece4Schristos dst = malloc(dstCapacity); 820*3117ece4Schristos } 821*3117ece4Schristos /* Create the cctx and cdict */ 822*3117ece4Schristos cctx = ZSTD_createCCtx(); 823*3117ece4Schristos cdict = ZSTD_createCDict(dict, dictBufferCapacity, 824*3117ece4Schristos parameters.zParams.compressionLevel); 825*3117ece4Schristos if (!dst || !cctx || !cdict) { 826*3117ece4Schristos goto _compressCleanup; 827*3117ece4Schristos } 828*3117ece4Schristos /* Compress each sample and sum their sizes (or error) */ 829*3117ece4Schristos totalCompressedSize = dictBufferCapacity; 830*3117ece4Schristos i = parameters.splitPoint < 1.0 ? nbTrainSamples : 0; 831*3117ece4Schristos for (; i < nbSamples; ++i) { 832*3117ece4Schristos const size_t size = ZSTD_compress_usingCDict( 833*3117ece4Schristos cctx, dst, dstCapacity, samples + offsets[i], 834*3117ece4Schristos samplesSizes[i], cdict); 835*3117ece4Schristos if (ZSTD_isError(size)) { 836*3117ece4Schristos totalCompressedSize = size; 837*3117ece4Schristos goto _compressCleanup; 838*3117ece4Schristos } 839*3117ece4Schristos totalCompressedSize += size; 840*3117ece4Schristos } 841*3117ece4Schristos _compressCleanup: 842*3117ece4Schristos ZSTD_freeCCtx(cctx); 843*3117ece4Schristos ZSTD_freeCDict(cdict); 844*3117ece4Schristos if (dst) { 845*3117ece4Schristos free(dst); 846*3117ece4Schristos } 847*3117ece4Schristos return totalCompressedSize; 848*3117ece4Schristos } 849*3117ece4Schristos 850*3117ece4Schristos 851*3117ece4Schristos /** 852*3117ece4Schristos * Initialize the `COVER_best_t`. 853*3117ece4Schristos */ 854*3117ece4Schristos void COVER_best_init(COVER_best_t *best) { 855*3117ece4Schristos if (best==NULL) return; /* compatible with init on NULL */ 856*3117ece4Schristos (void)ZSTD_pthread_mutex_init(&best->mutex, NULL); 857*3117ece4Schristos (void)ZSTD_pthread_cond_init(&best->cond, NULL); 858*3117ece4Schristos best->liveJobs = 0; 859*3117ece4Schristos best->dict = NULL; 860*3117ece4Schristos best->dictSize = 0; 861*3117ece4Schristos best->compressedSize = (size_t)-1; 862*3117ece4Schristos memset(&best->parameters, 0, sizeof(best->parameters)); 863*3117ece4Schristos } 864*3117ece4Schristos 865*3117ece4Schristos /** 866*3117ece4Schristos * Wait until liveJobs == 0. 867*3117ece4Schristos */ 868*3117ece4Schristos void COVER_best_wait(COVER_best_t *best) { 869*3117ece4Schristos if (!best) { 870*3117ece4Schristos return; 871*3117ece4Schristos } 872*3117ece4Schristos ZSTD_pthread_mutex_lock(&best->mutex); 873*3117ece4Schristos while (best->liveJobs != 0) { 874*3117ece4Schristos ZSTD_pthread_cond_wait(&best->cond, &best->mutex); 875*3117ece4Schristos } 876*3117ece4Schristos ZSTD_pthread_mutex_unlock(&best->mutex); 877*3117ece4Schristos } 878*3117ece4Schristos 879*3117ece4Schristos /** 880*3117ece4Schristos * Call COVER_best_wait() and then destroy the COVER_best_t. 881*3117ece4Schristos */ 882*3117ece4Schristos void COVER_best_destroy(COVER_best_t *best) { 883*3117ece4Schristos if (!best) { 884*3117ece4Schristos return; 885*3117ece4Schristos } 886*3117ece4Schristos COVER_best_wait(best); 887*3117ece4Schristos if (best->dict) { 888*3117ece4Schristos free(best->dict); 889*3117ece4Schristos } 890*3117ece4Schristos ZSTD_pthread_mutex_destroy(&best->mutex); 891*3117ece4Schristos ZSTD_pthread_cond_destroy(&best->cond); 892*3117ece4Schristos } 893*3117ece4Schristos 894*3117ece4Schristos /** 895*3117ece4Schristos * Called when a thread is about to be launched. 896*3117ece4Schristos * Increments liveJobs. 897*3117ece4Schristos */ 898*3117ece4Schristos void COVER_best_start(COVER_best_t *best) { 899*3117ece4Schristos if (!best) { 900*3117ece4Schristos return; 901*3117ece4Schristos } 902*3117ece4Schristos ZSTD_pthread_mutex_lock(&best->mutex); 903*3117ece4Schristos ++best->liveJobs; 904*3117ece4Schristos ZSTD_pthread_mutex_unlock(&best->mutex); 905*3117ece4Schristos } 906*3117ece4Schristos 907*3117ece4Schristos /** 908*3117ece4Schristos * Called when a thread finishes executing, both on error or success. 909*3117ece4Schristos * Decrements liveJobs and signals any waiting threads if liveJobs == 0. 910*3117ece4Schristos * If this dictionary is the best so far save it and its parameters. 911*3117ece4Schristos */ 912*3117ece4Schristos void COVER_best_finish(COVER_best_t* best, 913*3117ece4Schristos ZDICT_cover_params_t parameters, 914*3117ece4Schristos COVER_dictSelection_t selection) 915*3117ece4Schristos { 916*3117ece4Schristos void* dict = selection.dictContent; 917*3117ece4Schristos size_t compressedSize = selection.totalCompressedSize; 918*3117ece4Schristos size_t dictSize = selection.dictSize; 919*3117ece4Schristos if (!best) { 920*3117ece4Schristos return; 921*3117ece4Schristos } 922*3117ece4Schristos { 923*3117ece4Schristos size_t liveJobs; 924*3117ece4Schristos ZSTD_pthread_mutex_lock(&best->mutex); 925*3117ece4Schristos --best->liveJobs; 926*3117ece4Schristos liveJobs = best->liveJobs; 927*3117ece4Schristos /* If the new dictionary is better */ 928*3117ece4Schristos if (compressedSize < best->compressedSize) { 929*3117ece4Schristos /* Allocate space if necessary */ 930*3117ece4Schristos if (!best->dict || best->dictSize < dictSize) { 931*3117ece4Schristos if (best->dict) { 932*3117ece4Schristos free(best->dict); 933*3117ece4Schristos } 934*3117ece4Schristos best->dict = malloc(dictSize); 935*3117ece4Schristos if (!best->dict) { 936*3117ece4Schristos best->compressedSize = ERROR(GENERIC); 937*3117ece4Schristos best->dictSize = 0; 938*3117ece4Schristos ZSTD_pthread_cond_signal(&best->cond); 939*3117ece4Schristos ZSTD_pthread_mutex_unlock(&best->mutex); 940*3117ece4Schristos return; 941*3117ece4Schristos } 942*3117ece4Schristos } 943*3117ece4Schristos /* Save the dictionary, parameters, and size */ 944*3117ece4Schristos if (dict) { 945*3117ece4Schristos memcpy(best->dict, dict, dictSize); 946*3117ece4Schristos best->dictSize = dictSize; 947*3117ece4Schristos best->parameters = parameters; 948*3117ece4Schristos best->compressedSize = compressedSize; 949*3117ece4Schristos } 950*3117ece4Schristos } 951*3117ece4Schristos if (liveJobs == 0) { 952*3117ece4Schristos ZSTD_pthread_cond_broadcast(&best->cond); 953*3117ece4Schristos } 954*3117ece4Schristos ZSTD_pthread_mutex_unlock(&best->mutex); 955*3117ece4Schristos } 956*3117ece4Schristos } 957*3117ece4Schristos 958*3117ece4Schristos static COVER_dictSelection_t setDictSelection(BYTE* buf, size_t s, size_t csz) 959*3117ece4Schristos { 960*3117ece4Schristos COVER_dictSelection_t ds; 961*3117ece4Schristos ds.dictContent = buf; 962*3117ece4Schristos ds.dictSize = s; 963*3117ece4Schristos ds.totalCompressedSize = csz; 964*3117ece4Schristos return ds; 965*3117ece4Schristos } 966*3117ece4Schristos 967*3117ece4Schristos COVER_dictSelection_t COVER_dictSelectionError(size_t error) { 968*3117ece4Schristos return setDictSelection(NULL, 0, error); 969*3117ece4Schristos } 970*3117ece4Schristos 971*3117ece4Schristos unsigned COVER_dictSelectionIsError(COVER_dictSelection_t selection) { 972*3117ece4Schristos return (ZSTD_isError(selection.totalCompressedSize) || !selection.dictContent); 973*3117ece4Schristos } 974*3117ece4Schristos 975*3117ece4Schristos void COVER_dictSelectionFree(COVER_dictSelection_t selection){ 976*3117ece4Schristos free(selection.dictContent); 977*3117ece4Schristos } 978*3117ece4Schristos 979*3117ece4Schristos COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent, size_t dictBufferCapacity, 980*3117ece4Schristos size_t dictContentSize, const BYTE* samplesBuffer, const size_t* samplesSizes, unsigned nbFinalizeSamples, 981*3117ece4Schristos size_t nbCheckSamples, size_t nbSamples, ZDICT_cover_params_t params, size_t* offsets, size_t totalCompressedSize) { 982*3117ece4Schristos 983*3117ece4Schristos size_t largestDict = 0; 984*3117ece4Schristos size_t largestCompressed = 0; 985*3117ece4Schristos BYTE* customDictContentEnd = customDictContent + dictContentSize; 986*3117ece4Schristos 987*3117ece4Schristos BYTE* largestDictbuffer = (BYTE*)malloc(dictBufferCapacity); 988*3117ece4Schristos BYTE* candidateDictBuffer = (BYTE*)malloc(dictBufferCapacity); 989*3117ece4Schristos double regressionTolerance = ((double)params.shrinkDictMaxRegression / 100.0) + 1.00; 990*3117ece4Schristos 991*3117ece4Schristos if (!largestDictbuffer || !candidateDictBuffer) { 992*3117ece4Schristos free(largestDictbuffer); 993*3117ece4Schristos free(candidateDictBuffer); 994*3117ece4Schristos return COVER_dictSelectionError(dictContentSize); 995*3117ece4Schristos } 996*3117ece4Schristos 997*3117ece4Schristos /* Initial dictionary size and compressed size */ 998*3117ece4Schristos memcpy(largestDictbuffer, customDictContent, dictContentSize); 999*3117ece4Schristos dictContentSize = ZDICT_finalizeDictionary( 1000*3117ece4Schristos largestDictbuffer, dictBufferCapacity, customDictContent, dictContentSize, 1001*3117ece4Schristos samplesBuffer, samplesSizes, nbFinalizeSamples, params.zParams); 1002*3117ece4Schristos 1003*3117ece4Schristos if (ZDICT_isError(dictContentSize)) { 1004*3117ece4Schristos free(largestDictbuffer); 1005*3117ece4Schristos free(candidateDictBuffer); 1006*3117ece4Schristos return COVER_dictSelectionError(dictContentSize); 1007*3117ece4Schristos } 1008*3117ece4Schristos 1009*3117ece4Schristos totalCompressedSize = COVER_checkTotalCompressedSize(params, samplesSizes, 1010*3117ece4Schristos samplesBuffer, offsets, 1011*3117ece4Schristos nbCheckSamples, nbSamples, 1012*3117ece4Schristos largestDictbuffer, dictContentSize); 1013*3117ece4Schristos 1014*3117ece4Schristos if (ZSTD_isError(totalCompressedSize)) { 1015*3117ece4Schristos free(largestDictbuffer); 1016*3117ece4Schristos free(candidateDictBuffer); 1017*3117ece4Schristos return COVER_dictSelectionError(totalCompressedSize); 1018*3117ece4Schristos } 1019*3117ece4Schristos 1020*3117ece4Schristos if (params.shrinkDict == 0) { 1021*3117ece4Schristos free(candidateDictBuffer); 1022*3117ece4Schristos return setDictSelection(largestDictbuffer, dictContentSize, totalCompressedSize); 1023*3117ece4Schristos } 1024*3117ece4Schristos 1025*3117ece4Schristos largestDict = dictContentSize; 1026*3117ece4Schristos largestCompressed = totalCompressedSize; 1027*3117ece4Schristos dictContentSize = ZDICT_DICTSIZE_MIN; 1028*3117ece4Schristos 1029*3117ece4Schristos /* Largest dict is initially at least ZDICT_DICTSIZE_MIN */ 1030*3117ece4Schristos while (dictContentSize < largestDict) { 1031*3117ece4Schristos memcpy(candidateDictBuffer, largestDictbuffer, largestDict); 1032*3117ece4Schristos dictContentSize = ZDICT_finalizeDictionary( 1033*3117ece4Schristos candidateDictBuffer, dictBufferCapacity, customDictContentEnd - dictContentSize, dictContentSize, 1034*3117ece4Schristos samplesBuffer, samplesSizes, nbFinalizeSamples, params.zParams); 1035*3117ece4Schristos 1036*3117ece4Schristos if (ZDICT_isError(dictContentSize)) { 1037*3117ece4Schristos free(largestDictbuffer); 1038*3117ece4Schristos free(candidateDictBuffer); 1039*3117ece4Schristos return COVER_dictSelectionError(dictContentSize); 1040*3117ece4Schristos 1041*3117ece4Schristos } 1042*3117ece4Schristos 1043*3117ece4Schristos totalCompressedSize = COVER_checkTotalCompressedSize(params, samplesSizes, 1044*3117ece4Schristos samplesBuffer, offsets, 1045*3117ece4Schristos nbCheckSamples, nbSamples, 1046*3117ece4Schristos candidateDictBuffer, dictContentSize); 1047*3117ece4Schristos 1048*3117ece4Schristos if (ZSTD_isError(totalCompressedSize)) { 1049*3117ece4Schristos free(largestDictbuffer); 1050*3117ece4Schristos free(candidateDictBuffer); 1051*3117ece4Schristos return COVER_dictSelectionError(totalCompressedSize); 1052*3117ece4Schristos } 1053*3117ece4Schristos 1054*3117ece4Schristos if ((double)totalCompressedSize <= (double)largestCompressed * regressionTolerance) { 1055*3117ece4Schristos free(largestDictbuffer); 1056*3117ece4Schristos return setDictSelection( candidateDictBuffer, dictContentSize, totalCompressedSize ); 1057*3117ece4Schristos } 1058*3117ece4Schristos dictContentSize *= 2; 1059*3117ece4Schristos } 1060*3117ece4Schristos dictContentSize = largestDict; 1061*3117ece4Schristos totalCompressedSize = largestCompressed; 1062*3117ece4Schristos free(candidateDictBuffer); 1063*3117ece4Schristos return setDictSelection( largestDictbuffer, dictContentSize, totalCompressedSize ); 1064*3117ece4Schristos } 1065*3117ece4Schristos 1066*3117ece4Schristos /** 1067*3117ece4Schristos * Parameters for COVER_tryParameters(). 1068*3117ece4Schristos */ 1069*3117ece4Schristos typedef struct COVER_tryParameters_data_s { 1070*3117ece4Schristos const COVER_ctx_t *ctx; 1071*3117ece4Schristos COVER_best_t *best; 1072*3117ece4Schristos size_t dictBufferCapacity; 1073*3117ece4Schristos ZDICT_cover_params_t parameters; 1074*3117ece4Schristos } COVER_tryParameters_data_t; 1075*3117ece4Schristos 1076*3117ece4Schristos /** 1077*3117ece4Schristos * Tries a set of parameters and updates the COVER_best_t with the results. 1078*3117ece4Schristos * This function is thread safe if zstd is compiled with multithreaded support. 1079*3117ece4Schristos * It takes its parameters as an *OWNING* opaque pointer to support threading. 1080*3117ece4Schristos */ 1081*3117ece4Schristos static void COVER_tryParameters(void *opaque) 1082*3117ece4Schristos { 1083*3117ece4Schristos /* Save parameters as local variables */ 1084*3117ece4Schristos COVER_tryParameters_data_t *const data = (COVER_tryParameters_data_t*)opaque; 1085*3117ece4Schristos const COVER_ctx_t *const ctx = data->ctx; 1086*3117ece4Schristos const ZDICT_cover_params_t parameters = data->parameters; 1087*3117ece4Schristos size_t dictBufferCapacity = data->dictBufferCapacity; 1088*3117ece4Schristos size_t totalCompressedSize = ERROR(GENERIC); 1089*3117ece4Schristos /* Allocate space for hash table, dict, and freqs */ 1090*3117ece4Schristos COVER_map_t activeDmers; 1091*3117ece4Schristos BYTE* const dict = (BYTE*)malloc(dictBufferCapacity); 1092*3117ece4Schristos COVER_dictSelection_t selection = COVER_dictSelectionError(ERROR(GENERIC)); 1093*3117ece4Schristos U32* const freqs = (U32*)malloc(ctx->suffixSize * sizeof(U32)); 1094*3117ece4Schristos if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) { 1095*3117ece4Schristos DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n"); 1096*3117ece4Schristos goto _cleanup; 1097*3117ece4Schristos } 1098*3117ece4Schristos if (!dict || !freqs) { 1099*3117ece4Schristos DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n"); 1100*3117ece4Schristos goto _cleanup; 1101*3117ece4Schristos } 1102*3117ece4Schristos /* Copy the frequencies because we need to modify them */ 1103*3117ece4Schristos memcpy(freqs, ctx->freqs, ctx->suffixSize * sizeof(U32)); 1104*3117ece4Schristos /* Build the dictionary */ 1105*3117ece4Schristos { 1106*3117ece4Schristos const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict, 1107*3117ece4Schristos dictBufferCapacity, parameters); 1108*3117ece4Schristos selection = COVER_selectDict(dict + tail, dictBufferCapacity, dictBufferCapacity - tail, 1109*3117ece4Schristos ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbTrainSamples, ctx->nbTrainSamples, ctx->nbSamples, parameters, ctx->offsets, 1110*3117ece4Schristos totalCompressedSize); 1111*3117ece4Schristos 1112*3117ece4Schristos if (COVER_dictSelectionIsError(selection)) { 1113*3117ece4Schristos DISPLAYLEVEL(1, "Failed to select dictionary\n"); 1114*3117ece4Schristos goto _cleanup; 1115*3117ece4Schristos } 1116*3117ece4Schristos } 1117*3117ece4Schristos _cleanup: 1118*3117ece4Schristos free(dict); 1119*3117ece4Schristos COVER_best_finish(data->best, parameters, selection); 1120*3117ece4Schristos free(data); 1121*3117ece4Schristos COVER_map_destroy(&activeDmers); 1122*3117ece4Schristos COVER_dictSelectionFree(selection); 1123*3117ece4Schristos free(freqs); 1124*3117ece4Schristos } 1125*3117ece4Schristos 1126*3117ece4Schristos ZDICTLIB_STATIC_API size_t ZDICT_optimizeTrainFromBuffer_cover( 1127*3117ece4Schristos void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, 1128*3117ece4Schristos const size_t* samplesSizes, unsigned nbSamples, 1129*3117ece4Schristos ZDICT_cover_params_t* parameters) 1130*3117ece4Schristos { 1131*3117ece4Schristos /* constants */ 1132*3117ece4Schristos const unsigned nbThreads = parameters->nbThreads; 1133*3117ece4Schristos const double splitPoint = 1134*3117ece4Schristos parameters->splitPoint <= 0.0 ? COVER_DEFAULT_SPLITPOINT : parameters->splitPoint; 1135*3117ece4Schristos const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d; 1136*3117ece4Schristos const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d; 1137*3117ece4Schristos const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k; 1138*3117ece4Schristos const unsigned kMaxK = parameters->k == 0 ? 2000 : parameters->k; 1139*3117ece4Schristos const unsigned kSteps = parameters->steps == 0 ? 40 : parameters->steps; 1140*3117ece4Schristos const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1); 1141*3117ece4Schristos const unsigned kIterations = 1142*3117ece4Schristos (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); 1143*3117ece4Schristos const unsigned shrinkDict = 0; 1144*3117ece4Schristos /* Local variables */ 1145*3117ece4Schristos const int displayLevel = parameters->zParams.notificationLevel; 1146*3117ece4Schristos unsigned iteration = 1; 1147*3117ece4Schristos unsigned d; 1148*3117ece4Schristos unsigned k; 1149*3117ece4Schristos COVER_best_t best; 1150*3117ece4Schristos POOL_ctx *pool = NULL; 1151*3117ece4Schristos int warned = 0; 1152*3117ece4Schristos 1153*3117ece4Schristos /* Checks */ 1154*3117ece4Schristos if (splitPoint <= 0 || splitPoint > 1) { 1155*3117ece4Schristos LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n"); 1156*3117ece4Schristos return ERROR(parameter_outOfBound); 1157*3117ece4Schristos } 1158*3117ece4Schristos if (kMinK < kMaxD || kMaxK < kMinK) { 1159*3117ece4Schristos LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n"); 1160*3117ece4Schristos return ERROR(parameter_outOfBound); 1161*3117ece4Schristos } 1162*3117ece4Schristos if (nbSamples == 0) { 1163*3117ece4Schristos DISPLAYLEVEL(1, "Cover must have at least one input file\n"); 1164*3117ece4Schristos return ERROR(srcSize_wrong); 1165*3117ece4Schristos } 1166*3117ece4Schristos if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { 1167*3117ece4Schristos DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", 1168*3117ece4Schristos ZDICT_DICTSIZE_MIN); 1169*3117ece4Schristos return ERROR(dstSize_tooSmall); 1170*3117ece4Schristos } 1171*3117ece4Schristos if (nbThreads > 1) { 1172*3117ece4Schristos pool = POOL_create(nbThreads, 1); 1173*3117ece4Schristos if (!pool) { 1174*3117ece4Schristos return ERROR(memory_allocation); 1175*3117ece4Schristos } 1176*3117ece4Schristos } 1177*3117ece4Schristos /* Initialization */ 1178*3117ece4Schristos COVER_best_init(&best); 1179*3117ece4Schristos /* Turn down global display level to clean up display at level 2 and below */ 1180*3117ece4Schristos g_displayLevel = displayLevel == 0 ? 0 : displayLevel - 1; 1181*3117ece4Schristos /* Loop through d first because each new value needs a new context */ 1182*3117ece4Schristos LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n", 1183*3117ece4Schristos kIterations); 1184*3117ece4Schristos for (d = kMinD; d <= kMaxD; d += 2) { 1185*3117ece4Schristos /* Initialize the context for this value of d */ 1186*3117ece4Schristos COVER_ctx_t ctx; 1187*3117ece4Schristos LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d); 1188*3117ece4Schristos { 1189*3117ece4Schristos const size_t initVal = COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint); 1190*3117ece4Schristos if (ZSTD_isError(initVal)) { 1191*3117ece4Schristos LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n"); 1192*3117ece4Schristos COVER_best_destroy(&best); 1193*3117ece4Schristos POOL_free(pool); 1194*3117ece4Schristos return initVal; 1195*3117ece4Schristos } 1196*3117ece4Schristos } 1197*3117ece4Schristos if (!warned) { 1198*3117ece4Schristos COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.suffixSize, displayLevel); 1199*3117ece4Schristos warned = 1; 1200*3117ece4Schristos } 1201*3117ece4Schristos /* Loop through k reusing the same context */ 1202*3117ece4Schristos for (k = kMinK; k <= kMaxK; k += kStepSize) { 1203*3117ece4Schristos /* Prepare the arguments */ 1204*3117ece4Schristos COVER_tryParameters_data_t *data = (COVER_tryParameters_data_t *)malloc( 1205*3117ece4Schristos sizeof(COVER_tryParameters_data_t)); 1206*3117ece4Schristos LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k); 1207*3117ece4Schristos if (!data) { 1208*3117ece4Schristos LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n"); 1209*3117ece4Schristos COVER_best_destroy(&best); 1210*3117ece4Schristos COVER_ctx_destroy(&ctx); 1211*3117ece4Schristos POOL_free(pool); 1212*3117ece4Schristos return ERROR(memory_allocation); 1213*3117ece4Schristos } 1214*3117ece4Schristos data->ctx = &ctx; 1215*3117ece4Schristos data->best = &best; 1216*3117ece4Schristos data->dictBufferCapacity = dictBufferCapacity; 1217*3117ece4Schristos data->parameters = *parameters; 1218*3117ece4Schristos data->parameters.k = k; 1219*3117ece4Schristos data->parameters.d = d; 1220*3117ece4Schristos data->parameters.splitPoint = splitPoint; 1221*3117ece4Schristos data->parameters.steps = kSteps; 1222*3117ece4Schristos data->parameters.shrinkDict = shrinkDict; 1223*3117ece4Schristos data->parameters.zParams.notificationLevel = g_displayLevel; 1224*3117ece4Schristos /* Check the parameters */ 1225*3117ece4Schristos if (!COVER_checkParameters(data->parameters, dictBufferCapacity)) { 1226*3117ece4Schristos DISPLAYLEVEL(1, "Cover parameters incorrect\n"); 1227*3117ece4Schristos free(data); 1228*3117ece4Schristos continue; 1229*3117ece4Schristos } 1230*3117ece4Schristos /* Call the function and pass ownership of data to it */ 1231*3117ece4Schristos COVER_best_start(&best); 1232*3117ece4Schristos if (pool) { 1233*3117ece4Schristos POOL_add(pool, &COVER_tryParameters, data); 1234*3117ece4Schristos } else { 1235*3117ece4Schristos COVER_tryParameters(data); 1236*3117ece4Schristos } 1237*3117ece4Schristos /* Print status */ 1238*3117ece4Schristos LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ", 1239*3117ece4Schristos (unsigned)((iteration * 100) / kIterations)); 1240*3117ece4Schristos ++iteration; 1241*3117ece4Schristos } 1242*3117ece4Schristos COVER_best_wait(&best); 1243*3117ece4Schristos COVER_ctx_destroy(&ctx); 1244*3117ece4Schristos } 1245*3117ece4Schristos LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", ""); 1246*3117ece4Schristos /* Fill the output buffer and parameters with output of the best parameters */ 1247*3117ece4Schristos { 1248*3117ece4Schristos const size_t dictSize = best.dictSize; 1249*3117ece4Schristos if (ZSTD_isError(best.compressedSize)) { 1250*3117ece4Schristos const size_t compressedSize = best.compressedSize; 1251*3117ece4Schristos COVER_best_destroy(&best); 1252*3117ece4Schristos POOL_free(pool); 1253*3117ece4Schristos return compressedSize; 1254*3117ece4Schristos } 1255*3117ece4Schristos *parameters = best.parameters; 1256*3117ece4Schristos memcpy(dictBuffer, best.dict, dictSize); 1257*3117ece4Schristos COVER_best_destroy(&best); 1258*3117ece4Schristos POOL_free(pool); 1259*3117ece4Schristos return dictSize; 1260*3117ece4Schristos } 1261*3117ece4Schristos } 1262