Coverage Report

Created: 2026-04-01 06:20

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