LCOV - code coverage report
Current view: top level - port - cpl_compressor.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 682 963 70.8 %
Date: 2025-07-08 21:33:46 Functions: 72 76 94.7 %

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

Generated by: LCOV version 1.14