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

OSGeo / gdal / 13836648005

13 Mar 2025 02:09PM UTC coverage: 70.436% (-0.01%) from 70.446%
13836648005

push

github

web-flow
New Transform type: Homography (#11949)

Add new transform type, Homography.
Add functions to compute homography from a list of GCPs.
Add functions to serialize and deserialize a homography
Automatically select homography transfrom when there are 4 or 5 GCPs present.

Fixes #11940

231 of 274 new or added lines in 2 files covered. (84.31%)

16257 existing lines in 42 files now uncovered.

553736 of 786159 relevant lines covered (70.44%)

221595.72 hits per line

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

70.72
/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
#ifdef HAVE_BLOSC
19
#include <blosc.h>
20
#endif
21

22
#ifdef HAVE_LIBDEFLATE
23
#include "libdeflate.h"
24
#else
25
#include "zlib.h"
26
#endif
27

28
#ifdef HAVE_LZMA
29
#if defined(__clang__)
30
#pragma clang diagnostic push
31
#pragma clang diagnostic ignored "-Wdocumentation"
32
#endif
33
#include <lzma.h>
34
#if defined(__clang__)
35
#pragma clang diagnostic pop
36
#endif
37
#endif
38

39
#ifdef HAVE_ZSTD
40
#include <zstd.h>
41
#endif
42

43
#ifdef HAVE_LZ4
44
#include <lz4.h>
45
#endif
46

47
#include <limits>
48
#include <mutex>
49
#include <type_traits>
50
#include <vector>
51

52
static std::mutex gMutex;
53
static std::vector<CPLCompressor *> *gpCompressors = nullptr;
54
static std::vector<CPLCompressor *> *gpDecompressors = nullptr;
55

56
#ifdef HAVE_BLOSC
57
static bool CPLBloscCompressor(const void *input_data, size_t input_size,
7✔
58
                               void **output_data, size_t *output_size,
59
                               CSLConstList options,
60
                               void * /* compressor_user_data */)
61
{
62
    if (output_data != nullptr && *output_data != nullptr &&
7✔
63
        output_size != nullptr && *output_size != 0)
5✔
64
    {
65
        const int clevel = atoi(CSLFetchNameValueDef(options, "CLEVEL", "5"));
5✔
66
        const char *pszShuffle =
67
            CSLFetchNameValueDef(options, "SHUFFLE", "BYTE");
5✔
68
        const int shuffle =
5✔
69
            (EQUAL(pszShuffle, "BYTE") || EQUAL(pszShuffle, "1"))
1✔
70
                ? BLOSC_SHUFFLE
6✔
71
            : (EQUAL(pszShuffle, "BIT") || EQUAL(pszShuffle, "2"))
1✔
72
                ? BLOSC_BITSHUFFLE
2✔
73
                : BLOSC_NOSHUFFLE;
74
        const int typesize =
75
            atoi(CSLFetchNameValueDef(options, "TYPESIZE", "1"));
5✔
76
        const char *compressor =
77
            CSLFetchNameValueDef(options, "CNAME", BLOSC_LZ4_COMPNAME);
5✔
78
        const int blocksize =
79
            atoi(CSLFetchNameValueDef(options, "BLOCKSIZE", "0"));
5✔
80
        if (blocksize < 0)
5✔
81
        {
82
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid BLOCKSIZE");
×
83
            return false;
×
84
        }
85
        const char *pszNumThreads =
86
            CSLFetchNameValueDef(options, "NUM_THREADS", "1");
5✔
87
        const int numthreads = EQUAL(pszNumThreads, "ALL_CPUS")
5✔
88
                                   ? CPLGetNumCPUs()
5✔
89
                                   : atoi(pszNumThreads);
5✔
90
        int ret = blosc_compress_ctx(clevel, shuffle, typesize, input_size,
5✔
91
                                     input_data, *output_data, *output_size,
92
                                     compressor, blocksize, numthreads);
93
        if (ret < 0)
5✔
94
        {
95
            *output_size = 0;
1✔
96
            return false;
1✔
97
        }
98
        if (ret == 0)
4✔
99
        {
100
            *output_size = input_size + BLOSC_MAX_OVERHEAD;
×
101
            return false;
×
102
        }
103
        *output_size = ret;
4✔
104
        return true;
4✔
105
    }
106

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

113
    if (output_data != nullptr && *output_data == nullptr &&
1✔
114
        output_size != nullptr)
115
    {
116
        size_t nSafeSize = input_size + BLOSC_MAX_OVERHEAD;
1✔
117
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
118
        *output_size = nSafeSize;
1✔
119
        if (*output_data == nullptr)
1✔
120
            return false;
×
121
        bool ret = CPLBloscCompressor(input_data, input_size, output_data,
1✔
122
                                      output_size, options, nullptr);
123
        if (!ret)
1✔
124
        {
125
            VSIFree(*output_data);
×
126
            *output_data = nullptr;
×
127
        }
128
        return ret;
1✔
129
    }
130

131
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
132
    return false;
×
133
}
134

135
static bool CPLBloscDecompressor(const void *input_data, size_t input_size,
7✔
136
                                 void **output_data, size_t *output_size,
137
                                 CSLConstList options,
138
                                 void * /* compressor_user_data */)
139
{
140
    size_t nSafeSize = 0;
7✔
141
    if (blosc_cbuffer_validate(input_data, input_size, &nSafeSize) < 0)
7✔
142
    {
143
        *output_size = 0;
×
144
        return false;
×
145
    }
146

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

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

168
        *output_size = nSafeSize;
5✔
169
        return true;
5✔
170
    }
171

172
    if (output_data == nullptr && output_size != nullptr)
2✔
173
    {
174
        *output_size = nSafeSize;
1✔
175
        return true;
1✔
176
    }
177

178
    if (output_data != nullptr && *output_data == nullptr &&
1✔
179
        output_size != nullptr)
180
    {
181
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
182
        *output_size = nSafeSize;
1✔
183
        if (*output_data == nullptr)
1✔
184
            return false;
×
185
        bool ret = CPLBloscDecompressor(input_data, input_size, output_data,
1✔
186
                                        output_size, options, nullptr);
187
        if (!ret)
1✔
188
        {
189
            VSIFree(*output_data);
×
190
            *output_data = nullptr;
×
191
        }
192
        return ret;
1✔
193
    }
194

195
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
196
    return false;
×
197
}
198

