• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.82
/port/cpl_compressor.cpp
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,
7✔
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 &&
7✔
66
        output_size != nullptr && *output_size != 0)
5✔
67
    {
68
        const int clevel = atoi(CSLFetchNameValueDef(options, "CLEVEL", "5"));
5✔
69
        const char *pszShuffle =
70
            CSLFetchNameValueDef(options, "SHUFFLE", "BYTE");
5✔
71
        const int shuffle =
5✔
72
            (EQUAL(pszShuffle, "BYTE") || EQUAL(pszShuffle, "1"))
1✔
73
                ? BLOSC_SHUFFLE
6✔
74
            : (EQUAL(pszShuffle, "BIT") || EQUAL(pszShuffle, "2"))
1✔
75
                ? BLOSC_BITSHUFFLE
2✔
76
                : BLOSC_NOSHUFFLE;
77
        const int typesize =
78
            atoi(CSLFetchNameValueDef(options, "TYPESIZE", "1"));
5✔
79
        const char *compressor =
80
            CSLFetchNameValueDef(options, "CNAME", BLOSC_LZ4_COMPNAME);
5✔
81
        const int blocksize =
82
            atoi(CSLFetchNameValueDef(options, "BLOCKSIZE", "0"));
5✔
83
        if (blocksize < 0)
5✔
84
        {
85
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid BLOCKSIZE");
×
86
            return false;
×
87
        }
88
        const char *pszNumThreads =
89
            CSLFetchNameValueDef(options, "NUM_THREADS", "1");
5✔
90
        const int numthreads = EQUAL(pszNumThreads, "ALL_CPUS")
5✔
91
                                   ? CPLGetNumCPUs()
5✔
92
                                   : atoi(pszNumThreads);
5✔
93
        int ret = blosc_compress_ctx(clevel, shuffle, typesize, input_size,
5✔
94
                                     input_data, *output_data, *output_size,
95
                                     compressor, blocksize, numthreads);
96
        if (ret < 0)
5✔
97
        {
98
            *output_size = 0;
1✔
99
            return false;
1✔
100
        }
101
        if (ret == 0)
4✔
102
        {
103
            *output_size = input_size + BLOSC_MAX_OVERHEAD;
×
104
            return false;
×
105
        }
106
        *output_size = ret;
4✔
107
        return true;
4✔
108
    }
109

110
    if (output_data == nullptr && output_size != nullptr)
2✔
111
    {
112
        *output_size = input_size + BLOSC_MAX_OVERHEAD;
1✔
113
        return true;
1✔
114
    }
115

116
    if (output_data != nullptr && *output_data == nullptr &&
1✔
117
        output_size != nullptr)
118
    {
119
        size_t nSafeSize = input_size + BLOSC_MAX_OVERHEAD;
1✔
120
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
121
        *output_size = nSafeSize;
1✔
122
        if (*output_data == nullptr)
1✔
123
            return false;
×
124
        bool ret = CPLBloscCompressor(input_data, input_size, output_data,
1✔
125
                                      output_size, options, nullptr);
126
        if (!ret)
1✔
127
        {
128
            VSIFree(*output_data);
×
129
            *output_data = nullptr;
×
130
        }
131
        return ret;
1✔
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,
7✔
139
                                 void **output_data, size_t *output_size,
140
                                 CSLConstList options,
141
                                 void * /* compressor_user_data */)
142
{
143
    size_t nSafeSize = 0;
7✔
144
    if (blosc_cbuffer_validate(input_data, input_size, &nSafeSize) < 0)
7✔
145
    {
146
        *output_size = 0;
×
147
        return false;
×
148
    }
149

150
    if (output_data != nullptr && *output_data != nullptr &&
7✔
151
        output_size != nullptr && *output_size != 0)
5✔
152
    {
153
        if (*output_size < nSafeSize)
5✔
154
        {
155
            *output_size = nSafeSize;
×
156
            return false;
×
157
        }
158

159
        const char *pszNumThreads =
160
            CSLFetchNameValueDef(options, "NUM_THREADS", "1");
5✔
161
        const int numthreads = EQUAL(pszNumThreads, "ALL_CPUS")
5✔
162
                                   ? CPLGetNumCPUs()
5✔
163
                                   : atoi(pszNumThreads);
5✔
164
        if (blosc_decompress_ctx(input_data, *output_data, *output_size,
5✔
165
                                 numthreads) <= 0)
5✔
166
        {
167
            *output_size = 0;
×
168
            return false;
×
169
        }
170

171
        *output_size = nSafeSize;
5✔
172
        return true;
5✔
173
    }
174

175
    if (output_data == nullptr && output_size != nullptr)
2✔
176
    {
177
        *output_size = nSafeSize;
1✔
178
        return true;
1✔
179
    }
180

181
    if (output_data != nullptr && *output_data == nullptr &&
1✔
182
        output_size != nullptr)
183
    {
184
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
185
        *output_size = nSafeSize;
1✔
186
        if (*output_data == nullptr)
1✔
187
            return false;
×
188
        bool ret = CPLBloscDecompressor(input_data, input_size, output_data,
1✔
189
                                        output_size, options, nullptr);
190
        if (!ret)
1✔
191
        {
192
            VSIFree(*output_data);
×
193
            *output_data = nullptr;
×
194
        }
195
        return ret;
1✔
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,
5✔
206
                              void **output_data, size_t *output_size,
207
                              CSLConstList options,
208
                              void * /* compressor_user_data */)
209
{
210
    if (output_data != nullptr && *output_data != nullptr &&
5✔
211
        output_size != nullptr && *output_size != 0)
3✔
212
    {
213
        const int preset = atoi(CSLFetchNameValueDef(options, "PRESET", "6"));
3✔
214
        const int delta = atoi(CSLFetchNameValueDef(options, "DELTA", "1"));
3✔
215

216
        lzma_filter filters[3];
217
        lzma_options_delta opt_delta;
218
        lzma_options_lzma opt_lzma;
219

220
        opt_delta.type = LZMA_DELTA_TYPE_BYTE;
3✔
221
        opt_delta.dist = delta;
3✔
222
        filters[0].id = LZMA_FILTER_DELTA;
3✔
223
        filters[0].options = &opt_delta;
3✔
224

225
        lzma_lzma_preset(&opt_lzma, preset);
3✔
226
        filters[1].id = LZMA_FILTER_LZMA2;
3✔
227
        filters[1].options = &opt_lzma;
3✔
228

229
        filters[2].id = LZMA_VLI_UNKNOWN;
3✔
230
        filters[2].options = nullptr;
3✔
231

232
        size_t out_pos = 0;
3✔
233
        lzma_ret ret = lzma_stream_buffer_encode(
3✔
234
            filters, LZMA_CHECK_NONE,
235
            nullptr,  // allocator,
236
            static_cast<const uint8_t *>(input_data), input_size,
237
            static_cast<uint8_t *>(*output_data), &out_pos, *output_size);
238
        if (ret != LZMA_OK)
3✔
239
        {
240
            *output_size = 0;
1✔
241
            return false;
1✔
242
        }
243
        *output_size = out_pos;
2✔
244
        return true;
2✔
245
    }
246

247
    if (output_data == nullptr && output_size != nullptr)
2✔
248
    {
249
        *output_size = lzma_stream_buffer_bound(input_size);
1✔
250
        return true;
1✔
251
    }
252

253
    if (output_data != nullptr && *output_data == nullptr &&
1✔
254
        output_size != nullptr)
255
    {
256
        size_t nSafeSize = lzma_stream_buffer_bound(input_size);
1✔
257
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
258
        *output_size = nSafeSize;
1✔
259
        if (*output_data == nullptr)
1✔
260
            return false;
×
261
        bool ret = CPLLZMACompressor(input_data, input_size, output_data,
1✔
262
                                     output_size, options, nullptr);
263
        if (!ret)
1✔
264
        {
265
            VSIFree(*output_data);
×
266
            *output_data = nullptr;
×
267
        }
268
        return ret;
1✔
269
    }
270

271
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
272
    return false;
×
273
}
274

275
static bool CPLLZMADecompressor(const void *input_data, size_t input_size,
167✔
276
                                void **output_data, size_t *output_size,
277
                                CSLConstList options,
278
                                void * /* compressor_user_data */)
279
{
280
    if (output_data != nullptr && *output_data != nullptr &&
167✔
281
        output_size != nullptr && *output_size != 0)
164✔
282
    {
283
        size_t in_pos = 0;
164✔
284
        size_t out_pos = 0;
164✔
285
        uint64_t memlimit = 100 * 1024 * 1024;
164✔
286
        lzma_ret ret = lzma_stream_buffer_decode(
164✔
287
            &memlimit,
288
            0,        // flags
289
            nullptr,  // allocator,
290
            static_cast<const uint8_t *>(input_data), &in_pos, input_size,
291
            static_cast<uint8_t *>(*output_data), &out_pos, *output_size);
292
        if (ret != LZMA_OK)
164✔
293
        {
294
            *output_size = 0;
×
295
            return false;
×
296
        }
297
        *output_size = out_pos;
164✔
298
        return true;
164✔
299
    }
300

301
    if (output_data == nullptr && output_size != nullptr)
3✔
302
    {
303
        // inefficient !
304
        void *tmpBuffer = nullptr;
1✔
305
        bool ret = CPLLZMADecompressor(input_data, input_size, &tmpBuffer,
1✔
306
                                       output_size, options, nullptr);
307
        VSIFree(tmpBuffer);
1✔
308
        return ret;
1✔
309
    }
310

311
    if (output_data != nullptr && *output_data == nullptr &&
2✔
312
        output_size != nullptr)
313
    {
314
        size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 2
2✔
315
                              ? input_size * 2
2✔
316
                              : input_size;
2✔
317
        *output_data = VSI_MALLOC_VERBOSE(nOutSize);
2✔
318
        if (*output_data == nullptr)
2✔
319
        {
320
            *output_size = 0;
×
321
            return false;
×
322
        }
323

324
        while (true)
325
        {
326
            size_t in_pos = 0;
2✔
327
            size_t out_pos = 0;
2✔
328
            uint64_t memlimit = 100 * 1024 * 1024;
2✔
329
            lzma_ret ret = lzma_stream_buffer_decode(
2✔
330
                &memlimit,
331
                0,        // flags
332
                nullptr,  // allocator,
333
                static_cast<const uint8_t *>(input_data), &in_pos, input_size,
334
                static_cast<uint8_t *>(*output_data), &out_pos, nOutSize);
335
            if (ret == LZMA_OK)
2✔
336
            {
337
                *output_size = out_pos;
2✔
338
                return true;
2✔
339
            }
340
            else if (ret == LZMA_BUF_ERROR &&
×
341
                     nOutSize < std::numeric_limits<size_t>::max() / 2)
×
342
            {
343
                nOutSize *= 2;
×
344
                void *tmpBuffer = VSI_REALLOC_VERBOSE(*output_data, nOutSize);
×
345
                if (tmpBuffer == nullptr)
×
346
                {
347
                    VSIFree(*output_data);
×
348
                    *output_data = nullptr;
×
349
                    *output_size = 0;
×
350
                    return false;
×
351
                }
352
                *output_data = tmpBuffer;
×
353
            }
354
            else
355
            {
356
                VSIFree(*output_data);
×
357
                *output_data = nullptr;
×
358
                *output_size = 0;
×
359
                return false;
×
360
            }
361
        }
×
362
    }
363

UNCOV
364
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
365
    return false;
×
366
}
367

368
#endif  // HAVE_LZMA
369

370
#ifdef HAVE_ZSTD
371
static bool CPLZSTDCompressor(const void *input_data, size_t input_size,
7✔
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 &&
7✔
377
        output_size != nullptr && *output_size != 0)
5✔
378
    {
379
        ZSTD_CCtx *ctx = ZSTD_createCCtx();
5✔
380
        if (ctx == nullptr)
5✔
381
        {
382
            *output_size = 0;
×
383
            return false;
×
384
        }
385

386
        const int level = atoi(CSLFetchNameValueDef(options, "LEVEL", "13"));
5✔
387
        if (ZSTD_isError(
5✔
388
                ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, level)))
5✔
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")))
5✔
397
        {
398
            CPL_IGNORE_RET_VAL(
1✔
399
                ZSTD_CCtx_setParameter(ctx, ZSTD_c_checksumFlag, 1));
1✔
400
        }
401

402
        size_t ret = ZSTD_compress2(ctx, *output_data, *output_size, input_data,
5✔
403
                                    input_size);
404
        ZSTD_freeCCtx(ctx);
5✔
405
        if (ZSTD_isError(ret))
5✔
406
        {
407
            *output_size = 0;
1✔
408
            return false;
1✔
409
        }
410

411
        *output_size = ret;
4✔
412
        return true;
4✔
413
    }
