LCOV - code coverage report
Current view: top level - port - cpl_vsisimple.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 186 265 70.2 %
Date: 2025-09-10 17:48:50 Functions: 29 43 67.4 %

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

Generated by: LCOV version 1.14