LCOV - code coverage report
Current view: top level - port - cpl_compressor.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 673 943 71.4 %
Date: 2026-04-20 19:56:30 Functions: 66 66 100.0 %

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

Generated by: LCOV version 1.14