414

415
    if (output_data == nullptr && output_size != nullptr)
2✔
416
    {
417
        *output_size = ZSTD_compressBound(input_size);
1✔
418
        return true;
1✔
419
    }
420

421
    if (output_data != nullptr && *output_data == nullptr &&
1✔
422
        output_size != nullptr)
423
    {
424
        size_t nSafeSize = ZSTD_compressBound(input_size);
1✔
425
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
426
        *output_size = nSafeSize;
1✔
427
        if (*output_data == nullptr)
1✔
428
            return false;
×
429
        bool ret = CPLZSTDCompressor(input_data, input_size, output_data,
1✔
430
                                     output_size, options, nullptr);
431
        if (!ret)
1✔
432
        {
433
            VSIFree(*output_data);
×
434
            *output_data = nullptr;
×
435
        }
436
        return ret;
1✔
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,
4✔
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);
4✔
452
    if (nRet == ZSTD_CONTENTSIZE_ERROR)
4✔
453
    {
454
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
455
                 "Error while retrieving decompressed size of ZSTD frame.");
456
        nRet = 0;
1✔
457
    }
458
    else if (nRet == ZSTD_CONTENTSIZE_UNKNOWN)
3✔
459
    {
460
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
461
                 "Decompressed size of ZSTD frame is unknown.");
462
        nRet = 0;
1✔
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);
4✔
483
}
484

485
static bool CPLZSTDDecompressor(const void *input_data, size_t input_size,
168✔
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 &&
168✔
491
        output_size != nullptr && *output_size != 0)
166✔
492
    {
493
        size_t ret =
494
            ZSTD_decompress(*output_data, *output_size, input_data, input_size);
166✔
495
        if (ZSTD_isError(ret))
167✔
496
        {
497
            *output_size = CPLZSTDGetDecompressedSize(input_data, input_size);
2✔
498
            return false;
2✔
499
        }
500

501
        *output_size = ret;
165✔
502
        return true;
165✔
503
    }
504

505
    if (output_data == nullptr && output_size != nullptr)
2✔
506
    {
507
        *output_size = CPLZSTDGetDecompressedSize(input_data, input_size);
1✔
508
        return *output_size != 0;
1✔
509
    }
510

511
    if (output_data != nullptr && *output_data == nullptr &&
1✔
512
        output_size != nullptr)
513
    {
514
        size_t nOutSize = CPLZSTDGetDecompressedSize(input_data, input_size);
1✔
515
        *output_data = VSI_MALLOC_VERBOSE(nOutSize);
1✔
516
        if (*output_data == nullptr)
1✔
517
        {
518
            *output_size = 0;
×
519
            return false;
×
520
        }
521

522
        size_t ret =
523
            ZSTD_decompress(*output_data, nOutSize, input_data, input_size);
1✔
524
        if (ZSTD_isError(ret))
1✔
525
        {
526
            *output_size = 0;
×
527
            VSIFree(*output_data);
×
528
            *output_data = nullptr;
×
529
            return false;
×
530
        }
531

532
        *output_size = ret;
1✔
533
        return true;
1✔
534
    }
535

UNCOV
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,
5✔
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()))
5✔
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"));
5✔
559
    const int header_size = bHeader ? static_cast<int>(sizeof(int32_t)) : 0;
5✔
560

561
    if (output_data != nullptr && *output_data != nullptr &&
5✔
562
        output_size != nullptr && *output_size != 0)
3✔
563
    {
564
        const int acceleration =
565
            atoi(CSLFetchNameValueDef(options, "ACCELERATION", "1"));
3✔
566
        if (*output_size >
6✔
567
            static_cast<size_t>(std::numeric_limits<int>::max() - 4))
3✔
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)
3✔
577
        {
578
            *output_size = 0;
1✔
579
            return false;
1✔
580
        }
581

582
        int ret = LZ4_compress_fast(
4✔
583
            static_cast<const char *>(input_data),
584
            static_cast<char *>(*output_data) + header_size,
2✔
585
            static_cast<int>(input_size),
586
            static_cast<int>(*output_size) - header_size, acceleration);
2✔
587
        if (ret <= 0 || ret > std::numeric_limits<int>::max() - header_size)
2✔
588
        {
589
            *output_size = 0;
×
590
            return false;
×
591
        }