199
#endif
200

201
#ifdef HAVE_LZMA
202
static bool CPLLZMACompressor(const void *input_data, size_t input_size,
5✔
203
                              void **output_data, size_t *output_size,
204
                              CSLConstList options,
205
                              void * /* compressor_user_data */)
206
{
207
    if (output_data != nullptr && *output_data != nullptr &&
5✔
208
        output_size != nullptr && *output_size != 0)
3✔
209
    {
210
        const int preset = atoi(CSLFetchNameValueDef(options, "PRESET", "6"));
3✔
211
        const int delta = atoi(CSLFetchNameValueDef(options, "DELTA", "1"));
3✔
212

213
        lzma_filter filters[3];
214
        lzma_options_delta opt_delta;
215
        lzma_options_lzma opt_lzma;
216

217
        opt_delta.type = LZMA_DELTA_TYPE_BYTE;
3✔
218
        opt_delta.dist = delta;
3✔
219
        filters[0].id = LZMA_FILTER_DELTA;
3✔
220
        filters[0].options = &opt_delta;
3✔
221

222
        lzma_lzma_preset(&opt_lzma, preset);
3✔
223
        filters[1].id = LZMA_FILTER_LZMA2;
3✔
224
        filters[1].options = &opt_lzma;
3✔
225

226
        filters[2].id = LZMA_VLI_UNKNOWN;
3✔
227
        filters[2].options = nullptr;
3✔
228

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

244
    if (output_data == nullptr && output_size != nullptr)
2✔
245
    {
246
        *output_size = lzma_stream_buffer_bound(input_size);
1✔
247
        return true;
1✔
248
    }
249

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

268
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
269
    return false;
×
270
}
271

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

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

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

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

UNCOV
361
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
362
    return false;
×
363
}
364

365
#endif  // HAVE_LZMA
366

367
#ifdef HAVE_ZSTD
368
static bool CPLZSTDCompressor(const void *input_data, size_t input_size,
7✔
369
                              void **output_data, size_t *output_size,
370
                              CSLConstList options,
371
                              void * /* compressor_user_data */)
372
{
373
    if (output_data != nullptr && *output_data != nullptr &&
7✔
374
        output_size != nullptr && *output_size != 0)
5✔
375
    {
376
        ZSTD_CCtx *ctx = ZSTD_createCCtx();
5✔
377
        if (ctx == nullptr)
5✔
378
        {
379
            *output_size = 0;
×
380
            return false;
×
381
        }
382

383
        const int level = atoi(CSLFetchNameValueDef(options, "LEVEL", "13"));
5✔
384
        if (ZSTD_isError(
5✔
385
                ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, level)))
5✔
386
        {
387
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid compression level");
×
388
            ZSTD_freeCCtx(ctx);
×
389
            *output_size = 0;
×
390
            return false;
×
391
        }
392

393
        if (CPLTestBool(CSLFetchNameValueDef(options, "CHECKSUM", "NO")))
5✔
394
        {
395
            CPL_IGNORE_RET_VAL(
×
396
                ZSTD_CCtx_setParameter(ctx, ZSTD_c_checksumFlag, 1));
×
397
        }
398

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

408
        *output_size = ret;
4✔
409
        return true;
4✔
410
    }
411

412
    if (output_data == nullptr && output_size != nullptr)
2✔
413
    {
414
        *output_size = ZSTD_compressBound(input_size);
1✔
415
        return true;
1✔
416
    }
417

418
    if (output_data != nullptr && *output_data == nullptr &&
1✔
419
        output_size != nullptr)
420
    {
421
        size_t nSafeSize = ZSTD_compressBound(input_size);
1✔
422
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
423
        *output_size = nSafeSize;
1✔
424
        if (*output_data == nullptr)
1✔
425
            return false;
×
426
        bool ret = CPLZSTDCompressor(input_data, input_size, output_data,
1✔
427
                                     output_size, options, nullptr);
428
        if (!ret)
1✔
429
        {
430
            VSIFree(*output_data);
×
431
            *output_data = nullptr;
×
432
        }
433
        return ret;
1✔
434
    }
435

436
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
437
    return false;
×
438
}
439

440
// CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW because ZSTD_CONTENTSIZE_ERROR expands
441
// to (0ULL - 2)...
442
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
443
static size_t CPLZSTDGetDecompressedSize(const void *input_data,
4✔
444
                                         size_t input_size)
445
{
446
#if (ZSTD_VERSION_MAJOR > 1) ||                                                \
447
    (ZSTD_VERSION_MAJOR == 1 && ZSTD_VERSION_MINOR >= 3)
448
    uint64_t nRet = ZSTD_getFrameContentSize(input_data, input_size);
4✔
449
    if (nRet == ZSTD_CONTENTSIZE_ERROR)
4✔
450
    {
451
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
452
                 "Error while retrieving decompressed size of ZSTD frame.");
453
        nRet = 0;
1✔
454
    }
455
    else if (nRet == ZSTD_CONTENTSIZE_UNKNOWN)
3✔
456
    {
457
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
458
                 "Decompressed size of ZSTD frame is unknown.");
459
        nRet = 0;
1✔
460
    }
461
#else
462
    uint64_t nRet = ZSTD_getDecompressedSize(input_data, input_size);
463
    if (nRet == 0)
464
    {
465
        CPLError(CE_Failure, CPLE_AppDefined,
466
                 "Decompressed size of ZSTD frame is unknown.");
467
    }
468
#endif
469

470
#if SIZEOF_VOIDP == 4
471
    if (nRet > std::numeric_limits<size_t>::max())
472
    {
473
        CPLError(CE_Failure, CPLE_AppDefined,
474
                 "Decompressed size of ZSTD frame is bigger than 4GB.");
475
        nRet = 0;
476
    }
477
#endif
478

479
    return static_cast<size_t>(nRet);
4✔
480
}
481

482
static bool CPLZSTDDecompressor(const void *input_data, size_t input_size,
169✔
483
                                void **output_data, size_t *output_size,
484
                                CSLConstList /* options */,
485
                                void * /* compressor_user_data */)
