1 /***********************************************************************************************************************************
2 Compression Helper
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5 
6 #include <string.h>
7 
8 #include "common/compress/helper.h"
9 #include "common/compress/bz2/common.h"
10 #include "common/compress/bz2/compress.h"
11 #include "common/compress/bz2/decompress.h"
12 #include "common/compress/gz/common.h"
13 #include "common/compress/gz/compress.h"
14 #include "common/compress/gz/decompress.h"
15 #include "common/compress/lz4/common.h"
16 #include "common/compress/lz4/compress.h"
17 #include "common/compress/lz4/decompress.h"
18 #include "common/compress/zst/common.h"
19 #include "common/compress/zst/compress.h"
20 #include "common/compress/zst/decompress.h"
21 #include "common/debug.h"
22 #include "common/log.h"
23 #include "version.h"
24 
25 /***********************************************************************************************************************************
26 Compression type constants
27 ***********************************************************************************************************************************/
28 #define COMPRESS_TYPE_NONE                                          "none"
29 
30 // Constants for currently unsupported compression types
31 #define XZ_EXT                                                      "xz"
32 
33 /***********************************************************************************************************************************
34 Configuration for supported and future compression types
35 ***********************************************************************************************************************************/
36 static const struct CompressHelperLocal
37 {
38     const String *const type;                                       // Compress type -- must be extension without period prefixed
39     const String *const ext;                                        // File extension with period prefixed
40     const char *compressType;                                       // Type of the compression filter
41     IoFilter *(*compressNew)(int);                                  // Function to create new compression filter
42     const char *decompressType;                                     // Type of the decompression filter
43     IoFilter *(*decompressNew)(void);                               // Function to create new decompression filter
44     int levelDefault;                                               // Default compression level
45 } compressHelperLocal[] =
46 {
47     {
48         .type = STRDEF(COMPRESS_TYPE_NONE),
49         .ext = STRDEF(""),
50     },
51     {
52         .type = STRDEF(BZ2_EXT),
53         .ext = STRDEF("." BZ2_EXT),
54         .compressType = BZ2_COMPRESS_FILTER_TYPE,
55         .compressNew = bz2CompressNew,
56         .decompressType = BZ2_DECOMPRESS_FILTER_TYPE,
57         .decompressNew = bz2DecompressNew,
58         .levelDefault = 9,
59     },
60     {
61         .type = STRDEF(GZ_EXT),
62         .ext = STRDEF("." GZ_EXT),
63         .compressType = GZ_COMPRESS_FILTER_TYPE,
64         .compressNew = gzCompressNew,
65         .decompressType = GZ_DECOMPRESS_FILTER_TYPE,
66         .decompressNew = gzDecompressNew,
67         .levelDefault = 6,
68     },
69     {
70         .type = STRDEF(LZ4_EXT),
71         .ext = STRDEF("." LZ4_EXT),
72 #ifdef HAVE_LIBLZ4
73         .compressType = LZ4_COMPRESS_FILTER_TYPE,
74         .compressNew = lz4CompressNew,
75         .decompressType = LZ4_DECOMPRESS_FILTER_TYPE,
76         .decompressNew = lz4DecompressNew,
77         .levelDefault = 1,
78 #endif
79     },
80     {
81         .type = STRDEF(ZST_EXT),
82         .ext = STRDEF("." ZST_EXT),
83 #ifdef HAVE_LIBZST
84         .compressType = ZST_COMPRESS_FILTER_TYPE,
85         .compressNew = zstCompressNew,
86         .decompressType = ZST_DECOMPRESS_FILTER_TYPE,
87         .decompressNew = zstDecompressNew,
88         .levelDefault = 3,
89 #endif
90     },
91     {
92         .type = STRDEF(XZ_EXT),
93         .ext = STRDEF("." XZ_EXT),
94     },
95     {
96         .type = STRDEF(BZ2_EXT),
97         .ext = STRDEF("." BZ2_EXT),
98     },
99 };
100 
101 #define COMPRESS_LIST_SIZE                                                                                                         \
102     (sizeof(compressHelperLocal) / sizeof(struct CompressHelperLocal))
103 
104 /**********************************************************************************************************************************/
105 CompressType
compressTypeEnum(const String * type)106 compressTypeEnum(const String *type)
107 {
108     FUNCTION_TEST_BEGIN();
109         FUNCTION_TEST_PARAM(STRING, type);
110     FUNCTION_TEST_END();
111 
112     ASSERT(type != NULL);
113 
114     CompressType result = compressTypeNone;
115 
116     for (; result < COMPRESS_LIST_SIZE; result++)
117     {
118         if (strEq(type, compressHelperLocal[result].type))
119             break;
120     }
121 
122     if (result == COMPRESS_LIST_SIZE)
123         THROW_FMT(AssertError, "invalid compression type '%s'", strZ(type));
124 
125     FUNCTION_TEST_RETURN(result);
126 }
127 
128 /**********************************************************************************************************************************/
129 void
compressTypePresent(CompressType type)130 compressTypePresent(CompressType type)
131 {
132     FUNCTION_TEST_BEGIN();
133         FUNCTION_TEST_PARAM(ENUM, type);
134     FUNCTION_TEST_END();
135 
136     ASSERT(type < COMPRESS_LIST_SIZE);
137 
138     if (type != compressTypeNone && compressHelperLocal[type].compressNew == NULL)
139         THROW_FMT(OptionInvalidValueError, PROJECT_NAME " not compiled with %s support", strZ(compressHelperLocal[type].type));
140 
141     FUNCTION_TEST_RETURN_VOID();
142 }
143 
144 /**********************************************************************************************************************************/
145 const String *
compressTypeStr(CompressType type)146 compressTypeStr(CompressType type)
147 {
148     FUNCTION_TEST_BEGIN();
149         FUNCTION_TEST_PARAM(ENUM, type);
150     FUNCTION_TEST_END();
151 
152     ASSERT(type < COMPRESS_LIST_SIZE);
153 
154     FUNCTION_TEST_RETURN(compressHelperLocal[type].type);
155 }
156 
157 /**********************************************************************************************************************************/
158 CompressType
compressTypeFromName(const String * name)159 compressTypeFromName(const String *name)
160 {
161     FUNCTION_TEST_BEGIN();
162         FUNCTION_TEST_PARAM(STRING, name);
163     FUNCTION_TEST_END();
164 
165     CompressType result = compressTypeNone + 1;
166 
167     for (; result < COMPRESS_LIST_SIZE; result++)
168     {
169         if (strEndsWith(name, compressHelperLocal[result].ext))
170             break;
171     }
172 
173     if (result == COMPRESS_LIST_SIZE)
174         result = compressTypeNone;
175 
176     FUNCTION_TEST_RETURN(result);
177 }
178 
179 /**********************************************************************************************************************************/
180 int
compressLevelDefault(CompressType type)181 compressLevelDefault(CompressType type)
182 {
183     FUNCTION_TEST_BEGIN();
184         FUNCTION_TEST_PARAM(ENUM, type);
185     FUNCTION_TEST_END();
186 
187     ASSERT(type < COMPRESS_LIST_SIZE);
188     compressTypePresent(type);
189 
190     FUNCTION_TEST_RETURN(compressHelperLocal[type].levelDefault);
191 }
192 
193 /**********************************************************************************************************************************/
194 IoFilter *
compressFilter(CompressType type,int level)195 compressFilter(CompressType type, int level)
196 {
197     FUNCTION_TEST_BEGIN();
198         FUNCTION_TEST_PARAM(ENUM, type);
199         FUNCTION_TEST_PARAM(INT, level);
200     FUNCTION_TEST_END();
201 
202     ASSERT(type < COMPRESS_LIST_SIZE);
203     ASSERT(type != compressTypeNone);
204     compressTypePresent(type);
205 
206     FUNCTION_TEST_RETURN(compressHelperLocal[type].compressNew(level));
207 }
208 
209 /**********************************************************************************************************************************/
210 IoFilter *
compressFilterVar(const String * filterType,const VariantList * filterParamList)211 compressFilterVar(const String *filterType, const VariantList *filterParamList)
212 {
213     FUNCTION_LOG_BEGIN(logLevelTrace);
214         FUNCTION_LOG_PARAM(STRING, filterType);
215         FUNCTION_LOG_PARAM(VARIANT_LIST, filterParamList);
216     FUNCTION_LOG_END();
217 
218     ASSERT(filterType != NULL);
219 
220     IoFilter *result = NULL;
221 
222     for (CompressType compressIdx = compressTypeNone + 1; compressIdx < COMPRESS_LIST_SIZE; compressIdx++)
223     {
224         const struct CompressHelperLocal *compress = &compressHelperLocal[compressIdx];
225 
226         if (compress->compressType != NULL && strEqZ(filterType, compress->compressType))
227         {
228             result = compress->compressNew(varIntForce(varLstGet(filterParamList, 0)));
229             break;
230         }
231         else if (compress->decompressType != NULL && strEqZ(filterType, compress->decompressType))
232         {
233             result = compress->decompressNew();
234             break;
235         }
236     }
237 
238     FUNCTION_LOG_RETURN(IO_FILTER, result);
239 }
240 
241 /**********************************************************************************************************************************/
242 IoFilter *
decompressFilter(CompressType type)243 decompressFilter(CompressType type)
244 {
245     FUNCTION_TEST_BEGIN();
246         FUNCTION_TEST_PARAM(ENUM, type);
247     FUNCTION_TEST_END();
248 
249     ASSERT(type < COMPRESS_LIST_SIZE);
250     ASSERT(type != compressTypeNone);
251     compressTypePresent(type);
252 
253     FUNCTION_TEST_RETURN(compressHelperLocal[type].decompressNew());
254 }
255 
256 /**********************************************************************************************************************************/
257 const String *
compressExtStr(CompressType type)258 compressExtStr(CompressType type)
259 {
260     FUNCTION_TEST_BEGIN();
261         FUNCTION_TEST_PARAM(ENUM, type);
262     FUNCTION_TEST_END();
263 
264     ASSERT(type < COMPRESS_LIST_SIZE);
265 
266     FUNCTION_TEST_RETURN(compressHelperLocal[type].ext);
267 }
268 
269 /**********************************************************************************************************************************/
270 void
compressExtCat(String * file,CompressType type)271 compressExtCat(String *file, CompressType type)
272 {
273     FUNCTION_TEST_BEGIN();
274         FUNCTION_TEST_PARAM(STRING, file);
275         FUNCTION_TEST_PARAM(ENUM, type);
276     FUNCTION_TEST_END();
277 
278     ASSERT(file != NULL);
279 
280     strCat(file, compressExtStr(type));
281 
282     FUNCTION_TEST_RETURN_VOID();
283 }
284 
285 /**********************************************************************************************************************************/
286 String *
compressExtStrip(const String * file,CompressType type)287 compressExtStrip(const String *file, CompressType type)
288 {
289     FUNCTION_TEST_BEGIN();
290         FUNCTION_TEST_PARAM(STRING, file);
291         FUNCTION_TEST_PARAM(ENUM, type);
292     FUNCTION_TEST_END();
293 
294     ASSERT(file != NULL);
295 
296     if (!strEndsWith(file, compressExtStr(type)))
297         THROW_FMT(FormatError, "'%s' must have '%s' extension", strZ(file), strZ(compressExtStr(type)));
298 
299     FUNCTION_TEST_RETURN(strSubN(file, 0, strSize(file) - strSize(compressExtStr(type))));
300 }
301