592

593
        int32_t sizeLSB = CPL_LSBWORD32(static_cast<int>(input_size));
2✔
594
        memcpy(*output_data, &sizeLSB, sizeof(sizeLSB));
2✔
595

596
        *output_size = static_cast<size_t>(header_size + ret);
2✔
597
        return true;
2✔
598
    }
599

600
    if (output_data == nullptr && output_size != nullptr)
2✔
601
    {
602
        *output_size = static_cast<size_t>(header_size) +
2✔
603
                       LZ4_compressBound(static_cast<int>(input_size));
1✔
604
        return true;
1✔
605
    }
606

607
    if (output_data != nullptr && *output_data == nullptr &&
1✔
608
        output_size != nullptr)
609
    {
610
        size_t nSafeSize = static_cast<size_t>(header_size) +
1✔
611
                           LZ4_compressBound(static_cast<int>(input_size));
1✔
612
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
613
        *output_size = nSafeSize;
1✔
614
        if (*output_data == nullptr)
1✔
615
            return false;
×
616
        bool ret = CPLLZ4Compressor(input_data, input_size, output_data,
1✔
617
                                    output_size, options, nullptr);
618
        if (!ret)
1✔
619
        {
620
            VSIFree(*output_data);
×
621
            *output_data = nullptr;
×
622
        }
623
        return ret;
1✔
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,
5✔
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()))
5✔
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"));
5✔
646
    const int header_size = bHeader ? static_cast<int>(sizeof(int32_t)) : 0;
5✔
647
    if (bHeader && static_cast<int>(input_size) < header_size)
5✔
648
    {
649
        *output_size = 0;
×
650
        return false;
×
651
    }
652

653
    if (output_data != nullptr && *output_data != nullptr &&
5✔
654
        output_size != nullptr && *output_size != 0)
3✔
655
    {
656
        if (*output_size > static_cast<size_t>(std::numeric_limits<int>::max()))
3✔
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(
6✔
666
            static_cast<const char *>(input_data) + header_size,
3✔
667
            static_cast<char *>(*output_data),
668
            static_cast<int>(input_size) - header_size,
669
            static_cast<int>(*output_size));
3✔
670
        if (ret <= 0)
3✔
671
        {
672
            *output_size = 0;
×
673
            return false;
×
674
        }
675

676
        *output_size = ret;
3✔
677
        return true;
3✔
678
    }
679

680
    if (output_data == nullptr && output_size != nullptr)