486
{
487
    if (output_data != nullptr && *output_data != nullptr &&
169✔
488
        output_size != nullptr && *output_size != 0)
164✔
489
    {
490
        size_t ret =
491
            ZSTD_decompress(*output_data, *output_size, input_data, input_size);
165✔
492
        if (ZSTD_isError(ret))
167✔
493
        {
494
            *output_size = CPLZSTDGetDecompressedSize(input_data, input_size);
2✔
495
            return false;
2✔
496
        }
497

498
        *output_size = ret;
165✔
499
        return true;
165✔
500
    }
501

502
    if (output_data == nullptr && output_size != nullptr)
4✔
503
    {
504
        *output_size = CPLZSTDGetDecompressedSize(input_data, input_size);
1✔
505
        return *output_size != 0;
1✔
506
    }
507

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

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

529
        *output_size = ret;
1✔
530
        return true;
1✔
531
    }
532

533
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
2✔
534
    return false;
×
535
}
536

537
#endif  // HAVE_ZSTD
538

539
#ifdef HAVE_LZ4
540
static bool CPLLZ4Compressor(const void *input_data, size_t input_size,
5✔
541
                             void **output_data, size_t *output_size,
542
                             CSLConstList options,
543
                             void * /* compressor_user_data */)
544
{
545
    if (input_size > static_cast<size_t>(std::numeric_limits<int>::max()))
5✔
546
    {
547
        CPLError(CE_Failure, CPLE_NotSupported,
×
548
                 "Too large input buffer. "
549
                 "Max supported is INT_MAX");
550
        *output_size = 0;
×
551
        return false;
×
552
    }
553

554
    const bool bHeader =
555
        CPLTestBool(CSLFetchNameValueDef(options, "HEADER", "YES"));
5✔
556
    const int header_size = bHeader ? static_cast<int>(sizeof(int32_t)) : 0;
5✔
557

558
    if (output_data != nullptr && *output_data != nullptr &&
5✔
559
        output_size != nullptr && *output_size != 0)
3✔
560
    {
561
        const int acceleration =
562
            atoi(CSLFetchNameValueDef(options, "ACCELERATION", "1"));
3✔
563
        if (*output_size >
6✔
564
            static_cast<size_t>(std::numeric_limits<int>::max() - 4))
3✔
565
        {
566
            CPLError(CE_Failure, CPLE_NotSupported,
×
567
                     "Too large output buffer. "
568
                     "Max supported is INT_MAX");
569
            *output_size = 0;
×
570
            return false;
×
571
        }
572

573
        if (bHeader && static_cast<int>(*output_size) < header_size)
3✔
574
        {
575
            *output_size = 0;
1✔
576
            return false;
1✔
577
        }
578

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

590
        int32_t sizeLSB = CPL_LSBWORD32(static_cast<int>(input_size));
2✔
591
        memcpy(*output_data, &sizeLSB, sizeof(sizeLSB));
2✔
592

593
        *output_size = static_cast<size_t>(header_size + ret);
2✔
594
        return true;
2✔
595
    }
596

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

604
    if (output_data != nullptr && *output_data == nullptr &&
1✔
605
        output_size != nullptr)
606
    {
607
        size_t nSafeSize = static_cast<size_t>(header_size) +
1✔
608
                           LZ4_compressBound(static_cast<int>(input_size));
1✔
609
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
610
        *output_size = nSafeSize;
1✔
611
        if (*output_data == nullptr)
1✔
612
            return false;
×
613
        bool ret = CPLLZ4Compressor(input_data, input_size, output_data,
1✔
614
                                    output_size, options, nullptr);
615
        if (!ret)
1✔
616
        {
617
            VSIFree(*output_data);
×
618
            *output_data = nullptr;
×
619
        }
620
        return ret;
1✔
621
    }
622

623
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
624
    return false;
×
625
}
626

627
static bool CPLLZ4Decompressor(const void *input_data, size_t input_size,
5✔
628
                               void **output_data, size_t *output_size,
629
                               CSLConstList options,
630
                               void * /* compressor_user_data */)
