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

OSGeo / gdal / 12706066811

10 Jan 2025 08:38AM UTC coverage: 70.084% (-2.5%) from 72.549%
12706066811

Pull #11629

github

web-flow
Merge 9418dc48f into 0df468c56
Pull Request #11629: add uv documentation for python package

563296 of 803749 relevant lines covered (70.08%)

223434.74 hits per line

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

70.96
/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,
167✔
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 &&
167✔
278
        output_size != nullptr && *output_size != 0)
164✔
279
    {
280
        size_t in_pos = 0;
164✔
281
        size_t out_pos = 0;
164✔
282
        uint64_t memlimit = 100 * 1024 * 1024;
164✔
283
        lzma_ret ret = lzma_stream_buffer_decode(
164✔
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)
164✔
290
        {
291
            *output_size = 0;
×
292
            return false;
×
293
        }
294
        *output_size = out_pos;
164✔
295
        return true;
164✔
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

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,
5✔
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 &&
5✔
374
        output_size != nullptr && *output_size != 0)
3✔
375
    {
376
        const int level = atoi(CSLFetchNameValueDef(options, "LEVEL", "13"));
3✔
377
        ZSTD_CCtx *ctx = ZSTD_createCCtx();
3✔
378
        if (ctx == nullptr)
3✔
379
        {
380
            *output_size = 0;
×
381
            return false;
×
382
        }
383

384
        size_t ret = ZSTD_compressCCtx(ctx, *output_data, *output_size,
3✔
385
                                       input_data, input_size, level);
386
        ZSTD_freeCCtx(ctx);
3✔
387
        if (ZSTD_isError(ret))
3✔
388
        {
389
            *output_size = 0;
1✔
390
            return false;
1✔
391
        }
392

393
        *output_size = ret;
2✔
394
        return true;
2✔
395
    }
396

397
    if (output_data == nullptr && output_size != nullptr)
2✔
398
    {
399
        *output_size = ZSTD_compressBound(input_size);
1✔
400
        return true;
1✔
401
    }
402

403
    if (output_data != nullptr && *output_data == nullptr &&
1✔
404
        output_size != nullptr)
405
    {
406
        size_t nSafeSize = ZSTD_compressBound(input_size);
1✔
407
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
408
        *output_size = nSafeSize;
1✔
409
        if (*output_data == nullptr)
1✔
410
            return false;
×
411
        bool ret = CPLZSTDCompressor(input_data, input_size, output_data,
1✔
412
                                     output_size, options, nullptr);
413
        if (!ret)
1✔
414
        {
415
            VSIFree(*output_data);
×
416
            *output_data = nullptr;
×
417
        }
418
        return ret;
1✔
419
    }
420

421
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
422
    return false;
×
423
}
424

425
// CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW because ZSTD_CONTENTSIZE_ERROR expands
426
// to (0ULL - 2)...
427
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
428
static size_t CPLZSTDGetDecompressedSize(const void *input_data,
4✔
429
                                         size_t input_size)
430
{
431
#if (ZSTD_VERSION_MAJOR > 1) ||                                                \
432
    (ZSTD_VERSION_MAJOR == 1 && ZSTD_VERSION_MINOR >= 3)
433
    uint64_t nRet = ZSTD_getFrameContentSize(input_data, input_size);
4✔
434
    if (nRet == ZSTD_CONTENTSIZE_ERROR)
4✔
435
    {
436
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
437
                 "Error while retrieving decompressed size of ZSTD frame.");
438
        nRet = 0;
1✔
439
    }
440
    else if (nRet == ZSTD_CONTENTSIZE_UNKNOWN)
3✔
441
    {
442
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
443
                 "Decompressed size of ZSTD frame is unknown.");
444
        nRet = 0;
1✔
445
    }
446
#else
447
    uint64_t nRet = ZSTD_getDecompressedSize(input_data, input_size);
448
    if (nRet == 0)
449
    {
450
        CPLError(CE_Failure, CPLE_AppDefined,
451
                 "Decompressed size of ZSTD frame is unknown.");
452
    }
453
#endif
454

455
#if SIZEOF_VOIDP == 4
456
    if (nRet > std::numeric_limits<size_t>::max())
457
    {
458
        CPLError(CE_Failure, CPLE_AppDefined,
459
                 "Decompressed size of ZSTD frame is bigger than 4GB.");
460
        nRet = 0;
461
    }
462
#endif
463

464
    return static_cast<size_t>(nRet);
4✔
465
}
466

467
static bool CPLZSTDDecompressor(const void *input_data, size_t input_size,
168✔
468
                                void **output_data, size_t *output_size,
469
                                CSLConstList /* options */,
470
                                void * /* compressor_user_data */)
471
{
472
    if (output_data != nullptr && *output_data != nullptr &&
168✔
473
        output_size != nullptr && *output_size != 0)
166✔
474
    {
475
        size_t ret =
476
            ZSTD_decompress(*output_data, *output_size, input_data, input_size);
166✔
477
        if (ZSTD_isError(ret))
166✔
478
        {
479
            *output_size = CPLZSTDGetDecompressedSize(input_data, input_size);
2✔
480
            return false;
2✔
481
        }
482

483
        *output_size = ret;
164✔
484
        return true;
164✔
485
    }
486

487
    if (output_data == nullptr && output_size != nullptr)
2✔
488
    {
489
        *output_size = CPLZSTDGetDecompressedSize(input_data, input_size);
1✔
490
        return *output_size != 0;
1✔
491
    }
492

493
    if (output_data != nullptr && *output_data == nullptr &&
1✔
494
        output_size != nullptr)
495
    {
496
        size_t nOutSize = CPLZSTDGetDecompressedSize(input_data, input_size);
1✔
497
        *output_data = VSI_MALLOC_VERBOSE(nOutSize);
1✔
498
        if (*output_data == nullptr)
1✔
499
        {
500
            *output_size = 0;
×
501
            return false;
×
502
        }
503

504
        size_t ret =
505
            ZSTD_decompress(*output_data, nOutSize, input_data, input_size);
1✔
506
        if (ZSTD_isError(ret))
1✔
507
        {
508
            *output_size = 0;
×
509
            VSIFree(*output_data);
×
510
            *output_data = nullptr;
×
511
            return false;
×
512
        }
513

514
        *output_size = ret;
1✔
515
        return true;
1✔
516
    }
517

518
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
519
    return false;
×
520
}
521