2✔
681
    {
682
        if (bHeader)
1✔
683
        {
684
            int nSize = CPL_LSBSINT32PTR(input_data);
1✔
685
            if (nSize < 0)
1✔
686
            {
687
                *output_size = 0;
×
688
                return false;
×
689
            }
690
            *output_size = nSize;
1✔
691
            return true;
1✔
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 &&
1✔
703
        output_size != nullptr)
704
    {
705
        if (bHeader)
1✔
706
        {
707
            int nSize = CPL_LSBSINT32PTR(input_data);
1✔
708
            if (nSize <= 0)
1✔
709
            {
710
                *output_size = 0;
×
711
                return false;
×
712
            }
713
            if (nSize > INT_MAX - 1 || /* to make Coverity scan happy */
1✔
714
                nSize / 10000 > static_cast<int>(input_size))
1✔
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);
1✔
724
            *output_size = nSize;
1✔
725
            if (*output_data == nullptr)
1✔
726
            {
727
                return false;
×
728
            }
729
            if (!CPLLZ4Decompressor(input_data, input_size, output_data,
1✔
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;
1✔
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,
10,756✔
800
                             void *outptr, size_t nOutAvailableBytes,
801
                             size_t *pnOutBytes)
802
{
803
    if (pnOutBytes != nullptr)
10,756✔
804
        *pnOutBytes = 0;
10,756✔
805

806
    size_t nTmpSize = 0;
10,756✔
807
    void *pTmp;
808
#ifdef HAVE_LIBDEFLATE
809
    struct libdeflate_compressor *enc =
810
        libdeflate_alloc_compressor(nLevel < 0 ? 7 : nLevel);
10,756✔
811
    if (enc == nullptr)
10,756✔
812
    {
813
        return nullptr;
×
814
    }
815
#endif
816
    if (outptr == nullptr)
10,756✔
817
    {
818
#ifdef HAVE_LIBDEFLATE
819
        nTmpSize = libdeflate_gzip_compress_bound(enc, nBytes);
1✔
820
#else
821
        nTmpSize = 32 + nBytes * 2;
822
#endif
823
        pTmp = VSIMalloc(nTmpSize);
1✔
824
        if (pTmp == nullptr)
1✔
825
        {
826
#ifdef HAVE_LIBDEFLATE
827
            libdeflate_free_compressor(enc);
×
828
#endif
829
            return nullptr;
×
830
        }
831
    }
832
    else
833
    {
834
        pTmp = outptr;
10,755✔
835
        nTmpSize = nOutAvailableBytes;
10,755✔
836
    }
837

838
#ifdef HAVE_LIBDEFLATE
839
    size_t nCompressedBytes =
840
        libdeflate_gzip_compress(enc, ptr, nBytes, pTmp, nTmpSize);
10,756✔
841
    libdeflate_free_compressor(enc);
10,756✔
842
    if (nCompressedBytes == 0)
10,756✔
843
    {
844
        if (pTmp != outptr)
1✔
845
            VSIFree(pTmp);
×
846
        return nullptr;
1✔
847
    }
848
    if (pnOutBytes != nullptr)
10,755✔
849
        *pnOutBytes = nCompressedBytes;
10,755✔
850
#else
851
    z_stream strm;
852
    strm.zalloc = nullptr;
853
    strm.zfree = nullptr;
854
    strm.opaque = nullptr;
855
    constexpr int windowsBits = 15;
856
    constexpr int gzipEncoding = 16;
857
    int ret = deflateInit2(&strm, nLevel < 0 ? Z_DEFAULT_COMPRESSION : nLevel,
858
                           Z_DEFLATED, windowsBits + gzipEncoding, 8,
859
                           Z_DEFAULT_STRATEGY);
860
    if (ret != Z_OK)
861
    {
862
        if (pTmp != outptr)
863
            VSIFree(pTmp);
864
        return nullptr;
865
    }
866

867
    strm.avail_in = static_cast<uInt>(nBytes);
868
    strm.next_in = reinterpret_cast<Bytef *>(const_cast<void *>(ptr));
869
    strm.avail_out = static_cast<uInt>(nTmpSize);
870
    strm.next_out = reinterpret_cast<Bytef *>(pTmp);
871
    ret = deflate(&strm, Z_FINISH);
872
    if (ret != Z_STREAM_END)
873
    {
874
        if (pTmp != outptr)
875
            VSIFree(pTmp);
876
        return nullptr;
877
    }
878
    if (pnOutBytes != nullptr)
879
        *pnOutBytes = nTmpSize - strm.avail_out;
880
    deflateEnd(&strm);
881
#endif
882

883
    return pTmp;
10,755✔
884
}
885

886
static bool CPLZlibCompressor(const void *input_data, size_t input_size,
10,860✔
887
                              void **output_data, size_t *output_size,
888
                              CSLConstList options, void *compressor_user_data)
889
{
890
    const char *alg = static_cast<const char *>(compressor_user_data);
10,860✔
891
    const auto pfnCompress =
10,860✔
892
        strcmp(alg, "zlib") == 0 ? CPLZLibDeflate : CPLGZipCompress;
10,860✔
893
    const int clevel = atoi(CSLFetchNameValueDef(options, "LEVEL",
10,860✔
894
#if HAVE_LIBDEFLATE
895
                                                 "7"
896
#else
897
                                                 "6"
898
#endif
899
                                                 ));
900

901
    if (output_data != nullptr && *output_data != nullptr &&
10,860✔
902
        output_size != nullptr && *output_size != 0)
10,855✔
903
    {
904
        size_t nOutBytes = 0;
10,855✔
905
        if (nullptr == pfnCompress(input_data, input_size, clevel, *output_data,
10,855✔
906
                                   *output_size, &nOutBytes))
907
        {
908
            *output_size = 0;
2✔
909
            return false;
2✔
910
        }
911

912
        *output_size = nOutBytes;
10,853✔
913
        return true;
10,853✔
914
    }
915

916
    if (output_data == nullptr && output_size != nullptr)
5✔
917
    {
918
#if HAVE_LIBDEFLATE
919
        struct libdeflate_compressor *enc = libdeflate_alloc_compressor(clevel);
2✔
920
        if (enc == nullptr)
2✔
921
        {
922
            *output_size = 0;
×
923
            return false;
×
924
        }
925
        if (strcmp(alg, "zlib") == 0)
2✔
926
            *output_size = libdeflate_zlib_compress_bound(enc, input_size);
1✔
927
        else
928
            *output_size = libdeflate_gzip_compress_bound(enc, input_size);
1✔
929
        libdeflate_free_compressor(enc);
2✔
930
#else
931
        // Really inefficient !
932
        size_t nOutSize = 0;
933
        void *outbuffer =
934
            pfnCompress(input_data, input_size, clevel, nullptr, 0, &nOutSize);
935
        if (outbuffer == nullptr)
936
        {
937
            *output_size = 0;
938
            return false;
939
        }
940
        VSIFree(outbuffer);
941
        *output_size = nOutSize;
942
#endif
943
        return true;
2✔
944
    }
945

946
    if (output_data != nullptr && *output_data == nullptr &&
3✔
947
        output_size != nullptr)
948
    {
949
        size_t nOutSize = 0;
3✔
950
        *output_data =
3✔
951
            pfnCompress(input_data, input_size, clevel, nullptr, 0, &nOutSize);
3✔
952
        if (*output_data == nullptr)
3✔
953
        {
954
            *output_size = 0;
×
955
            return false;
×
956
        }
957
        *output_size = nOutSize;
3✔
958
        return true;
3✔
959
    }
960

961
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
962
    return false;
×
963
}
964

965
namespace
966
{
967
template <class T> inline T swap(T x)
×
968
{
969
    return x;
×
970
}
971

972
template <> inline uint16_t swap<uint16_t>(uint16_t x)
12✔
973
{
974
    return CPL_SWAP16(x);
12✔
975
}
976

977
template <> inline int16_t swap<int16_t>(int16_t x)
12✔
978
{
979
    return CPL_SWAP16(x);
12✔
980
}
981

982
template <> inline uint32_t swap<uint32_t>(uint32_t x)
12✔
983
{
984
    return CPL_SWAP32(x);
12✔
985
}
986

987
template <> inline int32_t swap<int32_t>(int32_t x)
12✔
988
{
989
    return CPL_SWAP32(x);
12✔
990
}
991

992
template <> inline uint64_t swap<uint64_t>(uint64_t x)
12✔
993
{
994
    return CPL_SWAP64(x);
12✔
995
}
996

997
template <> inline int64_t swap<int64_t>(int64_t x)
12✔
998
{
999
    return CPL_SWAP64(x);
12✔
1000
}
1001

1002
template <> inline float swap<float>(float x)
×
1003
{
1004
    float ret = x;
×
1005
    CPL_SWAP32PTR(&ret);
×
1006
    return ret;
×
1007
}
1008

1009
template <> inline double swap<double>(double x)
×
1010
{
1011
    double ret = x;
×
1012
    CPL_SWAP64PTR(&ret);
×
1013
    return ret;
×
1014
}
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)
448✔
1022
{
1023
    typedef typename std::make_unsigned<T>::type U;
1024
    U leftU = static_cast<U>(left);
448✔
1025
    U rightU = static_cast<U>(right);
448✔
1026
    leftU = static_cast<U>(leftU - rightU);
448✔
1027
    T ret;
1028
    memcpy(&ret, &leftU, sizeof(ret));
448✔
1029
    return leftU;
448✔
1030
}
1031

1032
template <> inline float SubNoOverflow<float>(float x, float y)
4✔
1033
{
1034
    return x - y;
4✔
1035
}
1036

1037
template <> inline double SubNoOverflow<double>(double x, double y)
4✔
1038
{
1039
    return x - y;
4✔
1040
}
1041
}  // namespace
1042

1043
template <class T>
1044
static bool DeltaCompressor(const void *input_data, size_t input_size,
26✔
1045
                            const char *dtype, void *output_data)
1046
{
1047
    if ((input_size % sizeof(T)) != 0)
24✔
1048
    {
1049
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid input size");
×
1050
        return false;
×
1051
    }
1052

1053
    const size_t nElts = input_size / sizeof(T);
26✔
1054
    const T *pSrc = static_cast<const T *>(input_data);
26✔
1055
    T *pDst = static_cast<T *>(output_data);
26✔
1056
#ifdef CPL_MSB
1057
    const bool bNeedSwap = dtype[0] == '<';
1058
#else
1059
    const bool bNeedSwap = dtype[0] == '>';
26✔
1060
#endif
1061
    for (size_t i = 0; i < nElts; i++)
508✔
1062
    {
1063
        if (i == 0)
482✔
1064
        {
1065
            pDst[0] = pSrc[0];
26✔
1066
        }
1067
        else
1068
        {
1069
            if (bNeedSwap)
456✔
1070
            {
1071
                pDst[i] = swap(SubNoOverflow(swap(pSrc[i]), swap(pSrc[i - 1])));
12✔
1072
            }
1073
            else
1074
            {
1075
                pDst[i] = SubNoOverflow(pSrc[i], pSrc[i - 1]);
444✔
1076
            }
1077
        }
1078
    }
1079
    return true;
26✔
1080
}
1081

1082
static bool CPLDeltaCompressor(const void *input_data, size_t input_size,
26✔
1083
                               void **output_data, size_t *output_size,
1084
                               CSLConstList options,
1085
                               void * /* compressor_user_data */)
1086
{
1087
    const char *dtype = CSLFetchNameValue(options, "DTYPE");
26✔
1088
    if (dtype == nullptr)
26✔
1089
    {
1090
        CPLError(CE_Failure, CPLE_AppDefined, "Missing DTYPE parameter");
×
1091
        if (output_size)
×
1092
            *output_size = 0;
×
1093
        return false;
×
1094
    }
1095
    const char *astype = CSLFetchNameValue(options, "ASTYPE");
26✔
1096
    if (astype != nullptr && !EQUAL(astype, dtype))
26✔
1097
    {
1098
        CPLError(CE_Failure, CPLE_AppDefined,
×
1099
                 "Only ASTYPE=DTYPE currently supported");
1100
        if (output_size)
×
1101
            *output_size = 0;
×
1102
        return false;
×
1103
    }
1104

1105
    if (output_data != nullptr && *output_data != nullptr &&
26✔
1106
        output_size != nullptr && *output_size != 0)
26✔
1107
    {
1108
        if (*output_size < input_size)
26✔
1109
        {
1110
            CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
×
1111
            *output_size = input_size;
×
1112
            return false;
×
1113
        }
1114

1115
        if (EQUAL(dtype, "i1"))
26✔
1116
        {
1117
            if (!DeltaCompressor<int8_t>(input_data, input_size, dtype,
1✔
1118
                                         *output_data))
1119
            {
1120
                *output_size = 0;
×
1121
                return false;
×
1122
            }
1123
        }
1124
        else if (EQUAL(dtype, "u1"))
25✔
1125
        {
1126
            if (!DeltaCompressor<uint8_t>(input_data, input_size, dtype,
1✔
1127
                                          *output_data))
1128
            {
1129
                *output_size = 0;
×
1130
                return false;
×
1131
            }
1132
        }
1133
        else if (EQUAL(dtype, "<i2") || EQUAL(dtype, ">i2") ||
24✔
1134
                 EQUAL(dtype, "i2"))
22✔
1135
        {
1136
            if (!DeltaCompressor<int16_t>(input_data, input_size, dtype,
3✔
1137
                                          *output_data))
1138
            {
1139
                *output_size = 0;
×
1140
                return false;
×
1141
            }
1142
        }
1143
        else if (EQUAL(dtype, "<u2") || EQUAL(dtype, ">u2") ||
21✔
1144
                 EQUAL(dtype, "u2"))
18✔
1145
        {
1146
            if (!DeltaCompressor<uint16_t>(input_data, input_size, dtype,
4✔
1147
                                           *output_data))
1148
            {
1149
                *output_size = 0;
×
1150
                return false;
×
1151
            }
1152
        }
1153
        else if (EQUAL(dtype, "<i4") || EQUAL(dtype, ">i4") ||
17✔
1154
                 EQUAL(dtype, "i4"))
14✔
1155
        {
1156
            if (!DeltaCompressor<int32_t>(input_data, input_size, dtype,
4✔
1157
                                          *output_data))
1158
            {
1159
                *output_size = 0;
×
1160
                return false;
×
1161
            }
1162
        }
1163
        else if (EQUAL(dtype, "<u4") || EQUAL(dtype, ">u4") ||
13✔
1164
                 EQUAL(dtype, "u4"))
11✔
1165
        {
1166
            if (!DeltaCompressor<uint32_t>(input_data, input_size, dtype,
3✔
1167
                                           *output_data))
1168
            {
1169
                *output_size = 0;
×
1170
                return false;
×
1171
            }
1172
        }
1173
        else if (EQUAL(dtype, "<i8") || EQUAL(dtype, ">i8") ||
10✔
1174
                 EQUAL(dtype, "i8"))
8✔
1175
        {
1176
            if (!DeltaCompressor<int64_t>(input_data, input_size, dtype,
3✔
1177
                                          *output_data))
1178
            {
1179
                *output_size = 0;
×
1180
                return false;
×
1181
            }
1182
        }
1183
        else if (EQUAL(dtype, "<u8") || EQUAL(dtype, ">u8") ||
7✔
1184
                 EQUAL(dtype, "u8"))
5✔
1185
        {
1186
            if (!DeltaCompressor<uint64_t>(input_data, input_size, dtype,
3✔
1187
                                           *output_data))
1188
            {
1189
                *output_size = 0;
×
1190
                return false;
×
1191
            }
1192
        }
1193
        else if (EQUAL(dtype, "<f4") || EQUAL(dtype, ">f4") ||
4✔
1194
                 EQUAL(dtype, "f4"))
3✔
1195
        {
1196
            if (!DeltaCompressor<float>(input_data, input_size, dtype,
2✔
1197
                                        *output_data))
1198
            {
1199
                *output_size = 0;
×
1200
                return false;
×
1201
            }
1202
        }
1203
        else if (EQUAL(dtype, "<f8") || EQUAL(dtype, ">f8") ||
2✔
1204
                 EQUAL(dtype, "f8"))
1✔
1205
        {
1206
            if (!DeltaCompressor<double>(input_data, input_size, dtype,
2✔
1207
                                         *output_data))
1208
            {
1209
                *output_size = 0;
×
1210
                return false;
×
1211
            }
1212
        }
1213
        else
1214
        {
1215
            CPLError(CE_Failure, CPLE_NotSupported,
×
1216
                     "Unsupported dtype=%s for delta filter", dtype);
1217
            *output_size = 0;
×
1218
            return false;
×
1219
        }
1220

1221
        *output_size = input_size;
26✔
1222
        return true;
26✔
1223
    }
1224

1225
    if (output_data == nullptr && output_size != nullptr)
×
1226
    {
1227
        *output_size = input_size;
×
1228
        return true;
×
1229
    }
1230

1231
    if (output_data != nullptr && *output_data == nullptr &&
×
1232
        output_size != nullptr)
1233
    {
1234
        *output_data = VSI_MALLOC_VERBOSE(input_size);
×
1235
        *output_size = input_size;
×
1236
        if (*output_data == nullptr)
×
1237
            return false;
×
1238
        bool ret = CPLDeltaCompressor(input_data, input_size, output_data,
×
1239
                                      output_size, options, nullptr);
1240
        if (!ret)
×
1241
        {
1242
            VSIFree(*output_data);
×
1243
            *output_data = nullptr;
×
1244
        }
1245
        return ret;
×
1246
    }
1247

1248
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
1249
    return false;
×
1250
}
1251

1252
static void CPLAddCompressor(const CPLCompressor *compressor)
1,499✔
1253
{
1254
    CPLCompressor *copy = new CPLCompressor(*compressor);
1,499✔
1255
    // cppcheck-suppress uninitdata
1256
    copy->pszId = CPLStrdup(compressor->pszId);
1,499✔
1257
    // cppcheck-suppress uninitdata
1258
    copy->papszMetadata = CSLDuplicate(compressor->papszMetadata);
1,499✔
1259
    gpCompressors->emplace_back(copy);
1,499✔
1260
}
1,499✔
1261

1262
static void CPLAddBuiltinCompressors()
214✔
1263
{
1264
#ifdef HAVE_BLOSC
1265
    do
1266
    {
1267
        CPLCompressor sComp;
1268
        sComp.nStructVersion = 1;
214✔
1269
        sComp.eType = CCT_COMPRESSOR;
214✔
1270
        sComp.pszId = "blosc";
214✔
1271

1272
        const CPLStringList aosCompressors(
1273
            CSLTokenizeString2(blosc_list_compressors(), ",", 0));
214✔
1274
        if (aosCompressors.size() == 0)
214✔
1275
            break;
×
1276
        std::string options("OPTIONS=<Options>"
1277
                            "  <Option name='CNAME' type='string-select' "
1278
                            "description='Compressor name' default='");
428✔
1279
        std::string values;
428✔
1280
        std::string defaultCompressor;
428✔
1281
        bool bFoundLZ4 = false;
214✔
1282
        bool bFoundSnappy = false;
214✔
1283
        bool bFoundZlib = false;
214✔
1284
        for (int i = 0; i < aosCompressors.size(); i++)
1,498✔
1285
        {
1286
            values += "<Value>";
1,284✔
1287
            values += aosCompressors[i];
1,284✔
1288
            values += "</Value>";
1,284✔
1289
            if (strcmp(aosCompressors[i], "lz4") == 0)
1,284✔
1290
                bFoundLZ4 = true;
214✔
1291
            else if (strcmp(aosCompressors[i], "snappy") == 0)
1,070✔
1292
                bFoundSnappy = true;
214✔
1293
            else if (strcmp(aosCompressors[i], "zlib") == 0)
856✔
1294
                bFoundZlib = true;
214✔
1295
        }
1296
        options += bFoundLZ4      ? "lz4"
1297
                   : bFoundSnappy ? "snappy"
×
1298
                   : bFoundZlib   ? "zlib"
×
1299
                                  : aosCompressors[0];
214✔
1300
        options += "'>";
214✔
1301
        options += values;
214✔
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>";
214✔
1320

1321
        const char *const apszMetadata[] = {
214✔
1322
            "BLOSC_VERSION=" BLOSC_VERSION_STRING, options.c_str(), nullptr};
214✔
1323
        sComp.papszMetadata = apszMetadata;
214✔
1324
        sComp.pfnFunc = CPLBloscCompressor;
214✔
1325
        sComp.user_data = nullptr;
214✔
1326
        CPLAddCompressor(&sComp);
214✔
1327
    } while (0);
1328
#endif
1329
    {
1330
        CPLCompressor sComp;
1331
        sComp.nStructVersion = 1;
214✔
1332
        sComp.eType = CCT_COMPRESSOR;
214✔
1333
        sComp.pszId = "zlib";
214✔
1334
        const char *pszOptions =
214✔
1335
            "OPTIONS=<Options>"
1336
            "  <Option name='LEVEL' type='int' description='Compression level' "
1337
            "min='1' max='9' default='6' />"
1338
            "</Options>";
1339
        const char *const apszMetadata[] = {pszOptions, nullptr};
214✔
1340
        sComp.papszMetadata = apszMetadata;
214✔
1341
        sComp.pfnFunc = CPLZlibCompressor;
214✔
1342
        sComp.user_data = const_cast<char *>("zlib");
214✔
1343
        CPLAddCompressor(&sComp);
214✔
1344
    }
1345
    {
1346
        CPLCompressor sComp;
1347
        sComp.nStructVersion = 1;
214✔
1348
        sComp.eType = CCT_COMPRESSOR;
214✔
1349
        sComp.pszId = "gzip";
214✔
1350
        const char *pszOptions =
214✔
1351
            "OPTIONS=<Options>"
1352
            "  <Option name='LEVEL' type='int' description='Compression level' "
1353
            "min='1' max='9' default='6' />"
1354
            "</Options>";
1355
        const char *const apszMetadata[] = {pszOptions, nullptr};
214✔
1356
        sComp.papszMetadata = apszMetadata;
214✔
1357
        sComp.pfnFunc = CPLZlibCompressor;
214✔
1358
        sComp.user_data = const_cast<char *>("gzip");
214✔
1359
        CPLAddCompressor(&sComp);
214✔
1360
    }
1361
#ifdef HAVE_LZMA
1362
    {
1363
        CPLCompressor sComp;
1364
        sComp.nStructVersion = 1;
214✔
1365
        sComp.eType = CCT_COMPRESSOR;
214✔
1366
        sComp.pszId = "lzma";
214✔
1367
        const char *pszOptions =
214✔
1368
            "OPTIONS=<Options>"
1369
            "  <Option name='PRESET' type='int' description='Compression "
1370
            "level' min='0' max='9' default='6' />"
1371
            "  <Option name='DELTA' type='int' description='Delta distance in "
1372
            "byte' default='1' />"
1373
            "</Options>";
1374
        const char *const apszMetadata[] = {pszOptions, nullptr};
214✔
1375
        sComp.papszMetadata = apszMetadata;
214✔
1376
        sComp.pfnFunc = CPLLZMACompressor;
214✔
1377
        sComp.user_data = nullptr;
214✔
1378
        CPLAddCompressor(&sComp);
214✔
1379
    }
1380
#endif
1381
#ifdef HAVE_ZSTD
1382
    {
1383
        CPLCompressor sComp;
1384
        sComp.nStructVersion = 1;
214✔
1385
        sComp.eType = CCT_COMPRESSOR;
214✔
1386
        sComp.pszId = "zstd";
214✔
1387
        const char *pszOptions =
214✔
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};
214✔
1396
        sComp.papszMetadata = apszMetadata;
214✔
1397
        sComp.pfnFunc = CPLZSTDCompressor;
214✔
1398
        sComp.user_data = nullptr;
214✔
1399
        CPLAddCompressor(&sComp);
214✔
1400
    }
1401
#endif
1402
#ifdef HAVE_LZ4
1403
    {
1404
        CPLCompressor sComp;
1405
        sComp.nStructVersion = 1;
214✔
1406
        sComp.eType = CCT_COMPRESSOR;
214✔
1407
        sComp.pszId = "lz4";
214✔
1408
        const char *pszOptions =
214✔
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};
214✔
1418
        sComp.papszMetadata = apszMetadata;
214✔
1419
        sComp.pfnFunc = CPLLZ4Compressor;
214✔
1420
        sComp.user_data = nullptr;
214✔
1421
        CPLAddCompressor(&sComp);
214✔
1422
    }
1423
#endif
1424
    {
1425
        CPLCompressor sComp;
1426
        sComp.nStructVersion = 1;
214✔
1427
        sComp.eType = CCT_FILTER;
214✔
1428
        sComp.pszId = "delta";
214✔
1429
        const char *pszOptions =
214✔
1430
            "OPTIONS=<Options>"
1431
            "  <Option name='DTYPE' type='string' description='Data type "
1432
            "following NumPy array protocol type string (typestr) format'/>"
1433
            "</Options>";
1434
        const char *const apszMetadata[] = {pszOptions, nullptr};
214✔
1435
        sComp.papszMetadata = apszMetadata;
214✔
1436
        sComp.pfnFunc = CPLDeltaCompressor;
214✔
1437
        sComp.user_data = nullptr;
214✔
1438
        CPLAddCompressor(&sComp);
214✔
1439
    }
1440
}
214✔
1441

