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

Generated by: LCOV version 1.14