522
#endif  // HAVE_ZSTD
523

524
#ifdef HAVE_LZ4
525
static bool CPLLZ4Compressor(const void *input_data, size_t input_size,
5✔
526
                             void **output_data, size_t *output_size,
527
                             CSLConstList options,
528
                             void * /* compressor_user_data */)
529
{
530
    if (input_size > static_cast<size_t>(std::numeric_limits<int>::max()))
5✔
531
    {
532
        CPLError(CE_Failure, CPLE_NotSupported,
×
533
                 "Too large input buffer. "
534
                 "Max supported is INT_MAX");
535
        *output_size = 0;
×
536
        return false;
×
537
    }
538

539
    const bool bHeader =
540
        CPLTestBool(CSLFetchNameValueDef(options, "HEADER", "YES"));
5✔
541
    const int header_size = bHeader ? static_cast<int>(sizeof(int32_t)) : 0;
5✔
542

543
    if (output_data != nullptr && *output_data != nullptr &&
5✔
544
        output_size != nullptr && *output_size != 0)
3✔
545
    {
546
        const int acceleration =
547
            atoi(CSLFetchNameValueDef(options, "ACCELERATION", "1"));
3✔
548
        if (*output_size >
6✔
549
            static_cast<size_t>(std::numeric_limits<int>::max() - 4))
3✔
550
        {
551
            CPLError(CE_Failure, CPLE_NotSupported,
×
552
                     "Too large output buffer. "
553
                     "Max supported is INT_MAX");
554
            *output_size = 0;
×
555
            return false;
×
556
        }
557

558
        if (bHeader && static_cast<int>(*output_size) < header_size)
3✔
559
        {
560
            *output_size = 0;
1✔
561
            return false;
1✔
562
        }
563

564
        int ret = LZ4_compress_fast(
4✔
565
            static_cast<const char *>(input_data),
566
            static_cast<char *>(*output_data) + header_size,
2✔
567
            static_cast<int>(input_size),
568
            static_cast<int>(*output_size) - header_size, acceleration);
2✔
569
        if (ret <= 0 || ret > std::numeric_limits<int>::max() - header_size)
2✔
570
        {
571
            *output_size = 0;
×
572
            return false;
×
573
        }
574

575
        int32_t sizeLSB = CPL_LSBWORD32(static_cast<int>(input_size));
2✔
576
        memcpy(*output_data, &sizeLSB, sizeof(sizeLSB));
2✔
577

578
        *output_size = static_cast<size_t>(header_size + ret);
2✔
579
        return true;
2✔
580
    }
581

582
    if (output_data == nullptr && output_size != nullptr)
2✔
583
    {
584
        *output_size = static_cast<size_t>(header_size) +
2✔
585
                       LZ4_compressBound(static_cast<int>(input_size));
1✔
586
        return true;
1✔
587
    }
588

589
    if (output_data != nullptr && *output_data == nullptr &&
1✔
590
        output_size != nullptr)
591
    {
592
        size_t nSafeSize = static_cast<size_t>(header_size) +
1✔
593
                           LZ4_compressBound(static_cast<int>(input_size));
1✔
594
        *output_data = VSI_MALLOC_VERBOSE(nSafeSize);
1✔
595
        *output_size = nSafeSize;
1✔
596
        if (*output_data == nullptr)
1✔
597
            return false;
×
598
        bool ret = CPLLZ4Compressor(input_data, input_size, output_data,
1✔
599
                                    output_size, options, nullptr);
600
        if (!ret)
1✔
601
        {
602
            VSIFree(*output_data);
×
603
            *output_data = nullptr;
×
604
        }
605
        return ret;
1✔
606
    }
607

608
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
609
    return false;
×
610
}
611

612
static bool CPLLZ4Decompressor(const void *input_data, size_t input_size,
5✔
613
                               void **output_data, size_t *output_size,
614
                               CSLConstList options,
615
                               void * /* compressor_user_data */)