1442
static bool CPLZlibDecompressor(const void *input_data, size_t input_size,
16,415✔
1443
                                void **output_data, size_t *output_size,
1444
                                CSLConstList /* options */,
1445
                                void * /* compressor_user_data */)
1446
{
1447
    if (output_data != nullptr && *output_data != nullptr &&
16,415✔
1448
        output_size != nullptr && *output_size != 0)
15,942✔
1449
    {
1450
        size_t nOutBytes = 0;
16,007✔
1451
        if (nullptr == CPLZLibInflate(input_data, input_size, *output_data,
16,007✔
1452
                                      *output_size, &nOutBytes))
1453
        {
1454
            *output_size = 0;
×
1455
            return false;
×
1456
        }
1457

1458
        *output_size = nOutBytes;
16,281✔
1459
        return true;
16,281✔
1460
    }
1461

1462
    if (output_data == nullptr && output_size != nullptr)
408✔
1463
    {
1464
        size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 4
2✔
1465
                              ? input_size * 4
2✔
1466
                              : input_size;
2✔
1467
        void *tmpOutBuffer = VSIMalloc(nOutSize);
2✔
1468
        if (tmpOutBuffer == nullptr)
2✔
1469
        {
1470
            *output_size = 0;
×
1471
            return false;
×
1472
        }
1473
        tmpOutBuffer = CPLZLibInflateEx(input_data, input_size, tmpOutBuffer,
2✔
1474
                                        nOutSize, true, &nOutSize);
1475
        if (!tmpOutBuffer)
2✔
1476
        {
1477
            *output_size = 0;
×
1478
            return false;
×
1479
        }
1480
        VSIFree(tmpOutBuffer);
2✔
1481
        *output_size = nOutSize;
2✔
1482
        return true;
2✔
1483
    }
1484

1485
    if (output_data != nullptr && *output_data == nullptr &&
406✔
1486
        output_size != nullptr)
1487
    {
1488
        size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 4
3✔
1489
                              ? input_size * 4
3✔
1490
                              : input_size;
3✔
1491
        void *tmpOutBuffer = VSIMalloc(nOutSize);
3✔
1492
        if (tmpOutBuffer == nullptr)
3✔
1493
        {
1494
            *output_size = 0;
×
1495
            return false;
×
1496
        }
1497
        size_t nOutSizeOut = 0;
3✔
1498
        tmpOutBuffer = CPLZLibInflateEx(input_data, input_size, tmpOutBuffer,
3✔
1499
                                        nOutSize, true, &nOutSizeOut);
1500
        if (!tmpOutBuffer)
3✔
1501
        {
1502
            *output_size = 0;
×
1503
            return false;
×
1504
        }
1505
        *output_data = VSIRealloc(tmpOutBuffer, nOutSizeOut);  // cannot fail
3✔
1506
        *output_size = nOutSizeOut;
3✔
1507
        return true;
3✔
1508
    }
1509

1510
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
403✔
1511
    return false;
×
1512
}
1513

