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

Generated by: LCOV version 1.14