616
{
617
    if (input_size > static_cast<size_t>(std::numeric_limits<int>::max()))
5✔
618
    {
619
        CPLError(CE_Failure, CPLE_NotSupported,
×
620
                 "Too large input buffer. "
621
                 "Max supported is INT_MAX");
622
        *output_size = 0;
×
623
        return false;
×
624
    }
625

626
    const bool bHeader =
627
        CPLTestBool(CSLFetchNameValueDef(options, "HEADER", "YES"));
5✔
628
    const int header_size = bHeader ? static_cast<int>(sizeof(int32_t)) : 0;
5✔
629
    if (bHeader && static_cast<int>(input_size) < header_size)
5✔
630
    {
631
        *output_size = 0;
×
632
        return false;
×
633
    }
634

635
    if (output_data != nullptr && *output_data != nullptr &&
5✔
636
        output_size != nullptr && *output_size != 0)
3✔
637
    {
638
        if (*output_size > static_cast<size_t>(std::numeric_limits<int>::max()))
3✔
639
        {
640
            CPLError(CE_Failure, CPLE_NotSupported,
×
641
                     "Too large output buffer. "
642
                     "Max supported is INT_MAX");
643
            *output_size = 0;
×
644
            return false;
×
645
        }
646

647
        int ret = LZ4_decompress_safe(
6✔
648
            static_cast<const char *>(input_data) + header_size,
3✔
649
            static_cast<char *>(*output_data),
650
            static_cast<int>(input_size) - header_size,
651
            static_cast<int>(*output_size));
3✔
652
        if (ret <= 0)
3✔
653
        {
654
            *output_size = 0;
×
655
            return false;
×
656
        }
657

658
        *output_size = ret;
3✔
659
        return true;
3✔
660
    }
661

662
    if (output_data == nullptr && output_size != nullptr)
2✔
663
    {
664
        if (bHeader)
1✔
665
        {
666
            int nSize = CPL_LSBSINT32PTR(input_data);
1✔
667
            if (nSize < 0)
1✔
668
            {
669
                *output_size = 0;
×
670
                return false;
×
671
            }
672
            *output_size = nSize;
1✔
673
            return true;
1✔
674
        }
675

676
        // inefficient !
677
        void *tmpBuffer = nullptr;
×
678
        bool ret = CPLLZ4Decompressor(input_data, input_size, &tmpBuffer,
×
679
                                      output_size, options, nullptr);
680
        VSIFree(tmpBuffer);
×
681
        return ret;
×
682
    }
683

684
    if (output_data != nullptr && *output_data == nullptr &&
1✔
685
        output_size != nullptr)
686
    {
687
        if (bHeader)
1✔
688
        {
689
            int nSize = CPL_LSBSINT32PTR(input_data);
1✔
690
            if (nSize <= 0)
1✔
691
            {
692
                *output_size = 0;
×
693
                return false;
×
694
            }
695
            if (nSize > INT_MAX - 1 || /* to make Coverity scan happy */
1✔
696
                nSize / 10000 > static_cast<int>(input_size))
1✔
697
            {
698
                CPLError(CE_Failure, CPLE_AppDefined,
×
699
                         "Stored uncompressed size (%d) is much larger "
700
                         "than compressed size (%d)",
701
                         nSize, static_cast<int>(input_size));
702
                *output_size = nSize;
×
703
                return false;
×
704
            }
705
            *output_data = VSI_MALLOC_VERBOSE(nSize);
1✔
706
            *output_size = nSize;
1✔
707
            if (*output_data == nullptr)
1✔
708
            {
709
                return false;
×
710
            }
711
            if (!CPLLZ4Decompressor(input_data, input_size, output_data,
1✔
712
                                    output_size, options, nullptr))
713
            {
714
                VSIFree(*output_data);
×
715
                *output_data = nullptr;
×
716
                *output_size = 0;
×
717
                return false;
×
718
            }
719
            return true;
1✔
720
        }
721

722
        size_t nOutSize =
723
            static_cast<int>(input_size) < std::numeric_limits<int>::max() / 2
×
724
                ? input_size * 2
×
725
                : static_cast<size_t>(std::numeric_limits<int>::max());
×
726
        *output_data = VSI_MALLOC_VERBOSE(nOutSize);
×
727
        if (*output_data == nullptr)
×
728
        {
729
            *output_size = 0;
×
730
            return false;
×
731
        }
732

733
        while (true)
734
        {
735
            int ret = LZ4_decompress_safe_partial(
×
736
                static_cast<const char *>(input_data),
737
                static_cast<char *>(*output_data), static_cast<int>(input_size),
738
                static_cast<int>(nOutSize), static_cast<int>(nOutSize));
739
            if (ret <= 0)
×
740
            {
741
                VSIFree(*output_data);
×
742
                *output_data = nullptr;
×
743
                *output_size = 0;
×
744
                return false;
×
745
            }
746
            else if (ret < static_cast<int>(nOutSize))
×
747
            {
748
                *output_size = ret;
×
749
                return true;
×
750
            }
751
            else if (static_cast<int>(nOutSize) <
×
752
                     std::numeric_limits<int>::max() / 2)
×
753
            {
754
                nOutSize *= 2;
×
755
                void *tmpBuffer = VSI_REALLOC_VERBOSE(*output_data, nOutSize);
×
756
                if (tmpBuffer == nullptr)
×
757
                {
758
                    VSIFree(*output_data);
×
759
                    *output_data = nullptr;
×
760
                    *output_size = 0;
×
761
                    return false;
×
762
                }
763
                *output_data = tmpBuffer;
×
764
            }
765
            else
766
            {
767
                VSIFree(*output_data);
×
768
                *output_data = nullptr;
×
769
                *output_size = 0;
×
770
                return false;
×
771
            }
772
        }
×
773
    }
774

775
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
776
    return false;
×
777
}
778

779
#endif  // HAVE_LZ4
780

781
static void *CPLGZipCompress(const void *ptr, size_t nBytes, int nLevel,
10,756✔
782
                             void *outptr, size_t nOutAvailableBytes,
783
                             size_t *pnOutBytes)