631
{
632
    if (input_size > static_cast<size_t>(std::numeric_limits<int>::max()))
5✔
633
    {
634
        CPLError(CE_Failure, CPLE_NotSupported,
×
635
                 "Too large input buffer. "
636
                 "Max supported is INT_MAX");
637
        *output_size = 0;
×
638
        return false;
×
639
    }
640

641
    const bool bHeader =
642
        CPLTestBool(CSLFetchNameValueDef(options, "HEADER", "YES"));
5✔
643
    const int header_size = bHeader ? static_cast<int>(sizeof(int32_t)) : 0;
5✔
644
    if (bHeader && static_cast<int>(input_size) < header_size)
5✔
645
    {
646
        *output_size = 0;
×
647
        return false;
×
648
    }
649

650
    if (output_data != nullptr && *output_data != nullptr &&
5✔
651
        output_size != nullptr && *output_size != 0)
3✔
652
    {
653
        if (*output_size > static_cast<size_t>(std::numeric_limits<int>::max()))
3✔
654
        {
655
            CPLError(CE_Failure, CPLE_NotSupported,
×
656
                     "Too large output buffer. "
657
                     "Max supported is INT_MAX");
658
            *output_size = 0;
×
659
            return false;
×
660
        }
661

662
        int ret = LZ4_decompress_safe(
6✔
663
            static_cast<const char *>(input_data) + header_size,
3✔
664
            static_cast<char *>(*output_data),
665
            static_cast<int>(input_size) - header_size,
666
            static_cast<int>(*output_size));
3✔
667
        if (ret <= 0)
3✔
668
        {
669
            *output_size = 0;
×
670
            return false;
×
671
        }
672

673
        *output_size = ret;
3✔
674
        return true;
3✔
675
    }
676

677
    if (output_data == nullptr && output_size != nullptr)
2✔
678
    {
679
        if (bHeader)
1✔
680
        {
681
            int nSize = CPL_LSBSINT32PTR(input_data);
1✔
682
            if (nSize < 0)
1✔
683
            {
684
                *output_size = 0;
×
685
                return false;
×
686
            }
687
            *output_size = nSize;
1✔
688
            return true;
1✔
689
        }
690

691
        // inefficient !
692
        void *tmpBuffer = nullptr;
×
693
        bool ret = CPLLZ4Decompressor(input_data, input_size, &tmpBuffer,
×
694
                                      output_size, options, nullptr);
695
        VSIFree(tmpBuffer);
×
696
        return ret;
×
697
    }
698

699
    if (output_data != nullptr && *output_data == nullptr &&
1✔
700
        output_size != nullptr)
701
    {
702
        if (bHeader)
1✔
703
        {
704
            int nSize = CPL_LSBSINT32PTR(input_data);
1✔
705
            if (nSize <= 0)
1✔
706
            {
707
                *output_size = 0;
×
708
                return false;
×
709
            }
710
            if (nSize > INT_MAX - 1 || /* to make Coverity scan happy */
1✔
711
                nSize / 10000 > static_cast<int>(input_size))
1✔
712
            {
713
                CPLError(CE_Failure, CPLE_AppDefined,
×
714
                         "Stored uncompressed size (%d) is much larger "
715
                         "than compressed size (%d)",
716
                         nSize, static_cast<int>(input_size));
717
                *output_size = nSize;
×
718
                return false;
×
719
            }
720
            *output_data = VSI_MALLOC_VERBOSE(nSize);
1✔
721
            *output_size = nSize;
1✔
722
            if (*output_data == nullptr)
1✔
723
            {
724
                return false;
×
725
            }
726
            if (!CPLLZ4Decompressor(input_data, input_size, output_data,
1✔
727
                                    output_size, options, nullptr))
728
            {
729
                VSIFree(*output_data);
×
730
                *output_data = nullptr;
×
731
                *output_size = 0;
×
732
                return false;
×
733
            }
734
            return true;
1✔
735
        }
736

737
        size_t nOutSize =
738
            static_cast<int>(input_size) < std::numeric_limits<int>::max() / 2
×
739
                ? input_size * 2
×
740
                : static_cast<size_t>(std::numeric_limits<int>::max());
×
741
        *output_data = VSI_MALLOC_VERBOSE(nOutSize);
×
742
        if (*output_data == nullptr)
×
743
        {
744
            *output_size = 0;
×
745
            return false;
×
746
        }
747

748
        while (true)
749
        {
750
            int ret = LZ4_decompress_safe_partial(
×
751
                static_cast<const char *>(input_data),
752
                static_cast<char *>(*output_data), static_cast<int>(input_size),
753
                static_cast<int>(nOutSize), static_cast<int>(nOutSize));
754
            if (ret <= 0)
×
755
            {
756
                VSIFree(*output_data);
×
757
                *output_data = nullptr;
×
758
                *output_size = 0;
×
759
                return false;
×
760
            }
761
            else if (ret < static_cast<int>(nOutSize))
×
762
            {
763
                *output_size = ret;
×
764
                return true;
×
765
            }
766
            else if (static_cast<int>(nOutSize) <
×
767
                     std::numeric_limits<int>::max() / 2)
×
768
            {
769
                nOutSize *= 2;
×
770
                void *tmpBuffer = VSI_REALLOC_VERBOSE(*output_data, nOutSize);
×
771
                if (tmpBuffer == nullptr)
×
772
                {
773
                    VSIFree(*output_data);
×
774
                    *output_data = nullptr;
×
775
                    *output_size = 0;
×
776
                    return false;
×
777
                }
778
                *output_data = tmpBuffer;
×
779
            }
780
            else
781
            {
782
                VSIFree(*output_data);
×
783
                *output_data = nullptr;
×
784
                *output_size = 0;
×
785
                return false;
×
786
            }
787
        }
×
788
    }
789

790
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
791
    return false;
×
792
}
793

794
#endif  // HAVE_LZ4
795

796
static void *CPLGZipCompress(const void *ptr, size_t nBytes, int nLevel,
10,756✔
797
                             void *outptr, size_t nOutAvailableBytes,
798
                             size_t *pnOutBytes)
799
{
800
    if (pnOutBytes != nullptr)
10,756✔
801
        *pnOutBytes = 0;
10,756✔
802

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

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

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

880
    return pTmp;
10,755✔
881
}
882

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

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

909
        *output_size = nOutBytes;
10,853✔
910
        return true;
10,853✔
911
    }
912

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

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

958
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
959
    return false;
×
960
}
961

962
namespace
963
{
964
template <class T> inline T swap(T x)
×
965
{
966
    return x;
×
967
}
968

969
template <> inline uint16_t swap<uint16_t>(uint16_t x)
12✔
970
{
971
    return CPL_SWAP16(x);
12✔
972
}
973

974
template <> inline int16_t swap<int16_t>(int16_t x)
12✔
975
{
976
    return CPL_SWAP16(x);
12✔
977
}
978

979
template <> inline uint32_t swap<uint32_t>(uint32_t x)
12✔
980
{
981
    return CPL_SWAP32(x);
12✔
982
}
983

984
template <> inline int32_t swap<int32_t>(int32_t x)
12✔
985
{
986
    return CPL_SWAP32(x);
12✔
987
}
988

989
template <> inline uint64_t swap<uint64_t>(uint64_t x)
12✔
990
{
991
    return CPL_SWAP64(x);
12✔
992
}
993

994
template <> inline int64_t swap<int64_t>(int64_t x)
12✔
995
{
996
    return CPL_SWAP64(x);
12✔
997
}
998

999
template <> inline float swap<float>(float x)
×
1000
{
1001
    float ret = x;
×
1002
    CPL_SWAP32PTR(&ret);
×
1003
    return ret;
×
1004
}
1005

1006
template <> inline double swap<double>(double x)
×
1007
{
1008
    double ret = x;
×
1009
    CPL_SWAP64PTR(&ret);
×
1010
    return ret;
×
1011
}
1012
}  // namespace
1013

1014
namespace
1015
{
1016
// Workaround -ftrapv
1017
template <class T>
1018
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW inline T SubNoOverflow(T left, T right)
448✔
1019
{
1020
    typedef typename std::make_unsigned<T>::type U;
1021
    U leftU = static_cast<U>(left);
448✔
1022
    U rightU = static_cast<U>(right);
448✔
1023
    leftU = static_cast<U>(leftU - rightU);
448✔
1024
    T ret;
1025
    memcpy(&ret, &leftU, sizeof(ret));
448✔
1026
    return leftU;
448✔
1027
}
1028

1029
template <> inline float SubNoOverflow<float>(float x, float y)
4✔
1030
{
1031
    return x - y;
4✔
1032
}
1033

1034
template <> inline double SubNoOverflow<double>(double x, double y)
4✔
1035
{
1036
    return x - y;
4✔
1037
}
1038
}  // namespace
1039

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

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

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

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

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

