zstd-ruby 0.1.2 → 1.1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ */
9
+
10
+ #ifndef ZSTDMT_COMPRESS_H
11
+ #define ZSTDMT_COMPRESS_H
12
+
13
+ #if defined (__cplusplus)
14
+ extern "C" {
15
+ #endif
16
+
17
+
18
+ /* Note : All prototypes defined in this file shall be considered experimental.
19
+ * There is no guarantee of API continuity (yet) on any of these prototypes */
20
+
21
+ /* === Dependencies === */
22
+ #include <stddef.h> /* size_t */
23
+ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters */
24
+ #include "zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer, ZSTDLIB_API */
25
+
26
+
27
+ /* === Simple one-pass functions === */
28
+
29
+ typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx;
30
+ ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbThreads);
31
+ ZSTDLIB_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* cctx);
32
+
33
+ ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx,
34
+ void* dst, size_t dstCapacity,
35
+ const void* src, size_t srcSize,
36
+ int compressionLevel);
37
+
38
+
39
+ /* === Streaming functions === */
40
+
41
+ ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel);
42
+ ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */
43
+
44
+ ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
45
+
46
+ ZSTDLIB_API size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */
47
+ ZSTDLIB_API size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */
48
+
49
+
50
+ /* === Advanced functions and parameters === */
51
+
52
+ #ifndef ZSTDMT_SECTION_SIZE_MIN
53
+ # define ZSTDMT_SECTION_SIZE_MIN (1U << 20) /* 1 MB - Minimum size of each compression job */
54
+ #endif
55
+
56
+ ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* dict, size_t dictSize, /**< dict can be released after init, a local copy is preserved within zcs */
57
+ ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */
58
+
59
+ /* ZSDTMT_parameter :
60
+ * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */
61
+ typedef enum {
62
+ ZSTDMT_p_sectionSize, /* size of input "section". Each section is compressed in parallel. 0 means default, which is dynamically determined within compression functions */
63
+ ZSTDMT_p_overlapSectionLog /* Log of overlapped section; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window */
64
+ } ZSDTMT_parameter;
65
+
66
+ /* ZSTDMT_setMTCtxParameter() :
67
+ * allow setting individual parameters, one at a time, among a list of enums defined in ZSTDMT_parameter.
68
+ * The function must be called typically after ZSTD_createCCtx().
69
+ * Parameters not explicitly reset by ZSTDMT_init*() remain the same in consecutive compression sessions.
70
+ * @return : 0, or an error code (which can be tested using ZSTD_isError()) */
71
+ ZSTDLIB_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value);
72
+
73
+
74
+ #if defined (__cplusplus)
75
+ }
76
+ #endif
77
+
78
+ #endif /* ZSTDMT_COMPRESS_H */
@@ -1444,7 +1444,7 @@ size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx,
1444
1444
  #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1)
1445
1445
  if (ZSTD_isLegacy(src, srcSize)) return ZSTD_decompressLegacy(dst, dstCapacity, src, srcSize, dict, dictSize);
1446
1446
  #endif
1447
- ZSTD_decompressBegin_usingDict(dctx, dict, dictSize);
1447
+ CHECK_F(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize));
1448
1448
  ZSTD_checkContinuity(dctx, dst);
1449
1449
  return ZSTD_decompressFrame(dctx, dst, dstCapacity, src, srcSize);
1450
1450
  }
@@ -1671,9 +1671,9 @@ static size_t ZSTD_loadEntropy(ZSTD_DCtx* dctx, const void* const dict, size_t c
1671
1671
  }
1672
1672
 
1673
1673
  if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted);
1674
- dctx->rep[0] = MEM_readLE32(dictPtr+0); if (dctx->rep[0] >= dictSize) return ERROR(dictionary_corrupted);
1675
- dctx->rep[1] = MEM_readLE32(dictPtr+4); if (dctx->rep[1] >= dictSize) return ERROR(dictionary_corrupted);
1676
- dctx->rep[2] = MEM_readLE32(dictPtr+8); if (dctx->rep[2] >= dictSize) return ERROR(dictionary_corrupted);
1674
+ dctx->rep[0] = MEM_readLE32(dictPtr+0); if (dctx->rep[0] == 0 || dctx->rep[0] >= dictSize) return ERROR(dictionary_corrupted);
1675
+ dctx->rep[1] = MEM_readLE32(dictPtr+4); if (dctx->rep[1] == 0 || dctx->rep[1] >= dictSize) return ERROR(dictionary_corrupted);
1676
+ dctx->rep[2] = MEM_readLE32(dictPtr+8); if (dctx->rep[2] == 0 || dctx->rep[2] >= dictSize) return ERROR(dictionary_corrupted);
1677
1677
  dictPtr += 12;
1678
1678
 
1679
1679
  dctx->litEntropy = dctx->fseEntropy = 1;
@@ -1713,39 +1713,44 @@ size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t
1713
1713
  /* ====== ZSTD_DDict ====== */
1714
1714
 
1715
1715
  struct ZSTD_DDict_s {
1716
- void* dict;
1716
+ void* dictBuffer;
1717
+ const void* dictContent;
1717
1718
  size_t dictSize;
1718
1719
  ZSTD_DCtx* refContext;
1719
1720
  }; /* typedef'd to ZSTD_DDict within "zstd.h" */
1720
1721
 
