Coverage Report

Created: 2025-06-13 06:29

/src/gdal/port/cpl_compressor.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 * Project:  CPL - Common Portability Library
3
 * Purpose:  Registry of compression/decompression functions
4
 * Author:   Even Rouault <even.rouault at spatialys.com>
5
 *
6
 **********************************************************************
7
 * Copyright (c) 2021, Even Rouault <even.rouault at spatialys.com>
8
 *
9
 * SPDX-License-Identifier: MIT
10
 ****************************************************************************/
11
12
#include "cpl_compressor.h"
13
#include "cpl_error.h"
14
#include "cpl_multiproc.h"
15
#include "cpl_string.h"
16
#include "cpl_conv.h"  // CPLZLibInflate()
17
18
#if defined(__clang__)
19
#pragma clang diagnostic push
20
#pragma clang diagnostic ignored "-Wdocumentation"
21
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
22
#endif
23
24
#ifdef HAVE_BLOSC
25
#include <blosc.h>
26
#endif
27
28
#ifdef HAVE_LIBDEFLATE
29
#include "libdeflate.h"
30
#else
31
#include "zlib.h"
32
#endif
33
34
#ifdef HAVE_LZMA
35
#include <lzma.h>
36
#endif
37
38
#ifdef HAVE_ZSTD
39
#include <zstd.h>
40
#endif
41
42
#ifdef HAVE_LZ4
43
#include <lz4.h>
44
#endif
45
46
#if defined(__clang__)
47
#pragma clang diagnostic pop
48
#endif
49
50
#include <limits>
51
#include <mutex>
52
#include <type_traits>
53
#include <vector>
54
55
static std::mutex gMutex;
56
static std::vector<CPLCompressor *> *gpCompressors = nullptr;
57
static std::vector<CPLCompressor *> *gpDecompressors = nullptr;
58
59
#ifdef HAVE_BLOSC
60
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_LSBWORD32(static_cast<int>(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_LSBSINT32PTR(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_LSBSINT32PTR(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
template <class T> inline T swap(T x)
968
0
{
969
0
    return x;
970
0
}
Unexecuted instantiation: cpl_compressor.cpp:signed char (anonymous namespace)::swap<signed char>(signed char)
Unexecuted instantiation: cpl_compressor.cpp:unsigned char (anonymous namespace)::swap<unsigned char>(unsigned char)
971
972
template <> inline uint16_t swap<uint16_t>(uint16_t x)
973
0
{
974
0
    return CPL_SWAP16(x);
975
0
}
976
977
template <> inline int16_t swap<int16_t>(int16_t x)
978
0
{
979
0
    return CPL_SWAP16(x);
980
0
}
981
982
template <> inline uint32_t swap<uint32_t>(uint32_t x)
983
0
{
984
0
    return CPL_SWAP32(x);
985
0
}
986
987
template <> inline int32_t swap<int32_t>(int32_t x)
988
0
{
989
0
    return CPL_SWAP32(x);
990
0
}
991
992
template <> inline uint64_t swap<uint64_t>(uint64_t x)
993
0
{
994
0
    return CPL_SWAP64(x);
995
0
}
996
997
template <> inline int64_t swap<int64_t>(int64_t x)
998
0
{
999
0
    return CPL_SWAP64(x);
1000
0
}
1001
1002
template <> inline float swap<float>(float x)
1003
0
{
1004
0
    float ret = x;
1005
0
    CPL_SWAP32PTR(&ret);
1006
0
    return ret;
1007
0
}
1008
1009
template <> inline double swap<double>(double x)
1010
0
{
1011
0
    double ret = x;
1012
0
    CPL_SWAP64PTR(&ret);
1013
0
    return ret;
1014
0
}
1015
}  // namespace
1016
1017
namespace
1018
{
1019
// Workaround -ftrapv
1020
template <class T>
1021
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW inline T SubNoOverflow(T left, T right)
1022
0
{
1023
0
    typedef typename std::make_unsigned<T>::type U;
1024
0
    U leftU = static_cast<U>(left);
1025
0
    U rightU = static_cast<U>(right);
1026
0
    leftU = static_cast<U>(leftU - rightU);
1027
0
    T ret;
1028
0
    memcpy(&ret, &leftU, sizeof(ret));
1029
0
    return leftU;
1030
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)
1031
1032
template <> inline float SubNoOverflow<float>(float x, float y)
1033
0
{
1034
0
    return x - y;
1035
0
}
1036
1037
template <> inline double SubNoOverflow<double>(double x, double y)
1038
0
{
1039
0
    return x - y;
1040
0
}
1041
}  // namespace
1042
1043
template <class T>
1044
static bool DeltaCompressor(const void *input_data, size_t input_size,
1045
                            const char *dtype, void *output_data)
1046
0
{
1047
0
    if ((input_size % sizeof(T)) != 0)
1048
0
    {
1049
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid input size");
1050
0
        return false;
1051
0
    }
1052
1053
0
    const size_t nElts = input_size / sizeof(T);
1054
0
    const T *pSrc = static_cast<const T *>(input_data);
1055
0
    T *pDst = static_cast<T *>(output_data);
1056
#ifdef CPL_MSB
1057
    const bool bNeedSwap = dtype[0] == '<';
1058
#else
1059
0
    const bool bNeedSwap = dtype[0] == '>';
1060
0
#endif
1061
0
    for (size_t i = 0; i < nElts; i++)
1062
0
    {
1063
0
        if (i == 0)
1064
0
        {
1065
0
            pDst[0] = pSrc[0];
1066
0
        }
1067
0
        else
1068
0
        {
1069
0
            if (bNeedSwap)
1070
0
            {
1071
0
                pDst[i] = swap(SubNoOverflow(swap(pSrc[i]), swap(pSrc[i - 1])));
1072
0
            }
1073
0
            else
1074
0
            {
1075
0
                pDst[i] = SubNoOverflow(pSrc[i], pSrc[i - 1]);
1076
0
            }
1077
0
        }
1078
0
    }
1079
0
    return true;
1080
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*)
1081
1082
static bool CPLDeltaCompressor(const void *input_data, size_t input_size,
1083
                               void **output_data, size_t *output_size,
1084
                               CSLConstList options,
1085
                               void * /* compressor_user_data */)
1086
0
{
1087
0
    const char *dtype = CSLFetchNameValue(options, "DTYPE");
1088
0
    if (dtype == nullptr)
1089
0
    {
1090
0
        CPLError(CE_Failure, CPLE_AppDefined, "Missing DTYPE parameter");
1091
0
        if (output_size)
1092
0
            *output_size = 0;
1093
0
        return false;
1094
0
    }
1095
0
    const char *astype = CSLFetchNameValue(options, "ASTYPE");
1096
0
    if (astype != nullptr && !EQUAL(astype, dtype))
1097
0
    {
1098
0
        CPLError(CE_Failure, CPLE_AppDefined,
1099
0
                 "Only ASTYPE=DTYPE currently supported");
1100
0
        if (output_size)
1101
0
            *output_size = 0;
1102
0
        return false;
1103
0
    }
1104
1105
0
    if (output_data != nullptr && *output_data != nullptr &&
1106
0
        output_size != nullptr && *output_size != 0)
1107
0
    {
1108
0
        if (*output_size < input_size)
1109
0
        {
1110
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
1111
0
            *output_size = input_size;
1112
0
            return false;
1113
0
        }
1114
1115
0
        if (EQUAL(dtype, "i1"))
1116
0
        {
1117
0
            if (!DeltaCompressor<int8_t>(input_data, input_size, dtype,
1118
0
                                         *output_data))
1119
0
            {
1120
0
                *output_size = 0;
1121
0
                return false;
1122
0
            }
1123
0
        }
1124
0
        else if (EQUAL(dtype, "u1"))
1125
0
        {
1126
0
            if (!DeltaCompressor<uint8_t>(input_data, input_size, dtype,
1127
0
                                          *output_data))
1128
0
            {
1129
0
                *output_size = 0;
1130
0
                return false;
1131
0
            }
1132
0
        }
1133
0
        else if (EQUAL(dtype, "<i2") || EQUAL(dtype, ">i2") ||
1134
0
                 EQUAL(dtype, "i2"))
1135
0
        {
1136
0
            if (!DeltaCompressor<int16_t>(input_data, input_size, dtype,
1137
0
                                          *output_data))
1138
0
            {
1139
0
                *output_size = 0;
1140
0
                return false;
1141
0
            }
1142
0
        }
1143
0
        else if (EQUAL(dtype, "<u2") || EQUAL(dtype, ">u2") ||
1144
0
                 EQUAL(dtype, "u2"))
1145
0
        {
1146
0
            if (!DeltaCompressor<uint16_t>(input_data, input_size, dtype,
1147
0
                                           *output_data))
1148
0
            {
1149
0
                *output_size = 0;
1150
0
                return false;
1151
0
            }
1152
0
        }
1153
0
        else if (EQUAL(dtype, "<i4") || EQUAL(dtype, ">i4") ||
1154
0
                 EQUAL(dtype, "i4"))
1155
0
        {
1156
0
            if (!DeltaCompressor<int32_t>(input_data, input_size, dtype,
1157
0
                                          *output_data))
1158
0
            {
1159
0
                *output_size = 0;
1160
0
                return false;
1161
0
            }
1162
0
        }
1163
0
        else if (EQUAL(dtype, "<u4") || EQUAL(dtype, ">u4") ||
1164
0
                 EQUAL(dtype, "u4"))
1165
0
        {
1166
0
            if (!DeltaCompressor<uint32_t>(input_data, input_size, dtype,
1167
0
                                           *output_data))
1168
0
            {
1169
0
                *output_size = 0;
1170
0
                return false;
1171
0
            }
1172
0
        }
1173
0
        else if (EQUAL(dtype, "<i8") || EQUAL(dtype, ">i8") ||
1174
0
                 EQUAL(dtype, "i8"))
1175
0
        {
1176
0
            if (!DeltaCompressor<int64_t>(input_data, input_size, dtype,
1177
0
                                          *output_data))
1178
0
            {
1179
0
                *output_size = 0;
1180
0
                return false;
1181
0
            }
1182
0
        }
1183
0
        else if (EQUAL(dtype, "<u8") || EQUAL(dtype, ">u8") ||
1184
0
                 EQUAL(dtype, "u8"))
1185
0
        {
1186
0
            if (!DeltaCompressor<uint64_t>(input_data, input_size, dtype,
1187
0
                                           *output_data))
1188
0
            {
1189
0
                *output_size = 0;
1190
0
                return false;
1191
0
            }
1192
0
        }
1193
0
        else if (EQUAL(dtype, "<f4") || EQUAL(dtype, ">f4") ||
1194
0
                 EQUAL(dtype, "f4"))
1195
0
        {
1196
0
            if (!DeltaCompressor<float>(input_data, input_size, dtype,
1197
0
                                        *output_data))
1198
0
            {
1199
0
                *output_size = 0;
1200
0
                return false;
1201
0
            }
1202
0
        }
1203
0
        else if (EQUAL(dtype, "<f8") || EQUAL(dtype, ">f8") ||
1204
0
                 EQUAL(dtype, "f8"))
1205
0
        {
1206
0
            if (!DeltaCompressor<double>(input_data, input_size, dtype,
1207
0
                                         *output_data))
1208
0
            {
1209
0
                *output_size = 0;
1210
0
                return false;
1211
0
            }
1212
0
        }
1213
0
        else
1214
0
        {
1215
0
            CPLError(CE_Failure, CPLE_NotSupported,
1216
0
                     "Unsupported dtype=%s for delta filter", dtype);
1217
0
            *output_size = 0;
1218
0
            return false;
1219
0
        }
1220
1221
0
        *output_size = input_size;
1222
0
        return true;
1223
0
    }
1224
1225
0
    if (output_data == nullptr && output_size != nullptr)
1226
0
    {
1227
0
        *output_size = input_size;
1228
0
        return true;
1229
0
    }
1230
1231
0
    if (output_data != nullptr && *output_data == nullptr &&
1232
0
        output_size != nullptr)
1233
0
    {
1234
0
        *output_data = VSI_MALLOC_VERBOSE(input_size);
1235
0
        *output_size = input_size;
1236
0
        if (*output_data == nullptr)
1237
0
            return false;
1238
0
        bool ret = CPLDeltaCompressor(input_data, input_size, output_data,
1239
0
                                      output_size, options, nullptr);
1240
0
        if (!ret)
1241
0
        {
1242
0
            VSIFree(*output_data);
1243
0
            *output_data = nullptr;
1244
0
        }
1245
0
        return ret;
1246
0
    }
1247
1248
0
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
1249
0
    return false;
1250
0
}
1251
1252
static void CPLAddCompressor(const CPLCompressor *compressor)
1253
0
{
1254
0
    CPLCompressor *copy = new CPLCompressor(*compressor);
1255
    // cppcheck-suppress uninitdata
1256
0
    copy->pszId = CPLStrdup(compressor->pszId);
1257
    // cppcheck-suppress uninitdata
1258
0
    copy->papszMetadata = CSLDuplicate(compressor->papszMetadata);
1259
0
    gpCompressors->emplace_back(copy);
1260
0
}
1261
1262
static void CPLAddBuiltinCompressors()
1263
0
{
1264
#ifdef HAVE_BLOSC
1265
    do
1266
    {
1267
        CPLCompressor sComp;
1268
        sComp.nStructVersion = 1;
1269
        sComp.eType = CCT_COMPRESSOR;
1270
        sComp.pszId = "blosc";
1271
1272
        const CPLStringList aosCompressors(
1273
            CSLTokenizeString2(blosc_list_compressors(), ",", 0));
1274
        if (aosCompressors.size() == 0)
1275
            break;
1276
        std::string options("OPTIONS=<Options>"
1277
                            "  <Option name='CNAME' type='string-select' "
1278
                            "description='Compressor name' default='");
1279
        std::string values;
1280
        std::string defaultCompressor;
1281
        bool bFoundLZ4 = false;
1282
        bool bFoundSnappy = false;
1283
        bool bFoundZlib = false;
1284
        for (int i = 0; i < aosCompressors.size(); i++)
1285
        {
1286
            values += "<Value>";
1287
            values += aosCompressors[i];
1288
            values += "</Value>";
1289
            if (strcmp(aosCompressors[i], "lz4") == 0)
1290
                bFoundLZ4 = true;
1291
            else if (strcmp(aosCompressors[i], "snappy") == 0)
1292
                bFoundSnappy = true;
1293
            else if (strcmp(aosCompressors[i], "zlib") == 0)
1294
                bFoundZlib = true;
1295
        }
1296
        options += bFoundLZ4      ? "lz4"
1297
                   : bFoundSnappy ? "snappy"
1298
                   : bFoundZlib   ? "zlib"
1299
                                  : aosCompressors[0];
1300
        options += "'>";
1301
        options += values;
1302
        options +=
1303
            "  </Option>"
1304
            "  <Option name='CLEVEL' type='int' description='Compression "
1305
            "level' min='1' max='9' default='5' />"
1306
            "  <Option name='SHUFFLE' type='string-select' description='Type "
1307
            "of shuffle algorithm' default='BYTE'>"
1308
            "    <Value alias='0'>NONE</Value>"
1309
            "    <Value alias='1'>BYTE</Value>"
1310
            "    <Value alias='2'>BIT</Value>"
1311
            "  </Option>"
1312
            "  <Option name='BLOCKSIZE' type='int' description='Block size' "
1313
            "default='0' />"
1314
            "  <Option name='TYPESIZE' type='int' description='Number of bytes "
1315
            "for the atomic type' default='1' />"
1316
            "  <Option name='NUM_THREADS' type='string' "
1317
            "description='Number of worker threads for compression. Can be set "
1318
            "to ALL_CPUS' default='1' />"
1319
            "</Options>";
1320
1321
        const char *const apszMetadata[] = {
1322
            "BLOSC_VERSION=" BLOSC_VERSION_STRING, options.c_str(), nullptr};
1323
        sComp.papszMetadata = apszMetadata;
1324
        sComp.pfnFunc = CPLBloscCompressor;
1325
        sComp.user_data = nullptr;
1326
        CPLAddCompressor(&sComp);
1327
    } while (0);
1328
#endif
1329
0
    {
1330
0
        CPLCompressor sComp;
1331
0
        sComp.nStructVersion = 1;
1332
0
        sComp.eType = CCT_COMPRESSOR;
1333
0
        sComp.pszId = "zlib";
1334
0
        const char *pszOptions =
1335
0
            "OPTIONS=<Options>"
1336
0
            "  <Option name='LEVEL' type='int' description='Compression level' "
1337
0
            "min='1' max='9' default='6' />"
1338
0
            "</Options>";
1339
0
        const char *const apszMetadata[] = {pszOptions, nullptr};
1340
0
        sComp.papszMetadata = apszMetadata;
1341
0
        sComp.pfnFunc = CPLZlibCompressor;
1342
0
        sComp.user_data = const_cast<char *>("zlib");
1343
0
        CPLAddCompressor(&sComp);
1344
0
    }
1345
0
    {
1346
0
        CPLCompressor sComp;
1347
0
        sComp.nStructVersion = 1;
1348
0
        sComp.eType = CCT_COMPRESSOR;
1349
0
        sComp.pszId = "gzip";
1350
0
        const char *pszOptions =
1351
0
            "OPTIONS=<Options>"
1352
0
            "  <Option name='LEVEL' type='int' description='Compression level' "
1353
0
            "min='1' max='9' default='6' />"
1354
0
            "</Options>";
1355
0
        const char *const apszMetadata[] = {pszOptions, nullptr};
1356
0
        sComp.papszMetadata = apszMetadata;
1357
0
        sComp.pfnFunc = CPLZlibCompressor;
1358
0
        sComp.user_data = const_cast<char *>("gzip");
1359
0
        CPLAddCompressor(&sComp);
1360
0
    }
1361
0
#ifdef HAVE_LZMA
1362
0
    {
1363
0
        CPLCompressor sComp;
1364
0
        sComp.nStructVersion = 1;
1365
0
        sComp.eType = CCT_COMPRESSOR;
1366
0
        sComp.pszId = "lzma";
1367
0
        const char *pszOptions =
1368
0
            "OPTIONS=<Options>"
1369
0
            "  <Option name='PRESET' type='int' description='Compression "
1370
0
            "level' min='0' max='9' default='6' />"
1371
0
            "  <Option name='DELTA' type='int' description='Delta distance in "
1372
0
            "byte' default='1' />"
1373
0
            "</Options>";
1374
0
        const char *const apszMetadata[] = {pszOptions, nullptr};
1375
0
        sComp.papszMetadata = apszMetadata;
1376
0
        sComp.pfnFunc = CPLLZMACompressor;
1377
0
        sComp.user_data = nullptr;
1378
0
        CPLAddCompressor(&sComp);
1379
0
    }
1380
0
#endif
1381
#ifdef HAVE_ZSTD
1382
    {
1383
        CPLCompressor sComp;
1384
        sComp.nStructVersion = 1;
1385
        sComp.eType = CCT_COMPRESSOR;
1386
        sComp.pszId = "zstd";
1387
        const char *pszOptions =
1388
            "OPTIONS=<Options>"
1389
            "  <Option name='LEVEL' type='int' description='Compression level' "
1390
            "min='1' max='22' default='13' />"
1391
            "  <Option name='CHECKSUM' type='boolean' description='Whether "
1392
            "to store a checksum when writing that will be verified' "
1393
            "default='NO' />"
1394
            "</Options>";
1395
        const char *const apszMetadata[] = {pszOptions, nullptr};
1396
        sComp.papszMetadata = apszMetadata;
1397
        sComp.pfnFunc = CPLZSTDCompressor;
1398
        sComp.user_data = nullptr;
1399
        CPLAddCompressor(&sComp);
1400
    }
1401
#endif
1402
#ifdef HAVE_LZ4
1403
    {
1404
        CPLCompressor sComp;
1405
        sComp.nStructVersion = 1;
1406
        sComp.eType = CCT_COMPRESSOR;
1407
        sComp.pszId = "lz4";
1408
        const char *pszOptions =
1409
            "OPTIONS=<Options>"
1410
            "  <Option name='ACCELERATION' type='int' "
1411
            "description='Acceleration factor. The higher, the less "
1412
            "compressed' min='1' default='1' />"
1413
            "  <Option name='HEADER' type='boolean' description='Whether a "
1414
            "header with the uncompressed size should be included (as used by "
1415
            "Zarr)' default='YES' />"
1416
            "</Options>";
1417
        const char *const apszMetadata[] = {pszOptions, nullptr};
1418
        sComp.papszMetadata = apszMetadata;
1419
        sComp.pfnFunc = CPLLZ4Compressor;
1420
        sComp.user_data = nullptr;
1421
        CPLAddCompressor(&sComp);
1422
    }
1423
#endif
1424
0
    {
1425
0
        CPLCompressor sComp;
1426
0
        sComp.nStructVersion = 1;
1427
0
        sComp.eType = CCT_FILTER;
1428
0
        sComp.pszId = "delta";
1429
0
        const char *pszOptions =
1430
0
            "OPTIONS=<Options>"
1431
0
            "  <Option name='DTYPE' type='string' description='Data type "
1432
0
            "following NumPy array protocol type string (typestr) format'/>"
1433
0
            "</Options>";
1434
0
        const char *const apszMetadata[] = {pszOptions, nullptr};
1435
0
        sComp.papszMetadata = apszMetadata;
1436
0
        sComp.pfnFunc = CPLDeltaCompressor;
1437
0
        sComp.user_data = nullptr;
1438
0
        CPLAddCompressor(&sComp);
1439
0
    }
1440
0
}
1441
1442
static bool CPLZlibDecompressor(const void *input_data, size_t input_size,
1443
                                void **output_data, size_t *output_size,
1444
                                CSLConstList /* options */,
1445
                                void * /* compressor_user_data */)
1446
0
{
1447
0
    if (output_data != nullptr && *output_data != nullptr &&
1448
0
        output_size != nullptr && *output_size != 0)
1449
0
    {
1450
0
        size_t nOutBytes = 0;
1451
0
        if (nullptr == CPLZLibInflate(input_data, input_size, *output_data,
1452
0
                                      *output_size, &nOutBytes))
1453
0
        {
1454
0
            *output_size = 0;
1455
0
            return false;
1456
0
        }
1457
1458
0
        *output_size = nOutBytes;
1459
0
        return true;
1460
0
    }
1461
1462
0
    if (output_data == nullptr && output_size != nullptr)
1463
0
    {
1464
0
        size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 4
1465
0
                              ? input_size * 4
1466
0
                              : input_size;
1467
0
        void *tmpOutBuffer = VSIMalloc(nOutSize);
1468
0
        if (tmpOutBuffer == nullptr)
1469
0
        {
1470
0
            *output_size = 0;
1471
0
            return false;
1472
0
        }
1473
0
        tmpOutBuffer = CPLZLibInflateEx(input_data, input_size, tmpOutBuffer,
1474
0
                                        nOutSize, true, &nOutSize);
1475
0
        if (!tmpOutBuffer)
1476
0
        {
1477
0
            *output_size = 0;
1478
0
            return false;
1479
0
        }
1480
0
        VSIFree(tmpOutBuffer);
1481
0
        *output_size = nOutSize;
1482
0
        return true;
1483
0
    }
1484
1485
0
    if (output_data != nullptr && *output_data == nullptr &&
1486
0
        output_size != nullptr)
1487
0
    {
1488
0
        size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 4
1489
0
                              ? input_size * 4
1490
0
                              : input_size;
1491
0
        void *tmpOutBuffer = VSIMalloc(nOutSize);
1492
0
        if (tmpOutBuffer == nullptr)
1493
0
        {
1494
0
            *output_size = 0;
1495
0
            return false;
1496
0
        }
1497
0
        size_t nOutSizeOut = 0;
1498
0
        tmpOutBuffer = CPLZLibInflateEx(input_data, input_size, tmpOutBuffer,
1499
0
                                        nOutSize, true, &nOutSizeOut);
1500
0
        if (!tmpOutBuffer)
1501
0
        {
1502
0
            *output_size = 0;
1503
0
            return false;
1504
0
        }
1505
0
        *output_data = VSIRealloc(tmpOutBuffer, nOutSizeOut);  // cannot fail
1506
0
        *output_size = nOutSizeOut;
1507
0
        return true;
1508
0
    }
1509
1510
0
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
1511
0
    return false;
1512
0
}
1513
1514
namespace
1515
{
1516
// Workaround -ftrapv
1517
template <class T>
1518
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW inline T AddNoOverflow(T left, T right)
1519
0
{
1520
0
    typedef typename std::make_unsigned<T>::type U;
1521
0
    U leftU = static_cast<U>(left);
1522
0
    U rightU = static_cast<U>(right);
1523
0
    leftU = static_cast<U>(leftU + rightU);
1524
0
    T ret;
1525
0
    memcpy(&ret, &leftU, sizeof(ret));
1526
0
    return leftU;
1527
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)
1528
1529
template <> inline float AddNoOverflow<float>(float x, float y)
1530
0
{
1531
0
    return x + y;
1532
0
}
1533
1534
template <> inline double AddNoOverflow<double>(double x, double y)
1535
0
{
1536
0
    return x + y;
1537
0
}
1538
}  // namespace
1539
1540
template <class T>
1541
static bool DeltaDecompressor(const void *input_data, size_t input_size,
1542
                              const char *dtype, void *output_data)
1543
0
{
1544
0
    if ((input_size % sizeof(T)) != 0)
1545
0
    {
1546
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid input size");
1547
0
        return false;
1548
0
    }
1549
1550
0
    const size_t nElts = input_size / sizeof(T);
1551
0
    const T *pSrc = static_cast<const T *>(input_data);
1552
0
    T *pDst = static_cast<T *>(output_data);
1553
#ifdef CPL_MSB
1554
    const bool bNeedSwap = dtype[0] == '<';
1555
#else
1556
0
    const bool bNeedSwap = dtype[0] == '>';
1557
0
#endif
1558
0
    for (size_t i = 0; i < nElts; i++)
1559
0
    {
1560
0
        if (i == 0)
1561
0
        {
1562
0
            pDst[0] = pSrc[0];
1563
0
        }
1564
0
        else
1565
0
        {
1566
0
            if (bNeedSwap)
1567
0
            {
1568
0
                pDst[i] = swap(AddNoOverflow(swap(pDst[i - 1]), swap(pSrc[i])));
1569
0
            }
1570
0
            else
1571
0
            {
1572
0
                pDst[i] = AddNoOverflow(pDst[i - 1], pSrc[i]);
1573
0
            }
1574
0
        }
1575
0
    }
1576
0
    return true;
1577
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*)
1578
1579
static bool CPLDeltaDecompressor(const void *input_data, size_t input_size,
1580
                                 void **output_data, size_t *output_size,
1581
                                 CSLConstList options,
1582
                                 void * /* compressor_user_data */)
1583
0
{
1584
0
    const char *dtype = CSLFetchNameValue(options, "DTYPE");
1585
0
    if (dtype == nullptr)
1586
0
    {
1587
0
        CPLError(CE_Failure, CPLE_AppDefined, "Missing DTYPE parameter");
1588
0
        if (output_size)
1589
0
            *output_size = 0;
1590
0
        return false;
1591
0
    }
1592
0
    const char *astype = CSLFetchNameValue(options, "ASTYPE");
1593
0
    if (astype != nullptr && !EQUAL(astype, dtype))
1594
0
    {
1595
0
        CPLError(CE_Failure, CPLE_AppDefined,
1596
0
                 "Only ASTYPE=DTYPE currently supported");
1597
0
        if (output_size)
1598
0
            *output_size = 0;
1599
0
        return false;
1600
0
    }
1601
1602
0
    if (output_data != nullptr && *output_data != nullptr &&
1603
0
        output_size != nullptr && *output_size != 0)
1604
0
    {
1605
0
        if (*output_size < input_size)
1606
0
        {
1607
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
1608
0
            *output_size = input_size;
1609
0
            return false;
1610
0
        }
1611
1612
0
        if (EQUAL(dtype, "i1"))
1613
0
        {
1614
0
            if (!DeltaDecompressor<int8_t>(input_data, input_size, dtype,
1615
0
                                           *output_data))
1616
0
            {
1617
0
                *output_size = 0;
1618
0
                return false;
1619
0
            }
1620
0
        }
1621
0
        else if (EQUAL(dtype, "u1"))
1622
0
        {
1623
0
            if (!DeltaDecompressor<uint8_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, "<i2") || EQUAL(dtype, ">i2") ||
1631
0
                 EQUAL(dtype, "i2"))
1632
0
        {
1633
0
            if (!DeltaDecompressor<int16_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, "<u2") || EQUAL(dtype, ">u2") ||
1641
0
                 EQUAL(dtype, "u2"))
1642
0
        {
1643
0
            if (!DeltaDecompressor<uint16_t>(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, "<i4") || EQUAL(dtype, ">i4") ||
1651
0
                 EQUAL(dtype, "i4"))
1652
0
        {
1653
0
            if (!DeltaDecompressor<int32_t>(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 if (EQUAL(dtype, "<u4") || EQUAL(dtype, ">u4") ||
1661
0
                 EQUAL(dtype, "u4"))
1662
0
        {
1663
0
            if (!DeltaDecompressor<uint32_t>(input_data, input_size, dtype,
1664
0
                                             *output_data))
1665
0
            {
1666
0
                *output_size = 0;
1667
0
                return false;
1668
0
            }
1669
0
        }
1670
0
        else if (EQUAL(dtype, "<i8") || EQUAL(dtype, ">i8") ||
1671
0
                 EQUAL(dtype, "i8"))
1672
0
        {
1673
0
            if (!DeltaDecompressor<int64_t>(input_data, input_size, dtype,
1674
0
                                            *output_data))
1675
0
            {
1676
0
                *output_size = 0;
1677
0
                return false;
1678
0
            }
1679
0
        }
1680
0
        else if (EQUAL(dtype, "<u8") || EQUAL(dtype, ">u8") ||
1681
0
                 EQUAL(dtype, "u8"))
1682
0
        {
1683
0
            if (!DeltaDecompressor<uint64_t>(input_data, input_size, dtype,
1684
0
                                             *output_data))
1685
0
            {
1686
0
                *output_size = 0;
1687
0
                return false;
1688
0
            }
1689
0
        }
1690
0
        else if (EQUAL(dtype, "<f4") || EQUAL(dtype, ">f4") ||
1691
0
                 EQUAL(dtype, "f4"))
1692
0
        {
1693
0
            if (!DeltaDecompressor<float>(input_data, input_size, dtype,
1694
0
                                          *output_data))
1695
0
            {
1696
0
                *output_size = 0;
1697
0
                return false;
1698
0
            }
1699
0
        }
1700
0
        else if (EQUAL(dtype, "<f8") || EQUAL(dtype, ">f8") ||
1701
0
                 EQUAL(dtype, "f8"))
1702
0
        {
1703
0
            if (!DeltaDecompressor<double>(input_data, input_size, dtype,
1704
0
                                           *output_data))
1705
0
            {
1706
0
                *output_size = 0;
1707
0
                return false;
1708
0
            }
1709
0
        }
1710
0
        else
1711
0
        {
1712
0
            CPLError(CE_Failure, CPLE_NotSupported,
1713
0
                     "Unsupported dtype=%s for delta filter", dtype);
1714
0
            *output_size = 0;
1715
0
            return false;
1716
0
        }
1717
1718
0
        *output_size = input_size;
1719
0
        return true;
1720
0
    }
1721
1722
0
    if (output_data == nullptr && output_size != nullptr)
1723
0
    {
1724
0
        *output_size = input_size;
1725
0
        return true;
1726
0
    }
1727
1728
0
    if (output_data != nullptr && *output_data == nullptr &&
1729
0
        output_size != nullptr)
1730
0
    {
1731
0
        *output_data = VSI_MALLOC_VERBOSE(input_size);
1732
0
        *output_size = input_size;
1733
0
        if (*output_data == nullptr)
1734
0
            return false;
1735
0
        bool ret = CPLDeltaDecompressor(input_data, input_size, output_data,
1736
0
                                        output_size, options, nullptr);
1737
0
        if (!ret)
1738
0
        {
1739
0
            VSIFree(*output_data);
1740
0
            *output_data = nullptr;
1741
0
        }
1742
0
        return ret;
1743
0
    }
1744
1745
0
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
1746
0
    return false;
1747
0
}
1748
1749
static void CPLAddDecompressor(const CPLCompressor *decompressor)
1750
0
{
1751
0
    CPLCompressor *copy = new CPLCompressor(*decompressor);
1752
    // cppcheck-suppress uninitdata
1753
0
    copy->pszId = CPLStrdup(decompressor->pszId);
1754
    // cppcheck-suppress uninitdata
1755
0
    copy->papszMetadata = CSLDuplicate(decompressor->papszMetadata);
1756
0
    gpDecompressors->emplace_back(copy);
1757
0
}
1758
1759
static void CPLAddBuiltinDecompressors()
1760
0
{
1761
#ifdef HAVE_BLOSC
1762
    {
1763
        CPLCompressor sComp;
1764
        sComp.nStructVersion = 1;
1765
        sComp.eType = CCT_COMPRESSOR;
1766
        sComp.pszId = "blosc";
1767
        const char *pszOptions =
1768
            "OPTIONS=<Options>"
1769
            "  <Option name='NUM_THREADS' type='string' "
1770
            "description='Number of worker threads for decompression. Can be "
1771
            "set to ALL_CPUS' default='1' />"
1772
            "</Options>";
1773
        const char *const apszMetadata[] = {
1774
            "BLOSC_VERSION=" BLOSC_VERSION_STRING, pszOptions, nullptr};
1775
        sComp.papszMetadata = apszMetadata;
1776
        sComp.pfnFunc = CPLBloscDecompressor;
1777
        sComp.user_data = nullptr;
1778
        CPLAddDecompressor(&sComp);
1779
    }
1780
#endif
1781
0
    {
1782
0
        CPLCompressor sComp;
1783
0
        sComp.nStructVersion = 1;
1784
0
        sComp.eType = CCT_COMPRESSOR;
1785
0
        sComp.pszId = "zlib";
1786
0
        sComp.papszMetadata = nullptr;
1787
0
        sComp.pfnFunc = CPLZlibDecompressor;
1788
0
        sComp.user_data = nullptr;
1789
0
        CPLAddDecompressor(&sComp);
1790
0
    }
1791
0
    {
1792
0
        CPLCompressor sComp;
1793
0
        sComp.nStructVersion = 1;
1794
0
        sComp.eType = CCT_COMPRESSOR;
1795
0
        sComp.pszId = "gzip";
1796
0
        sComp.papszMetadata = nullptr;
1797
0
        sComp.pfnFunc = CPLZlibDecompressor;
1798
0
        sComp.user_data = nullptr;
1799
0
        CPLAddDecompressor(&sComp);
1800
0
    }
1801
0
#ifdef HAVE_LZMA
1802
0
    {
1803
0
        CPLCompressor sComp;
1804
0
        sComp.nStructVersion = 1;
1805
0
        sComp.eType = CCT_COMPRESSOR;
1806
0
        sComp.pszId = "lzma";
1807
0
        sComp.papszMetadata = nullptr;
1808
0
        sComp.pfnFunc = CPLLZMADecompressor;
1809
0
        sComp.user_data = nullptr;
1810
0
        CPLAddDecompressor(&sComp);
1811
0
    }
1812
0
#endif
1813
#ifdef HAVE_ZSTD
1814
    {
1815
        CPLCompressor sComp;
1816
        sComp.nStructVersion = 1;
1817
        sComp.eType = CCT_COMPRESSOR;
1818
        sComp.pszId = "zstd";
1819
        sComp.papszMetadata = nullptr;
1820
        sComp.pfnFunc = CPLZSTDDecompressor;
1821
        sComp.user_data = nullptr;
1822
        CPLAddDecompressor(&sComp);
1823
    }
1824
#endif
1825
#ifdef HAVE_LZ4
1826
    {
1827
        CPLCompressor sComp;
1828
        sComp.nStructVersion = 1;
1829
        sComp.eType = CCT_COMPRESSOR;
1830
        sComp.pszId = "lz4";
1831
        const char *pszOptions =
1832
            "OPTIONS=<Options>"
1833
            "  <Option name='HEADER' type='boolean' description='Whether a "
1834
            "header with the uncompressed size should be included (as used by "
1835
            "Zarr)' default='YES' />"
1836
            "</Options>";
1837
        const char *const apszMetadata[] = {pszOptions, nullptr};
1838
        sComp.papszMetadata = apszMetadata;
1839
        sComp.pfnFunc = CPLLZ4Decompressor;
1840
        sComp.user_data = nullptr;
1841
        CPLAddDecompressor(&sComp);
1842
    }
1843
#endif
1844
0
    {
1845
0
        CPLCompressor sComp;
1846
0
        sComp.nStructVersion = 1;
1847
0
        sComp.eType = CCT_FILTER;
1848
0
        sComp.pszId = "delta";
1849
0
        const char *pszOptions =
1850
0
            "OPTIONS=<Options>"
1851
0
            "  <Option name='DTYPE' type='string' description='Data type "
1852
0
            "following NumPy array protocol type string (typestr) format'/>"
1853
0
            "</Options>";
1854
0
        const char *const apszMetadata[] = {pszOptions, nullptr};
1855
0
        sComp.papszMetadata = apszMetadata;
1856
0
        sComp.pfnFunc = CPLDeltaDecompressor;
1857
0
        sComp.user_data = nullptr;
1858
0
        CPLAddDecompressor(&sComp);
1859
0
    }
1860
0
}
1861
1862
/** Register a new compressor.
1863
 *
1864
 * The provided structure is copied. Its pfnFunc and user_data members should
1865
 * remain valid beyond this call however.
1866
 *
1867
 * @param compressor Compressor structure. Should not be null.
1868
 * @return true if successful
1869
 * @since GDAL 3.4
1870
 */
1871
bool CPLRegisterCompressor(const CPLCompressor *compressor)
1872
0
{
1873
0
    if (compressor->nStructVersion < 1)
1874
0
        return false;
1875
0
    std::lock_guard<std::mutex> lock(gMutex);
1876
0
    if (gpCompressors == nullptr)
1877
0
    {
1878
0
        gpCompressors = new std::vector<CPLCompressor *>();
1879
0
        CPLAddBuiltinCompressors();
1880
0
    }
1881
0
    for (size_t i = 0; i < gpCompressors->size(); ++i)
1882
0
    {
1883
0
        if (strcmp(compressor->pszId, (*gpCompressors)[i]->pszId) == 0)
1884
0
        {
1885
0
            CPLError(CE_Failure, CPLE_AppDefined,
1886
0
                     "Compressor %s already registered", compressor->pszId);
1887
0
            return false;
1888
0
        }
1889
0
    }
1890
0
    CPLAddCompressor(compressor);
1891
0
    return true;
1892
0
}
1893
1894
/** Register a new decompressor.
1895
 *
1896
 * The provided structure is copied. Its pfnFunc and user_data members should
1897
 * remain valid beyond this call however.
1898
 *
1899
 * @param decompressor Compressor structure. Should not be null.
1900
 * @return true if successful
1901
 * @since GDAL 3.4
1902
 */
1903
bool CPLRegisterDecompressor(const CPLCompressor *decompressor)
1904
0
{
1905
0
    if (decompressor->nStructVersion < 1)
1906
0
        return false;
1907
0
    std::lock_guard<std::mutex> lock(gMutex);
1908
0
    if (gpDecompressors == nullptr)
1909
0
    {
1910
0
        gpDecompressors = new std::vector<CPLCompressor *>();
1911
0
        CPLAddBuiltinDecompressors();
1912
0
    }
1913
0
    for (size_t i = 0; i < gpDecompressors->size(); ++i)
1914
0
    {
1915
0
        if (strcmp(decompressor->pszId, (*gpDecompressors)[i]->pszId) == 0)
1916
0
        {
1917
0
            CPLError(CE_Failure, CPLE_AppDefined,
1918
0
                     "Decompressor %s already registered", decompressor->pszId);
1919
0
            return false;
1920
0
        }
1921
0
    }
1922
0
    CPLAddDecompressor(decompressor);
1923
0
    return true;
1924
0
}
1925
1926
/** Return the list of registered compressors.
1927
 *
1928
 * @return list of strings. Should be freed with CSLDestroy()
1929
 * @since GDAL 3.4
1930
 */
1931
char **CPLGetCompressors(void)
1932
0
{
1933
0
    std::lock_guard<std::mutex> lock(gMutex);
1934
0
    if (gpCompressors == nullptr)
1935
0
    {
1936
0
        gpCompressors = new std::vector<CPLCompressor *>();
1937
0
        CPLAddBuiltinCompressors();
1938
0
    }
1939
0
    char **papszRet = nullptr;
1940
0
    for (size_t i = 0; i < gpCompressors->size(); ++i)
1941
0
    {
1942
0
        papszRet = CSLAddString(papszRet, (*gpCompressors)[i]->pszId);
1943
0
    }
1944
0
    return papszRet;
1945
0
}
1946
1947
/** Return the list of registered decompressors.
1948
 *
1949
 * @return list of strings. Should be freed with CSLDestroy()
1950
 * @since GDAL 3.4
1951
 */
1952
char **CPLGetDecompressors(void)
1953
0
{
1954
0
    std::lock_guard<std::mutex> lock(gMutex);
1955
0
    if (gpDecompressors == nullptr)
1956
0
    {
1957
0
        gpDecompressors = new std::vector<CPLCompressor *>();
1958
0
        CPLAddBuiltinDecompressors();
1959
0
    }
1960
0
    char **papszRet = nullptr;
1961
0
    for (size_t i = 0;
1962
0
         gpDecompressors != nullptr && i < gpDecompressors->size(); ++i)
1963
0
    {
1964
0
        papszRet = CSLAddString(papszRet, (*gpDecompressors)[i]->pszId);
1965
0
    }
1966
0
    return papszRet;
1967
0
}
1968
1969
/** Return a compressor.
1970
 *
1971
 * @param pszId Compressor id. Should NOT be NULL.
1972
 * @return compressor structure, or NULL.
1973
 * @since GDAL 3.4
1974
 */
1975
const CPLCompressor *CPLGetCompressor(const char *pszId)
1976
0
{
1977
0
    std::lock_guard<std::mutex> lock(gMutex);
1978
0
    if (gpCompressors == nullptr)
1979
0
    {
1980
0
        gpCompressors = new std::vector<CPLCompressor *>();
1981
0
        CPLAddBuiltinCompressors();
1982
0
    }
1983
0
    for (size_t i = 0; i < gpCompressors->size(); ++i)
1984
0
    {
1985
0
        if (EQUAL(pszId, (*gpCompressors)[i]->pszId))
1986
0
        {
1987
0
            return (*gpCompressors)[i];
1988
0
        }
1989
0
    }
1990
0
    return nullptr;
1991
0
}
1992
1993
/** Return a decompressor.
1994
 *
1995
 * @param pszId Decompressor id. Should NOT be NULL.
1996
 * @return compressor structure, or NULL.
1997
 * @since GDAL 3.4
1998
 */
1999
const CPLCompressor *CPLGetDecompressor(const char *pszId)
2000
0
{
2001
0
    std::lock_guard<std::mutex> lock(gMutex);
2002
0
    if (gpDecompressors == nullptr)
2003
0
    {
2004
0
        gpDecompressors = new std::vector<CPLCompressor *>();
2005
0
        CPLAddBuiltinDecompressors();
2006
0
    }
2007
0
    for (size_t i = 0; i < gpDecompressors->size(); ++i)
2008
0
    {
2009
0
        if (EQUAL(pszId, (*gpDecompressors)[i]->pszId))
2010
0
        {
2011
0
            return (*gpDecompressors)[i];
2012
0
        }
2013
0
    }
2014
0
    return nullptr;
2015
0
}
2016
2017
static void
2018
CPLDestroyCompressorRegistryInternal(std::vector<CPLCompressor *> *&v)
2019
0
{
2020
0
    for (size_t i = 0; v != nullptr && i < v->size(); ++i)
2021
0
    {
2022
0
        CPLFree(const_cast<char *>((*v)[i]->pszId));
2023
0
        CSLDestroy(const_cast<char **>((*v)[i]->papszMetadata));
2024
0
        delete (*v)[i];
2025
0
    }
2026
0
    delete v;
2027
0
    v = nullptr;
2028
0
}
2029
2030
/*! @cond Doxygen_Suppress */
2031
void CPLDestroyCompressorRegistry(void)
2032
0
{
2033
0
    std::lock_guard<std::mutex> lock(gMutex);
2034
2035
0
    CPLDestroyCompressorRegistryInternal(gpCompressors);
2036
0
    CPLDestroyCompressorRegistryInternal(gpDecompressors);
2037
0
}
2038
2039
/*! @endcond */