784
{
785
    if (pnOutBytes != nullptr)
10,756✔
786
        *pnOutBytes = 0;
10,756✔
787

788
    size_t nTmpSize = 0;
10,756✔
789
    void *pTmp;
790
#ifdef HAVE_LIBDEFLATE
791
    struct libdeflate_compressor *enc =
792
        libdeflate_alloc_compressor(nLevel < 0 ? 7 : nLevel);
10,756✔
793
    if (enc == nullptr)
10,756✔
794
    {
795
        return nullptr;
×
796
    }
797
#endif
798
    if (outptr == nullptr)
10,756✔
799
    {
800
#ifdef HAVE_LIBDEFLATE
801
        nTmpSize = libdeflate_gzip_compress_bound(enc, nBytes);
1✔
802
#else
803
        nTmpSize = 32 + nBytes * 2;
804
#endif
805
        pTmp = VSIMalloc(nTmpSize);
1✔
806
        if (pTmp == nullptr)
1✔
807
        {
808
#ifdef HAVE_LIBDEFLATE
809
            libdeflate_free_compressor(enc);
×
810
#endif
811
            return nullptr;
×
812
        }
813
    }
814
    else
815
    {
816
        pTmp = outptr;
10,755✔
817
        nTmpSize = nOutAvailableBytes;
10,755✔
818
    }
819

820
#ifdef HAVE_LIBDEFLATE
821
    size_t nCompressedBytes =
822
        libdeflate_gzip_compress(enc, ptr, nBytes, pTmp, nTmpSize);
10,756✔
823
    libdeflate_free_compressor(enc);
10,756✔
824
    if (nCompressedBytes == 0)
10,756✔
825
    {
826
        if (pTmp != outptr)
1✔
827
            VSIFree(pTmp);
×
828
        return nullptr;
1✔
829
    }
830
    if (pnOutBytes != nullptr)
10,755✔
831
        *pnOutBytes = nCompressedBytes;
10,755✔
832
#else
833
    z_stream strm;
834
    strm.zalloc = nullptr;
835
    strm.zfree = nullptr;
836
    strm.opaque = nullptr;
837
    constexpr int windowsBits = 15;
838
    constexpr int gzipEncoding = 16;
839
    int ret = deflateInit2(&strm, nLevel < 0 ? Z_DEFAULT_COMPRESSION : nLevel,
840
                           Z_DEFLATED, windowsBits + gzipEncoding, 8,
841
                           Z_DEFAULT_STRATEGY);
842
    if (ret != Z_OK)
843
    {
844
        if (pTmp != outptr)
845
            VSIFree(pTmp);
846
        return nullptr;
847
    }
848

849
    strm.avail_in = static_cast<uInt>(nBytes);
850
    strm.next_in = reinterpret_cast<Bytef *>(const_cast<void *>(ptr));
851
    strm.avail_out = static_cast<uInt>(nTmpSize);
852
    strm.next_out = reinterpret_cast<Bytef *>(pTmp);
853
    ret = deflate(&strm, Z_FINISH);
854
    if (ret != Z_STREAM_END)
855
    {
856
        if (pTmp != outptr)
857
            VSIFree(pTmp);
858
        return nullptr;
859
    }
860
    if (pnOutBytes != nullptr)
861
        *pnOutBytes = nTmpSize - strm.avail_out;
862
    deflateEnd(&strm);
863
#endif
864

865
    return pTmp;
10,755✔
866
}
867

868
static bool CPLZlibCompressor(const void *input_data, size_t input_size,
10,860✔
869
                              void **output_data, size_t *output_size,
870
                              CSLConstList options, void *compressor_user_data)
871
{
872
    const char *alg = static_cast<const char *>(compressor_user_data);
10,860✔
873
    const auto pfnCompress =
10,860✔
874
        strcmp(alg, "zlib") == 0 ? CPLZLibDeflate : CPLGZipCompress;
10,860✔
875
    const int clevel = atoi(CSLFetchNameValueDef(options, "LEVEL",
10,860✔
876
#if HAVE_LIBDEFLATE
877
                                                 "7"
878
#else
879
                                                 "6"
880
#endif
881
                                                 ));
882

883
    if (output_data != nullptr && *output_data != nullptr &&
10,860✔
884
        output_size != nullptr && *output_size != 0)
10,855✔
885
    {
886
        size_t nOutBytes = 0;
10,855✔
887
        if (nullptr == pfnCompress(input_data, input_size, clevel, *output_data,
10,855✔
888
                                   *output_size, &nOutBytes))
889
        {
890
            *output_size = 0;
2✔
891
            return false;
2✔
892
        }
893

894
        *output_size = nOutBytes;
10,853✔
895
        return true;
10,853✔
896
    }
897

898
    if (output_data == nullptr && output_size != nullptr)
5✔
899
    {
900
#if HAVE_LIBDEFLATE
901
        struct libdeflate_compressor *enc = libdeflate_alloc_compressor(clevel);
2✔
902
        if (enc == nullptr)
2✔
903
        {
904
            *output_size = 0;
×
905
            return false;
×
906
        }
907
        if (strcmp(alg, "zlib") == 0)
2✔
908
            *output_size = libdeflate_zlib_compress_bound(enc, input_size);
1✔
909
        else
910
            *output_size = libdeflate_gzip_compress_bound(enc, input_size);
1✔
911
        libdeflate_free_compressor(enc);
2✔
912
#else
913
        // Really inefficient !
914
        size_t nOutSize = 0;
915
        void *outbuffer =
916
            pfnCompress(input_data, input_size, clevel, nullptr, 0, &nOutSize);
917
        if (outbuffer == nullptr)
918
        {
919
            *output_size = 0;
920
            return false;
921
        }
922
        VSIFree(outbuffer);
923
        *output_size = nOutSize;
924
#endif
925
        return true;
2✔
926
    }
927

928
    if (output_data != nullptr && *output_data == nullptr &&
3✔
929
        output_size != nullptr)
930
    {
931
        size_t nOutSize = 0;
3✔
932
        *output_data =
3✔
933
            pfnCompress(input_data, input_size, clevel, nullptr, 0, &nOutSize);
3✔
934
        if (*output_data == nullptr)
3✔
935
        {
936
            *output_size = 0;
×
937
            return false;
×
938
        }
939
        *output_size = nOutSize;
3✔
940
        return true;
3✔
941
    }
942

943
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
944
    return false;
×
945
}
946