1514
namespace
1515
{
1516
// Workaround -ftrapv
1517
template <class T>
1518
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW inline T AddNoOverflow(T left, T right)
466✔
1519
{
1520
    typedef typename std::make_unsigned<T>::type U;
1521
    U leftU = static_cast<U>(left);
466✔
1522
    U rightU = static_cast<U>(right);
466✔
1523
    leftU = static_cast<U>(leftU + rightU);
466✔
1524
    T ret;
1525
    memcpy(&ret, &leftU, sizeof(ret));
466✔
1526
    return leftU;
466✔
1527
}
1528

1529
template <> inline float AddNoOverflow<float>(float x, float y)
4✔
1530
{
1531
    return x + y;
4✔
1532
}
1533

1534
template <> inline double AddNoOverflow<double>(double x, double y)
4✔
1535
{
1536
    return x + y;
4✔
1537
}
1538
}  // namespace
1539

1540
template <class T>
1541
static bool DeltaDecompressor(const void *input_data, size_t input_size,
28✔
1542
                              const char *dtype, void *output_data)
1543
{
1544
    if ((input_size % sizeof(T)) != 0)
26✔
1545
    {
1546
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid input size");
×
1547
        return false;
×
1548
    }
1549

1550
    const size_t nElts = input_size / sizeof(T);
28✔
1551
    const T *pSrc = static_cast<const T *>(input_data);
28✔
1552
    T *pDst = static_cast<T *>(output_data);
28✔
1553
#ifdef CPL_MSB
1554
    const bool bNeedSwap = dtype[0] == '<';
1555
#else
1556
    const bool bNeedSwap = dtype[0] == '>';
28✔
1557
#endif
1558
    for (size_t i = 0; i < nElts; i++)
530✔
1559
    {
1560
        if (i == 0)
502✔
1561
        {
1562
            pDst[0] = pSrc[0];
28✔
1563
        }
1564
        else
1565
        {
1566
            if (bNeedSwap)
474✔
1567
            {
1568
                pDst[i] = swap(AddNoOverflow(swap(pDst[i - 1]), swap(pSrc[i])));
12✔
1569
            }
1570
            else
1571
            {
1572
                pDst[i] = AddNoOverflow(pDst[i - 1], pSrc[i]);
462✔
1573
            }
1574
        }
1575
    }
1576
    return true;
28✔
1577
}
1578

1579
static bool CPLDeltaDecompressor(const void *input_data, size_t input_size,
28✔
1580
                                 void **output_data, size_t *output_size,
1581
                                 CSLConstList options,
1582
                                 void * /* compressor_user_data */)
