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

Generated by: LCOV version 1.14