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