Coverage Report

Created: 2026-07-25 06:50

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