1218
        *output_size = input_size;
26✔
1219
        return true;
26✔
1220
    }
1221

1222
    if (output_data == nullptr && output_size != nullptr)
×
1223
    {
1224
        *output_size = input_size;
×
1225
        return true;
×
1226
    }
1227

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

1245
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
1246
    return false;
×
1247
}
1248

1249
static void CPLAddCompressor(const CPLCompressor *compressor)
92✔
1250
{
1251
    CPLCompressor *copy = new CPLCompressor(*compressor);
92✔
1252
    // cppcheck-suppress uninitdata
1253
    copy->pszId = CPLStrdup(compressor->pszId);
92✔
1254
    // cppcheck-suppress uninitdata
1255
    copy->papszMetadata = CSLDuplicate(compressor->papszMetadata);
92✔
1256
    gpCompressors->emplace_back(copy);
92✔
1257
}
92✔
1258

1259
static void CPLAddBuiltinCompressors()
13✔
1260
{
1261
#ifdef HAVE_BLOSC
1262
    do
1263
    {
1264
        CPLCompressor sComp;
1265
        sComp.nStructVersion = 1;
13✔
1266
        sComp.eType = CCT_COMPRESSOR;
13✔
1267
        sComp.pszId = "blosc";
13✔
1268

1269
        const CPLStringList aosCompressors(
1270
            CSLTokenizeString2(blosc_list_compressors(), ",", 0));
13✔
1271
        if (aosCompressors.size() == 0)
13✔
1272
            break;
×
1273
        std::string options("OPTIONS=<Options>"
1274
                            "  <Option name='CNAME' type='string-select' "
1275
                            "description='Compressor name' default='");
26✔
1276
        std::string values;
26✔
1277
        std::string defaultCompressor;
26✔
1278
        bool bFoundLZ4 = false;
13✔
1279
        bool bFoundSnappy = false;
13✔
1280
        bool bFoundZlib = false;
13✔
1281
        for (int i = 0; i < aosCompressors.size(); i++)
91✔
1282
        {
1283
            values += "<Value>";
78✔
1284
            values += aosCompressors[i];
78✔
1285
            values += "</Value>";
78✔
1286
            if (strcmp(aosCompressors[i], "lz4") == 0)
78✔
1287
                bFoundLZ4 = true;
13✔
1288
            else if (strcmp(aosCompressors[i], "snappy") == 0)
65✔
1289
                bFoundSnappy = true;
13✔
1290
            else if (strcmp(aosCompressors[i], "zlib") == 0)
52✔
1291
                bFoundZlib = true;
13✔
1292
        }
1293
        options += bFoundLZ4      ? "lz4"
1294
                   : bFoundSnappy ? "snappy"
×
1295
                   : bFoundZlib   ? "zlib"
×
1296
                                  : aosCompressors[0];
13✔
1297
        options += "'>";
13✔
1298
        options += values;
13✔
1299
        options +=
1300
            "  </Option>"
1301
            "  <Option name='CLEVEL' type='int' description='Compression "
1302
            "level' min='1' max='9' default='5' />"
1303
            "  <Option name='SHUFFLE' type='string-select' description='Type "
1304
            "of shuffle algorithm' default='BYTE'>"
1305
            "    <Value alias='0'>NONE</Value>"
1306
            "    <Value alias='1'>BYTE</Value>"
1307
            "    <Value alias='2'>BIT</Value>"
1308
            "  </Option>"
1309
            "  <Option name='BLOCKSIZE' type='int' description='Block size' "
1310
            "default='0' />"
1311
            "  <Option name='TYPESIZE' type='int' description='Number of bytes "
1312
            "for the atomic type' default='1' />"
1313
            "  <Option name='NUM_THREADS' type='string' "
1314
            "description='Number of worker threads for compression. Can be set "
1315
            "to ALL_CPUS' default='1' />"
1316
            "</Options>";
13✔
1317

1318
        const char *const apszMetadata[] = {
13✔
1319
            "BLOSC_VERSION=" BLOSC_VERSION_STRING, options.c_str(), nullptr};
13✔
1320
        sComp.papszMetadata = apszMetadata;
13✔
1321
        sComp.pfnFunc = CPLBloscCompressor;
13✔
1322
        sComp.user_data = nullptr;
13✔
1323
        CPLAddCompressor(&sComp);
13✔
1324
    } while (0);
1325
#endif
1326
    {
1327
        CPLCompressor sComp;
1328
        sComp.nStructVersion = 1;
13✔
1329
        sComp.eType = CCT_COMPRESSOR;
13✔
1330
        sComp.pszId = "zlib";
13✔
1331
        const char *pszOptions =
13✔
1332
            "OPTIONS=<Options>"
1333
            "  <Option name='LEVEL' type='int' description='Compression level' "
1334
            "min='1' max='9' default='6' />"
1335
            "</Options>";
1336
        const char *const apszMetadata[] = {pszOptions, nullptr};
13✔
1337
        sComp.papszMetadata = apszMetadata;
13✔
1338
        sComp.pfnFunc = CPLZlibCompressor;
13✔
1339
        sComp.user_data = const_cast<char *>("zlib");
13✔
1340
        CPLAddCompressor(&sComp);
13✔
1341
    }
1342
    {
1343
        CPLCompressor sComp;
1344
        sComp.nStructVersion = 1;
13✔
1345
        sComp.eType = CCT_COMPRESSOR;
13✔
1346
        sComp.pszId = "gzip";
13✔
1347
        const char *pszOptions =
13✔
1348
            "OPTIONS=<Options>"
1349
            "  <Option name='LEVEL' type='int' description='Compression level' "
1350
            "min='1' max='9' default='6' />"
1351
            "</Options>";
1352
        const char *const apszMetadata[] = {pszOptions, nullptr};
13✔
1353
        sComp.papszMetadata = apszMetadata;
13✔
1354
        sComp.pfnFunc = CPLZlibCompressor;
13✔
1355
        sComp.user_data = const_cast<char *>("gzip");
13✔
1356
        CPLAddCompressor(&sComp);
13✔
1357
    }
1358
#ifdef HAVE_LZMA
1359
    {
1360
        CPLCompressor sComp;
1361
        sComp.nStructVersion = 1;
13✔
1362
        sComp.eType = CCT_COMPRESSOR;
13✔
1363
        sComp.pszId = "lzma";
13✔
1364
        const char *pszOptions =
13✔
1365
            "OPTIONS=<Options>"
1366
            "  <Option name='PRESET' type='int' description='Compression "
1367
            "level' min='0' max='9' default='6' />"
1368
            "  <Option name='DELTA' type='int' description='Delta distance in "
1369
            "byte' default='1' />"
1370
            "</Options>";
1371
        const char *const apszMetadata[] = {pszOptions, nullptr};
13✔
1372
        sComp.papszMetadata = apszMetadata;
13✔
1373
        sComp.pfnFunc = CPLLZMACompressor;
13✔
1374
        sComp.user_data = nullptr;
13✔
1375
        CPLAddCompressor(&sComp);
13✔
1376
    }
1377
#endif
1378
#ifdef HAVE_ZSTD
1379
    {
1380
        CPLCompressor sComp;
1381
        sComp.nStructVersion = 1;
13✔
1382
        sComp.eType = CCT_COMPRESSOR;
13✔
1383
        sComp.pszId = "zstd";
13✔
1384
        const char *pszOptions =
13✔
1385
            "OPTIONS=<Options>"
1386
            "  <Option name='LEVEL' type='int' description='Compression level' "
1387
            "min='1' max='22' default='13' />"
1388
            "  <Option name='CHECKSUM' type='boolean' description='Whether "
1389
            "to store a checksum when writing that will be verified' "
1390
            "default='NO' />"
1391
            "</Options>";
1392
        const char *const apszMetadata[] = {pszOptions, nullptr};
13✔
1393
        sComp.papszMetadata = apszMetadata;
13✔
1394
        sComp.pfnFunc = CPLZSTDCompressor;
13✔
1395
        sComp.user_data = nullptr;
13✔
1396
        CPLAddCompressor(&sComp);
13✔
1397
    }
1398
#endif
1399
#ifdef HAVE_LZ4
1400
    {
1401
        CPLCompressor sComp;
1402
        sComp.nStructVersion = 1;
13✔
1403
        sComp.eType = CCT_COMPRESSOR;
13✔
1404
        sComp.pszId = "lz4";
13✔
1405
        const char *pszOptions =
13✔
1406
            "OPTIONS=<Options>"
1407
            "  <Option name='ACCELERATION' type='int' "
1408
            "description='Acceleration factor. The higher, the less "
1409
            "compressed' min='1' default='1' />"
1410
            "  <Option name='HEADER' type='boolean' description='Whether a "
1411
            "header with the uncompressed size should be included (as used by "
1412
            "Zarr)' default='YES' />"
1413
            "</Options>";
1414
        const char *const apszMetadata[] = {pszOptions, nullptr};
13✔
1415
        sComp.papszMetadata = apszMetadata;
13✔
1416
        sComp.pfnFunc = CPLLZ4Compressor;
13✔
1417
        sComp.user_data = nullptr;
13✔
1418
        CPLAddCompressor(&sComp);
13✔
1419
    }
1420
#endif
1421
    {
1422
        CPLCompressor sComp;
1423
        sComp.nStructVersion = 1;
13✔
1424
        sComp.eType = CCT_FILTER;
13✔
1425
        sComp.pszId = "delta";
13✔
1426
        const char *pszOptions =
13✔
1427
            "OPTIONS=<Options>"
1428
            "  <Option name='DTYPE' type='string' description='Data type "
1429
            "following NumPy array protocol type string (typestr) format'/>"
1430
            "</Options>";
1431
        const char *const apszMetadata[] = {pszOptions, nullptr};
13✔
1432
        sComp.papszMetadata = apszMetadata;
13✔
1433
        sComp.pfnFunc = CPLDeltaCompressor;
13✔
1434
        sComp.user_data = nullptr;
13✔
1435
        CPLAddCompressor(&sComp);
13✔
1436
    }
1437
}
13✔
1438

