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