1721
- ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_customMem customMem)
1722
+ ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, unsigned byReference, ZSTD_customMem customMem)
1722
1723
  {
1723
1724
  if (!customMem.customAlloc && !customMem.customFree) customMem = defaultCustomMem;
1724
1725
  if (!customMem.customAlloc || !customMem.customFree) return NULL;
1725
1726
 
1726
1727
  { ZSTD_DDict* const ddict = (ZSTD_DDict*) ZSTD_malloc(sizeof(ZSTD_DDict), customMem);
1727
- void* const dictContent = ZSTD_malloc(dictSize, customMem);
1728
1728
  ZSTD_DCtx* const dctx = ZSTD_createDCtx_advanced(customMem);
1729
1729
 
1730
- if (!dictContent || !ddict || !dctx) {
1731
- ZSTD_free(dictContent, customMem);
1730
+ if (!ddict || !dctx) {
1732
1731
  ZSTD_free(ddict, customMem);
1733
1732
  ZSTD_free(dctx, customMem);
1734
1733
  return NULL;
1735
1734
  }
1736
1735
 
1737
- if (dictSize) {
1738
- memcpy(dictContent, dict, dictSize);
1736
+ if ((byReference) || (!dict) || (!dictSize)) {
1737
+ ddict->dictBuffer = NULL;
1738
+ ddict->dictContent = dict;
1739
+ } else {
1740
+ void* const internalBuffer = ZSTD_malloc(dictSize, customMem);
1741
+ if (!internalBuffer) { ZSTD_free(dctx, customMem); ZSTD_free(ddict, customMem); return NULL; }
1742
+ memcpy(internalBuffer, dict, dictSize);
1743
+ ddict->dictBuffer = internalBuffer;
1744
+ ddict->dictContent = internalBuffer;
1739
1745
  }
1740
- { size_t const errorCode = ZSTD_decompressBegin_usingDict(dctx, dictContent, dictSize);
1746
+ { size_t const errorCode = ZSTD_decompressBegin_usingDict(dctx, ddict->dictContent, dictSize);
1741
1747
  if (ZSTD_isError(errorCode)) {
1742
- ZSTD_free(dictContent, customMem);
1748
+ ZSTD_free(ddict->dictBuffer, customMem);
1743
1749
  ZSTD_free(ddict, customMem);
1744
1750
  ZSTD_free(dctx, customMem);
1745
1751
  return NULL;
1746
1752
  } }
1747
1753
 
1748
- ddict->dict = dictContent;
1749
1754
  ddict->dictSize = dictSize;
1750
1755
  ddict->refContext = dctx;
1751
1756
  return ddict;
@@ -1758,15 +1763,27 @@ ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_cu
1758
1763
  ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize)
1759
1764
  {
1760
1765
  ZSTD_customMem const allocator = { NULL, NULL, NULL };
1761
- return ZSTD_createDDict_advanced(dict, dictSize, allocator);
1766
+ return ZSTD_createDDict_advanced(dict, dictSize, 0, allocator);
1762
1767
  }
1763
1768
 
1769
+
1770
+ /*! ZSTD_createDDict_byReference() :
1771
+ * Create a digested dictionary, ready to start decompression operation without startup delay.
1772
+ * Dictionary content is simply referenced, and therefore stays in dictBuffer.
1773
+ * It is important that dictBuffer outlives DDict, it must remain read accessible throughout the lifetime of DDict */
1774
+ ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize)
1775
+ {
1776
+ ZSTD_customMem const allocator = { NULL, NULL, NULL };
1777
+ return ZSTD_createDDict_advanced(dictBuffer, dictSize, 1, allocator);
1778
+ }
1779
+
1780
+
1764
1781
  size_t ZSTD_freeDDict(ZSTD_DDict* ddict)
1765
1782
  {
1766
1783
  if (ddict==NULL) return 0; /* support free on NULL */
1767
1784
  { ZSTD_customMem const cMem = ddict->refContext->customMem;
1768
1785
  ZSTD_freeDCtx(ddict->refContext);
1769
- ZSTD_free(ddict->dict, cMem);
1786
+ ZSTD_free(ddict->dictBuffer, cMem);
1770
1787
  ZSTD_free(ddict, cMem);
1771
1788
  return 0;
1772
1789
  }
@@ -1775,7 +1792,7 @@ size_t ZSTD_freeDDict(ZSTD_DDict* ddict)
1775
1792
  size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict)
1776
1793
  {
1777
1794
  if (ddict==NULL) return 0; /* support sizeof on NULL */
1778
- return sizeof(*ddict) + sizeof(ddict->refContext) + ddict->dictSize;
1795
+ return sizeof(*ddict) + ZSTD_sizeof_DCtx(ddict->refContext) + (ddict->dictBuffer ? ddict->dictSize : 0) ;
1779
1796
  }
1780
1797
 
1781
1798
  /*! ZSTD_getDictID_fromDict() :
@@ -1796,7 +1813,7 @@ unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize)
1796
1813
  unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict)
1797
1814
  {
1798
1815
  if (ddict==NULL) return 0;
1799
- return ZSTD_getDictID_fromDict(ddict->dict, ddict->dictSize);
1816
+ return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize);
1800
1817
  }
1801
1818
 
1802
1819
  /*! ZSTD_getDictID_fromFrame() :
@@ -1827,7 +1844,7 @@ size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx,
1827
1844
  const ZSTD_DDict* ddict)
1828
1845
  {
1829
1846
  #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1)
1830
- if (ZSTD_isLegacy(src, srcSize)) return ZSTD_decompressLegacy(dst, dstCapacity, src, srcSize, ddict->dict, ddict->dictSize);
1847
+ if (ZSTD_isLegacy(src, srcSize)) return ZSTD_decompressLegacy(dst, dstCapacity, src, srcSize, ddict->dictContent, ddict->dictSize);
1831
1848
  #endif
1832
1849
  ZSTD_refDCtx(dctx, ddict->refContext);
1833
1850
  ZSTD_checkContinuity(dctx, dst);
@@ -1919,7 +1936,7 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di
1919
1936
  zds->stage = zdss_loadHeader;
1920
1937
  zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0;
1921
1938
  ZSTD_freeDDict(zds->ddictLocal);
1922
- if (dict) {
1939
+ if (dict && dictSize >= 8) {
1923
1940
  zds->ddictLocal = ZSTD_createDDict(dict, dictSize);
1924
1941
  if (zds->ddictLocal == NULL) return ERROR(memory_allocation);
1925
1942
  } else zds->ddictLocal = NULL;
@@ -1956,7 +1973,7 @@ size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds,
1956
1973
  switch(paramType)
1957
1974
  {
1958
1975
  default : return ERROR(parameter_unknown);
1959
- case ZSTDdsp_maxWindowSize : zds->maxWindowSize = paramValue ? paramValue : (U32)(-1); break;
1976
+ case DStream_p_maxWindowSize : zds->maxWindowSize = paramValue ? paramValue : (U32)(-1); break;
1960
1977
  }
1961
1978
  return 0;
1962
1979
  }
@@ -2007,7 +2024,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
2007
2024
  #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
2008
2025
  { U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart);
2009
2026
  if (legacyVersion) {
2010
- const void* const dict = zds->ddict ? zds->ddict->dict : NULL;
2027
+ const void* const dict = zds->ddict ? zds->ddict->dictContent : NULL;
2011
2028
  size_t const dictSize = zds->ddict ? zds->ddict->dictSize : 0;
2012
2029
  CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext, zds->previousLegacyVersion, legacyVersion,
2013
2030
  dict, dictSize));
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ */
9
+
10
+ /*-*************************************
11
+ * Dependencies
12
+ ***************************************/
13
+ #include "error_private.h"
14
+ #include "zbuff.h"
15
+
16
+ /*-****************************************
17
+ * ZBUFF Error Management (deprecated)
18
+ ******************************************/
19
+
20
+ /*! ZBUFF_isError() :
21
+ * tells if a return value is an error code */
22
+ unsigned ZBUFF_isError(size_t errorCode) { return ERR_isError(errorCode); }
23
+ /*! ZBUFF_getErrorName() :
24
+ * provides error code string from function result (useful for debugging) */
25
+ const char* ZBUFF_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); }
26
+
@@ -0,0 +1,1021 @@
1
+ /**
2
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ */
9
+
10
+ /*-*************************************
11
+ * Dependencies
12
+ ***************************************/
13
+ #include <stdio.h> /* fprintf */
14
+ #include <stdlib.h> /* malloc, free, qsort */
15
+ #include <string.h> /* memset */
16
+ #include <time.h> /* clock */
17
+
18
+ #include "mem.h" /* read */
19
+ #include "pool.h"
20
+ #include "threading.h"
21
+ #include "zstd_internal.h" /* includes zstd.h */
22
+ #ifndef ZDICT_STATIC_LINKING_ONLY
23
+ #define ZDICT_STATIC_LINKING_ONLY
24
+ #endif
25
+ #include "zdict.h"
26
+
27
+ /*-*************************************
28
+ * Constants
29
+ ***************************************/
30
+ #define COVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((U32)-1) : ((U32)1 GB))
31
+
32
+ /*-*************************************
33
+ * Console display
34
+ ***************************************/
35
+ static int g_displayLevel = 2;
36
+ #define DISPLAY(...) \
37
+ { \
38
+ fprintf(stderr, __VA_ARGS__); \
39
+ fflush(stderr); \
40
+ }
41
+ #define LOCALDISPLAYLEVEL(displayLevel, l, ...) \
42
+ if (displayLevel >= l) { \
43
+ DISPLAY(__VA_ARGS__); \
44
+ } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */
45
+ #define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__)
46
+
47
+ #define LOCALDISPLAYUPDATE(displayLevel, l, ...) \
48
+ if (displayLevel >= l) { \
49
+ if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) { \
50
+ g_time = clock(); \
51
+ DISPLAY(__VA_ARGS__); \
52
+ if (displayLevel >= 4) \
53
+ fflush(stdout); \
54
+ } \
55
+ }
56
+ #define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__)
57
+ static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100;
58
+ static clock_t g_time = 0;
59
+
60
+ /*-*************************************
61
+ * Hash table
62
+ ***************************************
63
+ * A small specialized hash map for storing activeDmers.
64
+ * The map does not resize, so if it becomes full it will loop forever.
65
+ * Thus, the map must be large enough to store every value.
66
+ * The map implements linear probing and keeps its load less than 0.5.
67
+ */
68
+
69
+ #define MAP_EMPTY_VALUE ((U32)-1)
70
+ typedef struct COVER_map_pair_t_s {
71
+ U32 key;
72
+ U32 value;
73
+ } COVER_map_pair_t;
74
+
75
+ typedef struct COVER_map_s {
76
+ COVER_map_pair_t *data;
77
+ U32 sizeLog;
78
+ U32 size;
79
+ U32 sizeMask;
80
+ } COVER_map_t;
81
+
82
+ /**
83
+ * Clear the map.
84
+ */
85
+ static void COVER_map_clear(COVER_map_t *map) {
86
+ memset(map->data, MAP_EMPTY_VALUE, map->size * sizeof(COVER_map_pair_t));
87
+ }
88
+
89
+ /**
90
+ * Initializes a map of the given size.
91
+ * Returns 1 on success and 0 on failure.
92
+ * The map must be destroyed with COVER_map_destroy().
93
+ * The map is only guaranteed to be large enough to hold size elements.
94
+ */
95
+ static int COVER_map_init(COVER_map_t *map, U32 size) {
96
+ map->sizeLog = ZSTD_highbit32(size) + 2;
97
+ map->size = (U32)1 << map->sizeLog;
98
+ map->sizeMask = map->size - 1;
99
+ map->data = (COVER_map_pair_t *)malloc(map->size * sizeof(COVER_map_pair_t));
100
+ if (!map->data) {
101
+ map->sizeLog = 0;
102
+ map->size = 0;
103
+ return 0;
104
+ }
105
+ COVER_map_clear(map);
106
+ return 1;
107
+ }
108
+
109
+ /**
110
+ * Internal hash function
111
+ */
112
+ static const U32 prime4bytes = 2654435761U;
113
+ static U32 COVER_map_hash(COVER_map_t *map, U32 key) {
114
+ return (key * prime4bytes) >> (32 - map->sizeLog);
115
+ }
116
+
117
+ /**
118
+ * Helper function that returns the index that a key should be placed into.
119
+ */
120
+ static U32 COVER_map_index(COVER_map_t *map, U32 key) {
121
+ const U32 hash = COVER_map_hash(map, key);
122
+ U32 i;
123
+ for (i = hash;; i = (i + 1) & map->sizeMask) {
124
+ COVER_map_pair_t *pos = &map->data[i];
125
+ if (pos->value == MAP_EMPTY_VALUE) {
126
+ return i;
127
+ }
128
+ if (pos->key == key) {
129
+ return i;
130
+ }
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Returns the pointer to the value for key.
136
+ * If key is not in the map, it is inserted and the value is set to 0.
137
+ * The map must not be full.
138
+ */
139
+ static U32 *COVER_map_at(COVER_map_t *map, U32 key) {
140
+ COVER_map_pair_t *pos = &map->data[COVER_map_index(map, key)];
141
+ if (pos->value == MAP_EMPTY_VALUE) {
142
+ pos->key = key;
143
+ pos->value = 0;
144
+ }
145
+ return &pos->value;
146
+ }
147
+
148
+ /**
149
+ * Deletes key from the map if present.
150
+ */
151
+ static void COVER_map_remove(COVER_map_t *map, U32 key) {
152
+ U32 i = COVER_map_index(map, key);
153
+ COVER_map_pair_t *del = &map->data[i];
154
+ U32 shift = 1;
155
+ if (del->value == MAP_EMPTY_VALUE) {
156
+ return;
157
+ }
158
+ for (i = (i + 1) & map->sizeMask;; i = (i + 1) & map->sizeMask) {
159
+ COVER_map_pair_t *const pos = &map->data[i];
160
+ /* If the position is empty we are done */
161
+ if (pos->value == MAP_EMPTY_VALUE) {
162
+ del->value = MAP_EMPTY_VALUE;
163
+ return;
164
+ }
165
+ /* If pos can be moved to del do so */
166
+ if (((i - COVER_map_hash(map, pos->key)) & map->sizeMask) >= shift) {
167
+ del->key = pos->key;
168
+ del->value = pos->value;
169
+ del = pos;
170
+ shift = 1;
171
+ } else {
172
+ ++shift;
173
+ }
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Destroyes a map that is inited with COVER_map_init().
179
+ */
180
+ static void COVER_map_destroy(COVER_map_t *map) {
181
+ if (map->data) {
182
+ free(map->data);
183
+ }
184
+ map->data = NULL;
185
+ map->size = 0;
186
+ }
187
+
188
+ /*-*************************************
189
+ * Context
190
+ ***************************************/
191
+
192
+ typedef struct {
193
+ const BYTE *samples;
194
+ size_t *offsets;
195
+ const size_t *samplesSizes;
196
+ size_t nbSamples;
197
+ U32 *suffix;
198
+ size_t suffixSize;
199
+ U32 *freqs;
200
+ U32 *dmerAt;
201
+ unsigned d;
202
+ } COVER_ctx_t;
203
+
204
+ /* We need a global context for qsort... */
205
+ static COVER_ctx_t *g_ctx = NULL;
206
+
207
+ /*-*************************************
208
+ * Helper functions
209
+ ***************************************/
210
+
211
+ /**
212
+ * Returns the sum of the sample sizes.
213
+ */
214
+ static size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) {
215
+ size_t sum = 0;
216
+ size_t i;
217
+ for (i = 0; i < nbSamples; ++i) {
218
+ sum += samplesSizes[i];
219
+ }
220
+ return sum;
221
+ }
222
+
223
+ /**
224
+ * Returns -1 if the dmer at lp is less than the dmer at rp.
225
+ * Return 0 if the dmers at lp and rp are equal.
226
+ * Returns 1 if the dmer at lp is greater than the dmer at rp.
227
+ */
228
+ static int COVER_cmp(COVER_ctx_t *ctx, const void *lp, const void *rp) {
229
+ const U32 lhs = *(const U32 *)lp;
230
+ const U32 rhs = *(const U32 *)rp;
231
+ return memcmp(ctx->samples + lhs, ctx->samples + rhs, ctx->d);
232
+ }
233
+
234
+ /**
235
+ * Same as COVER_cmp() except ties are broken by pointer value
236
+ * NOTE: g_ctx must be set to call this function. A global is required because
237
+ * qsort doesn't take an opaque pointer.
238
+ */
239
+ static int COVER_strict_cmp(const void *lp, const void *rp) {
240
+ int result = COVER_cmp(g_ctx, lp, rp);
241
+ if (result == 0) {
242
+ result = lp < rp ? -1 : 1;
243
+ }
244
+ return result;
245
+ }
246
+
247
+ /**
248
+ * Returns the first pointer in [first, last) whose element does not compare
249
+ * less than value. If no such element exists it returns last.
250
+ */
251
+ static const size_t *COVER_lower_bound(const size_t *first, const size_t *last,
252
+ size_t value) {
253
+ size_t count = last - first;
254
+ while (count != 0) {
255
+ size_t step = count / 2;
256
+ const size_t *ptr = first;
257
+ ptr += step;
258
+ if (*ptr < value) {
259
+ first = ++ptr;
260
+ count -= step + 1;
261
+ } else {
262
+ count = step;
263
+ }
264
+ }
265
+ return first;
266
+ }
267
+
268
+ /**
269
+ * Generic groupBy function.
270
+ * Groups an array sorted by cmp into groups with equivalent values.
271
+ * Calls grp for each group.
272
+ */
273
+ static void
274
+ COVER_groupBy(const void *data, size_t count, size_t size, COVER_ctx_t *ctx,
275
+ int (*cmp)(COVER_ctx_t *, const void *, const void *),
276
+ void (*grp)(COVER_ctx_t *, const void *, const void *)) {
277
+ const BYTE *ptr = (const BYTE *)data;
278
+ size_t num = 0;
279
+ while (num < count) {
280
+ const BYTE *grpEnd = ptr + size;
281
+ ++num;
282
+ while (num < count && cmp(ctx, ptr, grpEnd) == 0) {
283
+ grpEnd += size;
284
+ ++num;
285
+ }
286
+ grp(ctx, ptr, grpEnd);
287
+ ptr = grpEnd;
288
+ }
289
+ }
290
+
291
+ /*-*************************************
292
+ * Cover functions
293
+ ***************************************/
294
+
295
+ /**
296
+ * Called on each group of positions with the same dmer.
297
+ * Counts the frequency of each dmer and saves it in the suffix array.
298
+ * Fills `ctx->dmerAt`.
299
+ */
300
+ static void COVER_group(COVER_ctx_t *ctx, const void *group,
301
+ const void *groupEnd) {
302
+ /* The group consists of all the positions with the same first d bytes. */
303
+ const U32 *grpPtr = (const U32 *)group;
304
+ const U32 *grpEnd = (const U32 *)groupEnd;
305
+ /* The dmerId is how we will reference this dmer.
306
+ * This allows us to map the whole dmer space to a much smaller space, the
307
+ * size of the suffix array.
308
+ */
309
+ const U32 dmerId = (U32)(grpPtr - ctx->suffix);
310
+ /* Count the number of samples this dmer shows up in */
311
+ U32 freq = 0;
312
+ /* Details */
313
+ const size_t *curOffsetPtr = ctx->offsets;
314
+ const size_t *offsetsEnd = ctx->offsets + ctx->nbSamples;
315
+ /* Once *grpPtr >= curSampleEnd this occurrence of the dmer is in a
316
+ * different sample than the last.
317
+ */
318
+ size_t curSampleEnd = ctx->offsets[0];
319
+ for (; grpPtr != grpEnd; ++grpPtr) {
320
+ /* Save the dmerId for this position so we can get back to it. */
321
+ ctx->dmerAt[*grpPtr] = dmerId;
322
+ /* Dictionaries only help for the first reference to the dmer.
323
+ * After that zstd can reference the match from the previous reference.
324
+ * So only count each dmer once for each sample it is in.
325
+ */
326
+ if (*grpPtr < curSampleEnd) {
327
+ continue;
328
+ }
329
+ freq += 1;
330
+ /* Binary search to find the end of the sample *grpPtr is in.
331
+ * In the common case that grpPtr + 1 == grpEnd we can skip the binary
332
+ * search because the loop is over.
333
+ */
334
+ if (grpPtr + 1 != grpEnd) {
335
+ const size_t *sampleEndPtr =
336
+ COVER_lower_bound(curOffsetPtr, offsetsEnd, *grpPtr);
337
+ curSampleEnd = *sampleEndPtr;
338
+ curOffsetPtr = sampleEndPtr + 1;
339
+ }
340
+ }
341
+ /* At this point we are never going to look at this segment of the suffix
342
+ * array again. We take advantage of this fact to save memory.
343
+ * We store the frequency of the dmer in the first position of the group,
344
+ * which is dmerId.
345
+ */
346
+ ctx->suffix[dmerId] = freq;
347
+ }
348
+
349
+ /**
350
+ * A segment is a range in the source as well as the score of the segment.
351
+ */
352
+ typedef struct {
353
+ U32 begin;
354
+ U32 end;
355
+ double score;
356
+ } COVER_segment_t;
357
+
358
+ /**
359
+ * Selects the best segment in an epoch.
360
+ * Segments of are scored according to the function:
361
+ *
362
+ * Let F(d) be the frequency of dmer d.
363
+ * Let S_i be the dmer at position i of segment S which has length k.
364
+ *
365
+ * Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1})
366
+ *
367
+ * Once the dmer d is in the dictionay we set F(d) = 0.
368
+ */
369
+ static COVER_segment_t COVER_selectSegment(const COVER_ctx_t *ctx, U32 *freqs,
370
+ COVER_map_t *activeDmers, U32 begin,
371
+ U32 end, COVER_params_t parameters) {
372
+ /* Constants */
373
+ const U32 k = parameters.k;
374
+ const U32 d = parameters.d;
375
+ const U32 dmersInK = k - d + 1;
376
+ /* Try each segment (activeSegment) and save the best (bestSegment) */
377
+ COVER_segment_t bestSegment = {0, 0, 0};
378
+ COVER_segment_t activeSegment;
379
+ /* Reset the activeDmers in the segment */
380
+ COVER_map_clear(activeDmers);
381
+ /* The activeSegment starts at the beginning of the epoch. */
382
+ activeSegment.begin = begin;
383
+ activeSegment.end = begin;
384
+ activeSegment.score = 0;
385
+ /* Slide the activeSegment through the whole epoch.
386
+ * Save the best segment in bestSegment.
387
+ */
388
+ while (activeSegment.end < end) {
389
+ /* The dmerId for the dmer at the next position */
390
+ U32 newDmer = ctx->dmerAt[activeSegment.end];
391
+ /* The entry in activeDmers for this dmerId */
392
+ U32 *newDmerOcc = COVER_map_at(activeDmers, newDmer);
393
+ /* If the dmer isn't already present in the segment add its score. */
394
+ if (*newDmerOcc == 0) {
395
+ /* The paper suggest using the L-0.5 norm, but experiments show that it
396
+ * doesn't help.
397
+ */
398
+ activeSegment.score += freqs[newDmer];
399
+ }
400
+ /* Add the dmer to the segment */
401
+ activeSegment.end += 1;
402
+ *newDmerOcc += 1;
403
+
404
+ /* If the window is now too large, drop the first position */
405
+ if (activeSegment.end - activeSegment.begin == dmersInK + 1) {
406
+ U32 delDmer = ctx->dmerAt[activeSegment.begin];
407
+ U32 *delDmerOcc = COVER_map_at(activeDmers, delDmer);
408
+ activeSegment.begin += 1;
409
+ *delDmerOcc -= 1;
410
+ /* If this is the last occurence of the dmer, subtract its score */
411
+ if (*delDmerOcc == 0) {
412
+ COVER_map_remove(activeDmers, delDmer);
413
+ activeSegment.score -= freqs[delDmer];
414
+ }
415
+ }
416
+
417
+ /* If this segment is the best so far save it */
418
+ if (activeSegment.score > bestSegment.score) {
419
+ bestSegment = activeSegment;
420
+ }
421
+ }
422
+ {
423
+ /* Trim off the zero frequency head and tail from the segment. */
424
+ U32 newBegin = bestSegment.end;
425
+ U32 newEnd = bestSegment.begin;
426
+ U32 pos;
427
+ for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) {
428
+ U32 freq = freqs[ctx->dmerAt[pos]];
429
+ if (freq != 0) {
430
+ newBegin = MIN(newBegin, pos);
431
+ newEnd = pos + 1;
432
+ }
433
+ }
434
+ bestSegment.begin = newBegin;
435
+ bestSegment.end = newEnd;
436
+ }
437
+ {
438
+ /* Zero out the frequency of each dmer covered by the chosen segment. */
439
+ U32 pos;
440
+ for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) {
441
+ freqs[ctx->dmerAt[pos]] = 0;
442
+ }
443
+ }
444
+ return bestSegment;
445
+ }
446
+
447
+ /**
448
+ * Check the validity of the parameters.
449
+ * Returns non-zero if the parameters are valid and 0 otherwise.
450
+ */
451
+ static int COVER_checkParameters(COVER_params_t parameters) {
452
+ /* k and d are required parameters */
453
+ if (parameters.d == 0 || parameters.k == 0) {
454
+ return 0;
455
+ }
456
+ /* d <= k */
457
+ if (parameters.d > parameters.k) {
458
+ return 0;
459
+ }
460
+ return 1;
461
+ }
462
+
463
+ /**
464
+ * Clean up a context initialized with `COVER_ctx_init()`.
465
+ */
466
+ static void COVER_ctx_destroy(COVER_ctx_t *ctx) {
467
+ if (!ctx) {
468
+ return;
469
+ }
470
+ if (ctx->suffix) {
471
+ free(ctx->suffix);
472
+ ctx->suffix = NULL;
473
+ }
474
+ if (ctx->freqs) {
475
+ free(ctx->freqs);
476
+ ctx->freqs = NULL;
477
+ }
478
+ if (ctx->dmerAt) {
479
+ free(ctx->dmerAt);
480
+ ctx->dmerAt = NULL;
481
+ }
482
+ if (ctx->offsets) {
483
+ free(ctx->offsets);
484
+ ctx->offsets = NULL;
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Prepare a context for dictionary building.
490
+ * The context is only dependent on the parameter `d` and can used multiple
491
+ * times.
492
+ * Returns 1 on success or zero on error.
493
+ * The context must be destroyed with `COVER_ctx_destroy()`.
494
+ */
495
+ static int COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer,
496
+ const size_t *samplesSizes, unsigned nbSamples,
497
+ unsigned d) {
498
+ const BYTE *const samples = (const BYTE *)samplesBuffer;
499
+ const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples);
500
+ /* Checks */
501
+ if (totalSamplesSize < d ||
502
+ totalSamplesSize >= (size_t)COVER_MAX_SAMPLES_SIZE) {
503
+ DISPLAYLEVEL(1, "Total samples size is too large, maximum size is %u MB\n",
504
+ (COVER_MAX_SAMPLES_SIZE >> 20));
505
+ return 0;
506
+ }
507
+ /* Zero the context */
508
+ memset(ctx, 0, sizeof(*ctx));
509
+ DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbSamples,
510
+ (U32)totalSamplesSize);
511
+ ctx->samples = samples;
512
+ ctx->samplesSizes = samplesSizes;
513
+ ctx->nbSamples = nbSamples;
514
+ /* Partial suffix array */
515
+ ctx->suffixSize = totalSamplesSize - d + 1;
516
+ ctx->suffix = (U32 *)malloc(ctx->suffixSize * sizeof(U32));
517
+ /* Maps index to the dmerID */
518
+ ctx->dmerAt = (U32 *)malloc(ctx->suffixSize * sizeof(U32));
519
+ /* The offsets of each file */
520
+ ctx->offsets = (size_t *)malloc((nbSamples + 1) * sizeof(size_t));
521
+ if (!ctx->suffix || !ctx->dmerAt || !ctx->offsets) {
522
+ DISPLAYLEVEL(1, "Failed to allocate scratch buffers\n");
523
+ COVER_ctx_destroy(ctx);
524
+ return 0;
525
+ }
526
+ ctx->freqs = NULL;
527
+ ctx->d = d;
528
+
529
+ /* Fill offsets from the samlesSizes */
530
+ {
531
+ U32 i;
532
+ ctx->offsets[0] = 0;
533
+ for (i = 1; i <= nbSamples; ++i) {
534
+ ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1];
535
+ }
536
+ }
537
+ DISPLAYLEVEL(2, "Constructing partial suffix array\n");
538
+ {
539
+ /* suffix is a partial suffix array.
540
+ * It only sorts suffixes by their first parameters.d bytes.
541
+ * The sort is stable, so each dmer group is sorted by position in input.
542
+ */
543
+ U32 i;
544
+ for (i = 0; i < ctx->suffixSize; ++i) {
545
+ ctx->suffix[i] = i;
546
+ }
547
+ /* qsort doesn't take an opaque pointer, so pass as a global */
548
+ g_ctx = ctx;
549
+ qsort(ctx->suffix, ctx->suffixSize, sizeof(U32), &COVER_strict_cmp);
550
+ }
551
+ DISPLAYLEVEL(2, "Computing frequencies\n");
552
+ /* For each dmer group (group of positions with the same first d bytes):
553
+ * 1. For each position we set dmerAt[position] = dmerID. The dmerID is
554
+ * (groupBeginPtr - suffix). This allows us to go from position to
555
+ * dmerID so we can look up values in freq.
556
+ * 2. We calculate how many samples the dmer occurs in and save it in
557
+ * freqs[dmerId].
558
+ */
559
+ COVER_groupBy(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx, &COVER_cmp,
560
+ &COVER_group);
561
+ ctx->freqs = ctx->suffix;
562
+ ctx->suffix = NULL;
563
+ return 1;
564
+ }
565
+
566
+ /**
567
+ * Given the prepared context build the dictionary.
568
+ */
569
+ static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs,
570
+ COVER_map_t *activeDmers, void *dictBuffer,
571
+ size_t dictBufferCapacity,
572
+ COVER_params_t parameters) {
573
+ BYTE *const dict = (BYTE *)dictBuffer;
574
+ size_t tail = dictBufferCapacity;
575
+ /* Divide the data up into epochs of equal size.
576
+ * We will select at least one segment from each epoch.
577
+ */
578
+ const U32 epochs = (U32)(dictBufferCapacity / parameters.k);
579
+ const U32 epochSize = (U32)(ctx->suffixSize / epochs);
580
+ size_t epoch;
581
+ DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n", epochs,
582
+ epochSize);
583
+ /* Loop through the epochs until there are no more segments or the dictionary
584
+ * is full.
585
+ */
586
+ for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs) {
587
+ const U32 epochBegin = (U32)(epoch * epochSize);
588
+ const U32 epochEnd = epochBegin + epochSize;
589
+ size_t segmentSize;
590
+ /* Select a segment */
591
+ COVER_segment_t segment = COVER_selectSegment(
592
+ ctx, freqs, activeDmers, epochBegin, epochEnd, parameters);
593
+ /* Trim the segment if necessary and if it is empty then we are done */
594
+ segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail);
595
+ if (segmentSize == 0) {
596
+ break;
597
+ }
598
+ /* We fill the dictionary from the back to allow the best segments to be
599
+ * referenced with the smallest offsets.
600
+ */
601
+ tail -= segmentSize;
602
+ memcpy(dict + tail, ctx->samples + segment.begin, segmentSize);
603
+ DISPLAYUPDATE(
604
+ 2, "\r%u%% ",
605
+ (U32)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity));
606
+ }
607
+ DISPLAYLEVEL(2, "\r%79s\r", "");
608
+ return tail;
609
+ }
610
+
611
+ /**
612
+ * Translate from COVER_params_t to ZDICT_params_t required for finalizing the
613
+ * dictionary.
614
+ */
615
+ static ZDICT_params_t COVER_translateParams(COVER_params_t parameters) {
616
+ ZDICT_params_t zdictParams;
617
+ memset(&zdictParams, 0, sizeof(zdictParams));
618
+ zdictParams.notificationLevel = 1;
619
+ zdictParams.dictID = parameters.dictID;
620
+ zdictParams.compressionLevel = parameters.compressionLevel;
621
+ return zdictParams;
622
+ }
623
+
624
+ /**
625
+ * Constructs a dictionary using a heuristic based on the following paper:
626
+ *
627
+ * Liao, Petri, Moffat, Wirth
628
+ * Effective Construction of Relative Lempel-Ziv Dictionaries
629
+ * Published in WWW 2016.
630
+ */
631
+ ZDICTLIB_API size_t COVER_trainFromBuffer(
632
+ void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer,
633
+ const size_t *samplesSizes, unsigned nbSamples, COVER_params_t parameters) {
634
+ BYTE *const dict = (BYTE *)dictBuffer;
635
+ COVER_ctx_t ctx;
636
+ COVER_map_t activeDmers;
637
+ /* Checks */
638
+ if (!COVER_checkParameters(parameters)) {
639
+ DISPLAYLEVEL(1, "Cover parameters incorrect\n");
640
+ return ERROR(GENERIC);
641
+ }
642
+ if (nbSamples == 0) {
643
+ DISPLAYLEVEL(1, "Cover must have at least one input file\n");
644
+ return ERROR(GENERIC);
645
+ }
646
+ if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
647
+ DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",
648
+ ZDICT_DICTSIZE_MIN);
649
+ return ERROR(dstSize_tooSmall);
650
+ }
651
+ /* Initialize global data */
652
+ g_displayLevel = parameters.notificationLevel;
653
+ /* Initialize context and activeDmers */
654
+ if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples,
655
+ parameters.d)) {
656
+ return ERROR(GENERIC);
657
+ }
658
+ if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) {
659
+ DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n");
660
+ COVER_ctx_destroy(&ctx);
661
+ return ERROR(GENERIC);
662
+ }
663
+
664
+ DISPLAYLEVEL(2, "Building dictionary\n");
665
+ {
666
+ const size_t tail =
667
+ COVER_buildDictionary(&ctx, ctx.freqs, &activeDmers, dictBuffer,
668
+ dictBufferCapacity, parameters);
669
+ ZDICT_params_t zdictParams = COVER_translateParams(parameters);
670
+ const size_t dictionarySize = ZDICT_finalizeDictionary(
671
+ dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,
672
+ samplesBuffer, samplesSizes, nbSamples, zdictParams);
673
+ if (!ZSTD_isError(dictionarySize)) {
674
+ DISPLAYLEVEL(2, "Constructed dictionary of size %u\n",
675
+ (U32)dictionarySize);
676
+ }
677
+ COVER_ctx_destroy(&ctx);
678
+ COVER_map_destroy(&activeDmers);
679
+ return dictionarySize;
680
+ }
681
+ }
682
+
683
+ /**
684
+ * COVER_best_t is used for two purposes:
685
+ * 1. Synchronizing threads.
686
+ * 2. Saving the best parameters and dictionary.
687
+ *
688
+ * All of the methods except COVER_best_init() are thread safe if zstd is
689
+ * compiled with multithreaded support.
690
+ */
691
+ typedef struct COVER_best_s {
692
+ pthread_mutex_t mutex;
693
+ pthread_cond_t cond;
694
+ size_t liveJobs;
695
+ void *dict;
696
+ size_t dictSize;
697
+ COVER_params_t parameters;
698
+ size_t compressedSize;
699
+ } COVER_best_t;
700
+
701
+ /**
702
+ * Initialize the `COVER_best_t`.
703
+ */
704
+ static void COVER_best_init(COVER_best_t *best) {
705
+ if (!best) {
706
+ return;
707
+ }
708
+ pthread_mutex_init(&best->mutex, NULL);
709
+ pthread_cond_init(&best->cond, NULL);
710
+ best->liveJobs = 0;
711
+ best->dict = NULL;
712
+ best->dictSize = 0;
713
+ best->compressedSize = (size_t)-1;
714
+ memset(&best->parameters, 0, sizeof(best->parameters));
715
+ }
716
+
717
+ /**
718
+ * Wait until liveJobs == 0.
719
+ */
720
+ static void COVER_best_wait(COVER_best_t *best) {
721
+ if (!best) {
722
+ return;
723
+ }
724
+ pthread_mutex_lock(&best->mutex);
725
+ while (best->liveJobs != 0) {
726
+ pthread_cond_wait(&best->cond, &best->mutex);
727
+ }
728
+ pthread_mutex_unlock(&best->mutex);
729
+ }
730
+
731
+ /**
732
+ * Call COVER_best_wait() and then destroy the COVER_best_t.
733
+ */
734
+ static void COVER_best_destroy(COVER_best_t *best) {
735
+ if (!best) {
736
+ return;
737
+ }
738
+ COVER_best_wait(best);
739
+ if (best->dict) {
740
+ free(best->dict);
741
+ }
742
+ pthread_mutex_destroy(&best->mutex);
743
+ pthread_cond_destroy(&best->cond);
744
+ }
745
+
746
+ /**
747
+ * Called when a thread is about to be launched.
748
+ * Increments liveJobs.
749
+ */
750
+ static void COVER_best_start(COVER_best_t *best) {
751
+ if (!best) {
752
+ return;
753
+ }
754
+ pthread_mutex_lock(&best->mutex);
755
+ ++best->liveJobs;
756
+ pthread_mutex_unlock(&best->mutex);
757
+ }
758
+
759
+ /**
760
+ * Called when a thread finishes executing, both on error or success.
761
+ * Decrements liveJobs and signals any waiting threads if liveJobs == 0.
762
+ * If this dictionary is the best so far save it and its parameters.
763
+ */
764
+ static void COVER_best_finish(COVER_best_t *best, size_t compressedSize,
765
+ COVER_params_t parameters, void *dict,
766
+ size_t dictSize) {
767
+ if (!best) {
768
+ return;
769
+ }
770
+ {
771
+ size_t liveJobs;
772
+ pthread_mutex_lock(&best->mutex);
773
+ --best->liveJobs;
774
+ liveJobs = best->liveJobs;
775
+ /* If the new dictionary is better */
776
+ if (compressedSize < best->compressedSize) {
777
+ /* Allocate space if necessary */
778
+ if (!best->dict || best->dictSize < dictSize) {
779
+ if (best->dict) {
780
+ free(best->dict);
781
+ }
782
+ best->dict = malloc(dictSize);
783
+ if (!best->dict) {
784
+ best->compressedSize = ERROR(GENERIC);
785
+ best->dictSize = 0;
786
+ return;
787
+ }
788
+ }
789
+ /* Save the dictionary, parameters, and size */
790
+ memcpy(best->dict, dict, dictSize);
791
+ best->dictSize = dictSize;
792
+ best->parameters = parameters;
793
+ best->compressedSize = compressedSize;
794
+ }
795
+ pthread_mutex_unlock(&best->mutex);
796
+ if (liveJobs == 0) {
797
+ pthread_cond_broadcast(&best->cond);
798
+ }
799
+ }
800
+ }
801
+
802
+ /**
803
+ * Parameters for COVER_tryParameters().
804
+ */
805
+ typedef struct COVER_tryParameters_data_s {
806
+ const COVER_ctx_t *ctx;
807
+ COVER_best_t *best;
808
+ size_t dictBufferCapacity;
809
+ COVER_params_t parameters;
810
+ } COVER_tryParameters_data_t;
811
+
812
+ /**
813
+ * Tries a set of parameters and upates the COVER_best_t with the results.
814
+ * This function is thread safe if zstd is compiled with multithreaded support.
815
+ * It takes its parameters as an *OWNING* opaque pointer to support threading.
816
+ */
817
+ static void COVER_tryParameters(void *opaque) {
818
+ /* Save parameters as local variables */
819
+ COVER_tryParameters_data_t *const data = (COVER_tryParameters_data_t *)opaque;
820
+ const COVER_ctx_t *const ctx = data->ctx;
821
+ const COVER_params_t parameters = data->parameters;
822
+ size_t dictBufferCapacity = data->dictBufferCapacity;
823
+ size_t totalCompressedSize = ERROR(GENERIC);
824
+ /* Allocate space for hash table, dict, and freqs */
825
+ COVER_map_t activeDmers;
826
+ BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity);
827
+ U32 *freqs = (U32 *)malloc(ctx->suffixSize * sizeof(U32));
828
+ if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) {
829
+ DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n");
830
+ goto _cleanup;
831
+ }
832
+ if (!dict || !freqs) {
833
+ DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n");
834
+ goto _cleanup;
835
+ }
836
+ /* Copy the frequencies because we need to modify them */
837
+ memcpy(freqs, ctx->freqs, ctx->suffixSize * sizeof(U32));
838
+ /* Build the dictionary */
839
+ {
840
+ const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict,
841
+ dictBufferCapacity, parameters);
842
+ const ZDICT_params_t zdictParams = COVER_translateParams(parameters);
843
+ dictBufferCapacity = ZDICT_finalizeDictionary(
844
+ dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,
845
+ ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbSamples, zdictParams);
846
+ if (ZDICT_isError(dictBufferCapacity)) {
847
+ DISPLAYLEVEL(1, "Failed to finalize dictionary\n");
848
+ goto _cleanup;
849
+ }
850
+ }
851
+ /* Check total compressed size */
852
+ {
853
+ /* Pointers */
854
+ ZSTD_CCtx *cctx;
855
+ ZSTD_CDict *cdict;
856
+ void *dst;
857
+ /* Local variables */
858
+ size_t dstCapacity;
859
+ size_t i;
860
+ /* Allocate dst with enough space to compress the maximum sized sample */
861
+ {
862
+ size_t maxSampleSize = 0;
863
+ for (i = 0; i < ctx->nbSamples; ++i) {
864
+ maxSampleSize = MAX(ctx->samplesSizes[i], maxSampleSize);
865
+ }
866
+ dstCapacity = ZSTD_compressBound(maxSampleSize);
867
+ dst = malloc(dstCapacity);
868
+ }
869
+ /* Create the cctx and cdict */
870
+ cctx = ZSTD_createCCtx();
871
+ cdict =
872
+ ZSTD_createCDict(dict, dictBufferCapacity, parameters.compressionLevel);
873
+ if (!dst || !cctx || !cdict) {
874
+ goto _compressCleanup;
875
+ }
876
+ /* Compress each sample and sum their sizes (or error) */
877
+ totalCompressedSize = 0;
878
+ for (i = 0; i < ctx->nbSamples; ++i) {
879
+ const size_t size = ZSTD_compress_usingCDict(
880
+ cctx, dst, dstCapacity, ctx->samples + ctx->offsets[i],
881
+ ctx->samplesSizes[i], cdict);
882
+ if (ZSTD_isError(size)) {
883
+ totalCompressedSize = ERROR(GENERIC);
884
+ goto _compressCleanup;
885
+ }
886
+ totalCompressedSize += size;
887
+ }
888
+ _compressCleanup:
889
+ ZSTD_freeCCtx(cctx);
890
+ ZSTD_freeCDict(cdict);
891
+ if (dst) {
892
+ free(dst);
893
+ }
894
+ }
895
+
896
+ _cleanup:
897
+ COVER_best_finish(data->best, totalCompressedSize, parameters, dict,
898
+ dictBufferCapacity);
899
+ free(data);
900
+ COVER_map_destroy(&activeDmers);
901
+ if (dict) {
902
+ free(dict);
903
+ }
904
+ if (freqs) {
905
+ free(freqs);
906
+ }
907
+ }
908
+
909
+ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer,
910
+ size_t dictBufferCapacity,
911
+ const void *samplesBuffer,
912
+ const size_t *samplesSizes,
913
+ unsigned nbSamples,
914
+ COVER_params_t *parameters) {
915
+ /* constants */
916
+ const unsigned nbThreads = parameters->nbThreads;
917
+ const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d;
918
+ const unsigned kMaxD = parameters->d == 0 ? 16 : parameters->d;
919
+ const unsigned kMinK = parameters->k == 0 ? kMaxD : parameters->k;
920
+ const unsigned kMaxK = parameters->k == 0 ? 2048 : parameters->k;
921
+ const unsigned kSteps = parameters->steps == 0 ? 32 : parameters->steps;
922
+ const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1);
923
+ const unsigned kIterations =
924
+ (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize);
925
+ /* Local variables */
926
+ const int displayLevel = parameters->notificationLevel;
927
+ unsigned iteration = 1;
928
+ unsigned d;
929
+ unsigned k;
930
+ COVER_best_t best;
931
+ POOL_ctx *pool = NULL;
932
+ /* Checks */
933
+ if (kMinK < kMaxD || kMaxK < kMinK) {
934
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n");
935
+ return ERROR(GENERIC);
936
+ }
937
+ if (nbSamples == 0) {
938
+ DISPLAYLEVEL(1, "Cover must have at least one input file\n");
939
+ return ERROR(GENERIC);
940
+ }
941
+ if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
942
+ DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",
943
+ ZDICT_DICTSIZE_MIN);
944
+ return ERROR(dstSize_tooSmall);
945
+ }
946
+ if (nbThreads > 1) {
947
+ pool = POOL_create(nbThreads, 1);
948
+ if (!pool) {
949
+ return ERROR(memory_allocation);
950
+ }
951
+ }
952
+ /* Initialization */
953
+ COVER_best_init(&best);
954
+ /* Turn down global display level to clean up display at level 2 and below */
955
+ g_displayLevel = parameters->notificationLevel - 1;
956
+ /* Loop through d first because each new value needs a new context */
957
+ LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n",
958
+ kIterations);
959
+ for (d = kMinD; d <= kMaxD; d += 2) {
960
+ /* Initialize the context for this value of d */
961
+ COVER_ctx_t ctx;
962
+ LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d);
963
+ if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d)) {
964
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n");
965
+ COVER_best_destroy(&best);
966
+ return ERROR(GENERIC);
967
+ }
968
+ /* Loop through k reusing the same context */
969
+ for (k = kMinK; k <= kMaxK; k += kStepSize) {
970
+ /* Prepare the arguments */
971
+ COVER_tryParameters_data_t *data = (COVER_tryParameters_data_t *)malloc(
972
+ sizeof(COVER_tryParameters_data_t));
973
+ LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k);
974
+ if (!data) {
975
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n");
976
+ COVER_best_destroy(&best);
977
+ COVER_ctx_destroy(&ctx);
978
+ return ERROR(GENERIC);
979
+ }
980
+ data->ctx = &ctx;
981
+ data->best = &best;
982
+ data->dictBufferCapacity = dictBufferCapacity;
983
+ data->parameters = *parameters;
984
+ data->parameters.k = k;
985
+ data->parameters.d = d;
986
+ data->parameters.steps = kSteps;
987
+ /* Check the parameters */
988
+ if (!COVER_checkParameters(data->parameters)) {
989
+ DISPLAYLEVEL(1, "Cover parameters incorrect\n");
990
+ continue;
991
+ }
992
+ /* Call the function and pass ownership of data to it */
993
+ COVER_best_start(&best);
994
+ if (pool) {
995
+ POOL_add(pool, &COVER_tryParameters, data);
996
+ } else {
997
+ COVER_tryParameters(data);
998
+ }
999
+ /* Print status */
1000
+ LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ",
1001
+ (U32)((iteration * 100) / kIterations));
1002
+ ++iteration;
1003
+ }
1004
+ COVER_best_wait(&best);
1005
+ COVER_ctx_destroy(&ctx);
1006
+ }
1007
+ LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", "");
1008
+ /* Fill the output buffer and parameters with output of the best parameters */
1009
+ {
1010
+ const size_t dictSize = best.dictSize;
1011
+ if (ZSTD_isError(best.compressedSize)) {
1012
+ COVER_best_destroy(&best);
1013
+ return best.compressedSize;
1014
+ }
1015
+ *parameters = best.parameters;
1016
+ memcpy(dictBuffer, best.dict, dictSize);
1017
+ COVER_best_destroy(&best);
1018
+ POOL_free(pool);
1019
+ return dictSize;
1020
+ }
1021
+ }