LCOV - code coverage report
Current view: top level - port - cpl_vsisimple.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 183 262 69.8 %
Date: 2025-01-18 12:42:00 Functions: 28 42 66.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Common Portability Library
       4             :  * Purpose:  Simple implementation of POSIX VSI functions.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1998, Frank Warmerdam
       9             :  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************
      13             :  *
      14             :  * NB: Note that in wrappers we are always saving the error state (errno
      15             :  * variable) to avoid side effects during debug prints or other possible
      16             :  * standard function calls (error states will be overwritten after such
      17             :  * a call).
      18             :  *
      19             :  ****************************************************************************/
      20             : 
      21             : // Must be done before including cpl_port.h
      22             : // We need __MSVCRT_VERSION__ >= 0x0700 to have "_aligned_malloc"
      23             : // Latest versions of mingw32 define it, but with older ones,
      24             : // we need to define it manually.
      25             : #if defined(__MINGW32__)
      26             : #include <windows.h>
      27             : #ifndef __MSVCRT_VERSION__
      28             : #define __MSVCRT_VERSION__ 0x0700
      29             : #endif
      30             : #endif
      31             : 
      32             : #include "cpl_port.h"
      33             : #include "cpl_vsi.h"
      34             : 
      35             : #include <algorithm>
      36             : #include <cerrno>
      37             : #include <cstdarg>
      38             : #include <cstddef>
      39             : #include <cstdio>
      40             : #include <cstdlib>
      41             : #include <cstring>
      42             : #include <ctime>
      43             : #include <limits>
      44             : #if HAVE_SYS_STAT_H
      45             : #include <sys/stat.h>
      46             : #endif
      47             : #if HAVE_GETRLIMIT
      48             : #include <sys/time.h>
      49             : #include <sys/resource.h>
      50             : #endif
      51             : 
      52             : #include "cpl_config.h"
      53             : #include "cpl_error.h"
      54             : #include "cpl_string.h"
      55             : 
      56             : #ifdef _WIN32
      57             : #include <malloc.h>  // For _aligned_malloc
      58             : #endif
      59             : 
      60             : // Uncomment to check consistent usage of VSIMalloc(), VSIRealloc(),
      61             : // VSICalloc(), VSIFree(), VSIStrdup().
      62             : // #define DEBUG_VSIMALLOC
      63             : 
      64             : // Uncomment to compute memory usage statistics.
      65             : // DEBUG_VSIMALLOC must also be defined.
      66             : // #define DEBUG_VSIMALLOC_STATS
      67             : 
      68             : // Highly experimental, and likely buggy. Do not use, except for fixing it!
      69             : // DEBUG_VSIMALLOC must also be defined.
      70             : // #define DEBUG_VSIMALLOC_MPROTECT
      71             : 
      72             : #ifdef DEBUG_VSIMALLOC_MPROTECT
      73             : #include <sys/mman.h>
      74             : #endif
      75             : 
      76             : // Uncomment to print every memory allocation or deallocation.
      77             : // DEBUG_VSIMALLOC must also be defined.
      78             : // #define DEBUG_VSIMALLOC_VERBOSE
      79             : 
      80             : // Number of bytes of the malloc/calloc/free that triggers a debug trace.
      81             : // Can be 0 for all allocs.
      82             : #define THRESHOLD_PRINT 10000
      83             : 
      84             : // Uncomment to print GDAL block cache use.
      85             : // Only used if DEBUG_VSIMALLOC_VERBOSE is enabled.
      86             : // #define DEBUG_BLOCK_CACHE_USE
      87             : 
      88             : #ifdef DEBUG_BLOCK_CACHE_USE
      89             : extern "C" GIntBig CPL_DLL CPL_STDCALL GDALGetCacheUsed64(void);
      90             : #endif
      91             : 
      92             : /* Unix or Windows NT/2000/XP */
      93             : #if !defined(_WIN32)
      94             : #include <unistd.h>
      95             : #else
      96             : #include <io.h>
      97             : #include <fcntl.h>
      98             : #include <direct.h>
      99             : #endif
     100             : 
     101             : /************************************************************************/
     102             : /*                              VSIFOpen()                              */
     103             : /************************************************************************/
     104             : 
     105         117 : FILE *VSIFOpen(const char *pszFilename, const char *pszAccess)
     106             : 
     107             : {
     108             : #if defined(_WIN32)
     109             :     FILE *fp = nullptr;
     110             :     if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
     111             :     {
     112             :         wchar_t *pwszFilename =
     113             :             CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
     114             :         wchar_t *pwszAccess =
     115             :             CPLRecodeToWChar(pszAccess, CPL_ENC_UTF8, CPL_ENC_UCS2);
     116             : 
     117             :         fp = _wfopen(pwszFilename, pwszAccess);
     118             : 
     119             :         CPLFree(pwszFilename);
     120             :         CPLFree(pwszAccess);
     121             :     }
     122             :     else
     123             :     {
     124             :         // Are the casts really necessary?
     125             :         fp = fopen(const_cast<char *>(pszFilename),
     126             :                    const_cast<char *>(pszAccess));
     127             :     }
     128             : #else
     129         117 :     FILE *fp = fopen(pszFilename, pszAccess);
     130             : #endif
     131             : 
     132             : #ifdef VSI_DEBUG
     133             :     // Capture the error from fopen to avoid being overwritten by errors
     134             :     // from VSIDebug3.
     135             :     const int nError = errno;
     136             :     VSIDebug3("VSIFOpen(%s,%s) = %p", pszFilename, pszAccess, fp);
     137             :     errno = nError;
     138             : #endif
     139             : 
     140         117 :     return fp;
     141             : }
     142             : 
     143             : /************************************************************************/
     144             : /*                             VSIFClose()                              */
     145             : /************************************************************************/
     146             : 
     147         102 : int VSIFClose(FILE *fp)
     148             : 
     149             : {
     150             :     VSIDebug1("VSIClose(%p)", fp);
     151             : 
     152         102 :     return fclose(fp);
     153             : }
     154             : 
     155             : /************************************************************************/
     156             : /*                              VSIFSeek()                              */
     157             : /************************************************************************/
     158             : 
     159           0 : int VSIFSeek(FILE *fp, long nOffset, int nWhence)
     160             : 
     161             : {
     162             : #ifdef DEBUG
     163             :     // To workaround Coverity strange warning about potential negative seek
     164             :     // CID 1340084 when called from dgnwrite.cpp.
     165           0 :     if (nWhence == SEEK_SET && nOffset < 0)
     166           0 :         return -1;
     167             : #endif
     168           0 :     int nResult = fseek(fp, nOffset, nWhence);
     169             : 
     170             : #ifdef VSI_DEBUG
     171             :     // Capture the error from fseek to avoid being overwritten by errors
     172             :     // from VSIDebug.
     173             :     const int nError = errno;
     174             : 
     175             :     if (nWhence == SEEK_SET)
     176             :     {
     177             :         VSIDebug3("VSIFSeek(%p,%ld,SEEK_SET) = %d", fp, nOffset, nResult);
     178             :     }
     179             :     else if (nWhence == SEEK_END)
     180             :     {
     181             :         VSIDebug3("VSIFSeek(%p,%ld,SEEK_END) = %d", fp, nOffset, nResult);
     182             :     }
     183             :     else if (nWhence == SEEK_CUR)
     184             :     {
     185             :         VSIDebug3("VSIFSeek(%p,%ld,SEEK_CUR) = %d", fp, nOffset, nResult);
     186             :     }
     187             :     else
     188             :     {
     189             :         VSIDebug4("VSIFSeek(%p,%ld,%d-Unknown) = %d", fp, nOffset, nWhence,
     190             :                   nResult);
     191             :     }
     192             : 
     193             :     errno = nError;
     194             : #endif
     195             : 
     196           0 :     return nResult;
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*                              VSIFTell()                              */
     201             : /************************************************************************/
     202             : 
     203           0 : long VSIFTell(FILE *fp)
     204             : 
     205             : {
     206           0 :     const long nOffset = ftell(fp);
     207             : 
     208             : #ifdef VSI_DEBUG
     209             :     // Capture the error from ftell to avoid being overwritten by errors
     210             :     // from VSIDebug.
     211             :     const int nError = errno;
     212             :     VSIDebug2("VSIFTell(%p) = %ld", fp, nOffset);
     213             :     errno = nError;
     214             : #endif
     215             : 
     216           0 :     return nOffset;
     217             : }
     218             : 
     219             : /************************************************************************/
     220             : /*                             VSIRewind()                              */
     221             : /************************************************************************/
     222             : 
     223           0 : void VSIRewind(FILE *fp)
     224             : 
     225             : {
     226             :     VSIDebug1("VSIRewind(%p)", fp);
     227           0 :     rewind(fp);
     228             : #ifdef VSI_DEBUG
     229             :     // Capture the error rewind ftell to avoid being overwritten by errors
     230             :     // from VSIDebug.
     231             :     const int nError = errno;
     232             :     VSIDebug2("VSIRewind(%p) errno = %d", fp, nError);
     233             :     errno = nError;
     234             : #endif
     235           0 : }
     236             : 
     237             : /************************************************************************/
     238             : /*                              VSIFRead()                              */
     239             : /************************************************************************/
     240             : 
     241           0 : size_t VSIFRead(void *pBuffer, size_t nSize, size_t nCount, FILE *fp)
     242             : 
     243             : {
     244           0 :     const size_t nResult = fread(pBuffer, nSize, nCount, fp);
     245             : 
     246             : #ifdef VSI_DEBUG
     247             :     // Capture the error from fread to avoid being overwritten by errors
     248             :     // from VSIDebug.
     249             :     const int nError = errno;
     250             :     VSIDebug4("VSIFRead(%p,%ld,%ld) = %ld", fp, static_cast<long>(nSize),
     251             :               static_cast<long>(nCount), static_cast<long>(nResult));
     252             :     errno = nError;
     253             : #endif
     254             : 
     255           0 :     return nResult;
     256             : }
     257             : 
     258             : /************************************************************************/
     259             : /*                             VSIFWrite()                              */
     260             : /************************************************************************/
     261             : 
     262        1066 : size_t VSIFWrite(const void *pBuffer, size_t nSize, size_t nCount, FILE *fp)
     263             : 
     264             : {
     265        1066 :     const size_t nResult = fwrite(pBuffer, nSize, nCount, fp);
     266             : 
     267             : #ifdef VSI_DEBUG
     268             :     // Capture the error from fwrite to avoid being overwritten by errors
     269             :     // from VSIDebug.
     270             :     const int nError = errno;
     271             :     VSIDebug4("VSIFWrite(%p,%ld,%ld) = %ld", fp, static_cast<long>(nSize),
     272             :               static_cast<long>(nCount), static_cast<long>(nResult));
     273             :     errno = nError;
     274             : #endif
     275             : 
     276        1066 :     return nResult;
     277             : }
     278             : 
     279             : /************************************************************************/
     280             : /*                             VSIFFlush()                              */
     281             : /************************************************************************/
     282             : 
     283           0 : void VSIFFlush(FILE *fp)
     284             : 
     285             : {
     286             : #ifdef VSI_DEBUG
     287             :     VSIDebug1("VSIFFlush(%p)", fp);
     288             :     const int result =
     289             : #endif
     290           0 :         fflush(fp);
     291             : 
     292             : #ifdef VSI_DEBUG
     293             :     // Capture the error rewind ftell to avoid being overwritten by errors
     294             :     // from VSIDebug.
     295             :     const int nError = errno;
     296             :     VSIDebug2("VSIRewind(%p) errno = %d", fp, nError);
     297             :     if (result != 0)
     298             :     {
     299             :         CPLError(CE_Failure, CPLE_FileIO, "Flush failed.  errno = %d", nError);
     300             :     }
     301             :     errno = nError;
     302             : #endif
     303           0 : }
     304             : 
     305             : /************************************************************************/
     306             : /*                              VSIFGets()                              */
     307             : /************************************************************************/
     308             : 
     309           0 : char *VSIFGets(char *pszBuffer, int nBufferSize, FILE *fp)
     310             : 
     311             : {
     312           0 :     return fgets(pszBuffer, nBufferSize, fp);
     313             : }
     314             : 
     315             : /************************************************************************/
     316             : /*                              VSIFGetc()                              */
     317             : /************************************************************************/
     318             : 
     319           0 : int VSIFGetc(FILE *fp)
     320             : 
     321             : {
     322           0 :     return fgetc(fp);
     323             : }
     324             : 
     325             : /************************************************************************/
     326             : /*                             VSIUngetc()                              */
     327             : /************************************************************************/
     328             : 
     329           0 : int VSIUngetc(int c, FILE *fp)
     330             : 
     331             : {
     332           0 :     return ungetc(c, fp);
     333             : }
     334             : 
     335             : /************************************************************************/
     336             : /*                             VSIFPrintf()                             */
     337             : /*                                                                      */
     338             : /*      This is a little more complicated than just calling             */
     339             : /*      fprintf() because of the variable arguments.  Instead we        */
     340             : /*      have to use vfprintf().                                         */
     341             : /************************************************************************/
     342             : 
     343           0 : int VSIFPrintf(FILE *fp, CPL_FORMAT_STRING(const char *pszFormat), ...)
     344             : 
     345             : {
     346             :     va_list args;
     347             : 
     348           0 :     va_start(args, pszFormat);
     349           0 :     const int nReturn = vfprintf(fp, pszFormat, args);
     350           0 :     va_end(args);
     351             : 
     352           0 :     return nReturn;
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /*                              VSIFEof()                               */
     357             : /************************************************************************/
     358             : 
     359           0 : int VSIFEof(FILE *fp)
     360             : 
     361             : {
     362           0 :     return feof(fp);
     363             : }
     364             : 
     365             : /************************************************************************/
     366             : /*                              VSIFPuts()                              */
     367             : /************************************************************************/
     368             : 
     369           0 : int VSIFPuts(const char *pszString, FILE *fp)
     370             : 
     371             : {
     372           0 :     return fputs(pszString, fp);
     373             : }
     374             : 
     375             : /************************************************************************/
     376             : /*                              VSIFPutc()                              */
     377             : /************************************************************************/
     378             : 
     379           0 : int VSIFPutc(int nChar, FILE *fp)
     380             : 
     381             : {
     382           0 :     return fputc(nChar, fp);
     383             : }
     384             : 
     385             : #ifdef DEBUG_VSIMALLOC_STATS
     386             : #include "cpl_multiproc.h"
     387             : 
     388             : static CPLMutex *hMemStatMutex = nullptr;
     389             : static size_t nCurrentTotalAllocs = 0;
     390             : static size_t nMaxTotalAllocs = 0;
     391             : static GUIntBig nVSIMallocs = 0;
     392             : static GUIntBig nVSICallocs = 0;
     393             : static GUIntBig nVSIReallocs = 0;
     394             : static GUIntBig nVSIFrees = 0;
     395             : 
     396             : /************************************************************************/
     397             : /*                         VSIShowMemStats()                            */
     398             : /************************************************************************/
     399             : 
     400             : void VSIShowMemStats();
     401             : 
     402             : void VSIShowMemStats()
     403             : {
     404             :     char *pszShowMemStats = getenv("CPL_SHOW_MEM_STATS");
     405             :     if (pszShowMemStats == nullptr || pszShowMemStats[0] == '\0')
     406             :         return;
     407             :     printf("Current VSI memory usage        : " CPL_FRMT_GUIB " bytes\n", /*ok*/
     408             :            static_cast<GUIntBig>(nCurrentTotalAllocs));
     409             :     printf("Maximum VSI memory usage        : " CPL_FRMT_GUIB " bytes\n", /*ok*/
     410             :            static_cast<GUIntBig>(nMaxTotalAllocs));
     411             :     printf("Number of calls to VSIMalloc()  : " CPL_FRMT_GUIB "\n", /*ok*/
     412             :            nVSIMallocs);
     413             :     printf("Number of calls to VSICalloc()  : " CPL_FRMT_GUIB "\n", /*ok*/
     414             :            nVSICallocs);
     415             :     printf("Number of calls to VSIRealloc() : " CPL_FRMT_GUIB "\n", /*ok*/
     416             :            nVSIReallocs);
     417             :     printf("Number of calls to VSIFree()    : " CPL_FRMT_GUIB "\n", /*ok*/
     418             :            nVSIFrees);
     419             :     printf("VSIMalloc + VSICalloc - VSIFree : " CPL_FRMT_GUIB "\n", /*ok*/
     420             :            nVSIMallocs + nVSICallocs - nVSIFrees);
     421             : }
     422             : #endif
     423             : 
     424             : #ifdef DEBUG_VSIMALLOC
     425             : static GIntBig nMaxPeakAllocSize = -1;
     426             : static GIntBig nMaxCumulAllocSize = -1;
     427             : #endif
     428             : 
     429             : /************************************************************************/
     430             : /*                             VSICalloc()                              */
     431             : /************************************************************************/
     432             : 
     433             : #ifndef DEBUG_VSIMALLOC
     434             : 
     435             : /** Analog of calloc(). Use VSIFree() to free */
     436    31921800 : void *VSICalloc(size_t nCount, size_t nSize)
     437             : {
     438             :     // cppcheck-suppress invalidFunctionArg
     439    31921800 :     return calloc(nCount, nSize);
     440             : }
     441             : 
     442             : #else  // DEBUG_VSIMALLOC
     443             : 
     444             : void *VSICalloc(size_t nCount, size_t nSize)
     445             : {
     446             :     size_t nMul = nCount * nSize;
     447             :     if (nCount != 0 && nMul / nCount != nSize)
     448             :     {
     449             :         fprintf(stderr, "Overflow in VSICalloc(%d, %d)\n", /*ok*/
     450             :                 static_cast<int>(nCount), static_cast<int>(nSize));
     451             :         return nullptr;
     452             :     }
     453             :     if (nMaxPeakAllocSize < 0)
     454             :     {
     455             :         char *pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE");
     456             :         nMaxPeakAllocSize = pszMaxPeakAllocSize ? atoi(pszMaxPeakAllocSize) : 0;
     457             :         char *pszMaxCumulAllocSize = getenv("CPL_MAX_CUMUL_ALLOC_SIZE");
     458             :         nMaxCumulAllocSize =
     459             :             pszMaxCumulAllocSize ? atoi(pszMaxCumulAllocSize) : 0;
     460             :     }
     461             :     if (nMaxPeakAllocSize > 0 && static_cast<GIntBig>(nMul) > nMaxPeakAllocSize)
     462             :         return nullptr;
     463             : #ifdef DEBUG_VSIMALLOC_STATS
     464             :     if (nMaxCumulAllocSize > 0 &&
     465             :         static_cast<GIntBig>(nCurrentTotalAllocs) + static_cast<GIntBig>(nMul) >
     466             :             nMaxCumulAllocSize)
     467             :         return nullptr;
     468             : #endif
     469             : 
     470             : #ifdef DEBUG_VSIMALLOC_MPROTECT
     471             :     char *ptr = nullptr;
     472             :     const size_t nPageSize = getpagesize();
     473             :     const size_t nRequestedSize =
     474             :         (3 * sizeof(void *) + nMul + nPageSize - 1) & ~(nPageSize - 1);
     475             :     if (nRequestedSize < nMul)
     476             :         return nullptr;
     477             :     posix_memalign((void **)&ptr, nPageSize, nRequestedSize);
     478             :     if (ptr == nullptr)
     479             :         return nullptr;
     480             :     memset(ptr + 2 * sizeof(void *), 0, nMul);
     481             : #else
     482             :     const size_t nRequestedSize = 3 * sizeof(void *) + nMul;
     483             :     if (nRequestedSize < nMul)
     484             :         return nullptr;
     485             :     char *ptr = static_cast<char *>(calloc(1, nRequestedSize));
     486             :     if (ptr == nullptr)
     487             :         return nullptr;
     488             : #endif
     489             : 
     490             :     ptr[0] = 'V';
     491             :     ptr[1] = 'S';
     492             :     ptr[2] = 'I';
     493             :     ptr[3] = 'M';
     494             :     // cppcheck-suppress pointerSize
     495             :     memcpy(ptr + sizeof(void *), &nMul, sizeof(void *));
     496             :     ptr[2 * sizeof(void *) + nMul + 0] = 'E';
     497             :     ptr[2 * sizeof(void *) + nMul + 1] = 'V';
     498             :     ptr[2 * sizeof(void *) + nMul + 2] = 'S';
     499             :     ptr[2 * sizeof(void *) + nMul + 3] = 'I';
     500             : #if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
     501             :     {
     502             :         CPLMutexHolderD(&hMemStatMutex);
     503             : #ifdef DEBUG_VSIMALLOC_VERBOSE
     504             :         if (nMul > THRESHOLD_PRINT)
     505             :         {
     506             :             fprintf(stderr, /*ok*/
     507             :                     "Thread[%p] VSICalloc(%d,%d) = %p"
     508             : #ifdef DEBUG_VSIMALLOC_STATS
     509             :                     ", current_cumul = " CPL_FRMT_GUIB
     510             : #ifdef DEBUG_BLOCK_CACHE_USE
     511             :                     ", block_cache_used = " CPL_FRMT_GIB
     512             : #endif
     513             :                     ", mal+cal-free = %d"
     514             : #endif
     515             :                     "\n",
     516             :                     (void *)CPLGetPID(), static_cast<int>(nCount),
     517             :                     static_cast<int>(nSize), ptr + 2 * sizeof(void *)
     518             : #ifdef DEBUG_VSIMALLOC_STATS
     519             :                                                  ,
     520             :                     static_cast<GUIntBig>(nCurrentTotalAllocs + nMul)
     521             : #ifdef DEBUG_BLOCK_CACHE_USE
     522             :                         ,
     523             :                     GDALGetCacheUsed64()
     524             : #endif
     525             :                         ,
     526             :                     static_cast<int>(nVSIMallocs + nVSICallocs - nVSIFrees)
     527             : #endif
     528             :             );
     529             :         }
     530             : #endif
     531             : #ifdef DEBUG_VSIMALLOC_STATS
     532             :         nVSICallocs++;
     533             :         if (nMaxTotalAllocs == 0)
     534             :             atexit(VSIShowMemStats);
     535             :         nCurrentTotalAllocs += nMul;
     536             :         if (nCurrentTotalAllocs > nMaxTotalAllocs)
     537             :             nMaxTotalAllocs = nCurrentTotalAllocs;
     538             : #endif
     539             :     }
     540             : #endif
     541             :     // cppcheck-suppress memleak
     542             :     return ptr + 2 * sizeof(void *);
     543             : }
     544             : #endif  // DEBUG_VSIMALLOC
     545             : 
     546             : /************************************************************************/
     547             : /*                             VSIMalloc()                              */
     548             : /************************************************************************/
     549             : 
     550             : #ifndef DEBUG_VSIMALLOC
     551             : /** Analog of malloc(). Use VSIFree() to free */
     552    84572400 : void *VSIMalloc(size_t nSize)
     553             : 
     554             : {
     555    84572400 :     return malloc(nSize);
     556             : }
     557             : 
     558             : #else  // DEBUG_VSIMALLOC
     559             : 
     560             : void *VSIMalloc(size_t nSize)
     561             : 
     562             : {
     563             :     if (nMaxPeakAllocSize < 0)
     564             :     {
     565             :         char *pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE");
     566             :         nMaxPeakAllocSize = pszMaxPeakAllocSize ? atoi(pszMaxPeakAllocSize) : 0;
     567             :         char *pszMaxCumulAllocSize = getenv("CPL_MAX_CUMUL_ALLOC_SIZE");
     568             :         nMaxCumulAllocSize =
     569             :             pszMaxCumulAllocSize ? atoi(pszMaxCumulAllocSize) : 0;
     570             :     }
     571             :     if (nMaxPeakAllocSize > 0 &&
     572             :         static_cast<GIntBig>(nSize) > nMaxPeakAllocSize)
     573             :         return nullptr;
     574             : #ifdef DEBUG_VSIMALLOC_STATS
     575             :     if (nMaxCumulAllocSize > 0 && static_cast<GIntBig>(nCurrentTotalAllocs) +
     576             :                                           static_cast<GIntBig>(nSize) >
     577             :                                       nMaxCumulAllocSize)
     578             :         return nullptr;
     579             : #endif  // DEBUG_VSIMALLOC_STATS
     580             : 
     581             : #ifdef DEBUG_VSIMALLOC_MPROTECT
     582             :     char *ptr = nullptr;
     583             :     const size_t nPageSize = getpagesize();
     584             :     const size_t nRequestedSize =
     585             :         (3 * sizeof(void *) + nSize + nPageSize - 1) & ~(nPageSize - 1);
     586             :     if (nRequestedSize < nSize)
     587             :         return nullptr;
     588             :     posix_memalign((void **)&ptr, nPageSize, nRequestedSize);
     589             : #else
     590             :     const size_t nRequestedSize = 3 * sizeof(void *) + nSize;
     591             :     if (nRequestedSize < nSize)
     592             :         return nullptr;
     593             :     char *ptr = static_cast<char *>(malloc(nRequestedSize));
     594             : #endif  // DEBUG_VSIMALLOC_MPROTECT
     595             :     if (ptr == nullptr)
     596             :         return nullptr;
     597             :     ptr[0] = 'V';
     598             :     ptr[1] = 'S';
     599             :     ptr[2] = 'I';
     600             :     ptr[3] = 'M';
     601             :     // cppcheck-suppress pointerSize
     602             :     memcpy(ptr + sizeof(void *), &nSize, sizeof(void *));
     603             :     ptr[2 * sizeof(void *) + nSize + 0] = 'E';
     604             :     ptr[2 * sizeof(void *) + nSize + 1] = 'V';
     605             :     ptr[2 * sizeof(void *) + nSize + 2] = 'S';
     606             :     ptr[2 * sizeof(void *) + nSize + 3] = 'I';
     607             : #if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
     608             :     {
     609             :         CPLMutexHolderD(&hMemStatMutex);
     610             : #ifdef DEBUG_VSIMALLOC_VERBOSE
     611             :         if (nSize > THRESHOLD_PRINT)
     612             :         {
     613             :             fprintf(stderr, /*ok*/
     614             :                     "Thread[%p] VSIMalloc(%d) = %p"
     615             : #ifdef DEBUG_VSIMALLOC_STATS
     616             :                     ", current_cumul = " CPL_FRMT_GUIB
     617             : #ifdef DEBUG_BLOCK_CACHE_USE
     618             :                     ", block_cache_used = " CPL_FRMT_GIB
     619             : #endif
     620             :                     ", mal+cal-free = %d"
     621             : #endif
     622             :                     "\n",
     623             :                     (void *)CPLGetPID(), static_cast<int>(nSize),
     624             :                     ptr + 2 * sizeof(void *)
     625             : #ifdef DEBUG_VSIMALLOC_STATS
     626             :                         ,
     627             :                     static_cast<GUIntBig>(nCurrentTotalAllocs + nSize)
     628             : #ifdef DEBUG_BLOCK_CACHE_USE
     629             :                         ,
     630             :                     GDALGetCacheUsed64()
     631             : #endif
     632             :                         ,
     633             :                     static_cast<int>(nVSIMallocs + nVSICallocs - nVSIFrees)
     634             : #endif
     635             :             );
     636             :         }
     637             : #endif  // DEBUG_VSIMALLOC_VERBOSE
     638             : #ifdef DEBUG_VSIMALLOC_STATS
     639             :         nVSIMallocs++;
     640             :         if (nMaxTotalAllocs == 0)
     641             :             atexit(VSIShowMemStats);
     642             :         nCurrentTotalAllocs += nSize;
     643             :         if (nCurrentTotalAllocs > nMaxTotalAllocs)
     644             :             nMaxTotalAllocs = nCurrentTotalAllocs;
     645             : #endif  // DEBUG_VSIMALLOC_STATS
     646             :     }
     647             : #endif  // DEBUG_VSIMALLOC_STATS || DEBUG_VSIMALLOC_VERBOSE
     648             :     // cppcheck-suppress memleak
     649             :     return ptr + 2 * sizeof(void *);
     650             : }
     651             : 
     652             : static void VSICheckMarkerBegin(char *ptr)
     653             : {
     654             :     if (memcmp(ptr, "VSIM", 4) != 0)
     655             :     {
     656             :         CPLError(CE_Fatal, CPLE_AppDefined,
     657             :                  "Inconsistent use of VSI memory allocation primitives "
     658             :                  "for %p : %c%c%c%c",
     659             :                  ptr, ptr[0], ptr[1], ptr[2], ptr[3]);
     660             :     }
     661             : }
     662             : 
     663             : static void VSICheckMarkerEnd(char *ptr, size_t nEnd)
     664             : {
     665             :     if (memcmp(ptr + nEnd, "EVSI", 4) != 0)
     666             :     {
     667             :         CPLError(CE_Fatal, CPLE_AppDefined,
     668             :                  "Memory has been written after the end of %p", ptr);
     669             :     }
     670             : }
     671             : 
     672             : #endif  // DEBUG_VSIMALLOC
     673             : 
     674             : /************************************************************************/
     675             : /*                             VSIRealloc()                             */
     676             : /************************************************************************/
     677             : 
     678             : /** Analog of realloc(). Use VSIFree() to free.
     679             :  *
     680             :  * If the pointer is NULL, VSIRealloc is equivalent to VSIMalloc.
     681             :  */
     682    21625400 : void *VSIRealloc(void *pData, size_t nNewSize)
     683             : 
     684             : {
     685             : #ifdef DEBUG_VSIMALLOC
     686             :     if (pData == nullptr)
     687             :         return VSIMalloc(nNewSize);
     688             : 
     689             :     char *ptr = ((char *)pData) - 2 * sizeof(void *);
     690             :     VSICheckMarkerBegin(ptr);
     691             : 
     692             :     size_t nOldSize = 0;
     693             :     // cppcheck-suppress pointerSize
     694             :     memcpy(&nOldSize, ptr + sizeof(void *), sizeof(void *));
     695             :     VSICheckMarkerEnd(ptr, 2 * sizeof(void *) + nOldSize);
     696             : 
     697             :     if (nMaxPeakAllocSize < 0)
     698             :     {
     699             :         char *pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE");
     700             :         nMaxPeakAllocSize = pszMaxPeakAllocSize ? atoi(pszMaxPeakAllocSize) : 0;
     701             :     }
     702             :     if (nMaxPeakAllocSize > 0 &&
     703             :         static_cast<GIntBig>(nNewSize) > nMaxPeakAllocSize)
     704             :         return nullptr;
     705             : #ifdef DEBUG_VSIMALLOC_STATS
     706             :     if (nMaxCumulAllocSize > 0 && static_cast<GIntBig>(nCurrentTotalAllocs) +
     707             :                                           static_cast<GIntBig>(nNewSize) -
     708             :                                           static_cast<GIntBig>(nOldSize) >
     709             :                                       nMaxCumulAllocSize)
     710             :         return nullptr;
     711             : #endif
     712             : 
     713             :     ptr[2 * sizeof(void *) + nOldSize + 0] = 'I';
     714             :     ptr[2 * sizeof(void *) + nOldSize + 1] = 'S';
     715             :     ptr[2 * sizeof(void *) + nOldSize + 2] = 'V';
     716             :     ptr[2 * sizeof(void *) + nOldSize + 3] = 'E';
     717             : 
     718             : #ifdef DEBUG_VSIMALLOC_MPROTECT
     719             :     char *newptr = nullptr;
     720             :     const size_t nPageSize = getpagesize();
     721             :     const size_t nRequestedSize =
     722             :         (nNewSize + 3 * sizeof(void *) + nPageSize - 1) & ~(nPageSize - 1);
     723             :     if (nRequestedSize < nNewSize)
     724             :     {
     725             :         ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
     726             :         ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
     727             :         ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
     728             :         ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
     729             :         return nullptr;
     730             :     }
     731             :     posix_memalign((void **)&newptr, nPageSize, nRequestedSize);
     732             :     if (newptr == nullptr)
     733             :     {
     734             :         ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
     735             :         ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
     736             :         ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
     737             :         ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
     738             :         return nullptr;
     739             :     }
     740             :     memcpy(newptr + 2 * sizeof(void *), pData, nOldSize);
     741             :     ptr[0] = 'M';
     742             :     ptr[1] = 'I';
     743             :     ptr[2] = 'S';
     744             :     ptr[3] = 'V';
     745             :     free(ptr);
     746             :     newptr[0] = 'V';
     747             :     newptr[1] = 'S';
     748             :     newptr[2] = 'I';
     749             :     newptr[3] = 'M';
     750             : #else
     751             :     const size_t nRequestedSize = 3 * sizeof(void *) + nNewSize;
     752             :     if (nRequestedSize < nNewSize)
     753             :     {
     754             :         ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
     755             :         ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
     756             :         ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
     757             :         ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
     758             :         return nullptr;
     759             :     }
     760             :     void *newptr = realloc(ptr, nRequestedSize);
     761             :     if (newptr == nullptr)
     762             :     {
     763             :         ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
     764             :         ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
     765             :         ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
     766             :         ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
     767             :         return nullptr;
     768             :     }
     769             : #endif
     770             :     ptr = static_cast<char *>(newptr);
     771             :     // cppcheck-suppress pointerSize
     772             :     memcpy(ptr + sizeof(void *), &nNewSize, sizeof(void *));
     773             :     ptr[2 * sizeof(void *) + nNewSize + 0] = 'E';
     774             :     ptr[2 * sizeof(void *) + nNewSize + 1] = 'V';
     775             :     ptr[2 * sizeof(void *) + nNewSize + 2] = 'S';
     776             :     ptr[2 * sizeof(void *) + nNewSize + 3] = 'I';
     777             : 
     778             : #if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
     779             :     {
     780             :         CPLMutexHolderD(&hMemStatMutex);
     781             : #ifdef DEBUG_VSIMALLOC_VERBOSE
     782             :         if (nNewSize > THRESHOLD_PRINT)
     783             :         {
     784             :             fprintf(
     785             :                 stderr,
     786             :                 "Thread[%p] VSIRealloc(%p, %d) = %p" /*ok*/
     787             : #ifdef DEBUG_VSIMALLOC_STATS
     788             :                 ", current_cumul = " CPL_FRMT_GUIB
     789             : #ifdef DEBUG_BLOCK_CACHE_USE
     790             :                 ", block_cache_used = " CPL_FRMT_GIB
     791             : #endif
     792             :                 ", mal+cal-free = %d"
     793             : #endif
     794             :                 "\n",
     795             :                 (void *)CPLGetPID(), pData, static_cast<int>(nNewSize),
     796             :                 ptr + 2 * sizeof(void *)
     797             : #ifdef DEBUG_VSIMALLOC_STATS
     798             :                     ,
     799             :                 static_cast<GUIntBig>(nCurrentTotalAllocs - nOldSize + nNewSize)
     800             : #ifdef DEBUG_BLOCK_CACHE_USE
     801             :                     ,
     802             :                 GDALGetCacheUsed64()
     803             : #endif
     804             :                     ,
     805             :                 static_cast<int>(nVSIMallocs + nVSICallocs - nVSIFrees)
     806             : #endif
     807             :             );
     808             :         }
     809             : #endif
     810             : #ifdef DEBUG_VSIMALLOC_STATS
     811             :         nVSIReallocs++;
     812             :         nCurrentTotalAllocs -= nOldSize;
     813             :         nCurrentTotalAllocs += nNewSize;
     814             :         if (nCurrentTotalAllocs > nMaxTotalAllocs)
     815             :             nMaxTotalAllocs = nCurrentTotalAllocs;
     816             : #endif
     817             :     }
     818             : #endif
     819             :     return ptr + 2 * sizeof(void *);
     820             : #else
     821    21625400 :     return realloc(pData, nNewSize);
     822             : #endif
     823             : }
     824             : 
     825             : /************************************************************************/
     826             : /*                              VSIFree()                               */
     827             : /************************************************************************/
     828             : 
     829             : /** Analog of free() for data allocated with VSIMalloc(), VSICalloc(),
     830             :  * VSIRealloc().
     831             :  *
     832             :  * It is not an error to call VSIFree with a NULL pointer, and it will
     833             :  * have no effect.
     834             :  */
     835   135281000 : void VSIFree(void *pData)
     836             : 
     837             : {
     838             : #ifdef DEBUG_VSIMALLOC
     839             :     if (pData == nullptr)
     840             :         return;
     841             : 
     842             :     char *ptr = ((char *)pData) - 2 * sizeof(void *);
     843             :     VSICheckMarkerBegin(ptr);
     844             :     size_t nOldSize = 0;
     845             :     // cppcheck-suppress pointerSize
     846             :     memcpy(&nOldSize, ptr + sizeof(void *), sizeof(void *));
     847             :     VSICheckMarkerEnd(ptr, 2 * sizeof(void *) + nOldSize);
     848             :     ptr[0] = 'M';
     849             :     ptr[1] = 'I';
     850             :     ptr[2] = 'S';
     851             :     ptr[3] = 'V';
     852             :     ptr[2 * sizeof(void *) + nOldSize + 0] = 'I';
     853             :     ptr[2 * sizeof(void *) + nOldSize + 1] = 'S';
     854             :     ptr[2 * sizeof(void *) + nOldSize + 2] = 'V';
     855             :     ptr[2 * sizeof(void *) + nOldSize + 3] = 'E';
     856             : #if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
     857             :     {
     858             :         CPLMutexHolderD(&hMemStatMutex);
     859             : #ifdef DEBUG_VSIMALLOC_VERBOSE
     860             :         if (nOldSize > THRESHOLD_PRINT)
     861             :         {
     862             :             fprintf(stderr, "Thread[%p] VSIFree(%p, (%d bytes))\n", /*ok*/
     863             :                     (void *)CPLGetPID(), pData, static_cast<int>(nOldSize));
     864             :         }
     865             : #endif
     866             : #ifdef DEBUG_VSIMALLOC_STATS
     867             :         nVSIFrees++;
     868             :         nCurrentTotalAllocs -= nOldSize;
     869             : #endif
     870             :     }
     871             : #endif
     872             : 
     873             : #ifdef DEBUG_VSIMALLOC_MPROTECT
     874             :     mprotect(ptr, nOldSize + 2 * sizeof(void *), PROT_NONE);
     875             : #else
     876             :     free(ptr);
     877             : #endif
     878             : 
     879             : #else
     880   135281000 :     if (pData != nullptr)
     881   114146000 :         free(pData);
     882             : #endif
     883   135281000 : }
     884             : 
     885             : /************************************************************************/
     886             : /*                      VSIMallocAligned()                              */
     887             : /************************************************************************/
     888             : 
     889             : /** Allocates a buffer with an alignment constraint.
     890             :  *
     891             :  * The return value must be freed with VSIFreeAligned().
     892             :  *
     893             :  * @param nAlignment Must be a power of 2, multiple of sizeof(void*), and
     894             :  *                   lesser than 256.
     895             :  * @param nSize Size of the buffer to allocate.
     896             :  * @return a buffer aligned on nAlignment and of size nSize, or NULL
     897             :  * @since GDAL 2.2
     898             :  */
     899             : 
     900     3180340 : void *VSIMallocAligned(size_t nAlignment, size_t nSize)
     901             : {
     902             :     // In particular for posix_memalign() where behavior when passing
     903             :     // nSize == 0 is technically implementation defined (Valgrind complains),
     904             :     // so let's always return NULL.
     905     3180340 :     if (nSize == 0)
     906           0 :         return nullptr;
     907             : #if defined(HAVE_POSIX_MEMALIGN) && !defined(DEBUG_VSIMALLOC)
     908     3180340 :     void *pRet = nullptr;
     909     3180340 :     if (posix_memalign(&pRet, nAlignment, nSize) != 0)
     910             :     {
     911           4 :         pRet = nullptr;
     912             :     }
     913     3180340 :     return pRet;
     914             : #elif defined(_WIN32) && !defined(DEBUG_VSIMALLOC)
     915             :     return _aligned_malloc(nSize, nAlignment);
     916             : #else
     917             :     // Check constraints on alignment.
     918             :     if (nAlignment < sizeof(void *) || nAlignment >= 256 ||
     919             :         (nAlignment & (nAlignment - 1)) != 0)
     920             :         return nullptr;
     921             :     // Detect overflow.
     922             :     if (nSize + nAlignment < nSize)
     923             :         return nullptr;
     924             :     GByte *pabyData = static_cast<GByte *>(VSIMalloc(nSize + nAlignment));
     925             :     if (pabyData == nullptr)
     926             :         return nullptr;
     927             :     size_t nShift =
     928             :         nAlignment - (reinterpret_cast<size_t>(pabyData) % nAlignment);
     929             :     GByte *pabyAligned = pabyData + nShift;
     930             :     // Guaranteed to fit on a byte since nAlignment < 256.
     931             :     pabyAligned[-1] = static_cast<GByte>(nShift);
     932             :     return pabyAligned;
     933             : #endif
     934             : }
     935             : 
     936             : /************************************************************************/
     937             : /*                     VSIMallocAlignedAuto()                           */
     938             : /************************************************************************/
     939             : 
     940             : /** Allocates a buffer with an alignment constraint such that it can be
     941             :  * used by the most demanding vector instruction set on that platform.
     942             :  *
     943             :  * The return value must be freed with VSIFreeAligned().
     944             :  *
     945             :  * @param nSize Size of the buffer to allocate.
     946             :  * @return an aligned buffer of size nSize, or NULL
     947             :  * @since GDAL 2.2
     948             :  */
     949             : 
     950     3180340 : void *VSIMallocAlignedAuto(size_t nSize)
     951             : {
     952             :     // We could potentially dynamically detect the capability of the CPU
     953             :     // but to simplify use 64 for AVX512 requirements (we use only AVX256
     954             :     // currently).
     955     3180340 :     return VSIMallocAligned(64, nSize);
     956             : }
     957             : 
     958             : /************************************************************************/
     959             : /*                        VSIMallocAlignedAutoVerbose()                 */
     960             : /************************************************************************/
     961             : 
     962             : /** See VSIMallocAlignedAuto() */
     963     3180330 : void *VSIMallocAlignedAutoVerbose(size_t nSize, const char *pszFile, int nLine)
     964             : {
     965     3180330 :     void *pRet = VSIMallocAlignedAuto(nSize);
     966     3180270 :     if (pRet == nullptr && nSize != 0)
     967             :     {
     968           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     969             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
     970             :                  pszFile ? pszFile : "(unknown file)", nLine,
     971             :                  static_cast<GUIntBig>(nSize));
     972             :     }
     973     3180270 :     return pRet;
     974             : }
     975             : 
     976             : /************************************************************************/
     977             : /*                        VSIFreeAligned()                              */
     978             : /************************************************************************/
     979             : 
     980             : /** Free a buffer allocated with VSIMallocAligned().
     981             :  *
     982             :  * @param ptr Buffer to free.
     983             :  * @since GDAL 2.2
     984             :  */
     985             : 
     986     3202760 : void VSIFreeAligned(void *ptr)
     987             : {
     988             : #if defined(HAVE_POSIX_MEMALIGN) && !defined(DEBUG_VSIMALLOC)
     989     3202760 :     free(ptr);
     990             : #elif defined(_WIN32) && !defined(DEBUG_VSIMALLOC)
     991             :     _aligned_free(ptr);
     992             : #else
     993             :     if (ptr == nullptr)
     994             :         return;
     995             :     GByte *pabyAligned = static_cast<GByte *>(ptr);
     996             :     size_t nShift = pabyAligned[-1];
     997             :     VSIFree(pabyAligned - nShift);
     998             : #endif
     999     3202760 : }
    1000             : 
    1001             : /************************************************************************/
    1002             : /*                             VSIStrdup()                              */
    1003             : /************************************************************************/
    1004             : 
    1005             : /** Analog of strdup(). Use VSIFree() to free */
    1006    33077900 : char *VSIStrdup(const char *pszString)
    1007             : 
    1008             : {
    1009    33077900 :     const size_t nSize = strlen(pszString) + 1;
    1010    33077900 :     char *ptr = static_cast<char *>(VSIMalloc(nSize));
    1011    33077800 :     if (ptr == nullptr)
    1012           0 :         return nullptr;
    1013    33077800 :     memcpy(ptr, pszString, nSize);
    1014    33077800 :     return ptr;
    1015             : }
    1016             : 
    1017             : /************************************************************************/
    1018             : /*                          VSICheckMul2()                              */
    1019             : /************************************************************************/
    1020             : 
    1021             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
    1022      283085 : static size_t VSICheckMul2(size_t mul1, size_t mul2, bool *pbOverflowFlag,
    1023             :                            const char *pszFile, int nLine)
    1024             : {
    1025      283085 :     const size_t res = mul1 * mul2;
    1026      283085 :     if (mul1 != 0)
    1027             :     {
    1028      283082 :         if (res / mul1 == mul2)
    1029             :         {
    1030      283081 :             if (pbOverflowFlag)
    1031      283081 :                 *pbOverflowFlag = FALSE;
    1032      283081 :             return res;
    1033             :         }
    1034             :         else
    1035             :         {
    1036           1 :             if (pbOverflowFlag)
    1037           1 :                 *pbOverflowFlag = TRUE;
    1038           1 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1039             :                      "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
    1040             :                      " * " CPL_FRMT_GUIB,
    1041             :                      pszFile ? pszFile : "(unknown file)", nLine,
    1042             :                      static_cast<GUIntBig>(mul1), static_cast<GUIntBig>(mul2));
    1043             :         }
    1044             :     }
    1045             :     else
    1046             :     {
    1047           3 :         if (pbOverflowFlag)
    1048           8 :             *pbOverflowFlag = FALSE;
    1049             :     }
    1050           9 :     return 0;
    1051             : }
    1052             : 
    1053             : /************************************************************************/
    1054             : /*                          VSICheckMul3()                              */
    1055             : /************************************************************************/
    1056             : 
    1057             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
    1058       97561 : static size_t VSICheckMul3(size_t mul1, size_t mul2, size_t mul3,
    1059             :                            bool *pbOverflowFlag, const char *pszFile, int nLine)
    1060             : {
    1061       97561 :     if (mul1 != 0)
    1062             :     {
    1063       97556 :         const size_t res = mul1 * mul2;
    1064       97556 :         if (res / mul1 == mul2)
    1065             :         {
    1066       97550 :             const size_t res2 = res * mul3;
    1067       97550 :             if (mul3 != 0)
    1068             :             {
    1069       97549 :                 if (res2 / mul3 == res)
    1070             :                 {
    1071       97549 :                     if (pbOverflowFlag)
    1072       97548 :                         *pbOverflowFlag = false;
    1073       97549 :                     return res2;
    1074             :                 }
    1075             :                 else
    1076             :                 {
    1077           0 :                     if (pbOverflowFlag)
    1078           2 :                         *pbOverflowFlag = true;
    1079           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory,
    1080             :                              "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
    1081             :                              " * " CPL_FRMT_GUIB " * " CPL_FRMT_GUIB,
    1082             :                              pszFile ? pszFile : "(unknown file)", nLine,
    1083             :                              static_cast<GUIntBig>(mul1),
    1084             :                              static_cast<GUIntBig>(mul2),
    1085             :                              static_cast<GUIntBig>(mul3));
    1086             :                 }
    1087             :             }
    1088             :             else
    1089             :             {
    1090           1 :                 if (pbOverflowFlag)
    1091           1 :                     *pbOverflowFlag = false;
    1092             :             }
    1093             :         }
    1094             :         else
    1095             :         {
    1096           6 :             if (pbOverflowFlag)
    1097           1 :                 *pbOverflowFlag = true;
    1098           6 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1099             :                      "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
    1100             :                      " * " CPL_FRMT_GUIB " * " CPL_FRMT_GUIB,
    1101             :                      pszFile ? pszFile : "(unknown file)", nLine,
    1102             :                      static_cast<GUIntBig>(mul1), static_cast<GUIntBig>(mul2),
    1103             :                      static_cast<GUIntBig>(mul3));
    1104             :         }
    1105             :     }
    1106             :     else
    1107             :     {
    1108           5 :         if (pbOverflowFlag)
    1109           1 :             *pbOverflowFlag = false;
    1110             :     }
    1111           5 :     return 0;
    1112             : }
    1113             : 
    1114             : /**
    1115             :  VSIMalloc2 allocates (nSize1 * nSize2) bytes.
    1116             :  In case of overflow of the multiplication, or if memory allocation fails, a
    1117             :  NULL pointer is returned and a CE_Failure error is raised with CPLError().
    1118             :  If nSize1 == 0 || nSize2 == 0, a NULL pointer will also be returned.
    1119             :  CPLFree() or VSIFree() can be used to free memory allocated by this function.
    1120             : */
    1121         153 : void CPL_DLL *VSIMalloc2(size_t nSize1, size_t nSize2)
    1122             : {
    1123         153 :     return VSIMalloc2Verbose(nSize1, nSize2, nullptr, 0);
    1124             : }
    1125             : 
    1126             : /**
    1127             :  VSIMalloc3 allocates (nSize1 * nSize2 * nSize3) bytes.
    1128             :  In case of overflow of the multiplication, or if memory allocation fails, a
    1129             :  NULL pointer is returned and a CE_Failure error is raised with CPLError().
    1130             :  If nSize1 == 0 || nSize2 == 0 || nSize3 == 0, a NULL pointer will also be
    1131             :  returned.  CPLFree() or VSIFree() can be used to free memory allocated by this
    1132             :  function.
    1133             : */
    1134         884 : void CPL_DLL *VSIMalloc3(size_t nSize1, size_t nSize2, size_t nSize3)
    1135             : {
    1136         884 :     return VSIMalloc3Verbose(nSize1, nSize2, nSize3, nullptr, 0);
    1137             : }
    1138             : 
    1139             : /************************************************************************/
    1140             : /*                          VSIMallocVerbose()                          */
    1141             : /************************************************************************/
    1142             : 
    1143     7530340 : void *VSIMallocVerbose(size_t nSize, const char *pszFile, int nLine)
    1144             : {
    1145     7530340 :     void *pRet = VSIMalloc(nSize);
    1146     7530350 :     if (pRet == nullptr && nSize != 0)
    1147             :     {
    1148           2 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1149             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1150             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1151             :                  static_cast<GUIntBig>(nSize));
    1152             :     }
    1153     7530300 :     return pRet;
    1154             : }
    1155             : 
    1156             : /************************************************************************/
    1157             : /*                          VSIMalloc2Verbose()                         */
    1158             : /************************************************************************/
    1159             : 
    1160      283091 : void *VSIMalloc2Verbose(size_t nSize1, size_t nSize2, const char *pszFile,
    1161             :                         int nLine)
    1162             : {
    1163      283091 :     bool bOverflowFlag = false;
    1164             :     const size_t nSizeToAllocate =
    1165      283091 :         VSICheckMul2(nSize1, nSize2, &bOverflowFlag, pszFile, nLine);
    1166      283087 :     if (bOverflowFlag || nSizeToAllocate == 0)
    1167          39 :         return nullptr;
    1168             : 
    1169      283048 :     void *pRet = VSIMalloc(nSizeToAllocate);
    1170      283061 :     if (pRet == nullptr)
    1171             :     {
    1172           2 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1173             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1174             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1175             :                  static_cast<GUIntBig>(nSize1) * static_cast<GUIntBig>(nSize2));
    1176             :     }
    1177      283061 :     return pRet;
    1178             : }
    1179             : 
    1180             : /************************************************************************/
    1181             : /*                          VSIMalloc3Verbose()                         */
    1182             : /************************************************************************/
    1183             : 
    1184       97560 : void *VSIMalloc3Verbose(size_t nSize1, size_t nSize2, size_t nSize3,
    1185             :                         const char *pszFile, int nLine)
    1186             : {
    1187       97560 :     bool bOverflowFlag = false;
    1188             :     size_t nSizeToAllocate =
    1189       97560 :         VSICheckMul3(nSize1, nSize2, nSize3, &bOverflowFlag, pszFile, nLine);
    1190       97564 :     if (bOverflowFlag || nSizeToAllocate == 0)
    1191          24 :         return nullptr;
    1192             : 
    1193       97540 :     void *pRet = VSIMalloc(nSizeToAllocate);
    1194       97570 :     if (pRet == nullptr)
    1195             :     {
    1196           4 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1197             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1198             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1199           4 :                  static_cast<GUIntBig>(nSize1) * static_cast<GUIntBig>(nSize2) *
    1200             :                      static_cast<GUIntBig>(nSize3));
    1201             :     }
    1202       97570 :     return pRet;
    1203             : }
    1204             : 
    1205             : /************************************************************************/
    1206             : /*                          VSICallocVerbose()                          */
    1207             : /************************************************************************/
    1208             : 
    1209     8951200 : void *VSICallocVerbose(size_t nCount, size_t nSize, const char *pszFile,
    1210             :                        int nLine)
    1211             : {
    1212     8951200 :     void *pRet = VSICalloc(nCount, nSize);
    1213     8951180 :     if (pRet == nullptr && nCount != 0 && nSize != 0)
    1214             :     {
    1215           4 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1216             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB "x" CPL_FRMT_GUIB
    1217             :                  " bytes",
    1218             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1219             :                  static_cast<GUIntBig>(nCount), static_cast<GUIntBig>(nSize));
    1220             :     }
    1221     8951140 :     return pRet;
    1222             : }
    1223             : 
    1224             : /************************************************************************/
    1225             : /*                          VSIReallocVerbose()                         */
    1226             : /************************************************************************/
    1227             : 
    1228    17209000 : void *VSIReallocVerbose(void *pOldPtr, size_t nNewSize, const char *pszFile,
    1229             :                         int nLine)
    1230             : {
    1231    17209000 :     void *pRet = VSIRealloc(pOldPtr, nNewSize);
    1232    17208900 :     if (pRet == nullptr && nNewSize != 0)
    1233             :     {
    1234           2 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1235             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1236             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1237             :                  static_cast<GUIntBig>(nNewSize));
    1238             :     }
    1239    17208900 :     return pRet;
    1240             : }
    1241             : 
    1242             : /************************************************************************/
    1243             : /*                          VSIStrdupVerbose()                          */
    1244             : /************************************************************************/
    1245             : 
    1246    10308100 : char *VSIStrdupVerbose(const char *pszStr, const char *pszFile, int nLine)
    1247             : {
    1248    10308100 :     char *pRet = VSIStrdup(pszStr);
    1249    10308000 :     if (pRet == nullptr)
    1250             :     {
    1251           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1252             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1253             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1254           0 :                  static_cast<GUIntBig>(strlen(pszStr) + 1));
    1255             :     }
    1256    10307800 :     return pRet;
    1257             : }
    1258             : 
    1259             : /************************************************************************/
    1260             : /*                              VSIStat()                               */
    1261             : /************************************************************************/
    1262             : 
    1263         986 : int VSIStat(const char *pszFilename, VSIStatBuf *pStatBuf)
    1264             : 
    1265             : {
    1266             : #if defined(_WIN32)
    1267             :     if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
    1268             :     {
    1269             :         wchar_t *pwszFilename =
    1270             :             CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
    1271             : 
    1272             :         int nResult =
    1273             :             _wstat(pwszFilename, reinterpret_cast<struct _stat *>(pStatBuf));
    1274             : 
    1275             :         CPLFree(pwszFilename);
    1276             : 
    1277             :         return nResult;
    1278             :     }
    1279             : 
    1280             : #endif
    1281         986 :     return stat(pszFilename, pStatBuf);
    1282             : }
    1283             : 
    1284             : /************************************************************************/
    1285             : /*                              VSITime()                               */
    1286             : /************************************************************************/
    1287             : 
    1288           0 : unsigned long VSITime(unsigned long *pnTimeToSet)
    1289             : 
    1290             : {
    1291             :     time_t tTime;
    1292             : 
    1293           0 :     tTime = time(nullptr);
    1294             : 
    1295           0 :     if (pnTimeToSet != nullptr)
    1296           0 :         *pnTimeToSet = static_cast<unsigned long>(tTime);
    1297             : 
    1298           0 :     return static_cast<unsigned long>(tTime);
    1299             : }
    1300             : 
    1301             : /************************************************************************/
    1302             : /*                              VSICTime()                              */
    1303             : /************************************************************************/
    1304             : 
    1305           4 : const char *VSICTime(unsigned long nTime)
    1306             : 
    1307             : {
    1308           4 :     time_t tTime = static_cast<time_t>(nTime);
    1309             : #if HAVE_CTIME_R
    1310             :     // Cf https://linux.die.net/man/3/ctime_r:
    1311             :     // "The reentrant version ctime_r() does the same, but stores the string in
    1312             :     // a user-supplied buffer which should have room for at least 26 bytes"
    1313             :     char buffer[26];
    1314           4 :     char *ret = ctime_r(&tTime, buffer);
    1315           4 :     return ret ? CPLSPrintf("%s", ret) : nullptr;
    1316             : #elif defined(_WIN32)
    1317             :     char buffer[26];
    1318             :     return ctime_s(buffer, sizeof(buffer), &tTime) == 0
    1319             :                ? CPLSPrintf("%s", buffer)
    1320             :                : nullptr;
    1321             : #else
    1322             :     return reinterpret_cast<const char *>(ctime(&tTime));
    1323             : #endif
    1324             : }
    1325             : 
    1326             : /************************************************************************/
    1327             : /*                             VSIGMTime()                              */
    1328             : /************************************************************************/
    1329             : 
    1330           0 : struct tm *VSIGMTime(const time_t *pnTime, struct tm *poBrokenTime)
    1331             : {
    1332             : 
    1333             : #if HAVE_GMTIME_R
    1334           0 :     gmtime_r(pnTime, poBrokenTime);
    1335           0 :     return poBrokenTime;
    1336             : #elif defined(_WIN32)
    1337             :     return gmtime_s(poBrokenTime, pnTime) == 0 ? poBrokenTime : nullptr;
    1338             : #else
    1339             :     struct tm *poTime = gmtime(pnTime);
    1340             :     memcpy(poBrokenTime, poTime, sizeof(tm));
    1341             :     return poBrokenTime;
    1342             : #endif
    1343             : }
    1344             : 
    1345             : /************************************************************************/
    1346             : /*                             VSILocalTime()                           */
    1347             : /************************************************************************/
    1348             : 
    1349         527 : struct tm *VSILocalTime(const time_t *pnTime, struct tm *poBrokenTime)
    1350             : {
    1351             : 
    1352             : #if HAVE_LOCALTIME_R
    1353         527 :     localtime_r(pnTime, poBrokenTime);
    1354         527 :     return poBrokenTime;
    1355             : #elif defined(_WIN32)
    1356             :     return localtime_s(poBrokenTime, pnTime) == 0 ? poBrokenTime : nullptr;
    1357             : #else
    1358             :     struct tm *poTime = localtime(pnTime);
    1359             :     memcpy(poBrokenTime, poTime, sizeof(tm));
    1360             :     return poBrokenTime;
    1361             : #endif
    1362             : }
    1363             : 
    1364             : /************************************************************************/
    1365             : /*                            VSIStrerror()                             */
    1366             : /************************************************************************/
    1367             : 
    1368             : /** Return the error string corresponding to the error number. Do not free it */
    1369         153 : char *VSIStrerror(int nErrno)
    1370             : 
    1371             : {
    1372         153 :     return strerror(nErrno);
    1373             : }
    1374             : 
    1375             : /************************************************************************/
    1376             : /*                        CPLGetPhysicalRAM()                           */
    1377             : /************************************************************************/
    1378             : 
    1379             : #if HAVE_SC_PHYS_PAGES
    1380             : 
    1381             : /** Return the total physical RAM in bytes.
    1382             :  *
    1383             :  * In the context of a container using cgroups (typically Docker), this
    1384             :  * will take into account that limitation (starting with GDAL 2.4.0 and
    1385             :  * with extra fixes in GDAL 3.6.3)
    1386             :  *
    1387             :  * You should generally use CPLGetUsablePhysicalRAM() instead.
    1388             :  *
    1389             :  * @return the total physical RAM in bytes (or 0 in case of failure).
    1390             :  * @since GDAL 2.0
    1391             :  */
    1392        5902 : GIntBig CPLGetPhysicalRAM(void)
    1393             : {
    1394        5902 :     const long nPhysPages = sysconf(_SC_PHYS_PAGES);
    1395        5902 :     const long nPageSize = sysconf(_SC_PAGESIZE);
    1396       11804 :     if (nPhysPages <= 0 || nPageSize <= 0 ||
    1397        5902 :         nPhysPages > std::numeric_limits<GIntBig>::max() / nPageSize)
    1398             :     {
    1399           0 :         return 0;
    1400             :     }
    1401        5902 :     GIntBig nVal = static_cast<GIntBig>(nPhysPages) * nPageSize;
    1402             : 
    1403             : #ifdef __linux
    1404             :     {
    1405             :         // Take into account MemTotal in /proc/meminfo
    1406             :         // which seems to be necessary for some container solutions
    1407             :         // Cf https://lists.osgeo.org/pipermail/gdal-dev/2023-January/056784.html
    1408        5902 :         FILE *f = fopen("/proc/meminfo", "rb");
    1409        5902 :         if (f)
    1410             :         {
    1411             :             char szLine[256];
    1412        5902 :             while (fgets(szLine, sizeof(szLine), f))
    1413             :             {
    1414             :                 // Find line like "MemTotal:       32525176 kB"
    1415        5902 :                 if (strncmp(szLine, "MemTotal:", strlen("MemTotal:")) == 0)
    1416             :                 {
    1417        5902 :                     char *pszVal = szLine + strlen("MemTotal:");
    1418        5902 :                     pszVal += strspn(pszVal, " ");
    1419        5902 :                     char *pszEnd = strstr(pszVal, " kB");
    1420        5902 :                     if (pszEnd)
    1421             :                     {
    1422        5902 :                         *pszEnd = 0;
    1423        5902 :                         if (CPLGetValueType(pszVal) == CPL_VALUE_INTEGER)
    1424             :                         {
    1425             :                             const GUIntBig nLimit =
    1426       11804 :                                 CPLScanUIntBig(
    1427        5902 :                                     pszVal, static_cast<int>(strlen(pszVal))) *
    1428        5902 :                                 1024;
    1429        5902 :                             nVal = static_cast<GIntBig>(
    1430        5902 :                                 std::min(static_cast<GUIntBig>(nVal), nLimit));
    1431             :                         }
    1432             :                     }
    1433        5902 :                     break;
    1434             :                 }
    1435             :             }
    1436        5902 :             fclose(f);
    1437             :         }
    1438             :     }
    1439             : 
    1440             :     char szGroupName[256];
    1441        5902 :     bool bFromMemory = false;
    1442        5902 :     szGroupName[0] = 0;
    1443             :     {
    1444        5902 :         FILE *f = fopen("/proc/self/cgroup", "rb");
    1445        5902 :         if (f)
    1446             :         {
    1447             :             char szLine[256];
    1448             :             // Find line like "6:memory:/user.slice/user-1000.slice/user@1000.service"
    1449             :             // and store "/user.slice/user-1000.slice/user@1000.service" in
    1450             :             // szMemoryPath for cgroup V1 or single line "0::/...." for cgroup V2.
    1451        5902 :             while (fgets(szLine, sizeof(szLine), f))
    1452             :             {
    1453        5902 :                 const char *pszMemory = strstr(szLine, ":memory:");
    1454        5902 :                 if (pszMemory)
    1455             :                 {
    1456           0 :                     bFromMemory = true;
    1457           0 :                     snprintf(szGroupName, sizeof(szGroupName), "%s",
    1458             :                              pszMemory + strlen(":memory:"));
    1459           0 :                     char *pszEOL = strchr(szGroupName, '\n');
    1460           0 :                     if (pszEOL)
    1461           0 :                         *pszEOL = '\0';
    1462           0 :                     break;
    1463             :                 }
    1464        5902 :                 if (strncmp(szLine, "0::", strlen("0::")) == 0)
    1465             :                 {
    1466        5902 :                     snprintf(szGroupName, sizeof(szGroupName), "%s",
    1467             :                              szLine + strlen("0::"));
    1468        5902 :                     char *pszEOL = strchr(szGroupName, '\n');
    1469        5902 :                     if (pszEOL)
    1470        5902 :                         *pszEOL = '\0';
    1471        5902 :                     break;
    1472             :                 }
    1473             :             }
    1474        5902 :             fclose(f);
    1475             :         }
    1476             :     }
    1477        5902 :     if (szGroupName[0])
    1478             :     {
    1479             :         char szFilename[256 + 64];
    1480        5902 :         if (bFromMemory)
    1481             :         {
    1482             :             // cgroup V1
    1483             :             // Read memory.limit_in_byte in the whole szGroupName hierarchy
    1484             :             // Make sure to end up by reading
    1485             :             // /sys/fs/cgroup/memory/memory.limit_in_bytes itself, for
    1486             :             // scenarios like https://github.com/OSGeo/gdal/issues/8968
    1487             :             while (true)
    1488             :             {
    1489           0 :                 snprintf(szFilename, sizeof(szFilename),
    1490             :                          "/sys/fs/cgroup/memory/%s/memory.limit_in_bytes",
    1491             :                          szGroupName);
    1492           0 :                 FILE *f = fopen(szFilename, "rb");
    1493           0 :                 if (f)
    1494             :                 {
    1495             :                     // If no limitation, on 64 bit, 9223372036854771712 is returned.
    1496             :                     char szBuffer[32];
    1497             :                     const int nRead = static_cast<int>(
    1498           0 :                         fread(szBuffer, 1, sizeof(szBuffer) - 1, f));
    1499           0 :                     szBuffer[nRead] = 0;
    1500           0 :                     fclose(f);
    1501           0 :                     const GUIntBig nLimit = CPLScanUIntBig(szBuffer, nRead);
    1502           0 :                     nVal = static_cast<GIntBig>(
    1503           0 :                         std::min(static_cast<GUIntBig>(nVal), nLimit));
    1504             :                 }
    1505           0 :                 char *pszLastSlash = strrchr(szGroupName, '/');
    1506           0 :                 if (!pszLastSlash)
    1507           0 :                     break;
    1508           0 :                 *pszLastSlash = '\0';
    1509           0 :             }
    1510             :         }
    1511             :         else
    1512             :         {
    1513             :             // cgroup V2
    1514             :             // Read memory.max in the whole szGroupName hierarchy
    1515             :             while (true)
    1516             :             {
    1517        5902 :                 snprintf(szFilename, sizeof(szFilename),
    1518             :                          "/sys/fs/cgroup/%s/memory.max", szGroupName);
    1519        5902 :                 FILE *f = fopen(szFilename, "rb");
    1520        5902 :                 if (f)
    1521             :                 {
    1522             :                     // If no limitation, "max" is returned.
    1523             :                     char szBuffer[32];
    1524             :                     int nRead = static_cast<int>(
    1525        5902 :                         fread(szBuffer, 1, sizeof(szBuffer) - 1, f));
    1526        5902 :                     szBuffer[nRead] = 0;
    1527        5902 :                     if (nRead > 0 && szBuffer[nRead - 1] == '\n')
    1528             :                     {
    1529        5902 :                         nRead--;
    1530        5902 :                         szBuffer[nRead] = 0;
    1531             :                     }
    1532        5902 :                     fclose(f);
    1533        5902 :                     if (CPLGetValueType(szBuffer) == CPL_VALUE_INTEGER)
    1534             :                     {
    1535           0 :                         const GUIntBig nLimit = CPLScanUIntBig(szBuffer, nRead);
    1536           0 :                         nVal = static_cast<GIntBig>(
    1537           0 :                             std::min(static_cast<GUIntBig>(nVal), nLimit));
    1538             :                     }
    1539             :                 }
    1540        5902 :                 char *pszLastSlash = strrchr(szGroupName, '/');
    1541        5902 :                 if (!pszLastSlash || pszLastSlash == szGroupName)
    1542             :                     break;
    1543           0 :                 *pszLastSlash = '\0';
    1544           0 :             }
    1545             :         }
    1546             :     }
    1547             : #endif
    1548             : 
    1549        5902 :     return nVal;
    1550             : }
    1551             : 
    1552             : #elif defined(__MACH__) && defined(__APPLE__)
    1553             : 
    1554             : #include <sys/types.h>
    1555             : #include <sys/sysctl.h>
    1556             : 
    1557             : GIntBig CPLGetPhysicalRAM(void)
    1558             : {
    1559             :     GIntBig nPhysMem = 0;
    1560             : 
    1561             :     int mib[2] = {CTL_HW, HW_MEMSIZE};
    1562             :     size_t nLengthRes = sizeof(nPhysMem);
    1563             :     sysctl(mib, CPL_ARRAYSIZE(mib), &nPhysMem, &nLengthRes, nullptr, 0);
    1564             : 
    1565             :     return nPhysMem;
    1566             : }
    1567             : 
    1568             : #elif defined(_WIN32)
    1569             : 
    1570             : // GlobalMemoryStatusEx requires _WIN32_WINNT >= 0x0500.
    1571             : #ifndef _WIN32_WINNT
    1572             : #define _WIN32_WINNT 0x0500
    1573             : #endif
    1574             : #include <windows.h>
    1575             : 
    1576             : GIntBig CPLGetPhysicalRAM(void)
    1577             : {
    1578             :     MEMORYSTATUSEX statex;
    1579             :     statex.ullTotalPhys = 0;
    1580             :     statex.dwLength = sizeof(statex);
    1581             :     GlobalMemoryStatusEx(&statex);
    1582             :     return static_cast<GIntBig>(statex.ullTotalPhys);
    1583             : }
    1584             : 
    1585             : #else
    1586             : 
    1587             : GIntBig CPLGetPhysicalRAM(void)
    1588             : {
    1589             :     static bool bOnce = false;
    1590             :     if (!bOnce)
    1591             :     {
    1592             :         bOnce = true;
    1593             :         CPLDebug("PORT", "No implementation for CPLGetPhysicalRAM()");
    1594             :     }
    1595             :     return 0;
    1596             : }
    1597             : #endif
    1598             : 
    1599             : /************************************************************************/
    1600             : /*                       CPLGetUsablePhysicalRAM()                      */
    1601             : /************************************************************************/
    1602             : 
    1603             : /** Return the total physical RAM, usable by a process, in bytes.
    1604             :  *
    1605             :  * This is the same as CPLGetPhysicalRAM() except it will limit to 2 GB
    1606             :  * for 32 bit processes.
    1607             :  *
    1608             :  * Starting with GDAL 2.4.0, it will also take account resource limits (virtual
    1609             :  * memory) on Posix systems. Starting with GDAL 3.6.1, it will also take into
    1610             :  * account RLIMIT_RSS on Linux.
    1611             :  *
    1612             :  * Note: This memory may already be partly used by other processes.
    1613             :  *
    1614             :  * @return the total physical RAM, usable by a process, in bytes (or 0
    1615             :  * in case of failure).
    1616             :  * @since GDAL 2.0
    1617             :  */
    1618        5901 : GIntBig CPLGetUsablePhysicalRAM(void)
    1619             : {
    1620        5901 :     GIntBig nRAM = CPLGetPhysicalRAM();
    1621             : #if SIZEOF_VOIDP == 4
    1622             :     if (nRAM > INT_MAX)
    1623             :         nRAM = INT_MAX;
    1624             : #endif
    1625             : #if HAVE_GETRLIMIT
    1626             :     struct rlimit sLimit;
    1627             : #if HAVE_RLIMIT_AS
    1628        5901 :     const int res = RLIMIT_AS;
    1629             : #else
    1630             :     // OpenBSD currently doesn't support RLIMIT_AS (mandated by Posix though)
    1631             :     const int res = RLIMIT_DATA;
    1632             : #endif
    1633        5901 :     if (getrlimit(res, &sLimit) == 0 && sLimit.rlim_cur != RLIM_INFINITY &&
    1634           0 :         static_cast<GIntBig>(sLimit.rlim_cur) < nRAM)
    1635             :     {
    1636           0 :         nRAM = static_cast<GIntBig>(sLimit.rlim_cur);
    1637             :     }
    1638             : #ifdef RLIMIT_RSS
    1639             :     // Helps with RSS limit set by the srun utility. Cf
    1640             :     // https://github.com/OSGeo/gdal/issues/6669
    1641        5901 :     if (getrlimit(RLIMIT_RSS, &sLimit) == 0 &&
    1642        5901 :         sLimit.rlim_cur != RLIM_INFINITY &&
    1643           0 :         static_cast<GIntBig>(sLimit.rlim_cur) < nRAM)
    1644             :     {
    1645           0 :         nRAM = static_cast<GIntBig>(sLimit.rlim_cur);
    1646             :     }
    1647             : #endif
    1648             : #endif
    1649        5901 :     return nRAM;
    1650             : }

Generated by: LCOV version 1.14