1439
static bool CPLZlibDecompressor(const void *input_data, size_t input_size,
16,388✔
1440
                                void **output_data, size_t *output_size,
1441
                                CSLConstList /* options */,
1442
                                void * /* compressor_user_data */)
1443
{
1444
    if (output_data != nullptr && *output_data != nullptr &&
16,388✔
1445
        output_size != nullptr && *output_size != 0)
16,006✔
1446
    {
1447
        size_t nOutBytes = 0;
16,001✔
1448
        if (nullptr == CPLZLibInflate(input_data, input_size, *output_data,
16,001✔
1449
                                      *output_size, &nOutBytes))
1450
        {
1451
            *output_size = 0;
×
1452
            return false;
×
1453
        }
1454

1455
        *output_size = nOutBytes;
16,285✔
1456
        return true;
16,285✔
1457
    }
1458

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

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

1507
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
382✔
1508
    return false;
×
1509
}
1510

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

1526
template <> inline float AddNoOverflow<float>(float x, float y)
4✔
1527
{
1528
    return x + y;
4✔
1529
}
1530

1531
template <> inline double AddNoOverflow<double>(double x, double y)
4✔
1532
{
1533
    return x + y;
4✔
1534
}
1535
}  // namespace
1536

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

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

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

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

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

1715
        *output_size = input_size;
28✔
1716
        return true;
28✔
1717
    }
1718

1719
    if (output_data == nullptr && output_size != nullptr)
×
1720
    {
1721
        *output_size = input_size;
×
1722
        return true;
×
1723
    }
1724

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

1742
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
1743
    return false;
×
1744
}
1745

1746
static void CPLAddDecompressor(const CPLCompressor *decompressor)
9,570✔
1747
{
1748
    CPLCompressor *copy = new CPLCompressor(*decompressor);
9,570✔
1749
    // cppcheck-suppress uninitdata
1750
    copy->pszId = CPLStrdup(decompressor->pszId);
9,570✔
1751
    // cppcheck-suppress uninitdata
1752
    copy->papszMetadata = CSLDuplicate(decompressor->papszMetadata);
9,570✔
1753
    gpDecompressors->emplace_back(copy);
9,570✔
1754
}
9,570✔
1755