947
namespace
948
{
949
template <class T> inline T swap(T x)
×
950
{
951
    return x;
×
952
}
953

954
template <> inline uint16_t swap<uint16_t>(uint16_t x)
12✔
955
{
956
    return CPL_SWAP16(x);
12✔
957
}
958

959
template <> inline int16_t swap<int16_t>(int16_t x)
12✔
960
{
961
    return CPL_SWAP16(x);
12✔
962
}
963

964
template <> inline uint32_t swap<uint32_t>(uint32_t x)
12✔
965
{
966
    return CPL_SWAP32(x);
12✔
967
}
968

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

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

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

984
template <> inline float swap<float>(float x)
×
985
{
986
    float ret = x;
×
987
    CPL_SWAP32PTR(&ret);
×
988
    return ret;
×
989
}
990

991
template <> inline double swap<double>(double x)
×
992
{
993
    double ret = x;
×
994
    CPL_SWAP64PTR(&ret);
×
995
    return ret;
×
996
}
997
}  // namespace
998

999
namespace
1000
{
1001
// Workaround -ftrapv
1002
template <class T>
1003
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW inline T SubNoOverflow(T left, T right)
448✔
1004
{
1005
    typedef typename std::make_unsigned<T>::type U;
1006
    U leftU = static_cast<U>(left);
448✔
1007
    U rightU = static_cast<U>(right);
448✔
1008
    leftU = static_cast<U>(leftU - rightU);
448✔
1009
    T ret;
1010
    memcpy(&ret, &leftU, sizeof(ret));
448✔
1011
    return leftU;
448✔
1012
}
1013

1014
template <> inline float SubNoOverflow<float>(float x, float y)
4✔
1015
{
1016
    return x - y;
4✔
1017
}
1018

1019
template <> inline double SubNoOverflow<double>(double x, double y)
4✔
1020
{
1021
    return x - y;
4✔
1022
}
1023
}  // namespace
1024

1025
template <class T>
1026
static bool DeltaCompressor(const void *input_data, size_t input_size,
26✔
1027
                            const char *dtype, void *output_data)
1028
{
1029
    if ((input_size % sizeof(T)) != 0)
24✔
1030
    {
1031
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid input size");
×
1032
        return false;
×
1033
    }
1034

1035
    const size_t nElts = input_size / sizeof(T);
26✔
1036
    const T *pSrc = static_cast<const T *>(input_data);
26✔
1037
    T *pDst = static_cast<T *>(output_data);
26✔
1038
#ifdef CPL_MSB
1039
    const bool bNeedSwap = dtype[0] == '<';
1040
#else
1041
    const bool bNeedSwap = dtype[0] == '>';
26✔
1042
#endif
1043
    for (size_t i = 0; i < nElts; i++)
508✔
1044
    {
1045
        if (i == 0)
482✔
1046
        {
1047
            pDst[0] = pSrc[0];
26✔
1048
        }
1049
        else
1050
        {
1051
            if (bNeedSwap)
456✔
1052
            {
1053
                pDst[i] = swap(SubNoOverflow(swap(pSrc[i]), swap(pSrc[i - 1])));
12✔
1054
            }
1055
            else
1056
            {
1057
                pDst[i] = SubNoOverflow(pSrc[i], pSrc[i - 1]);
444✔
1058
            }
1059
        }
1060
    }
1061
    return true;
26✔
1062
}
1063

1064
static bool CPLDeltaCompressor(const void *input_data, size_t input_size,
26✔
1065
                               void **output_data, size_t *output_size,
1066
                               CSLConstList options,
1067
                               void * /* compressor_user_data */)
1068
{
1069
    const char *dtype = CSLFetchNameValue(options, "DTYPE");
26✔
1070
    if (dtype == nullptr)
26✔
1071
    {
1072
        CPLError(CE_Failure, CPLE_AppDefined, "Missing DTYPE parameter");
×
1073
        if (output_size)
×
1074
            *output_size = 0;
×
1075
        return false;
×
1076
    }
1077
    const char *astype = CSLFetchNameValue(options, "ASTYPE");
26✔
1078
    if (astype != nullptr && !EQUAL(astype, dtype))
26✔
1079
    {
1080
        CPLError(CE_Failure, CPLE_AppDefined,
×
1081
                 "Only ASTYPE=DTYPE currently supported");
1082
        if (output_size)
×
1083
            *output_size = 0;
×
1084
        return false;
×
1085
    }
1086

1087
    if (output_data != nullptr && *output_data != nullptr &&
26✔
1088
        output_size != nullptr && *output_size != 0)
26✔
1089
    {
1090
        if (*output_size < input_size)
26✔
1091
        {
1092
            CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
×
1093
            *output_size = input_size;
×
1094
            return false;
×
1095
        }
1096

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

1203
        *output_size = input_size;
26✔
1204
        return true;
26✔
1205
    }
1206

1207
    if (output_data == nullptr && output_size != nullptr)
×
1208
    {
1209
        *output_size = input_size;
×
1210
        return true;
×
1211
    }
1212

1213
    if (output_data != nullptr && *output_data == nullptr &&
×
1214
        output_size != nullptr)
1215
    {
1216
        *output_data = VSI_MALLOC_VERBOSE(input_size);
×
1217
        *output_size = input_size;
×
1218
        if (*output_data == nullptr)
×
1219
            return false;
×
1220
        bool ret = CPLDeltaCompressor(input_data, input_size, output_data,
×
1221
                                      output_size, options, nullptr);
1222
        if (!ret)
×
1223
        {
1224
            VSIFree(*output_data);
×
1225
            *output_data = nullptr;
×
1226
        }
1227
        return ret;
×
1228
    }
1229

1230
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
1231
    return false;
×
1232
}
1233