1583
{
1584
    const char *dtype = CSLFetchNameValue(options, "DTYPE");
28✔
1585
    if (dtype == nullptr)
28✔
1586
    {
1587
        CPLError(CE_Failure, CPLE_AppDefined, "Missing DTYPE parameter");
×
1588
        if (output_size)
×
1589
            *output_size = 0;
×
1590
        return false;
×
1591
    }
1592
    const char *astype = CSLFetchNameValue(options, "ASTYPE");
28✔
1593
    if (astype != nullptr && !EQUAL(astype, dtype))
28✔
1594
    {
1595
        CPLError(CE_Failure, CPLE_AppDefined,
×
1596
                 "Only ASTYPE=DTYPE currently supported");
1597
        if (output_size)
×
1598
            *output_size = 0;
×
1599
        return false;
×
1600
    }
1601

1602
    if (output_data != nullptr && *output_data != nullptr &&
28✔
1603
        output_size != nullptr && *output_size != 0)
28✔
1604
    {
1605
        if (*output_size < input_size)
28✔
1606
        {
1607
            CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
×
1608
            *output_size = input_size;
×
1609
            return false;
×
1610
        }
1611

1612
        if (EQUAL(dtype, "i1"))
28✔
1613
        {
1614
            if (!DeltaDecompressor<int8_t>(input_data, input_size, dtype,
1✔
1615
                                           *output_data))
1616
            {
1617
                *output_size = 0;
×
1618
                return false;
×
1619
            }
1620
        }
1621
        else if (EQUAL(dtype, "u1"))
27✔
1622
        {
1623
            if (!DeltaDecompressor<uint8_t>(input_data, input_size, dtype,
1✔
1624
                                            *output_data))
1625
            {
1626
                *output_size = 0;
×
1627
                return false;
×
1628
            }
1629
        }
1630
        else if (EQUAL(dtype, "<i2") || EQUAL(dtype, ">i2") ||
26✔
1631
                 EQUAL(dtype, "i2"))
24✔
1632
        {
1633
            if (!DeltaDecompressor<int16_t>(input_data, input_size, dtype,
3✔
1634
                                            *output_data))
1635
            {
1636
                *output_size = 0;
×
1637
                return false;
×
1638
            }
1639
        }
1640
        else if (EQUAL(dtype, "<u2") || EQUAL(dtype, ">u2") ||
23✔
1641
                 EQUAL(dtype, "u2"))
20✔
1642
        {
1643
            if (!DeltaDecompressor<uint16_t>(input_data, input_size, dtype,
4✔
1644
                                             *output_data))
1645
            {
1646
                *output_size = 0;
×
1647
                return false;
×
1648
            }
1649
        }
1650
        else if (EQUAL(dtype, "<i4") || EQUAL(dtype, ">i4") ||
19✔
1651
                 EQUAL(dtype, "i4"))
14✔
1652
        {
1653
            if (!DeltaDecompressor<int32_t>(input_data, input_size, dtype,
6✔
1654
                                            *output_data))
1655
            {
1656
                *output_size = 0;
×
1657
                return false;
×
1658
            }
1659
        }
1660
        else if (EQUAL(dtype, "<u4") || EQUAL(dtype, ">u4") ||
13✔
1661
                 EQUAL(dtype, "u4"))
11✔
1662
        {
1663
            if (!DeltaDecompressor<uint32_t>(input_data, input_size, dtype,
3✔
1664
                                             *output_data))
1665
            {
1666
                *output_size = 0;
×
1667
                return false;
×
1668
            }
1669
        }
1670
        else if (EQUAL(dtype, "<i8") || EQUAL(dtype, ">i8") ||
10✔
1671
                 EQUAL(dtype, "i8"))
8✔
1672
        {
1673
            if (!DeltaDecompressor<int64_t>(input_data, input_size, dtype,
3✔
1674
                                            *output_data))
1675
            {
1676
                *output_size = 0;
×
1677
                return false;
×
1678
            }
1679
        }
1680
        else if (EQUAL(dtype, "<u8") || EQUAL(dtype, ">u8") ||
7✔
1681
                 EQUAL(dtype, "u8"))
5✔
1682
        {
1683
            if (!DeltaDecompressor<uint64_t>(input_data, input_size, dtype,
3✔
1684
                                             *output_data))
1685
            {
1686
                *output_size = 0;
×
1687
                return false;
×
1688
            }
1689
        }
1690
        else if (EQUAL(dtype, "<f4") || EQUAL(dtype, ">f4") ||
4✔
1691
                 EQUAL(dtype, "f4"))
3✔
1692
        {
1693
            if (!DeltaDecompressor<float>(input_data, input_size, dtype,
2✔
1694
                                          *output_data))
1695
            {
1696
                *output_size = 0;
×
1697
                return false;
×
1698
            }
1699
        }
1700
        else if (EQUAL(dtype, "<f8") || EQUAL(dtype, ">f8") ||
2✔
1701
                 EQUAL(dtype, "f8"))
1✔
1702
        {
1703
            if (!DeltaDecompressor<double>(input_data, input_size, dtype,
2✔
1704
                                           *output_data))
1705
            {
1706
                *output_size = 0;
×
1707
                return false;
×
1708
            }
1709
        }
1710
        else
1711
        {
1712
            CPLError(CE_Failure, CPLE_NotSupported,
×
1713
                     "Unsupported dtype=%s for delta filter", dtype);
1714
            *output_size = 0;
×
1715
            return false;
×
1716
        }
1717

1718
        *output_size = input_size;
28✔
1719
        return true;
28✔
1720
    }
1721

1722
    if (output_data == nullptr && output_size != nullptr)
×
1723
    {
1724
        *output_size = input_size;
×
1725
        return true;
×
1726
    }
1727

1728
    if (output_data != nullptr && *output_data == nullptr &&
×
1729
        output_size != nullptr)
1730
    {
1731
        *output_data = VSI_MALLOC_VERBOSE(input_size);
×
1732
        *output_size = input_size;
×
1733
        if (*output_data == nullptr)
×
1734
            return false;
×
1735
        bool ret = CPLDeltaDecompressor(input_data, input_size, output_data,
×
1736
                                        output_size, options, nullptr);
1737
        if (!ret)
×
1738
        {
1739
            VSIFree(*output_data);
×
1740
            *output_data = nullptr;
×
1741
        }
1742
        return ret;
×
1743
    }
1744

1745
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
1746
    return false;
×
1747
}
1748

1749
static void CPLAddDecompressor(const CPLCompressor *decompressor)
11,418✔
1750
{
1751
    CPLCompressor *copy = new CPLCompressor(*decompressor);
11,418✔
1752
    // cppcheck-suppress uninitdata
1753
    copy->pszId = CPLStrdup(decompressor->pszId);
11,418✔
1754
    // cppcheck-suppress uninitdata
1755
    copy->papszMetadata = CSLDuplicate(decompressor->papszMetadata);
11,418✔
1756
    gpDecompressors->emplace_back(copy);
11,418✔
1757
}
11,418✔
1758

1759
static void CPLAddBuiltinDecompressors()
1,631✔
1760
{
1761
#ifdef HAVE_BLOSC
1762
    {
1763
        CPLCompressor sComp;
1764
        sComp.nStructVersion = 1;
1,631✔
1765
        sComp.eType = CCT_COMPRESSOR;
1,631✔
1766
        sComp.pszId = "blosc";
1,631✔
1767
        const char *pszOptions =
1,631✔
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[] = {
1,631✔
1774
            "BLOSC_VERSION=" BLOSC_VERSION_STRING, pszOptions, nullptr};
1,631✔
1775
        sComp.papszMetadata = apszMetadata;
1,631✔
1776
        sComp.pfnFunc = CPLBloscDecompressor;
1,631✔
1777
        sComp.user_data = nullptr;
1,631✔
1778
        CPLAddDecompressor(&sComp);
1,631✔
1779
    }
1780
#endif
1781
    {
1782
        CPLCompressor sComp;
1783
        sComp.nStructVersion = 1;
1,631✔
1784
        sComp.eType = CCT_COMPRESSOR;
1,631✔
1785
        sComp.pszId = "zlib";
1,631✔
1786
        sComp.papszMetadata = nullptr;
1,631✔
1787
        sComp.pfnFunc = CPLZlibDecompressor;
1,631✔
1788
        sComp.user_data = nullptr;
1,631✔
1789
        CPLAddDecompressor(&sComp);
1,631✔
1790
    }
1791
    {
1792
        CPLCompressor sComp;
1793
        sComp.nStructVersion = 1;
1,631✔
1794
        sComp.eType = CCT_COMPRESSOR;
1,631✔
1795
        sComp.pszId = "gzip";
1,631✔
1796
        sComp.papszMetadata = nullptr;
1,631✔
1797
        sComp.pfnFunc = CPLZlibDecompressor;
1,631✔
1798
        sComp.user_data = nullptr;
1,631✔
1799
        CPLAddDecompressor(&sComp);
1,631✔
1800
    }
1801
#ifdef HAVE_LZMA
1802
    {
1803
        CPLCompressor sComp;
1804
        sComp.nStructVersion = 1;
1,631✔
1805
        sComp.eType = CCT_COMPRESSOR;
1,631✔
1806
        sComp.pszId = "lzma";
1,631✔
1807
        sComp.papszMetadata = nullptr;
1,631✔
1808
        sComp.pfnFunc = CPLLZMADecompressor;
1,631✔
1809
        sComp.user_data = nullptr;
1,631✔
1810
        CPLAddDecompressor(&sComp);
1,631✔
1811
    }
1812
#endif
1813
#ifdef HAVE_ZSTD
1814
    {
1815
        CPLCompressor sComp;
1816
        sComp.nStructVersion = 1;
1,631✔
1817
        sComp.eType = CCT_COMPRESSOR;
1,631✔
1818
        sComp.pszId = "zstd";
1,631✔
1819
        sComp.papszMetadata = nullptr;
1,631✔
1820
        sComp.pfnFunc = CPLZSTDDecompressor;
1,631✔
1821
        sComp.user_data = nullptr;
1,631✔
1822
        CPLAddDecompressor(&sComp);
1,631✔
1823
    }
1824
#endif
1825
#ifdef HAVE_LZ4
1826
    {
1827
        CPLCompressor sComp;
1828
        sComp.nStructVersion = 1;
1,631✔
1829
        sComp.eType = CCT_COMPRESSOR;
1,631✔
1830
        sComp.pszId = "lz4";
1,631✔
1831
        const char *pszOptions =
1,631✔
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};
1,631✔
1838
        sComp.papszMetadata = apszMetadata;
1,631✔
1839
        sComp.pfnFunc = CPLLZ4Decompressor;
1,631✔
1840
        sComp.user_data = nullptr;
1,631✔
1841
        CPLAddDecompressor(&sComp);
1,631✔
1842
    }