1756
static void CPLAddBuiltinDecompressors()
1,367✔
1757
{
1758
#ifdef HAVE_BLOSC
1759
    {
1760
        CPLCompressor sComp;
1761
        sComp.nStructVersion = 1;
1,367✔
1762
        sComp.eType = CCT_COMPRESSOR;
1,367✔
1763
        sComp.pszId = "blosc";
1,367✔
1764
        const char *pszOptions =
1,367✔
1765
            "OPTIONS=<Options>"
1766
            "  <Option name='NUM_THREADS' type='string' "
1767
            "description='Number of worker threads for decompression. Can be "
1768
            "set to ALL_CPUS' default='1' />"
1769
            "</Options>";
1770
        const char *const apszMetadata[] = {
1,367✔
1771
            "BLOSC_VERSION=" BLOSC_VERSION_STRING, pszOptions, nullptr};
1,367✔
1772
        sComp.papszMetadata = apszMetadata;
1,367✔
1773
        sComp.pfnFunc = CPLBloscDecompressor;
1,367✔
1774
        sComp.user_data = nullptr;
1,367✔
1775
        CPLAddDecompressor(&sComp);
1,367✔
1776
    }
1777
#endif
1778
    {
1779
        CPLCompressor sComp;
1780
        sComp.nStructVersion = 1;
1,367✔
1781
        sComp.eType = CCT_COMPRESSOR;
1,367✔
1782
        sComp.pszId = "zlib";
1,367✔
1783
        sComp.papszMetadata = nullptr;
1,367✔
1784
        sComp.pfnFunc = CPLZlibDecompressor;
1,367✔
1785
        sComp.user_data = nullptr;
1,367✔
1786
        CPLAddDecompressor(&sComp);
1,367✔
1787
    }
1788
    {
1789
        CPLCompressor sComp;
1790
        sComp.nStructVersion = 1;
1,367✔
1791
        sComp.eType = CCT_COMPRESSOR;
1,367✔
1792
        sComp.pszId = "gzip";
1,367✔
1793
        sComp.papszMetadata = nullptr;
1,367✔
1794
        sComp.pfnFunc = CPLZlibDecompressor;
1,367✔
1795
        sComp.user_data = nullptr;
1,367✔
1796
        CPLAddDecompressor(&sComp);
1,367✔
1797
    }
1798
#ifdef HAVE_LZMA
1799
    {
1800
        CPLCompressor sComp;
1801
        sComp.nStructVersion = 1;
1,367✔
1802
        sComp.eType = CCT_COMPRESSOR;
1,367✔
1803
        sComp.pszId = "lzma";
1,367✔
1804
        sComp.papszMetadata = nullptr;
1,367✔
1805
        sComp.pfnFunc = CPLLZMADecompressor;
1,367✔
1806
        sComp.user_data = nullptr;
1,367✔
1807
        CPLAddDecompressor(&sComp);
1,367✔
1808
    }
1809
#endif
1810
#ifdef HAVE_ZSTD
1811
    {
1812
        CPLCompressor sComp;
1813
        sComp.nStructVersion = 1;
1,367✔
1814
        sComp.eType = CCT_COMPRESSOR;
1,367✔
1815
        sComp.pszId = "zstd";
1,367✔
1816
        sComp.papszMetadata = nullptr;
1,367✔
1817
        sComp.pfnFunc = CPLZSTDDecompressor;
1,367✔
1818
        sComp.user_data = nullptr;
1,367✔
1819
        CPLAddDecompressor(&sComp);
1,367✔
1820
    }
1821
#endif
1822
#ifdef HAVE_LZ4
1823
    {
1824
        CPLCompressor sComp;
1825
        sComp.nStructVersion = 1;
1,367✔
1826
        sComp.eType = CCT_COMPRESSOR;
1,367✔
1827
        sComp.pszId = "lz4";
1,367✔
1828
        const char *pszOptions =
1,367✔
1829
            "OPTIONS=<Options>"
1830
            "  <Option name='HEADER' type='boolean' description='Whether a "
1831
            "header with the uncompressed size should be included (as used by "
1832
            "Zarr)' default='YES' />"
1833
            "</Options>";
1834
        const char *const apszMetadata[] = {pszOptions, nullptr};
1,367✔
1835
        sComp.papszMetadata = apszMetadata;
1,367✔
1836
        sComp.pfnFunc = CPLLZ4Decompressor;
1,367✔
1837
        sComp.user_data = nullptr;
1,367✔
1838
        CPLAddDecompressor(&sComp);
1,367✔
1839
    }
1840
#endif
1841
    {
1842
        CPLCompressor sComp;
1843
        sComp.nStructVersion = 1;
1,367✔
1844
        sComp.eType = CCT_FILTER;
1,367✔
1845
        sComp.pszId = "delta";
1,367✔
1846
        const char *pszOptions =
1,367✔
1847
            "OPTIONS=<Options>"
1848
            "  <Option name='DTYPE' type='string' description='Data type "
1849
            "following NumPy array protocol type string (typestr) format'/>"
1850
            "</Options>";
1851
        const char *const apszMetadata[] = {pszOptions, nullptr};
1,367✔
1852
        sComp.papszMetadata = apszMetadata;
1,367✔
1853
        sComp.pfnFunc = CPLDeltaDecompressor;
1,367✔
1854
        sComp.user_data = nullptr;
1,367✔
1855
        CPLAddDecompressor(&sComp);
1,367✔
1856
    }
1857
}
1,367✔
1858

1859
/** Register a new compressor.
1860
 *
1861
 * The provided structure is copied. Its pfnFunc and user_data members should
1862
 * remain valid beyond this call however.
1863
 *
1864
 * @param compressor Compressor structure. Should not be null.
1865
 * @return true if successful
1866
 * @since GDAL 3.4
1867
 */
1868
bool CPLRegisterCompressor(const CPLCompressor *compressor)
2✔
1869
{
1870
    if (compressor->nStructVersion < 1)
2✔
1871
        return false;
×
1872
    std::lock_guard<std::mutex> lock(gMutex);
4✔
1873
    if (gpCompressors == nullptr)
2✔
1874
    {
1875
        gpCompressors = new std::vector<CPLCompressor *>();
1✔
1876
        CPLAddBuiltinCompressors();
1✔
1877
    }
1878
    for (size_t i = 0; i < gpCompressors->size(); ++i)
16✔
1879
    {
1880
        if (strcmp(compressor->pszId, (*gpCompressors)[i]->pszId) == 0)
15✔
1881
        {
1882
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1883
                     "Compressor %s already registered", compressor->pszId);
1✔
1884
            return false;
1✔
1885
        }
1886
    }