1234
static void CPLAddCompressor(const CPLCompressor *compressor)
92✔
1235
{
1236
    CPLCompressor *copy = new CPLCompressor(*compressor);
92✔
1237
    // cppcheck-suppress uninitdata
1238
    copy->pszId = CPLStrdup(compressor->pszId);
92✔
1239
    // cppcheck-suppress uninitdata
1240
    copy->papszMetadata = CSLDuplicate(compressor->papszMetadata);
92✔
1241
    gpCompressors->emplace_back(copy);
92✔
1242
}
92✔
1243

1244
static void CPLAddBuiltinCompressors()
13✔
1245
{
1246
#ifdef HAVE_BLOSC
1247
    do
1248
    {
1249
        CPLCompressor sComp;
1250
        sComp.nStructVersion = 1;
13✔
1251
        sComp.eType = CCT_COMPRESSOR;
13✔
1252
        sComp.pszId = "blosc";
13✔
1253

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

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

1421
static bool CPLZlibDecompressor(const void *input_data, size_t input_size,
16,508✔
1422
                                void **output_data, size_t *output_size,
1423
                                CSLConstList /* options */,
1424
                                void * /* compressor_user_data */)
1425
{
1426
    if (output_data != nullptr && *output_data != nullptr &&
16,508✔
1427
        output_size != nullptr && *output_size != 0)
16,243✔
1428
    {
1429
        size_t nOutBytes = 0;
16,184✔
1430
        if (nullptr == CPLZLibInflate(input_data, input_size, *output_data,
16,184✔
1431
                                      *output_size, &nOutBytes))
1432
        {
1433
            *output_size = 0;
×
1434
            return false;
×
1435
        }
1436

1437
        *output_size = nOutBytes;
16,368✔
1438
        return true;
16,368✔
1439
    }
1440

1441
    if (output_data == nullptr && output_size != nullptr)
324✔
1442
    {
1443
        size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 4
2✔
1444
                              ? input_size * 4
2✔
1445
                              : input_size;
2✔
1446
        void *tmpOutBuffer = VSIMalloc(nOutSize);
2✔
1447
        if (tmpOutBuffer == nullptr)
2✔
1448
        {
1449
            *output_size = 0;
×
1450
            return false;
×
1451
        }
1452
        tmpOutBuffer = CPLZLibInflateEx(input_data, input_size, tmpOutBuffer,
2✔
1453
                                        nOutSize, true, &nOutSize);
1454
        if (!tmpOutBuffer)
2✔
1455
        {
1456
            *output_size = 0;
×
1457
            return false;
×
1458
        }
1459
        VSIFree(tmpOutBuffer);
2✔
1460
        *output_size = nOutSize;
2✔
1461
        return true;
2✔
1462
    }
1463

1464
    if (output_data != nullptr && *output_data == nullptr &&
322✔
1465
        output_size != nullptr)
1466
    {
1467
        size_t nOutSize = input_size < std::numeric_limits<size_t>::max() / 4
3✔
1468
                              ? input_size * 4
3✔
1469
                              : input_size;
3✔
1470
        void *tmpOutBuffer = VSIMalloc(nOutSize);
3✔
1471
        if (tmpOutBuffer == nullptr)
3✔
1472
        {
1473
            *output_size = 0;
×
1474
            return false;
×
1475
        }
1476
        size_t nOutSizeOut = 0;
3✔
1477
        tmpOutBuffer = CPLZLibInflateEx(input_data, input_size, tmpOutBuffer,
3✔
1478
                                        nOutSize, true, &nOutSizeOut);
1479
        if (!tmpOutBuffer)
3✔
1480
        {
1481
            *output_size = 0;
×
1482
            return false;
×
1483
        }
1484
        *output_data = VSIRealloc(tmpOutBuffer, nOutSizeOut);  // cannot fail
3✔
1485
        *output_size = nOutSizeOut;
3✔
1486
        return true;
3✔
1487
    }
1488

1489
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
319✔
1490
    return false;
×
1491
}
1492

1493
namespace
1494
{
1495
// Workaround -ftrapv
1496
template <class T>
1497
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW inline T AddNoOverflow(T left, T right)
466✔
1498
{
1499
    typedef typename std::make_unsigned<T>::type U;
1500
    U leftU = static_cast<U>(left);
466✔
1501
    U rightU = static_cast<U>(right);
466✔
1502
    leftU = static_cast<U>(leftU + rightU);
466✔
1503
    T ret;
1504
    memcpy(&ret, &leftU, sizeof(ret));
466✔
1505
    return leftU;
466✔
1506
}
1507

1508
template <> inline float AddNoOverflow<float>(float x, float y)
4✔
1509
{
1510
    return x + y;
4✔
1511
}
1512

1513
template <> inline double AddNoOverflow<double>(double x, double y)
4✔
1514
{
1515
    return x + y;
4✔
1516
}
1517
}  // namespace
1518

1519
template <class T>
1520
static bool DeltaDecompressor(const void *input_data, size_t input_size,
28✔
1521
                              const char *dtype, void *output_data)
1522
{
1523
    if ((input_size % sizeof(T)) != 0)
26✔
1524
    {
1525
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid input size");
×
1526
        return false;
×
1527
    }
1528

1529
    const size_t nElts = input_size / sizeof(T);
28✔
1530
    const T *pSrc = static_cast<const T *>(input_data);
28✔
1531
    T *pDst = static_cast<T *>(output_data);
28✔
1532
#ifdef CPL_MSB
1533
    const bool bNeedSwap = dtype[0] == '<';
1534
#else
1535
    const bool bNeedSwap = dtype[0] == '>';
28✔
1536
#endif
1537
    for (size_t i = 0; i < nElts; i++)
530✔
1538
    {
1539
        if (i == 0)
502✔
1540
        {
1541
            pDst[0] = pSrc[0];
28✔
1542
        }
1543
        else
1544
        {
1545
            if (bNeedSwap)
474✔
1546
            {
1547
                pDst[i] = swap(AddNoOverflow(swap(pDst[i - 1]), swap(pSrc[i])));
12✔
1548
            }
1549
            else
1550
            {
1551
                pDst[i] = AddNoOverflow(pDst[i - 1], pSrc[i]);
462✔
1552
            }
1553
        }
1554
    }
1555
    return true;
28✔
1556
}
1557

1558
static bool CPLDeltaDecompressor(const void *input_data, size_t input_size,
28✔
1559
                                 void **output_data, size_t *output_size,
1560
                                 CSLConstList options,
1561
                                 void * /* compressor_user_data */)
1562
{
1563
    const char *dtype = CSLFetchNameValue(options, "DTYPE");
28✔
1564
    if (dtype == nullptr)
28✔
1565
    {
1566
        CPLError(CE_Failure, CPLE_AppDefined, "Missing DTYPE parameter");
×
1567
        if (output_size)
×
1568
            *output_size = 0;
×
1569
        return false;
×
1570
    }
1571
    const char *astype = CSLFetchNameValue(options, "ASTYPE");
28✔
1572
    if (astype != nullptr && !EQUAL(astype, dtype))
28✔
1573
    {
1574
        CPLError(CE_Failure, CPLE_AppDefined,
×
1575
                 "Only ASTYPE=DTYPE currently supported");
1576
        if (output_size)
×
1577
            *output_size = 0;
×
1578
        return false;
×
1579
    }
1580

1581
    if (output_data != nullptr && *output_data != nullptr &&
28✔
1582
        output_size != nullptr && *output_size != 0)
28✔
1583
    {
1584
        if (*output_size < input_size)
28✔
1585
        {
1586
            CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
×
1587
            *output_size = input_size;
×
1588
            return false;
×
1589
        }
1590

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

1697
        *output_size = input_size;
28✔
1698
        return true;
28✔
1699
    }
1700

1701
    if (output_data == nullptr && output_size != nullptr)
×
1702
    {
1703
        *output_size = input_size;
×
1704
        return true;
×
1705
    }
1706

1707
    if (output_data != nullptr && *output_data == nullptr &&
×
1708
        output_size != nullptr)
1709
    {
1710
        *output_data = VSI_MALLOC_VERBOSE(input_size);
×
1711
        *output_size = input_size;
×
1712
        if (*output_data == nullptr)
×
1713
            return false;
×
1714
        bool ret = CPLDeltaDecompressor(input_data, input_size, output_data,
×
1715
                                        output_size, options, nullptr);
1716
        if (!ret)
×
1717
        {
1718
            VSIFree(*output_data);
×
1719
            *output_data = nullptr;
×
1720
        }
1721
        return ret;
×
1722
    }
1723

1724
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
×
1725
    return false;
×
1726
}
1727

1728
static void CPLAddDecompressor(const CPLCompressor *decompressor)
9,696✔
1729
{
1730
    CPLCompressor *copy = new CPLCompressor(*decompressor);
9,696✔
1731
    // cppcheck-suppress uninitdata
1732
    copy->pszId = CPLStrdup(decompressor->pszId);
9,696✔
1733
    // cppcheck-suppress uninitdata
1734
    copy->papszMetadata = CSLDuplicate(decompressor->papszMetadata);
9,696✔
1735
    gpDecompressors->emplace_back(copy);
9,696✔
1736
}
9,696✔
1737

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

1841
/** Register a new compressor.
1842
 *
1843
 * The provided structure is copied. Its pfnFunc and user_data members should
1844
 * remain valid beyond this call however.
1845
 *
1846
 * @param compressor Compressor structure. Should not be null.
1847
 * @return true if successful
1848
 * @since GDAL 3.4
1849
 */
1850
bool CPLRegisterCompressor(const CPLCompressor *compressor)
2✔
1851
{
1852
    if (compressor->nStructVersion < 1)
2✔
1853
        return false;
×
1854
    std::lock_guard<std::mutex> lock(gMutex);
4✔
1855
    if (gpCompressors == nullptr)
2✔
1856
    {
1857
        gpCompressors = new std::vector<CPLCompressor *>();
1✔
1858
        CPLAddBuiltinCompressors();
1✔
1859
    }
1860
    for (size_t i = 0; i < gpCompressors->size(); ++i)
16✔
1861
    {
1862
        if (strcmp(compressor->pszId, (*gpCompressors)[i]->pszId) == 0)
15✔
1863
        {
1864
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1865
                     "Compressor %s already registered", compressor->pszId);
1✔
1866
            return false;
1✔
1867
        }
1868
    }
1869
    CPLAddCompressor(compressor);
1✔
1870
    return true;
1✔
1871
}
1872

1873
/** Register a new decompressor.
1874
 *
1875
 * The provided structure is copied. Its pfnFunc and user_data members should
1876
 * remain valid beyond this call however.
1877
 *
1878
 * @param decompressor Compressor structure. Should not be null.
1879
 * @return true if successful
1880
 * @since GDAL 3.4
1881
 */
1882
bool CPLRegisterDecompressor(const CPLCompressor *decompressor)
2✔
1883
{
1884
    if (decompressor->nStructVersion < 1)
2✔
1885
        return false;
×
1886
    std::lock_guard<std::mutex> lock(gMutex);
4✔
1887
    if (gpDecompressors == nullptr)
2✔
1888
    {
1889
        gpDecompressors = new std::vector<CPLCompressor *>();
×
1890
        CPLAddBuiltinDecompressors();
×
1891
    }
1892
    for (size_t i = 0; i < gpDecompressors->size(); ++i)
16✔
1893
    {
1894
        if (strcmp(decompressor->pszId, (*gpDecompressors)[i]->pszId) == 0)
15✔
1895
        {
1896
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1897
                     "Decompressor %s already registered", decompressor->pszId);
1✔
1898
            return false;
1✔
1899
        }
1900
    }
1901
    CPLAddDecompressor(decompressor);
1✔
1902
    return true;
1✔
1903
}
1904

