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