Line data Source code
1 : /**********************************************************************
2 : * Project: CPL - Common Portability Library
3 : * Purpose: Registry of compression/decompression functions
4 : * Author: Even Rouault <even.rouault at spatialys.com>
5 : *
6 : **********************************************************************
7 : * Copyright (c) 2021, Even Rouault <even.rouault at spatialys.com>
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include "cpl_compressor.h"
13 : #include "cpl_error.h"
14 : #include "cpl_multiproc.h"
15 : #include "cpl_string.h"
16 : #include "cpl_conv.h" // CPLZLibInflate()
17 :
18 : #if defined(__clang__)
19 : #pragma clang diagnostic push
20 : #pragma clang diagnostic ignored "-Wdocumentation"
21 : #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
22 : #endif
23 :
24 : #ifdef HAVE_BLOSC
25 : #include <blosc.h>
26 : #endif
27 :
28 : #ifdef HAVE_LIBDEFLATE
29 : #include "libdeflate.h"
30 : #else
31 : #include "cpl_zlib_header.h" // to avoid warnings when including zlib.h
32 : #endif
33 :
34 : #ifdef HAVE_LZMA
35 : #include <lzma.h>
36 : #endif
37 :
38 : #ifdef HAVE_ZSTD
39 : #include <zstd.h>
40 : #endif
41 :
42 : #ifdef HAVE_LZ4
43 : #include <lz4.h>
44 : #endif
45 :
46 : #if defined(__clang__)
47 : #pragma clang diagnostic pop
48 : #endif
49 :
50 : #include <limits>
51 : #include <mutex>
52 : #include <type_traits>
53 : #include <vector>
54 :
55 : static std::mutex gMutex;
56 : static std::vector<CPLCompressor *> *gpCompressors = nullptr;
57 : static std::vector<CPLCompressor *> *gpDecompressors = nullptr;
58 :
59 : #ifdef HAVE_BLOSC
60 7 : static bool CPLBloscCompressor(const void *input_data, size_t input_size,
61 : void **output_data, size_t *output_size,
62 : CSLConstList options,
63 : void * /* compressor_user_data */)
64 : {
65 7 : if (output_data != nullptr && *output_data != nullptr &&
66 5 : output_size != nullptr && *output_size != 0)
67 : {
68 5 : const int clevel = atoi(CSLFetchNameValueDef(options, "CLEVEL", "5"));
69 : const char *pszShuffle =
70 5 : CSLFetchNameValueDef(options, "SHUFFLE", "BYTE");
71 5 : const int shuffle =
72 1 : (EQUAL(pszShuffle, "BYTE") || EQUAL(pszShuffle, "1"))
73 6 : ? BLOSC_SHUFFLE
74 1 : : (EQUAL(pszShuffle, "BIT") || EQUAL(pszShuffle, "2"))
75 2 : ? BLOSC_BITSHUFFLE
76 : : BLOSC_NOSHUFFLE;
77 : const int typesize =
78 5 : atoi(CSLFetchNameValueDef(options, "TYPESIZE", "1"));
79 : const char *compressor =
80 5 : CSLFetchNameValueDef(options, "CNAME", BLOSC_LZ4_COMPNAME);
81 : const int blocksize =
82 5 : atoi(CSLFetchNameValueDef(options, "BLOCKSIZE", "0"));
83 5 : if (blocksize < 0)
84 : {
85 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid BLOCKSIZE");
86 0 : return false;
87 : }
88 : const char *pszNumThreads =
89 5 : CSLFetchNameValueDef(options, "NUM_THREADS", "1");
90 5 : const int numthreads = EQUAL(pszNumThreads, "ALL_CPUS")
91 5 : ? CPLGetNumCPUs()
92 5 : : atoi(pszNumThreads);
93 5 : int ret = blosc_compress_ctx(clevel, shuffle, typesize, input_size,
94 : input_data, *output_data, *output_size,
95 : compressor, blocksize, numthreads);
96 5 : if (ret < 0)
97 : {
98 1 : *output_size = 0;
99 1 : return false;
100 : }
101 4 : if (ret == 0)
102 : {
103 0 : *output_size = input_size + BLOSC_MAX_OVERHEAD;
104 0 : return false;
105 : }
106 4 : *output_size = ret;
107 4 : return true;
108 : }
109 :
110 2 : if (output_data == nullptr && output_size != nullptr)
111 : {
112 1 : *output_size = input_size + BLOSC_MAX_OVERHEAD;
113 1 : return true;
114 : }
115 :
116 1 : if (output_data != nullptr && *output_data == nullptr &&
117 : output_size != nullptr)
118 : {
119 1 : size_t nSafeSize = input_size + BLOSC_MAX_OVERHEAD;
120 1 : *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
121 1 : *output_size = nSafeSize;
122 1 : if (*output_data == nullptr)
123 0 : return false;
124 1 : bool ret = CPLBloscCompressor(input_data, input_size, output_data,
125 : output_size, options, nullptr);
126 1 : if (!ret)
127 : {
128 0 : VSIFree(*output_data);
129 0 : *output_data = nullptr;
130 : }
131 1 : return ret;
132 : }
133 :
134 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
135 0 : return false;
136 : }
137 :
138 7 : static bool CPLBloscDecompressor(const void *input_data, size_t input_size,
139 : void **output_data, size_t *output_size,
140 : CSLConstList options,
141 : void * /* compressor_user_data */)
142 : {
143 7 : size_t nSafeSize = 0;
144 7 : if (blosc_cbuffer_validate(input_data, input_size, &nSafeSize) < 0)
145 : {
146 0 : *output_size = 0;
147 0 : return false;
148 : }
149 :
150 7 : if (output_data != nullptr && *output_data != nullptr &&
151 5 : output_size != nullptr && *output_size != 0)
152 : {
153 5 : if (*output_size < nSafeSize)
154 : {
155 0 : *output_size = nSafeSize;
156 0 : return false;
157 : }
158 :
159 : const char *pszNumThreads =
160 5 : CSLFetchNameValueDef(options, "NUM_THREADS", "1");
161 5 : const int numthreads = EQUAL(pszNumThreads, "ALL_CPUS")
162 5 : ? CPLGetNumCPUs()
163 5 : : atoi(pszNumThreads);
164 5 : if (blosc_decompress_ctx(input_data, *output_data, *output_size,
165 5 : numthreads) <= 0)
166 : {
167 0 : *output_size = 0;
168 0 : return false;
169 : }
170 :
171 5 : *output_size = nSafeSize;
172 5 : return true;
173 : }
174 :
175 2 : if (output_data == nullptr && output_size != nullptr)
176 : {
177 1 : *output_size = nSafeSize;
178 1 : return true;
179 : }
180 :
181 1 : if (output_data != nullptr && *output_data == nullptr &&
182 : output_size != nullptr)
183 : {
184 1 : *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
185 1 : *output_size = nSafeSize;
186 1 : if (*output_data == nullptr)
187 0 : return false;
188 1 : bool ret = CPLBloscDecompressor(input_data, input_size, output_data,
189 : output_size, options, nullptr);
190 1 : if (!ret)
191 : {
192 0 : VSIFree(*output_data);
193 0 : *output_data = nullptr;
194 : }
195 1 : return ret;
196 : }
197 :
198 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
199 0 : return false;
200 : }
201 :
202 : #endif
203 :
204 : #ifdef HAVE_LZMA
205 5 : static bool CPLLZMACompressor(const void *input_data, size_t input_size,
206 : void **output_data, size_t *output_size,
207 : CSLConstList options,
208 : void * /* compressor_user_data */)
209 : {
210 5 : if (output_data != nullptr && *output_data != nullptr &&
211 3 : output_size != nullptr && *output_size != 0)
212 : {
213 3 : const int preset = atoi(CSLFetchNameValueDef(options, "PRESET", "6"));
214 3 : const int delta = atoi(CSLFetchNameValueDef(options, "DELTA", "1"));
215 :
216 : lzma_filter filters[3];
217 : lzma_options_delta opt_delta;
218 : lzma_options_lzma opt_lzma;
219 :
220 3 : opt_delta.type = LZMA_DELTA_TYPE_BYTE;
221 3 : opt_delta.dist = delta;
222 3 : filters[0].id = LZMA_FILTER_DELTA;
223 3 : filters[0].options = &opt_delta;
224 :
225 3 : lzma_lzma_preset(&opt_lzma, preset);
226 3 : filters[1].id = LZMA_FILTER_LZMA2;
227 3 : filters[1].options = &opt_lzma;
228 :
229 3 : filters[2].id = LZMA_VLI_UNKNOWN;
230 3 : filters[2].options = nullptr;
231 :
232 3 : size_t out_pos = 0;
233 3 : lzma_ret ret = lzma_stream_buffer_encode(
234 : filters, LZMA_CHECK_NONE,
235 : nullptr, // allocator,
236 : static_cast<const uint8_t *>(input_data), input_size,
237 : static_cast<uint8_t *>(*output_data), &out_pos, *output_size);
238 3 : if (ret != LZMA_OK)
239 : {
240 1 : *output_size = 0;
241 1 : return false;
242 : }
243 2 : *output_size = out_pos;
244 2 : return true;
245 : }
246 :
247 2 : if (output_data == nullptr && output_size != nullptr)
248 : {
249 1 : *output_size = lzma_stream_buffer_bound(input_size);
250 1 : return true;
251 : }
252 :
253 1 : if (output_data != nullptr && *output_data == nullptr &&
254 : output_size != nullptr)
255 : {
256 1 : size_t nSafeSize = lzma_stream_buffer_bound(input_size);
257 1 : *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
258 1 : *output_size = nSafeSize;
259 1 : if (*output_data == nullptr)
260 0 : return false;
261 1 : bool ret = CPLLZMACompressor(input_data, input_size, output_data,
262 : output_size, options, nullptr);
263 1 : if (!ret)
264 : {
265 0 : VSIFree(*output_data);
266 0 : *output_data = nullptr;
267 : }
268 1 : return ret;
269 : }
270 :
271 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
272 0 : return false;
273 : }
274 :
275 166 : static bool CPLLZMADecompressor(const void *input_data, size_t input_size,
276 : void **output_data, size_t *output_size,
277 : CSLConstList options,
278 : void * /* compressor_user_data */)
279 : {
280 166 : if (output_data != nullptr && *output_data != nullptr &&
281 163 : output_size != nullptr && *output_size != 0)
282 : {
283 163 : size_t in_pos = 0;
284 163 : size_t out_pos = 0;
285 163 : uint64_t memlimit = 100 * 1024 * 1024;
286 163 : lzma_ret ret = lzma_stream_buffer_decode(
287 : &memlimit,
288 : 0, // flags
289 : nullptr, // allocator,
290 : static_cast<const uint8_t *>(input_data), &in_pos, input_size,
291 : static_cast<uint8_t *>(*output_data), &out_pos, *output_size);
292 163 : if (ret != LZMA_OK)
293 : {
294 0 : *output_size = 0;
295 0 : return false;
296 : }
297 163 : *output_size = out_pos;
298 163 : return true;
299 : }
300 :
301 3 : if (output_data == nullptr && output_size != nullptr)
302 : {
303 : // inefficient !
304 1 : void *tmpBuffer = nullptr;
305 1 : bool ret = CPLLZMADecompressor(input_data, input_size, &tmpBuffer,
306 : output_size, options, nullptr);
307 1 : VSIFree(tmpBuffer);
308 1 : return ret;
309 : }
310 :
311 2 : if (output_data != nullptr && *output_data == nullptr &&
312 : output_size != nullptr)
313 : {
314 2 : size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 2
315 2 : ? input_size * 2
316 2 : : input_size;
317 2 : *output_data = VSI_MALLOC_VERBOSE(nOutSize);
318 2 : if (*output_data == nullptr)
319 : {
320 0 : *output_size = 0;
321 0 : return false;
322 : }
323 :
324 : while (true)
325 : {
326 2 : size_t in_pos = 0;
327 2 : size_t out_pos = 0;
328 2 : uint64_t memlimit = 100 * 1024 * 1024;
329 2 : lzma_ret ret = lzma_stream_buffer_decode(
330 : &memlimit,
331 : 0, // flags
332 : nullptr, // allocator,
333 : static_cast<const uint8_t *>(input_data), &in_pos, input_size,
334 : static_cast<uint8_t *>(*output_data), &out_pos, nOutSize);
335 2 : if (ret == LZMA_OK)
336 : {
337 2 : *output_size = out_pos;
338 2 : return true;
339 : }
340 0 : else if (ret == LZMA_BUF_ERROR &&
341 0 : nOutSize < std::numeric_limits<size_t>::max() / 2)
342 : {
343 0 : nOutSize *= 2;
344 0 : void *tmpBuffer = VSI_REALLOC_VERBOSE(*output_data, nOutSize);
345 0 : if (tmpBuffer == nullptr)
346 : {
347 0 : VSIFree(*output_data);
348 0 : *output_data = nullptr;
349 0 : *output_size = 0;
350 0 : return false;
351 : }
352 0 : *output_data = tmpBuffer;
353 : }
354 : else
355 : {
356 0 : VSIFree(*output_data);
357 0 : *output_data = nullptr;
358 0 : *output_size = 0;
359 0 : return false;
360 : }
361 0 : }
362 : }
363 :
364 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
365 0 : return false;
366 : }
367 :
368 : #endif // HAVE_LZMA
369 :
370 : #ifdef HAVE_ZSTD
371 59 : static bool CPLZSTDCompressor(const void *input_data, size_t input_size,
372 : void **output_data, size_t *output_size,
373 : CSLConstList options,
374 : void * /* compressor_user_data */)
375 : {
376 59 : if (output_data != nullptr && *output_data != nullptr &&
377 57 : output_size != nullptr && *output_size != 0)
378 : {
379 57 : ZSTD_CCtx *ctx = ZSTD_createCCtx();
380 57 : if (ctx == nullptr)
381 : {
382 0 : *output_size = 0;
383 0 : return false;
384 : }
385 :
386 57 : const int level = atoi(CSLFetchNameValueDef(options, "LEVEL", "13"));
387 57 : if (ZSTD_isError(
388 57 : ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, level)))
389 : {
390 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid compression level");
391 0 : ZSTD_freeCCtx(ctx);
392 0 : *output_size = 0;
393 0 : return false;
394 : }
395 :
396 57 : if (CPLTestBool(CSLFetchNameValueDef(options, "CHECKSUM", "NO")))
397 : {
398 1 : CPL_IGNORE_RET_VAL(
399 1 : ZSTD_CCtx_setParameter(ctx, ZSTD_c_checksumFlag, 1));
400 : }
401 :
402 57 : size_t ret = ZSTD_compress2(ctx, *output_data, *output_size, input_data,
403 : input_size);
404 57 : ZSTD_freeCCtx(ctx);
405 57 : if (ZSTD_isError(ret))
406 : {
407 1 : *output_size = 0;
408 1 : return false;
409 : }
410 :
411 56 : *output_size = ret;
412 56 : return true;
413 : }
414 :
415 2 : if (output_data == nullptr && output_size != nullptr)
416 : {
417 1 : *output_size = ZSTD_compressBound(input_size);
418 1 : return true;
419 : }
420 :
421 1 : if (output_data != nullptr && *output_data == nullptr &&
422 : output_size != nullptr)
423 : {
424 1 : size_t nSafeSize = ZSTD_compressBound(input_size);
425 1 : *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
426 1 : *output_size = nSafeSize;
427 1 : if (*output_data == nullptr)
428 0 : return false;
429 1 : bool ret = CPLZSTDCompressor(input_data, input_size, output_data,
430 : output_size, options, nullptr);
431 1 : if (!ret)
432 : {
433 0 : VSIFree(*output_data);
434 0 : *output_data = nullptr;
435 : }
436 1 : return ret;
437 : }
438 :
439 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
440 0 : return false;
441 : }
442 :
443 : // CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW because ZSTD_CONTENTSIZE_ERROR expands
444 : // to (0ULL - 2)...
445 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
446 716 : static size_t CPLZSTDGetDecompressedSize(const void *input_data,
447 : size_t input_size)
448 : {
449 : #if (ZSTD_VERSION_MAJOR > 1) || \
450 : (ZSTD_VERSION_MAJOR == 1 && ZSTD_VERSION_MINOR >= 3)
451 716 : uint64_t nRet = ZSTD_getFrameContentSize(input_data, input_size);
452 716 : if (nRet == ZSTD_CONTENTSIZE_ERROR)
453 : {
454 55 : CPLError(CE_Failure, CPLE_AppDefined,
455 : "Error while retrieving decompressed size of ZSTD frame.");
456 55 : nRet = 0;
457 : }
458 661 : else if (nRet == ZSTD_CONTENTSIZE_UNKNOWN)
459 : {
460 1 : CPLError(CE_Failure, CPLE_AppDefined,
461 : "Decompressed size of ZSTD frame is unknown.");
462 1 : nRet = 0;
463 : }
464 : #else
465 : uint64_t nRet = ZSTD_getDecompressedSize(input_data, input_size);
466 : if (nRet == 0)
467 : {
468 : CPLError(CE_Failure, CPLE_AppDefined,
469 : "Decompressed size of ZSTD frame is unknown.");
470 : }
471 : #endif
472 :
473 : #if SIZEOF_VOIDP == 4
474 : if (nRet > std::numeric_limits<size_t>::max())
475 : {
476 : CPLError(CE_Failure, CPLE_AppDefined,
477 : "Decompressed size of ZSTD frame is bigger than 4GB.");
478 : nRet = 0;
479 : }
480 : #endif
481 :
482 716 : return static_cast<size_t>(nRet);
483 : }
484 :
485 12638 : static bool CPLZSTDDecompressor(const void *input_data, size_t input_size,
486 : void **output_data, size_t *output_size,
487 : CSLConstList /* options */,
488 : void * /* compressor_user_data */)
489 : {
490 12638 : if (output_data != nullptr && *output_data != nullptr &&
491 12636 : output_size != nullptr && *output_size != 0)
492 : {
493 : size_t ret =
494 12636 : ZSTD_decompress(*output_data, *output_size, input_data, input_size);
495 12636 : if (ZSTD_isError(ret))
496 : {
497 714 : *output_size = CPLZSTDGetDecompressedSize(input_data, input_size);
498 714 : return false;
499 : }
500 :
501 11922 : *output_size = ret;
502 11922 : return true;
503 : }
504 :
505 2 : if (output_data == nullptr && output_size != nullptr)
506 : {
507 1 : *output_size = CPLZSTDGetDecompressedSize(input_data, input_size);
508 1 : return *output_size != 0;
509 : }
510 :
511 1 : if (output_data != nullptr && *output_data == nullptr &&
512 : output_size != nullptr)
513 : {
514 1 : size_t nOutSize = CPLZSTDGetDecompressedSize(input_data, input_size);
515 1 : *output_data = VSI_MALLOC_VERBOSE(nOutSize);
516 1 : if (*output_data == nullptr)
517 : {
518 0 : *output_size = 0;
519 0 : return false;
520 : }
521 :
522 : size_t ret =
523 1 : ZSTD_decompress(*output_data, nOutSize, input_data, input_size);
524 1 : if (ZSTD_isError(ret))
525 : {
526 0 : *output_size = 0;
527 0 : VSIFree(*output_data);
528 0 : *output_data = nullptr;
529 0 : return false;
530 : }
531 :
532 1 : *output_size = ret;
533 1 : return true;
534 : }
535 :
536 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
537 0 : return false;
538 : }
539 :
540 : #endif // HAVE_ZSTD
541 :
542 : #ifdef HAVE_LZ4
543 5 : static bool CPLLZ4Compressor(const void *input_data, size_t input_size,
544 : void **output_data, size_t *output_size,
545 : CSLConstList options,
546 : void * /* compressor_user_data */)
547 : {
548 5 : if (input_size > static_cast<size_t>(std::numeric_limits<int>::max()))
549 : {
550 0 : CPLError(CE_Failure, CPLE_NotSupported,
551 : "Too large input buffer. "
552 : "Max supported is INT_MAX");
553 0 : *output_size = 0;
554 0 : return false;
555 : }
556 :
557 : const bool bHeader =
558 5 : CPLTestBool(CSLFetchNameValueDef(options, "HEADER", "YES"));
559 5 : const int header_size = bHeader ? static_cast<int>(sizeof(int32_t)) : 0;
560 :
561 5 : if (output_data != nullptr && *output_data != nullptr &&
562 3 : output_size != nullptr && *output_size != 0)
563 : {
564 : const int acceleration =
565 3 : atoi(CSLFetchNameValueDef(options, "ACCELERATION", "1"));
566 6 : if (*output_size >
567 3 : static_cast<size_t>(std::numeric_limits<int>::max() - 4))
568 : {
569 0 : CPLError(CE_Failure, CPLE_NotSupported,
570 : "Too large output buffer. "
571 : "Max supported is INT_MAX");
572 0 : *output_size = 0;
573 0 : return false;
574 : }
575 :
576 3 : if (bHeader && static_cast<int>(*output_size) < header_size)
577 : {
578 1 : *output_size = 0;
579 1 : return false;
580 : }
581 :
582 4 : int ret = LZ4_compress_fast(
583 : static_cast<const char *>(input_data),
584 2 : static_cast<char *>(*output_data) + header_size,
585 : static_cast<int>(input_size),
586 2 : static_cast<int>(*output_size) - header_size, acceleration);
587 2 : if (ret <= 0 || ret > std::numeric_limits<int>::max() - header_size)
588 : {
589 0 : *output_size = 0;
590 0 : return false;
591 : }
592 :
593 2 : int32_t sizeLSB = CPL_AS_LSB(static_cast<int32_t>(input_size));
594 2 : memcpy(*output_data, &sizeLSB, sizeof(sizeLSB));
595 :
596 2 : *output_size = static_cast<size_t>(header_size + ret);
597 2 : return true;
598 : }
599 :
600 2 : if (output_data == nullptr && output_size != nullptr)
601 : {
602 2 : *output_size = static_cast<size_t>(header_size) +
603 1 : LZ4_compressBound(static_cast<int>(input_size));
604 1 : return true;
605 : }
606 :
607 1 : if (output_data != nullptr && *output_data == nullptr &&
608 : output_size != nullptr)
609 : {
610 1 : size_t nSafeSize = static_cast<size_t>(header_size) +
611 1 : LZ4_compressBound(static_cast<int>(input_size));
612 1 : *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
613 1 : *output_size = nSafeSize;
614 1 : if (*output_data == nullptr)
615 0 : return false;
616 1 : bool ret = CPLLZ4Compressor(input_data, input_size, output_data,
617 : output_size, options, nullptr);
618 1 : if (!ret)
619 : {
620 0 : VSIFree(*output_data);
621 0 : *output_data = nullptr;
622 : }
623 1 : return ret;
624 : }
625 :
626 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
627 0 : return false;
628 : }
629 :
630 5 : static bool CPLLZ4Decompressor(const void *input_data, size_t input_size,
631 : void **output_data, size_t *output_size,
632 : CSLConstList options,
633 : void * /* compressor_user_data */)
634 : {
635 5 : if (input_size > static_cast<size_t>(std::numeric_limits<int>::max()))
636 : {
637 0 : CPLError(CE_Failure, CPLE_NotSupported,
638 : "Too large input buffer. "
639 : "Max supported is INT_MAX");
640 0 : *output_size = 0;
641 0 : return false;
642 : }
643 :
644 : const bool bHeader =
645 5 : CPLTestBool(CSLFetchNameValueDef(options, "HEADER", "YES"));
646 5 : const int header_size = bHeader ? static_cast<int>(sizeof(int32_t)) : 0;
647 5 : if (bHeader && static_cast<int>(input_size) < header_size)
648 : {
649 0 : *output_size = 0;
650 0 : return false;
651 : }
652 :
653 5 : if (output_data != nullptr && *output_data != nullptr &&
654 3 : output_size != nullptr && *output_size != 0)
655 : {
656 3 : if (*output_size > static_cast<size_t>(std::numeric_limits<int>::max()))
657 : {
658 0 : CPLError(CE_Failure, CPLE_NotSupported,
659 : "Too large output buffer. "
660 : "Max supported is INT_MAX");
661 0 : *output_size = 0;
662 0 : return false;
663 : }
664 :
665 6 : int ret = LZ4_decompress_safe(
666 3 : static_cast<const char *>(input_data) + header_size,
667 : static_cast<char *>(*output_data),
668 : static_cast<int>(input_size) - header_size,
669 3 : static_cast<int>(*output_size));
670 3 : if (ret <= 0)
671 : {
672 0 : *output_size = 0;
673 0 : return false;
674 : }
675 :
676 3 : *output_size = ret;
677 3 : return true;
678 : }
679 :
680 2 : if (output_data == nullptr && output_size != nullptr)
681 : {
682 1 : if (bHeader)
683 : {
684 1 : int nSize = CPL_FROM_LSB<int32_t>(input_data);
685 1 : if (nSize < 0)
686 : {
687 0 : *output_size = 0;
688 0 : return false;
689 : }
690 1 : *output_size = nSize;
691 1 : return true;
692 : }
693 :
694 : // inefficient !
695 0 : void *tmpBuffer = nullptr;
696 0 : bool ret = CPLLZ4Decompressor(input_data, input_size, &tmpBuffer,
697 : output_size, options, nullptr);
698 0 : VSIFree(tmpBuffer);
699 0 : return ret;
700 : }
701 :
702 1 : if (output_data != nullptr && *output_data == nullptr &&
703 : output_size != nullptr)
704 : {
705 1 : if (bHeader)
706 : {
707 1 : int nSize = CPL_FROM_LSB<int32_t>(input_data);
708 1 : if (nSize <= 0)
709 : {
710 0 : *output_size = 0;
711 0 : return false;
712 : }
713 1 : if (nSize > INT_MAX - 1 || /* to make Coverity scan happy */
714 1 : nSize / 10000 > static_cast<int>(input_size))
715 : {
716 0 : CPLError(CE_Failure, CPLE_AppDefined,
717 : "Stored uncompressed size (%d) is much larger "
718 : "than compressed size (%d)",
719 : nSize, static_cast<int>(input_size));
720 0 : *output_size = nSize;
721 0 : return false;
722 : }
723 1 : *output_data = VSI_MALLOC_VERBOSE(nSize);
724 1 : *output_size = nSize;
725 1 : if (*output_data == nullptr)
726 : {
727 0 : return false;
728 : }
729 1 : if (!CPLLZ4Decompressor(input_data, input_size, output_data,
730 : output_size, options, nullptr))
731 : {
732 0 : VSIFree(*output_data);
733 0 : *output_data = nullptr;
734 0 : *output_size = 0;
735 0 : return false;
736 : }
737 1 : return true;
738 : }
739 :
740 : size_t nOutSize =
741 0 : static_cast<int>(input_size) < std::numeric_limits<int>::max() / 2
742 0 : ? input_size * 2
743 0 : : static_cast<size_t>(std::numeric_limits<int>::max());
744 0 : *output_data = VSI_MALLOC_VERBOSE(nOutSize);
745 0 : if (*output_data == nullptr)
746 : {
747 0 : *output_size = 0;
748 0 : return false;
749 : }
750 :
751 : while (true)
752 : {
753 0 : int ret = LZ4_decompress_safe_partial(
754 : static_cast<const char *>(input_data),
755 : static_cast<char *>(*output_data), static_cast<int>(input_size),
756 : static_cast<int>(nOutSize), static_cast<int>(nOutSize));
757 0 : if (ret <= 0)
758 : {
759 0 : VSIFree(*output_data);
760 0 : *output_data = nullptr;
761 0 : *output_size = 0;
762 0 : return false;
763 : }
764 0 : else if (ret < static_cast<int>(nOutSize))
765 : {
766 0 : *output_size = ret;
767 0 : return true;
768 : }
769 0 : else if (static_cast<int>(nOutSize) <
770 0 : std::numeric_limits<int>::max() / 2)
771 : {
772 0 : nOutSize *= 2;
773 0 : void *tmpBuffer = VSI_REALLOC_VERBOSE(*output_data, nOutSize);
774 0 : if (tmpBuffer == nullptr)
775 : {
776 0 : VSIFree(*output_data);
777 0 : *output_data = nullptr;
778 0 : *output_size = 0;
779 0 : return false;
780 : }
781 0 : *output_data = tmpBuffer;
782 : }
783 : else
784 : {
785 0 : VSIFree(*output_data);
786 0 : *output_data = nullptr;
787 0 : *output_size = 0;
788 0 : return false;
789 : }
790 0 : }
791 : }
792 :
793 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
794 0 : return false;
795 : }
796 :
797 : #endif // HAVE_LZ4
798 :
799 14931 : static void *CPLGZipCompress(const void *ptr, size_t nBytes, int nLevel,
800 : void *outptr, size_t nOutAvailableBytes,
801 : size_t *pnOutBytes)
802 : {
803 14931 : if (pnOutBytes != nullptr)
804 14931 : *pnOutBytes = 0;
805 :
806 14931 : size_t nTmpSize = 0;
807 : void *pTmp;
808 : #ifdef HAVE_LIBDEFLATE
809 : struct libdeflate_compressor *enc =
810 14931 : libdeflate_alloc_compressor(nLevel < 0 ? 7 : nLevel);
811 14931 : if (enc == nullptr)
812 : {
813 0 : return nullptr;
814 : }
815 : #endif
816 14931 : if (outptr == nullptr)
817 : {
818 : #ifdef HAVE_LIBDEFLATE
819 1 : nTmpSize = libdeflate_gzip_compress_bound(enc, nBytes);
820 : #else
821 : nTmpSize = 32 + nBytes * 2;
822 : #endif
823 1 : pTmp = VSIMalloc(nTmpSize);
824 1 : if (pTmp == nullptr)
825 : {
826 : #ifdef HAVE_LIBDEFLATE
827 0 : libdeflate_free_compressor(enc);
828 : #endif
829 0 : return nullptr;
830 : }
831 : }
832 : else
833 : {
834 14930 : pTmp = outptr;
835 14930 : nTmpSize = nOutAvailableBytes;
836 : }
837 :
838 : #ifdef HAVE_LIBDEFLATE
839 : size_t nCompressedBytes =
840 14931 : libdeflate_gzip_compress(enc, ptr, nBytes, pTmp, nTmpSize);
841 14931 : libdeflate_free_compressor(enc);
842 14931 : if (nCompressedBytes == 0)
843 : {
844 1 : if (pTmp != outptr)
845 0 : VSIFree(pTmp);
846 1 : return nullptr;
847 : }
848 14930 : if (pnOutBytes != nullptr)
849 14930 : *pnOutBytes = nCompressedBytes;
850 : #else
851 : z_stream strm;
852 : strm.zalloc = nullptr;
853 : strm.zfree = nullptr;
854 : strm.opaque = nullptr;
855 : constexpr int windowsBits = 15;
856 : constexpr int gzipEncoding = 16;
857 : int ret = deflateInit2(&strm, nLevel < 0 ? Z_DEFAULT_COMPRESSION : nLevel,
858 : Z_DEFLATED, windowsBits + gzipEncoding, 8,
859 : Z_DEFAULT_STRATEGY);
860 : if (ret != Z_OK)
861 : {
862 : if (pTmp != outptr)
863 : VSIFree(pTmp);
864 : return nullptr;
865 : }
866 :
867 : strm.avail_in = static_cast<uInt>(nBytes);
868 : strm.next_in = reinterpret_cast<Bytef *>(const_cast<void *>(ptr));
869 : strm.avail_out = static_cast<uInt>(nTmpSize);
870 : strm.next_out = reinterpret_cast<Bytef *>(pTmp);
871 : ret = deflate(&strm, Z_FINISH);
872 : if (ret != Z_STREAM_END)
873 : {
874 : if (pTmp != outptr)
875 : VSIFree(pTmp);
876 : return nullptr;
877 : }
878 : if (pnOutBytes != nullptr)
879 : *pnOutBytes = nTmpSize - strm.avail_out;
880 : deflateEnd(&strm);
881 : #endif
882 :
883 14930 : return pTmp;
884 : }
885 :
886 14970 : static bool CPLZlibCompressor(const void *input_data, size_t input_size,
887 : void **output_data, size_t *output_size,
888 : CSLConstList options, void *compressor_user_data)
889 : {
890 14970 : const char *alg = static_cast<const char *>(compressor_user_data);
891 14970 : const auto pfnCompress =
892 14970 : strcmp(alg, "zlib") == 0 ? CPLZLibDeflate : CPLGZipCompress;
893 14970 : const int clevel = atoi(CSLFetchNameValueDef(options, "LEVEL",
894 : #if HAVE_LIBDEFLATE
895 : "7"
896 : #else
897 : "6"
898 : #endif
899 : ));
900 :
901 14971 : if (output_data != nullptr && *output_data != nullptr &&
902 14965 : output_size != nullptr && *output_size != 0)
903 : {
904 14965 : size_t nOutBytes = 0;
905 14965 : if (nullptr == pfnCompress(input_data, input_size, clevel, *output_data,
906 : *output_size, &nOutBytes))
907 : {
908 2 : *output_size = 0;
909 2 : return false;
910 : }
911 :
912 14964 : *output_size = nOutBytes;
913 14964 : return true;
914 : }
915 :
916 6 : if (output_data == nullptr && output_size != nullptr)
917 : {
918 : #if HAVE_LIBDEFLATE
919 2 : struct libdeflate_compressor *enc = libdeflate_alloc_compressor(clevel);
920 2 : if (enc == nullptr)
921 : {
922 0 : *output_size = 0;
923 0 : return false;
924 : }
925 2 : if (strcmp(alg, "zlib") == 0)
926 1 : *output_size = libdeflate_zlib_compress_bound(enc, input_size);
927 : else
928 1 : *output_size = libdeflate_gzip_compress_bound(enc, input_size);
929 2 : libdeflate_free_compressor(enc);
930 : #else
931 : // Really inefficient !
932 : size_t nOutSize = 0;
933 : void *outbuffer =
934 : pfnCompress(input_data, input_size, clevel, nullptr, 0, &nOutSize);
935 : if (outbuffer == nullptr)
936 : {
937 : *output_size = 0;
938 : return false;
939 : }
940 : VSIFree(outbuffer);
941 : *output_size = nOutSize;
942 : #endif
943 2 : return true;
944 : }
945 :
946 4 : if (output_data != nullptr && *output_data == nullptr &&
947 : output_size != nullptr)
948 : {
949 3 : size_t nOutSize = 0;
950 3 : *output_data =
951 3 : pfnCompress(input_data, input_size, clevel, nullptr, 0, &nOutSize);
952 3 : if (*output_data == nullptr)
953 : {
954 0 : *output_size = 0;
955 0 : return false;
956 : }
957 3 : *output_size = nOutSize;
958 3 : return true;
959 : }
960 :
961 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
962 0 : return false;
963 : }
964 :
965 : namespace
966 : {
967 : // Workaround -ftrapv
968 : template <class T>
969 448 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW inline T SubNoOverflow(T left, T right)
970 : {
971 : typedef typename std::make_unsigned<T>::type U;
972 448 : U leftU = static_cast<U>(left);
973 448 : U rightU = static_cast<U>(right);
974 448 : leftU = static_cast<U>(leftU - rightU);
975 : T ret;
976 448 : memcpy(&ret, &leftU, sizeof(ret));
977 448 : return leftU;
978 : }
979 :
980 4 : template <> inline float SubNoOverflow<float>(float x, float y)
981 : {
982 4 : return x - y;
983 : }
984 :
985 4 : template <> inline double SubNoOverflow<double>(double x, double y)
986 : {
987 4 : return x - y;
988 : }
989 : } // namespace
990 :
991 : template <class T>
992 26 : static bool DeltaCompressor(const void *input_data, size_t input_size,
993 : const char *dtype, void *output_data)
994 : {
995 24 : if ((input_size % sizeof(T)) != 0)
996 : {
997 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid input size");
998 0 : return false;
999 : }
1000 :
1001 26 : const size_t nElts = input_size / sizeof(T);
1002 26 : const T *pSrc = static_cast<const T *>(input_data);
1003 26 : T *pDst = static_cast<T *>(output_data);
1004 : #ifdef CPL_MSB
1005 : const bool bNeedSwap = dtype[0] == '<';
1006 : #else
1007 26 : const bool bNeedSwap = dtype[0] == '>';
1008 : #endif
1009 508 : for (size_t i = 0; i < nElts; i++)
1010 : {
1011 482 : if (i == 0)
1012 : {
1013 26 : pDst[0] = pSrc[0];
1014 : }
1015 : else
1016 : {
1017 456 : if (bNeedSwap)
1018 : {
1019 12 : pDst[i] = CPL_SWAP(
1020 12 : SubNoOverflow(CPL_SWAP(pSrc[i]), CPL_SWAP(pSrc[i - 1])));
1021 : }
1022 : else
1023 : {
1024 444 : pDst[i] = SubNoOverflow(pSrc[i], pSrc[i - 1]);
1025 : }
1026 : }
1027 : }
1028 26 : return true;
1029 : }
1030 :
1031 26 : static bool CPLDeltaCompressor(const void *input_data, size_t input_size,
1032 : void **output_data, size_t *output_size,
1033 : CSLConstList options,
1034 : void * /* compressor_user_data */)
1035 : {
1036 26 : const char *dtype = CSLFetchNameValue(options, "DTYPE");
1037 26 : if (dtype == nullptr)
1038 : {
1039 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing DTYPE parameter");
1040 0 : if (output_size)
1041 0 : *output_size = 0;
1042 0 : return false;
1043 : }
1044 26 : const char *astype = CSLFetchNameValue(options, "ASTYPE");
1045 26 : if (astype != nullptr && !EQUAL(astype, dtype))
1046 : {
1047 0 : CPLError(CE_Failure, CPLE_AppDefined,
1048 : "Only ASTYPE=DTYPE currently supported");
1049 0 : if (output_size)
1050 0 : *output_size = 0;
1051 0 : return false;
1052 : }
1053 :
1054 26 : if (output_data != nullptr && *output_data != nullptr &&
1055 26 : output_size != nullptr && *output_size != 0)
1056 : {
1057 26 : if (*output_size < input_size)
1058 : {
1059 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
1060 0 : *output_size = input_size;
1061 0 : return false;
1062 : }
1063 :
1064 26 : if (EQUAL(dtype, "i1"))
1065 : {
1066 1 : if (!DeltaCompressor<int8_t>(input_data, input_size, dtype,
1067 : *output_data))
1068 : {
1069 0 : *output_size = 0;
1070 0 : return false;
1071 : }
1072 : }
1073 25 : else if (EQUAL(dtype, "u1"))
1074 : {
1075 1 : if (!DeltaCompressor<uint8_t>(input_data, input_size, dtype,
1076 : *output_data))
1077 : {
1078 0 : *output_size = 0;
1079 0 : return false;
1080 : }
1081 : }
1082 24 : else if (EQUAL(dtype, "<i2") || EQUAL(dtype, ">i2") ||
1083 22 : EQUAL(dtype, "i2"))
1084 : {
1085 3 : if (!DeltaCompressor<int16_t>(input_data, input_size, dtype,
1086 : *output_data))
1087 : {
1088 0 : *output_size = 0;
1089 0 : return false;
1090 : }
1091 : }
1092 21 : else if (EQUAL(dtype, "<u2") || EQUAL(dtype, ">u2") ||
1093 18 : EQUAL(dtype, "u2"))
1094 : {
1095 4 : if (!DeltaCompressor<uint16_t>(input_data, input_size, dtype,
1096 : *output_data))
1097 : {
1098 0 : *output_size = 0;
1099 0 : return false;
1100 : }
1101 : }
1102 17 : else if (EQUAL(dtype, "<i4") || EQUAL(dtype, ">i4") ||
1103 14 : EQUAL(dtype, "i4"))
1104 : {
1105 4 : if (!DeltaCompressor<int32_t>(input_data, input_size, dtype,
1106 : *output_data))
1107 : {
1108 0 : *output_size = 0;
1109 0 : return false;
1110 : }
1111 : }
1112 13 : else if (EQUAL(dtype, "<u4") || EQUAL(dtype, ">u4") ||
1113 11 : EQUAL(dtype, "u4"))
1114 : {
1115 3 : if (!DeltaCompressor<uint32_t>(input_data, input_size, dtype,
1116 : *output_data))
1117 : {
1118 0 : *output_size = 0;
1119 0 : return false;
1120 : }
1121 : }
1122 10 : else if (EQUAL(dtype, "<i8") || EQUAL(dtype, ">i8") ||
1123 8 : EQUAL(dtype, "i8"))
1124 : {
1125 3 : if (!DeltaCompressor<int64_t>(input_data, input_size, dtype,
1126 : *output_data))
1127 : {
1128 0 : *output_size = 0;
1129 0 : return false;
1130 : }
1131 : }
1132 7 : else if (EQUAL(dtype, "<u8") || EQUAL(dtype, ">u8") ||
1133 5 : EQUAL(dtype, "u8"))
1134 : {
1135 3 : if (!DeltaCompressor<uint64_t>(input_data, input_size, dtype,
1136 : *output_data))
1137 : {
1138 0 : *output_size = 0;
1139 0 : return false;
1140 : }
1141 : }
1142 4 : else if (EQUAL(dtype, "<f4") || EQUAL(dtype, ">f4") ||
1143 3 : EQUAL(dtype, "f4"))
1144 : {
1145 2 : if (!DeltaCompressor<float>(input_data, input_size, dtype,
1146 : *output_data))
1147 : {
1148 0 : *output_size = 0;
1149 0 : return false;
1150 : }
1151 : }
1152 2 : else if (EQUAL(dtype, "<f8") || EQUAL(dtype, ">f8") ||
1153 1 : EQUAL(dtype, "f8"))
1154 : {
1155 2 : if (!DeltaCompressor<double>(input_data, input_size, dtype,
1156 : *output_data))
1157 : {
1158 0 : *output_size = 0;
1159 0 : return false;
1160 : }
1161 : }
1162 : else
1163 : {
1164 0 : CPLError(CE_Failure, CPLE_NotSupported,
1165 : "Unsupported dtype=%s for delta filter", dtype);
1166 0 : *output_size = 0;
1167 0 : return false;
1168 : }
1169 :
1170 26 : *output_size = input_size;
1171 26 : return true;
1172 : }
1173 :
1174 0 : if (output_data == nullptr && output_size != nullptr)
1175 : {
1176 0 : *output_size = input_size;
1177 0 : return true;
1178 : }
1179 :
1180 0 : if (output_data != nullptr && *output_data == nullptr &&
1181 : output_size != nullptr)
1182 : {
1183 0 : *output_data = VSI_MALLOC_VERBOSE(input_size);
1184 0 : *output_size = input_size;
1185 0 : if (*output_data == nullptr)
1186 0 : return false;
1187 0 : bool ret = CPLDeltaCompressor(input_data, input_size, output_data,
1188 : output_size, options, nullptr);
1189 0 : if (!ret)
1190 : {
1191 0 : VSIFree(*output_data);
1192 0 : *output_data = nullptr;
1193 : }
1194 0 : return ret;
1195 : }
1196 :
1197 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
1198 0 : return false;
1199 : }
1200 :
1201 1499 : static void CPLAddCompressor(const CPLCompressor *compressor)
1202 : {
1203 1499 : CPLCompressor *copy = new CPLCompressor(*compressor);
1204 : // cppcheck-suppress uninitdata
1205 1499 : copy->pszId = CPLStrdup(compressor->pszId);
1206 : // cppcheck-suppress uninitdata
1207 1499 : copy->papszMetadata = CSLDuplicate(compressor->papszMetadata);
1208 1499 : gpCompressors->emplace_back(copy);
1209 1499 : }
1210 :
1211 214 : static void CPLAddBuiltinCompressors()
1212 : {
1213 : #ifdef HAVE_BLOSC
1214 : do
1215 : {
1216 : CPLCompressor sComp;
1217 214 : sComp.nStructVersion = 1;
1218 214 : sComp.eType = CCT_COMPRESSOR;
1219 214 : sComp.pszId = "blosc";
1220 :
1221 : const CPLStringList aosCompressors(
1222 214 : CSLTokenizeString2(blosc_list_compressors(), ",", 0));
1223 214 : if (aosCompressors.size() == 0)
1224 0 : break;
1225 : std::string options("OPTIONS=<Options>"
1226 : " <Option name='CNAME' type='string-select' "
1227 428 : "description='Compressor name' default='");
1228 428 : std::string values;
1229 428 : std::string defaultCompressor;
1230 214 : bool bFoundLZ4 = false;
1231 214 : bool bFoundSnappy = false;
1232 214 : bool bFoundZlib = false;
1233 1498 : for (int i = 0; i < aosCompressors.size(); i++)
1234 : {
1235 1284 : values += "<Value>";
1236 1284 : values += aosCompressors[i];
1237 1284 : values += "</Value>";
1238 1284 : if (strcmp(aosCompressors[i], "lz4") == 0)
1239 214 : bFoundLZ4 = true;
1240 1070 : else if (strcmp(aosCompressors[i], "snappy") == 0)
1241 214 : bFoundSnappy = true;
1242 856 : else if (strcmp(aosCompressors[i], "zlib") == 0)
1243 214 : bFoundZlib = true;
1244 : }
1245 : options += bFoundLZ4 ? "lz4"
1246 0 : : bFoundSnappy ? "snappy"
1247 0 : : bFoundZlib ? "zlib"
1248 214 : : aosCompressors[0];
1249 214 : options += "'>";
1250 214 : options += values;
1251 : options +=
1252 : " </Option>"
1253 : " <Option name='CLEVEL' type='int' description='Compression "
1254 : "level' min='1' max='9' default='5' />"
1255 : " <Option name='SHUFFLE' type='string-select' description='Type "
1256 : "of shuffle algorithm' default='BYTE'>"
1257 : " <Value alias='0'>NONE</Value>"
1258 : " <Value alias='1'>BYTE</Value>"
1259 : " <Value alias='2'>BIT</Value>"
1260 : " </Option>"
1261 : " <Option name='BLOCKSIZE' type='int' description='Block size' "
1262 : "default='0' />"
1263 : " <Option name='TYPESIZE' type='int' description='Number of bytes "
1264 : "for the atomic type' default='1' />"
1265 : " <Option name='NUM_THREADS' type='string' "
1266 : "description='Number of worker threads for compression. Can be set "
1267 : "to ALL_CPUS' default='1' />"
1268 214 : "</Options>";
1269 :
1270 214 : const char *const apszMetadata[] = {
1271 214 : "BLOSC_VERSION=" BLOSC_VERSION_STRING, options.c_str(), nullptr};
1272 214 : sComp.papszMetadata = apszMetadata;
1273 214 : sComp.pfnFunc = CPLBloscCompressor;
1274 214 : sComp.user_data = nullptr;
1275 214 : CPLAddCompressor(&sComp);
1276 : } while (0);
1277 : #endif
1278 : {
1279 : CPLCompressor sComp;
1280 214 : sComp.nStructVersion = 1;
1281 214 : sComp.eType = CCT_COMPRESSOR;
1282 214 : sComp.pszId = "zlib";
1283 214 : const char *pszOptions =
1284 : "OPTIONS=<Options>"
1285 : " <Option name='LEVEL' type='int' description='Compression level' "
1286 : "min='1' max='9' default='6' />"
1287 : "</Options>";
1288 214 : const char *const apszMetadata[] = {pszOptions, nullptr};
1289 214 : sComp.papszMetadata = apszMetadata;
1290 214 : sComp.pfnFunc = CPLZlibCompressor;
1291 214 : sComp.user_data = const_cast<char *>("zlib");
1292 214 : CPLAddCompressor(&sComp);
1293 : }
1294 : {
1295 : CPLCompressor sComp;
1296 214 : sComp.nStructVersion = 1;
1297 214 : sComp.eType = CCT_COMPRESSOR;
1298 214 : sComp.pszId = "gzip";
1299 214 : const char *pszOptions =
1300 : "OPTIONS=<Options>"
1301 : " <Option name='LEVEL' type='int' description='Compression level' "
1302 : "min='1' max='9' default='6' />"
1303 : "</Options>";
1304 214 : const char *const apszMetadata[] = {pszOptions, nullptr};
1305 214 : sComp.papszMetadata = apszMetadata;
1306 214 : sComp.pfnFunc = CPLZlibCompressor;
1307 214 : sComp.user_data = const_cast<char *>("gzip");
1308 214 : CPLAddCompressor(&sComp);
1309 : }
1310 : #ifdef HAVE_LZMA
1311 : {
1312 : CPLCompressor sComp;
1313 214 : sComp.nStructVersion = 1;
1314 214 : sComp.eType = CCT_COMPRESSOR;
1315 214 : sComp.pszId = "lzma";
1316 214 : const char *pszOptions =
1317 : "OPTIONS=<Options>"
1318 : " <Option name='PRESET' type='int' description='Compression "
1319 : "level' min='0' max='9' default='6' />"
1320 : " <Option name='DELTA' type='int' description='Delta distance in "
1321 : "byte' default='1' />"
1322 : "</Options>";
1323 214 : const char *const apszMetadata[] = {pszOptions, nullptr};
1324 214 : sComp.papszMetadata = apszMetadata;
1325 214 : sComp.pfnFunc = CPLLZMACompressor;
1326 214 : sComp.user_data = nullptr;
1327 214 : CPLAddCompressor(&sComp);
1328 : }
1329 : #endif
1330 : #ifdef HAVE_ZSTD
1331 : {
1332 : CPLCompressor sComp;
1333 214 : sComp.nStructVersion = 1;
1334 214 : sComp.eType = CCT_COMPRESSOR;
1335 214 : sComp.pszId = "zstd";
1336 214 : const char *pszOptions =
1337 : "OPTIONS=<Options>"
1338 : " <Option name='LEVEL' type='int' description='Compression level' "
1339 : "min='1' max='22' default='13' />"
1340 : " <Option name='CHECKSUM' type='boolean' description='Whether "
1341 : "to store a checksum when writing that will be verified' "
1342 : "default='NO' />"
1343 : "</Options>";
1344 214 : const char *const apszMetadata[] = {pszOptions, nullptr};
1345 214 : sComp.papszMetadata = apszMetadata;
1346 214 : sComp.pfnFunc = CPLZSTDCompressor;
1347 214 : sComp.user_data = nullptr;
1348 214 : CPLAddCompressor(&sComp);
1349 : }
1350 : #endif
1351 : #ifdef HAVE_LZ4
1352 : {
1353 : CPLCompressor sComp;
1354 214 : sComp.nStructVersion = 1;
1355 214 : sComp.eType = CCT_COMPRESSOR;
1356 214 : sComp.pszId = "lz4";
1357 214 : const char *pszOptions =
1358 : "OPTIONS=<Options>"
1359 : " <Option name='ACCELERATION' type='int' "
1360 : "description='Acceleration factor. The higher, the less "
1361 : "compressed' min='1' default='1' />"
1362 : " <Option name='HEADER' type='boolean' description='Whether a "
1363 : "header with the uncompressed size should be included (as used by "
1364 : "Zarr)' default='YES' />"
1365 : "</Options>";
1366 214 : const char *const apszMetadata[] = {pszOptions, nullptr};
1367 214 : sComp.papszMetadata = apszMetadata;
1368 214 : sComp.pfnFunc = CPLLZ4Compressor;
1369 214 : sComp.user_data = nullptr;
1370 214 : CPLAddCompressor(&sComp);
1371 : }
1372 : #endif
1373 : {
1374 : CPLCompressor sComp;
1375 214 : sComp.nStructVersion = 1;
1376 214 : sComp.eType = CCT_FILTER;
1377 214 : sComp.pszId = "delta";
1378 214 : const char *pszOptions =
1379 : "OPTIONS=<Options>"
1380 : " <Option name='DTYPE' type='string' description='Data type "
1381 : "following NumPy array protocol type string (typestr) format'/>"
1382 : "</Options>";
1383 214 : const char *const apszMetadata[] = {pszOptions, nullptr};
1384 214 : sComp.papszMetadata = apszMetadata;
1385 214 : sComp.pfnFunc = CPLDeltaCompressor;
1386 214 : sComp.user_data = nullptr;
1387 214 : CPLAddCompressor(&sComp);
1388 : }
1389 214 : }
1390 :
1391 20474 : static bool CPLZlibDecompressor(const void *input_data, size_t input_size,
1392 : void **output_data, size_t *output_size,
1393 : CSLConstList /* options */,
1394 : void * /* compressor_user_data */)
1395 : {
1396 20474 : if (output_data != nullptr && *output_data != nullptr &&
1397 20384 : output_size != nullptr && *output_size != 0)
1398 : {
1399 20324 : size_t nOutBytes = 0;
1400 20324 : if (nullptr == CPLZLibInflate(input_data, input_size, *output_data,
1401 : *output_size, &nOutBytes))
1402 : {
1403 0 : *output_size = 0;
1404 0 : return false;
1405 : }
1406 :
1407 20535 : *output_size = nOutBytes;
1408 20535 : return true;
1409 : }
1410 :
1411 150 : if (output_data == nullptr && output_size != nullptr)
1412 : {
1413 2 : size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 4
1414 2 : ? input_size * 4
1415 2 : : input_size;
1416 2 : void *tmpOutBuffer = VSIMalloc(nOutSize);
1417 2 : if (tmpOutBuffer == nullptr)
1418 : {
1419 0 : *output_size = 0;
1420 0 : return false;
1421 : }
1422 2 : tmpOutBuffer = CPLZLibInflateEx(input_data, input_size, tmpOutBuffer,
1423 : nOutSize, true, &nOutSize);
1424 2 : if (!tmpOutBuffer)
1425 : {
1426 0 : *output_size = 0;
1427 0 : return false;
1428 : }
1429 2 : VSIFree(tmpOutBuffer);
1430 2 : *output_size = nOutSize;
1431 2 : return true;
1432 : }
1433 :
1434 148 : if (output_data != nullptr && *output_data == nullptr &&
1435 : output_size != nullptr)
1436 : {
1437 3 : size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 4
1438 3 : ? input_size * 4
1439 3 : : input_size;
1440 3 : void *tmpOutBuffer = VSIMalloc(nOutSize);
1441 3 : if (tmpOutBuffer == nullptr)
1442 : {
1443 0 : *output_size = 0;
1444 0 : return false;
1445 : }
1446 3 : size_t nOutSizeOut = 0;
1447 3 : tmpOutBuffer = CPLZLibInflateEx(input_data, input_size, tmpOutBuffer,
1448 : nOutSize, true, &nOutSizeOut);
1449 3 : if (!tmpOutBuffer)
1450 : {
1451 0 : *output_size = 0;
1452 0 : return false;
1453 : }
1454 3 : *output_data = VSIRealloc(tmpOutBuffer, nOutSizeOut); // cannot fail
1455 3 : *output_size = nOutSizeOut;
1456 3 : return true;
1457 : }
1458 :
1459 145 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
1460 0 : return false;
1461 : }
1462 :
1463 : namespace
1464 : {
1465 : // Workaround -ftrapv
1466 : template <class T>
1467 865 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW inline T AddNoOverflow(T left, T right)
1468 : {
1469 : typedef typename std::make_unsigned<T>::type U;
1470 865 : U leftU = static_cast<U>(left);
1471 865 : U rightU = static_cast<U>(right);
1472 865 : leftU = static_cast<U>(leftU + rightU);
1473 : T ret;
1474 865 : memcpy(&ret, &leftU, sizeof(ret));
1475 865 : return leftU;
1476 : }
1477 :
1478 4 : template <> inline float AddNoOverflow<float>(float x, float y)
1479 : {
1480 4 : return x + y;
1481 : }
1482 :
1483 4 : template <> inline double AddNoOverflow<double>(double x, double y)
1484 : {
1485 4 : return x + y;
1486 : }
1487 : } // namespace
1488 :
1489 : template <class T>
1490 29 : static bool DeltaDecompressor(const void *input_data, size_t input_size,
1491 : const char *dtype, void *output_data)
1492 : {
1493 26 : if ((input_size % sizeof(T)) != 0)
1494 : {
1495 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid input size");
1496 0 : return false;
1497 : }
1498 :
1499 29 : const size_t nElts = input_size / sizeof(T);
1500 29 : const T *pSrc = static_cast<const T *>(input_data);
1501 29 : T *pDst = static_cast<T *>(output_data);
1502 : #ifdef CPL_MSB
1503 : const bool bNeedSwap = dtype[0] == '<';
1504 : #else
1505 29 : const bool bNeedSwap = dtype[0] == '>';
1506 : #endif
1507 931 : for (size_t i = 0; i < nElts; i++)
1508 : {
1509 902 : if (i == 0)
1510 : {
1511 29 : pDst[0] = pSrc[0];
1512 : }
1513 : else
1514 : {
1515 873 : if (bNeedSwap)
1516 : {
1517 12 : pDst[i] = CPL_SWAP(
1518 12 : AddNoOverflow(CPL_SWAP(pDst[i - 1]), CPL_SWAP(pSrc[i])));
1519 : }
1520 : else
1521 : {
1522 861 : pDst[i] = AddNoOverflow(pDst[i - 1], pSrc[i]);
1523 : }
1524 : }
1525 : }
1526 29 : return true;
1527 : }
1528 :
1529 29 : static bool CPLDeltaDecompressor(const void *input_data, size_t input_size,
1530 : void **output_data, size_t *output_size,
1531 : CSLConstList options,
1532 : void * /* compressor_user_data */)
1533 : {
1534 29 : const char *dtype = CSLFetchNameValue(options, "DTYPE");
1535 29 : if (dtype == nullptr)
1536 : {
1537 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing DTYPE parameter");
1538 0 : if (output_size)
1539 0 : *output_size = 0;
1540 0 : return false;
1541 : }
1542 29 : const char *astype = CSLFetchNameValue(options, "ASTYPE");
1543 29 : if (astype != nullptr && !EQUAL(astype, dtype))
1544 : {
1545 0 : CPLError(CE_Failure, CPLE_AppDefined,
1546 : "Only ASTYPE=DTYPE currently supported");
1547 0 : if (output_size)
1548 0 : *output_size = 0;
1549 0 : return false;
1550 : }
1551 :
1552 29 : if (output_data != nullptr && *output_data != nullptr &&
1553 29 : output_size != nullptr && *output_size != 0)
1554 : {
1555 29 : if (*output_size < input_size)
1556 : {
1557 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
1558 0 : *output_size = input_size;
1559 0 : return false;
1560 : }
1561 :
1562 29 : if (EQUAL(dtype, "i1"))
1563 : {
1564 1 : if (!DeltaDecompressor<int8_t>(input_data, input_size, dtype,
1565 : *output_data))
1566 : {
1567 0 : *output_size = 0;
1568 0 : return false;
1569 : }
1570 : }
1571 28 : else if (EQUAL(dtype, "u1"))
1572 : {
1573 2 : if (!DeltaDecompressor<uint8_t>(input_data, input_size, dtype,
1574 : *output_data))
1575 : {
1576 0 : *output_size = 0;
1577 0 : return false;
1578 : }
1579 : }
1580 26 : else if (EQUAL(dtype, "<i2") || EQUAL(dtype, ">i2") ||
1581 24 : EQUAL(dtype, "i2"))
1582 : {
1583 3 : if (!DeltaDecompressor<int16_t>(input_data, input_size, dtype,
1584 : *output_data))
1585 : {
1586 0 : *output_size = 0;
1587 0 : return false;
1588 : }
1589 : }
1590 23 : else if (EQUAL(dtype, "<u2") || EQUAL(dtype, ">u2") ||
1591 20 : EQUAL(dtype, "u2"))
1592 : {
1593 4 : if (!DeltaDecompressor<uint16_t>(input_data, input_size, dtype,
1594 : *output_data))
1595 : {
1596 0 : *output_size = 0;
1597 0 : return false;
1598 : }
1599 : }
1600 19 : else if (EQUAL(dtype, "<i4") || EQUAL(dtype, ">i4") ||
1601 14 : EQUAL(dtype, "i4"))
1602 : {
1603 6 : if (!DeltaDecompressor<int32_t>(input_data, input_size, dtype,
1604 : *output_data))
1605 : {
1606 0 : *output_size = 0;
1607 0 : return false;
1608 : }
1609 : }
1610 13 : else if (EQUAL(dtype, "<u4") || EQUAL(dtype, ">u4") ||
1611 11 : EQUAL(dtype, "u4"))
1612 : {
1613 3 : if (!DeltaDecompressor<uint32_t>(input_data, input_size, dtype,
1614 : *output_data))
1615 : {
1616 0 : *output_size = 0;
1617 0 : return false;
1618 : }
1619 : }
1620 10 : else if (EQUAL(dtype, "<i8") || EQUAL(dtype, ">i8") ||
1621 8 : EQUAL(dtype, "i8"))
1622 : {
1623 3 : if (!DeltaDecompressor<int64_t>(input_data, input_size, dtype,
1624 : *output_data))
1625 : {
1626 0 : *output_size = 0;
1627 0 : return false;
1628 : }
1629 : }
1630 7 : else if (EQUAL(dtype, "<u8") || EQUAL(dtype, ">u8") ||
1631 5 : EQUAL(dtype, "u8"))
1632 : {
1633 3 : if (!DeltaDecompressor<uint64_t>(input_data, input_size, dtype,
1634 : *output_data))
1635 : {
1636 0 : *output_size = 0;
1637 0 : return false;
1638 : }
1639 : }
1640 4 : else if (EQUAL(dtype, "<f4") || EQUAL(dtype, ">f4") ||
1641 3 : EQUAL(dtype, "f4"))
1642 : {
1643 2 : if (!DeltaDecompressor<float>(input_data, input_size, dtype,
1644 : *output_data))
1645 : {
1646 0 : *output_size = 0;
1647 0 : return false;
1648 : }
1649 : }
1650 2 : else if (EQUAL(dtype, "<f8") || EQUAL(dtype, ">f8") ||
1651 1 : EQUAL(dtype, "f8"))
1652 : {
1653 2 : if (!DeltaDecompressor<double>(input_data, input_size, dtype,
1654 : *output_data))
1655 : {
1656 0 : *output_size = 0;
1657 0 : return false;
1658 : }
1659 : }
1660 : else
1661 : {
1662 0 : CPLError(CE_Failure, CPLE_NotSupported,
1663 : "Unsupported dtype=%s for delta filter", dtype);
1664 0 : *output_size = 0;
1665 0 : return false;
1666 : }
1667 :
1668 29 : *output_size = input_size;
1669 29 : return true;
1670 : }
1671 :
1672 0 : if (output_data == nullptr && output_size != nullptr)
1673 : {
1674 0 : *output_size = input_size;
1675 0 : return true;
1676 : }
1677 :
1678 0 : if (output_data != nullptr && *output_data == nullptr &&
1679 : output_size != nullptr)
1680 : {
1681 0 : *output_data = VSI_MALLOC_VERBOSE(input_size);
1682 0 : *output_size = input_size;
1683 0 : if (*output_data == nullptr)
1684 0 : return false;
1685 0 : bool ret = CPLDeltaDecompressor(input_data, input_size, output_data,
1686 : output_size, options, nullptr);
1687 0 : if (!ret)
1688 : {
1689 0 : VSIFree(*output_data);
1690 0 : *output_data = nullptr;
1691 : }
1692 0 : return ret;
1693 : }
1694 :
1695 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
1696 0 : return false;
1697 : }
1698 :
1699 12636 : static void CPLAddDecompressor(const CPLCompressor *decompressor)
1700 : {
1701 12636 : CPLCompressor *copy = new CPLCompressor(*decompressor);
1702 : // cppcheck-suppress uninitdata
1703 12636 : copy->pszId = CPLStrdup(decompressor->pszId);
1704 : // cppcheck-suppress uninitdata
1705 12636 : copy->papszMetadata = CSLDuplicate(decompressor->papszMetadata);
1706 12636 : gpDecompressors->emplace_back(copy);
1707 12636 : }
1708 :
1709 1805 : static void CPLAddBuiltinDecompressors()
1710 : {
1711 : #ifdef HAVE_BLOSC
1712 : {
1713 : CPLCompressor sComp;
1714 1805 : sComp.nStructVersion = 1;
1715 1805 : sComp.eType = CCT_COMPRESSOR;
1716 1805 : sComp.pszId = "blosc";
1717 1805 : const char *pszOptions =
1718 : "OPTIONS=<Options>"
1719 : " <Option name='NUM_THREADS' type='string' "
1720 : "description='Number of worker threads for decompression. Can be "
1721 : "set to ALL_CPUS' default='1' />"
1722 : "</Options>";
1723 1805 : const char *const apszMetadata[] = {
1724 1805 : "BLOSC_VERSION=" BLOSC_VERSION_STRING, pszOptions, nullptr};
1725 1805 : sComp.papszMetadata = apszMetadata;
1726 1805 : sComp.pfnFunc = CPLBloscDecompressor;
1727 1805 : sComp.user_data = nullptr;
1728 1805 : CPLAddDecompressor(&sComp);
1729 : }
1730 : #endif
1731 : {
1732 : CPLCompressor sComp;
1733 1805 : sComp.nStructVersion = 1;
1734 1805 : sComp.eType = CCT_COMPRESSOR;
1735 1805 : sComp.pszId = "zlib";
1736 1805 : sComp.papszMetadata = nullptr;
1737 1805 : sComp.pfnFunc = CPLZlibDecompressor;
1738 1805 : sComp.user_data = nullptr;
1739 1805 : CPLAddDecompressor(&sComp);
1740 : }
1741 : {
1742 : CPLCompressor sComp;
1743 1805 : sComp.nStructVersion = 1;
1744 1805 : sComp.eType = CCT_COMPRESSOR;
1745 1805 : sComp.pszId = "gzip";
1746 1805 : sComp.papszMetadata = nullptr;
1747 1805 : sComp.pfnFunc = CPLZlibDecompressor;
1748 1805 : sComp.user_data = nullptr;
1749 1805 : CPLAddDecompressor(&sComp);
1750 : }
1751 : #ifdef HAVE_LZMA
1752 : {
1753 : CPLCompressor sComp;
1754 1805 : sComp.nStructVersion = 1;
1755 1805 : sComp.eType = CCT_COMPRESSOR;
1756 1805 : sComp.pszId = "lzma";
1757 1805 : sComp.papszMetadata = nullptr;
1758 1805 : sComp.pfnFunc = CPLLZMADecompressor;
1759 1805 : sComp.user_data = nullptr;
1760 1805 : CPLAddDecompressor(&sComp);
1761 : }
1762 : #endif
1763 : #ifdef HAVE_ZSTD
1764 : {
1765 : CPLCompressor sComp;
1766 1805 : sComp.nStructVersion = 1;
1767 1805 : sComp.eType = CCT_COMPRESSOR;
1768 1805 : sComp.pszId = "zstd";
1769 1805 : sComp.papszMetadata = nullptr;
1770 1805 : sComp.pfnFunc = CPLZSTDDecompressor;
1771 1805 : sComp.user_data = nullptr;
1772 1805 : CPLAddDecompressor(&sComp);
1773 : }
1774 : #endif
1775 : #ifdef HAVE_LZ4
1776 : {
1777 : CPLCompressor sComp;
1778 1805 : sComp.nStructVersion = 1;
1779 1805 : sComp.eType = CCT_COMPRESSOR;
1780 1805 : sComp.pszId = "lz4";
1781 1805 : const char *pszOptions =
1782 : "OPTIONS=<Options>"
1783 : " <Option name='HEADER' type='boolean' description='Whether a "
1784 : "header with the uncompressed size should be included (as used by "
1785 : "Zarr)' default='YES' />"
1786 : "</Options>";
1787 1805 : const char *const apszMetadata[] = {pszOptions, nullptr};
1788 1805 : sComp.papszMetadata = apszMetadata;
1789 1805 : sComp.pfnFunc = CPLLZ4Decompressor;
1790 1805 : sComp.user_data = nullptr;
1791 1805 : CPLAddDecompressor(&sComp);
1792 : }
1793 : #endif
1794 : {
1795 : CPLCompressor sComp;
1796 1805 : sComp.nStructVersion = 1;
1797 1805 : sComp.eType = CCT_FILTER;
1798 1805 : sComp.pszId = "delta";
1799 1805 : const char *pszOptions =
1800 : "OPTIONS=<Options>"
1801 : " <Option name='DTYPE' type='string' description='Data type "
1802 : "following NumPy array protocol type string (typestr) format'/>"
1803 : "</Options>";
1804 1805 : const char *const apszMetadata[] = {pszOptions, nullptr};
1805 1805 : sComp.papszMetadata = apszMetadata;
1806 1805 : sComp.pfnFunc = CPLDeltaDecompressor;
1807 1805 : sComp.user_data = nullptr;
1808 1805 : CPLAddDecompressor(&sComp);
1809 : }
1810 1805 : }
1811 :
1812 : /** Register a new compressor.
1813 : *
1814 : * The provided structure is copied. Its pfnFunc and user_data members should
1815 : * remain valid beyond this call however.
1816 : *
1817 : * @param compressor Compressor structure. Should not be null.
1818 : * @return true if successful
1819 : * @since GDAL 3.4
1820 : */
1821 2 : bool CPLRegisterCompressor(const CPLCompressor *compressor)
1822 : {
1823 2 : if (compressor->nStructVersion < 1)
1824 0 : return false;
1825 4 : std::lock_guard<std::mutex> lock(gMutex);
1826 2 : if (gpCompressors == nullptr)
1827 : {
1828 1 : gpCompressors = new std::vector<CPLCompressor *>();
1829 1 : CPLAddBuiltinCompressors();
1830 : }
1831 16 : for (size_t i = 0; i < gpCompressors->size(); ++i)
1832 : {
1833 15 : if (strcmp(compressor->pszId, (*gpCompressors)[i]->pszId) == 0)
1834 : {
1835 1 : CPLError(CE_Failure, CPLE_AppDefined,
1836 1 : "Compressor %s already registered", compressor->pszId);
1837 1 : return false;
1838 : }
1839 : }
1840 1 : CPLAddCompressor(compressor);
1841 1 : return true;
1842 : }
1843 :
1844 : /** Register a new decompressor.
1845 : *
1846 : * The provided structure is copied. Its pfnFunc and user_data members should
1847 : * remain valid beyond this call however.
1848 : *
1849 : * @param decompressor Compressor structure. Should not be null.
1850 : * @return true if successful
1851 : * @since GDAL 3.4
1852 : */
1853 2 : bool CPLRegisterDecompressor(const CPLCompressor *decompressor)
1854 : {
1855 2 : if (decompressor->nStructVersion < 1)
1856 0 : return false;
1857 4 : std::lock_guard<std::mutex> lock(gMutex);
1858 2 : if (gpDecompressors == nullptr)
1859 : {
1860 0 : gpDecompressors = new std::vector<CPLCompressor *>();
1861 0 : CPLAddBuiltinDecompressors();
1862 : }
1863 16 : for (size_t i = 0; i < gpDecompressors->size(); ++i)
1864 : {
1865 15 : if (strcmp(decompressor->pszId, (*gpDecompressors)[i]->pszId) == 0)
1866 : {
1867 1 : CPLError(CE_Failure, CPLE_AppDefined,
1868 1 : "Decompressor %s already registered", decompressor->pszId);
1869 1 : return false;
1870 : }
1871 : }
1872 1 : CPLAddDecompressor(decompressor);
1873 1 : return true;
1874 : }
1875 :
1876 : /** Return the list of registered compressors.
1877 : *
1878 : * @return list of strings. Should be freed with CSLDestroy()
1879 : * @since GDAL 3.4
1880 : */
1881 211 : char **CPLGetCompressors(void)
1882 : {
1883 211 : std::lock_guard<std::mutex> lock(gMutex);
1884 211 : if (gpCompressors == nullptr)
1885 : {
1886 0 : gpCompressors = new std::vector<CPLCompressor *>();
1887 0 : CPLAddBuiltinCompressors();
1888 : }
1889 211 : char **papszRet = nullptr;
1890 1689 : for (size_t i = 0; i < gpCompressors->size(); ++i)
1891 : {
1892 1478 : papszRet = CSLAddString(papszRet, (*gpCompressors)[i]->pszId);
1893 : }
1894 422 : return papszRet;
1895 : }
1896 :
1897 : /** Return the list of registered decompressors.
1898 : *
1899 : * @return list of strings. Should be freed with CSLDestroy()
1900 : * @since GDAL 3.4
1901 : */
1902 211 : char **CPLGetDecompressors(void)
1903 : {
1904 211 : std::lock_guard<std::mutex> lock(gMutex);
1905 211 : if (gpDecompressors == nullptr)
1906 : {
1907 0 : gpDecompressors = new std::vector<CPLCompressor *>();
1908 0 : CPLAddBuiltinDecompressors();
1909 : }
1910 211 : char **papszRet = nullptr;
1911 211 : for (size_t i = 0;
1912 1689 : gpDecompressors != nullptr && i < gpDecompressors->size(); ++i)
1913 : {
1914 1478 : papszRet = CSLAddString(papszRet, (*gpDecompressors)[i]->pszId);
1915 : }
1916 422 : return papszRet;
1917 : }
1918 :
1919 : /** Return a compressor.
1920 : *
1921 : * @param pszId Compressor id. Should NOT be NULL.
1922 : * @return compressor structure, or NULL.
1923 : * @since GDAL 3.4
1924 : */
1925 3711 : const CPLCompressor *CPLGetCompressor(const char *pszId)
1926 : {
1927 7422 : std::lock_guard<std::mutex> lock(gMutex);
1928 3711 : if (gpCompressors == nullptr)
1929 : {
1930 213 : gpCompressors = new std::vector<CPLCompressor *>();
1931 213 : CPLAddBuiltinCompressors();
1932 : }
1933 15274 : for (size_t i = 0; i < gpCompressors->size(); ++i)
1934 : {
1935 15271 : if (EQUAL(pszId, (*gpCompressors)[i]->pszId))
1936 : {
1937 3708 : return (*gpCompressors)[i];
1938 : }
1939 : }
1940 3 : return nullptr;
1941 : }
1942 :
1943 : /** Return a decompressor.
1944 : *
1945 : * @param pszId Decompressor id. Should NOT be NULL.
1946 : * @return compressor structure, or NULL.
1947 : * @since GDAL 3.4
1948 : */
1949 3712 : const CPLCompressor *CPLGetDecompressor(const char *pszId)
1950 : {
1951 7426 : std::lock_guard<std::mutex> lock(gMutex);
1952 3714 : if (gpDecompressors == nullptr)
1953 : {
1954 1805 : gpDecompressors = new std::vector<CPLCompressor *>();
1955 1805 : CPLAddBuiltinDecompressors();
1956 : }
1957 18888 : for (size_t i = 0; i < gpDecompressors->size(); ++i)
1958 : {
1959 17939 : if (EQUAL(pszId, (*gpDecompressors)[i]->pszId))
1960 : {
1961 2765 : return (*gpDecompressors)[i];
1962 : }
1963 : }
1964 949 : return nullptr;
1965 : }
1966 :
1967 : static void
1968 2276 : CPLDestroyCompressorRegistryInternal(std::vector<CPLCompressor *> *&v)
1969 : {
1970 11700 : for (size_t i = 0; v != nullptr && i < v->size(); ++i)
1971 : {
1972 9424 : CPLFree(const_cast<char *>((*v)[i]->pszId));
1973 9424 : CSLDestroy(const_cast<char **>((*v)[i]->papszMetadata));
1974 9424 : delete (*v)[i];
1975 : }
1976 2276 : delete v;
1977 2276 : v = nullptr;
1978 2276 : }
1979 :
1980 : /*! @cond Doxygen_Suppress */
1981 1138 : void CPLDestroyCompressorRegistry(void)
1982 : {
1983 2276 : std::lock_guard<std::mutex> lock(gMutex);
1984 :
1985 1138 : CPLDestroyCompressorRegistryInternal(gpCompressors);
1986 1138 : CPLDestroyCompressorRegistryInternal(gpDecompressors);
1987 1138 : }
1988 :
1989 : /*! @endcond */
|