1905
/** Return the list of registered compressors.
1906
 *
1907
 * @return list of strings. Should be freed with CSLDestroy()
1908
 * @since GDAL 3.4
1909
 */
1910
char **CPLGetCompressors(void)
10✔
1911
{
1912
    std::lock_guard<std::mutex> lock(gMutex);
10✔
1913
    if (gpCompressors == nullptr)
10✔
1914
    {
1915
        gpCompressors = new std::vector<CPLCompressor *>();
×
1916
        CPLAddBuiltinCompressors();
×
1917
    }
1918
    char **papszRet = nullptr;
10✔
1919
    for (size_t i = 0; i < gpCompressors->size(); ++i)
81✔
1920
    {
1921
        papszRet = CSLAddString(papszRet, (*gpCompressors)[i]->pszId);
71✔
1922
    }
1923
    return papszRet;
20✔
1924
}
1925

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

1948
/** Return a compressor.
1949
 *
1950
 * @param pszId Compressor id. Should NOT be NULL.
1951
 * @return compressor structure, or NULL.
1952
 * @since GDAL 3.4
1953
 */
1954
const CPLCompressor *CPLGetCompressor(const char *pszId)
302✔
1955
{
1956
    std::lock_guard<std::mutex> lock(gMutex);
604✔
1957
    if (gpCompressors == nullptr)
302✔
1958
    {
1959
        gpCompressors = new std::vector<CPLCompressor *>();
12✔
1960
        CPLAddBuiltinCompressors();
12✔
1961
    }
1962
    for (size_t i = 0; i < gpCompressors->size(); ++i)
1,172✔
1963
    {
1964
        if (EQUAL(pszId, (*gpCompressors)[i]->pszId))
1,169✔
1965
        {
1966
            return (*gpCompressors)[i];
299✔
1967
        }
1968
    }
1969
    return nullptr;
3✔
1970
}
1971

