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

Generated by: LCOV version 1.14