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