1972
/** Return a decompressor.
1973
 *
1974
 * @param pszId Decompressor id. Should NOT be NULL.
1975
 * @return compressor structure, or NULL.
1976
 * @since GDAL 3.4
1977
 */
1978
const CPLCompressor *CPLGetDecompressor(const char *pszId)
1,761✔
1979
{
1980
    std::lock_guard<std::mutex> lock(gMutex);
3,522✔
1981
    if (gpDecompressors == nullptr)
1,761✔
1982
    {
1983
        gpDecompressors = new std::vector<CPLCompressor *>();
1,385✔
1984
        CPLAddBuiltinDecompressors();
1,385✔
1985
    }
1986
    for (size_t i = 0; i < gpDecompressors->size(); ++i)
6,865✔
1987
    {
1988
        if (EQUAL(pszId, (*gpDecompressors)[i]->pszId))
6,852✔
1989
        {
1990
            return (*gpDecompressors)[i];
1,748✔
1991
        }
1992
    }
1993
    return nullptr;
13✔
1994
}
1995

1996
static void
1997
CPLDestroyCompressorRegistryInternal(std::vector<CPLCompressor *> *&v)
1,884✔
1998
{
1999
    for (size_t i = 0; v != nullptr && i < v->size(); ++i)
8,536✔
2000
    {
2001
        CPLFree(const_cast<char *>((*v)[i]->pszId));
6,652✔
2002
        CSLDestroy(const_cast<char **>((*v)[i]->papszMetadata));
6,652✔
2003
        delete (*v)[i];
6,652✔
2004
    }
2005
    delete v;
1,884✔
2006
    v = nullptr;
1,884✔
2007
}
1,884✔
2008

2009
/*! @cond Doxygen_Suppress */
2010
void CPLDestroyCompressorRegistry(void)
942✔
2011
{
2012
    std::lock_guard<std::mutex> lock(gMutex);
1,884✔
2013

2014
    CPLDestroyCompressorRegistryInternal(gpCompressors);
942✔
2015
    CPLDestroyCompressorRegistryInternal(gpDecompressors);
942✔
2016
}
942✔
2017

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