1887
    CPLAddCompressor(compressor);
1✔
1888
    return true;
1✔
1889
}
1890

1891
/** Register a new decompressor.
1892
 *
1893
 * The provided structure is copied. Its pfnFunc and user_data members should
1894
 * remain valid beyond this call however.
1895
 *
1896
 * @param decompressor Compressor structure. Should not be null.
1897
 * @return true if successful
1898
 * @since GDAL 3.4
1899
 */
1900
bool CPLRegisterDecompressor(const CPLCompressor *decompressor)
2✔
1901
{
1902
    if (decompressor->nStructVersion < 1)
2✔
1903
        return false;
×
1904
    std::lock_guard<std::mutex> lock(gMutex);
4✔
1905
    if (gpDecompressors == nullptr)
2✔
1906
    {
1907
        gpDecompressors = new std::vector<CPLCompressor *>();
×
1908
        CPLAddBuiltinDecompressors();
×
1909
    }
1910
    for (size_t i = 0; i < gpDecompressors->size(); ++i)
16✔
1911
    {
1912
        if (strcmp(decompressor->pszId, (*gpDecompressors)[i]->pszId) == 0)
15✔
1913
        {
1914
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1915
                     "Decompressor %s already registered", decompressor->pszId);
1✔
1916
            return false;
1✔
1917
        }
1918
    }
1919
    CPLAddDecompressor(decompressor);
1✔
1920
    return true;
1✔
1921
}
1922

1923
/** Return the list of registered compressors.
1924
 *
1925
 * @return list of strings. Should be freed with CSLDestroy()
1926
 * @since GDAL 3.4
1927
 */
1928
char **CPLGetCompressors(void)
10✔
1929
{
1930
    std::lock_guard<std::mutex> lock(gMutex);
10✔
1931
    if (gpCompressors == nullptr)
10✔
1932
    {
1933
        gpCompressors = new std::vector<CPLCompressor *>();
×
1934
        CPLAddBuiltinCompressors();
×
1935
    }
1936
    char **papszRet = nullptr;
10✔
1937
    for (size_t i = 0; i < gpCompressors->size(); ++i)
81✔
1938
    {
1939
        papszRet = CSLAddString(papszRet, (*gpCompressors)[i]->pszId);
71✔
1940
    }
1941
    return papszRet;
20✔
1942
}
1943

1944
/** Return the list of registered decompressors.
1945
 *
1946
 * @return list of strings. Should be freed with CSLDestroy()
1947
 * @since GDAL 3.4
1948
 */
1949
char **CPLGetDecompressors(void)
10✔
1950
{
1951
    std::lock_guard<std::mutex> lock(gMutex);
10✔
1952
    if (gpDecompressors == nullptr)
10✔
1953
    {
1954
        gpDecompressors = new std::vector<CPLCompressor *>();
×
1955
        CPLAddBuiltinDecompressors();
×
1956
    }
1957
    char **papszRet = nullptr;
10✔
1958
    for (size_t i = 0;
10✔
1959
         gpDecompressors != nullptr && i < gpDecompressors->size(); ++i)
81✔
1960
    {
1961
        papszRet = CSLAddString(papszRet, (*gpDecompressors)[i]->pszId);
71✔
1962
    }
1963
    return papszRet;
20✔
1964
}
1965

1966
/** Return a compressor.
1967
 *
1968
 * @param pszId Compressor id. Should NOT be NULL.
1969
 * @return compressor structure, or NULL.
1970
 * @since GDAL 3.4
1971
 */
1972
const CPLCompressor *CPLGetCompressor(const char *pszId)
307✔
1973
{
1974
    std::lock_guard<std::mutex> lock(gMutex);
614✔
1975
    if (gpCompressors == nullptr)
307✔
1976
    {
1977
        gpCompressors = new std::vector<CPLCompressor *>();
12✔
1978
        CPLAddBuiltinCompressors();
12✔
1979
    }
1980
    for (size_t i = 0; i < gpCompressors->size(); ++i)
1,195✔
1981
    {
1982
        if (EQUAL(pszId, (*gpCompressors)[i]->pszId))
1,192✔
1983
        {
1984
            return (*gpCompressors)[i];
304✔
1985
        }
1986
    }
1987
    return nullptr;
3✔
1988
}
1989

1990
/** Return a decompressor.
1991
 *
1992
 * @param pszId Decompressor id. Should NOT be NULL.
1993
 * @return compressor structure, or NULL.
1994
 * @since GDAL 3.4
1995
 */
1996
const CPLCompressor *CPLGetDecompressor(const char *pszId)
1,748✔
1997
{
1998
    std::lock_guard<std::mutex> lock(gMutex);
3,496✔
1999
    if (gpDecompressors == nullptr)
1,748✔
2000
    {
2001
        gpDecompressors = new std::vector<CPLCompressor *>();
1,367✔
2002
        CPLAddBuiltinDecompressors();
1,367✔
2003
    }
2004
    for (size_t i = 0; i < gpDecompressors->size(); ++i)
6,816✔
2005
    {
2006
        if (EQUAL(pszId, (*gpDecompressors)[i]->pszId))
6,803✔
2007
        {
2008
            return (*gpDecompressors)[i];
1,735✔
2009
        }
2010
    }
2011
    return nullptr;
13✔
2012
}
2013

2014
static void
2015
CPLDestroyCompressorRegistryInternal(std::vector<CPLCompressor *> *&v)
1,848✔
2016
{
2017
    for (size_t i = 0; v != nullptr && i < v->size(); ++i)
8,374✔
2018
    {
2019
        CPLFree(const_cast<char *>((*v)[i]->pszId));
6,526✔
2020
        CSLDestroy(const_cast<char **>((*v)[i]->papszMetadata));
6,526✔
2021
        delete (*v)[i];
6,526✔
2022
    }
2023
    delete v;
1,848✔
2024
    v = nullptr;
1,848✔
2025
}
1,848✔
2026

2027
/*! @cond Doxygen_Suppress */
2028
void CPLDestroyCompressorRegistry(void)
924✔
2029
{
2030
    std::lock_guard<std::mutex> lock(gMutex);
1,848✔
2031

2032
    CPLDestroyCompressorRegistryInternal(gpCompressors);
924✔
2033
    CPLDestroyCompressorRegistryInternal(gpDecompressors);
924✔
2034
}
924✔
2035

2036
/*! @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