1843
#endif
1844
    {
1845
        CPLCompressor sComp;
1846
        sComp.nStructVersion = 1;
1,631✔
1847
        sComp.eType = CCT_FILTER;
1,631✔
1848
        sComp.pszId = "delta";
1,631✔
1849
        const char *pszOptions =
1,631✔
1850
            "OPTIONS=<Options>"
1851
            "  <Option name='DTYPE' type='string' description='Data type "
1852
            "following NumPy array protocol type string (typestr) format'/>"
1853
            "</Options>";
1854
        const char *const apszMetadata[] = {pszOptions, nullptr};
1,631✔
1855
        sComp.papszMetadata = apszMetadata;
1,631✔
1856
        sComp.pfnFunc = CPLDeltaDecompressor;
1,631✔
1857
        sComp.user_data = nullptr;
1,631✔
1858
        CPLAddDecompressor(&sComp);
1,631✔
1859
    }
1860
}
1,631✔
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)
2✔
1872
{
1873
    if (compressor->nStructVersion < 1)
2✔
1874
        return false;
×
1875
    std::lock_guard<std::mutex> lock(gMutex);
4✔
1876
    if (gpCompressors == nullptr)
2✔
1877
    {
1878
        gpCompressors = new std::vector<CPLCompressor *>();
1✔
1879
        CPLAddBuiltinCompressors();
1✔
1880
    }
1881
    for (size_t i = 0; i < gpCompressors->size(); ++i)
16✔
1882
    {
1883
        if (strcmp(compressor->pszId, (*gpCompressors)[i]->pszId) == 0)
15✔
1884
        {
1885
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1886
                     "Compressor %s already registered", compressor->pszId);
1✔
1887
            return false;
1✔
1888
        }
1889
    }
1890
    CPLAddCompressor(compressor);
1✔
1891
    return true;
1✔
1892
}
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)
2✔
1904
{
1905
    if (decompressor->nStructVersion < 1)
2✔
1906
        return false;
×
1907
    std::lock_guard<std::mutex> lock(gMutex);
4✔
1908
    if (gpDecompressors == nullptr)
2✔
1909
    {
1910
        gpDecompressors = new std::vector<CPLCompressor *>();
×
1911
        CPLAddBuiltinDecompressors();
×
1912
    }
1913
    for (size_t i = 0; i < gpDecompressors->size(); ++i)
16✔
1914
    {
1915
        if (strcmp(decompressor->pszId, (*gpDecompressors)[i]->pszId) == 0)
15✔
1916
        {
1917
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1918
                     "Decompressor %s already registered", decompressor->pszId);
1✔
1919
            return false;
1✔
1920
        }
1921
    }
1922
    CPLAddDecompressor(decompressor);
1✔
1923
    return true;
1✔
1924
}
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)
211✔
1932
{
1933
    std::lock_guard<std::mutex> lock(gMutex);
211✔
1934
    if (gpCompressors == nullptr)
211✔
1935
    {
1936
        gpCompressors = new std::vector<CPLCompressor *>();
×
1937
        CPLAddBuiltinCompressors();
×
1938
    }
1939
    char **papszRet = nullptr;
211✔
1940
    for (size_t i = 0; i < gpCompressors->size(); ++i)
1,689✔
1941
    {
1942
        papszRet = CSLAddString(papszRet, (*gpCompressors)[i]->pszId);
1,478✔
1943
    }
1944
    return papszRet;
422✔
1945
}
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)
211✔
1953
{
1954
    std::lock_guard<std::mutex> lock(gMutex);
211✔
1955
    if (gpDecompressors == nullptr)
211✔
1956
    {
1957
        gpDecompressors = new std::vector<CPLCompressor *>();
×
1958
        CPLAddBuiltinDecompressors();
×
1959
    }
1960
    char **papszRet = nullptr;
211✔
1961
    for (size_t i = 0;
211✔
1962
         gpDecompressors != nullptr && i < gpDecompressors->size(); ++i)
1,689✔
1963
    {
1964
        papszRet = CSLAddString(papszRet, (*gpDecompressors)[i]->pszId);
1,478✔
1965
    }
1966
    return papszRet;
422✔
1967
}
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)
3,121✔
1976
{
1977
    std::lock_guard<std::mutex> lock(gMutex);
6,242✔
1978
    if (gpCompressors == nullptr)
3,121✔
1979
    {
1980
        gpCompressors = new std::vector<CPLCompressor *>();
213✔
1981
        CPLAddBuiltinCompressors();
213✔
1982
    }
1983
    for (size_t i = 0; i < gpCompressors->size(); ++i)
12,451✔
1984
    {
1985
        if (EQUAL(pszId, (*gpCompressors)[i]->pszId))
12,448✔
1986
        {
1987
            return (*gpCompressors)[i];
3,118✔
1988
        }
1989
    }
1990
    return nullptr;
3✔
1991
}
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)
2,010✔
2000
{
2001
    std::lock_guard<std::mutex> lock(gMutex);
4,020✔
2002
    if (gpDecompressors == nullptr)
2,010✔
2003
    {
2004
        gpDecompressors = new std::vector<CPLCompressor *>();
1,631✔
2005
        CPLAddBuiltinDecompressors();
1,631✔
2006
    }
2007
    for (size_t i = 0; i < gpDecompressors->size(); ++i)
7,866✔
2008
    {
2009
        if (EQUAL(pszId, (*gpDecompressors)[i]->pszId))
7,853✔
2010
        {
2011
            return (*gpDecompressors)[i];
1,997✔
2012
        }
2013
    }
2014
    return nullptr;
13✔
2015
}
2016

2017
static void
2018
CPLDestroyCompressorRegistryInternal(std::vector<CPLCompressor *> *&v)
2,230✔
2019
{
2020
    for (size_t i = 0; v != nullptr && i < v->size(); ++i)
11,493✔
2021
    {
2022
        CPLFree(const_cast<char *>((*v)[i]->pszId));
9,263✔
2023
        CSLDestroy(const_cast<char **>((*v)[i]->papszMetadata));
9,263✔
2024
        delete (*v)[i];
9,263✔
2025
    }
2026
    delete v;
2,230✔
2027
    v = nullptr;
2,230✔
2028
}
2,230✔
2029

2030
/*! @cond Doxygen_Suppress */
2031
void CPLDestroyCompressorRegistry(void)
1,115✔
2032
{
2033
    std::lock_guard<std::mutex> lock(gMutex);
2,230✔
2034

2035
    CPLDestroyCompressorRegistryInternal(gpCompressors);
1,115✔
2036
    CPLDestroyCompressorRegistryInternal(gpDecompressors);
1,115✔
2037
}
1,115✔
2038

2039
/*! @endcond */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc