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