LCOV - code coverage report
Current view: top level - port - cpl_vsisimple.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 188 265 70.9 %
Date: 2025-10-22 13:51:22 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    34020000 : void *VSICalloc(size_t nCount, size_t nSize)
     435             : {
     436             :     // cppcheck-suppress invalidFunctionArg
     437    34020000 :     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    93797800 : void *VSIMalloc(size_t nSize)
     551             : 
     552             : {
     553    93797800 :     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    23029700 : 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    23029700 :     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   148318000 : 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   148318000 :     if (pData != nullptr)
     879   125137000 :         free(pData);
     880             : #endif
     881   148318000 : }
     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             :  */
     896             : 
     897     3401770 : void *VSIMallocAligned(size_t nAlignment, size_t nSize)
     898             : {
     899             :     // In particular for posix_memalign() where behavior when passing
     900             :     // nSize == 0 is technically implementation defined (Valgrind complains),
     901             :     // so let's always return NULL.
     902     3401770 :     if (nSize == 0)
     903           0 :         return nullptr;
     904             : #if defined(HAVE_POSIX_MEMALIGN) && !defined(DEBUG_VSIMALLOC)
     905     3401770 :     void *pRet = nullptr;
     906     3401770 :     if (posix_memalign(&pRet, nAlignment, nSize) != 0)
     907             :     {
     908           4 :         pRet = nullptr;
     909             :     }
     910     3401770 :     return pRet;
     911             : #elif defined(_WIN32) && !defined(DEBUG_VSIMALLOC)
     912             :     return _aligned_malloc(nSize, nAlignment);
     913             : #else
     914             :     // Check constraints on alignment.
     915             :     if (nAlignment < sizeof(void *) || nAlignment >= 256 ||
     916             :         (nAlignment & (nAlignment - 1)) != 0)
     917             :         return nullptr;
     918             :     // Detect overflow.
     919             :     if (nSize + nAlignment < nSize)
     920             :         return nullptr;
     921             :     GByte *pabyData = static_cast<GByte *>(VSIMalloc(nSize + nAlignment));
     922             :     if (pabyData == nullptr)
     923             :         return nullptr;
     924             :     size_t nShift =
     925             :         nAlignment - (reinterpret_cast<size_t>(pabyData) % nAlignment);
     926             :     GByte *pabyAligned = pabyData + nShift;
     927             :     // Guaranteed to fit on a byte since nAlignment < 256.
     928             :     pabyAligned[-1] = static_cast<GByte>(nShift);
     929             :     return pabyAligned;
     930             : #endif
     931             : }
     932             : 
     933             : /************************************************************************/
     934             : /*                     VSIMallocAlignedAuto()                           */
     935             : /************************************************************************/
     936             : 
     937             : /** Allocates a buffer with an alignment constraint such that it can be
     938             :  * used by the most demanding vector instruction set on that platform.
     939             :  *
     940             :  * The return value must be freed with VSIFreeAligned().
     941             :  *
     942             :  * @param nSize Size of the buffer to allocate.
     943             :  * @return an aligned buffer of size nSize, or NULL
     944             :  */
     945             : 
     946     3401760 : void *VSIMallocAlignedAuto(size_t nSize)
     947             : {
     948             :     // We could potentially dynamically detect the capability of the CPU
     949             :     // but to simplify use 64 for AVX512 requirements (we use only AVX256
     950             :     // currently).
     951     3401760 :     return VSIMallocAligned(64, nSize);
     952             : }
     953             : 
     954             : /************************************************************************/
     955             : /*                        VSIMallocAlignedAutoVerbose()                 */
     956             : /************************************************************************/
     957             : 
     958             : /** See VSIMallocAlignedAuto() */
     959     3401770 : void *VSIMallocAlignedAutoVerbose(size_t nSize, const char *pszFile, int nLine)
     960             : {
     961     3401770 :     void *pRet = VSIMallocAlignedAuto(nSize);
     962     3401760 :     if (pRet == nullptr && nSize != 0)
     963             :     {
     964           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     965             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
     966             :                  pszFile ? pszFile : "(unknown file)", nLine,
     967             :                  static_cast<GUIntBig>(nSize));
     968             :     }
     969     3401760 :     return pRet;
     970             : }
     971             : 
     972             : /************************************************************************/
     973             : /*                        VSIFreeAligned()                              */
     974             : /************************************************************************/
     975             : 
     976             : /** Free a buffer allocated with VSIMallocAligned().
     977             :  *
     978             :  * @param ptr Buffer to free.
     979             :  */
     980             : 
     981     3424360 : void VSIFreeAligned(void *ptr)
     982             : {
     983             : #if defined(HAVE_POSIX_MEMALIGN) && !defined(DEBUG_VSIMALLOC)
     984     3424360 :     free(ptr);
     985             : #elif defined(_WIN32) && !defined(DEBUG_VSIMALLOC)
     986             :     _aligned_free(ptr);
     987             : #else
     988             :     if (ptr == nullptr)
     989             :         return;
     990             :     GByte *pabyAligned = static_cast<GByte *>(ptr);
     991             :     size_t nShift = pabyAligned[-1];
     992             :     VSIFree(pabyAligned - nShift);
     993             : #endif
     994     3424360 : }
     995             : 
     996             : /************************************************************************/
     997             : /*                             VSIStrdup()                              */
     998             : /************************************************************************/
     999             : 
    1000             : /** Analog of strdup(). Use VSIFree() to free */
    1001    35602100 : char *VSIStrdup(const char *pszString)
    1002             : 
    1003             : {
    1004    35602100 :     const size_t nSize = strlen(pszString) + 1;
    1005    35602100 :     char *ptr = static_cast<char *>(VSIMalloc(nSize));
    1006    35602000 :     if (ptr == nullptr)
    1007           0 :         return nullptr;
    1008    35602000 :     memcpy(ptr, pszString, nSize);
    1009    35602000 :     return ptr;
    1010             : }
    1011             : 
    1012             : /************************************************************************/
    1013             : /*                          VSICheckMul2()                              */
    1014             : /************************************************************************/
    1015             : 
    1016             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
    1017      308123 : static size_t VSICheckMul2(size_t mul1, size_t mul2, bool *pbOverflowFlag,
    1018             :                            const char *pszFile, int nLine)
    1019             : {
    1020      308123 :     const size_t res = mul1 * mul2;
    1021      308123 :     if (mul1 != 0)
    1022             :     {
    1023      308118 :         if (res / mul1 == mul2)
    1024             :         {
    1025      308115 :             if (pbOverflowFlag)
    1026      308115 :                 *pbOverflowFlag = FALSE;
    1027      308115 :             return res;
    1028             :         }
    1029             :         else
    1030             :         {
    1031           3 :             if (pbOverflowFlag)
    1032           1 :                 *pbOverflowFlag = TRUE;
    1033           3 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1034             :                      "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
    1035             :                      " * " CPL_FRMT_GUIB,
    1036             :                      pszFile ? pszFile : "(unknown file)", nLine,
    1037             :                      static_cast<GUIntBig>(mul1), static_cast<GUIntBig>(mul2));
    1038             :         }
    1039             :     }
    1040             :     else
    1041             :     {
    1042           5 :         if (pbOverflowFlag)
    1043           8 :             *pbOverflowFlag = FALSE;
    1044             :     }
    1045           9 :     return 0;
    1046             : }
    1047             : 
    1048             : /************************************************************************/
    1049             : /*                          VSICheckMul3()                              */
    1050             : /************************************************************************/
    1051             : 
    1052             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
    1053      193013 : static size_t VSICheckMul3(size_t mul1, size_t mul2, size_t mul3,
    1054             :                            bool *pbOverflowFlag, const char *pszFile, int nLine)
    1055             : {
    1056      193013 :     if (mul1 != 0)
    1057             :     {
    1058      193012 :         const size_t res = mul1 * mul2;
    1059      193012 :         if (res / mul1 == mul2)
    1060             :         {
    1061      193005 :             const size_t res2 = res * mul3;
    1062      193005 :             if (mul3 != 0)
    1063             :             {
    1064      193002 :                 if (res2 / mul3 == res)
    1065             :                 {
    1066      192998 :                     if (pbOverflowFlag)
    1067      192999 :                         *pbOverflowFlag = false;
    1068      192998 :                     return res2;
    1069             :                 }
    1070             :                 else
    1071             :                 {
    1072           4 :                     if (pbOverflowFlag)
    1073           2 :                         *pbOverflowFlag = true;
    1074           4 :                     CPLError(CE_Failure, CPLE_OutOfMemory,
    1075             :                              "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
    1076             :                              " * " CPL_FRMT_GUIB " * " CPL_FRMT_GUIB,
    1077             :                              pszFile ? pszFile : "(unknown file)", nLine,
    1078             :                              static_cast<GUIntBig>(mul1),
    1079             :                              static_cast<GUIntBig>(mul2),
    1080             :                              static_cast<GUIntBig>(mul3));
    1081             :                 }
    1082             :             }
    1083             :             else
    1084             :             {
    1085           3 :                 if (pbOverflowFlag)
    1086           1 :                     *pbOverflowFlag = false;
    1087             :             }
    1088             :         }
    1089             :         else
    1090             :         {
    1091           7 :             if (pbOverflowFlag)
    1092           1 :                 *pbOverflowFlag = true;
    1093           7 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1094             :                      "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
    1095             :                      " * " CPL_FRMT_GUIB " * " CPL_FRMT_GUIB,
    1096             :                      pszFile ? pszFile : "(unknown file)", nLine,
    1097             :                      static_cast<GUIntBig>(mul1), static_cast<GUIntBig>(mul2),
    1098             :                      static_cast<GUIntBig>(mul3));
    1099             :         }
    1100             :     }
    1101             :     else
    1102             :     {
    1103           1 :         if (pbOverflowFlag)
    1104           1 :             *pbOverflowFlag = false;
    1105             :     }
    1106           5 :     return 0;
    1107             : }
    1108             : 
    1109             : /**
    1110             :  VSIMalloc2 allocates (nSize1 * nSize2) bytes.
    1111             :  In case of overflow of the multiplication, or if memory allocation fails, a
    1112             :  NULL pointer is returned and a CE_Failure error is raised with CPLError().
    1113             :  If nSize1 == 0 || nSize2 == 0, a NULL pointer will also be returned.
    1114             :  CPLFree() or VSIFree() can be used to free memory allocated by this function.
    1115             : */
    1116         154 : void CPL_DLL *VSIMalloc2(size_t nSize1, size_t nSize2)
    1117             : {
    1118         154 :     return VSIMalloc2Verbose(nSize1, nSize2, nullptr, 0);
    1119             : }
    1120             : 
    1121             : /**
    1122             :  VSIMalloc3 allocates (nSize1 * nSize2 * nSize3) bytes.
    1123             :  In case of overflow of the multiplication, or if memory allocation fails, a
    1124             :  NULL pointer is returned and a CE_Failure error is raised with CPLError().
    1125             :  If nSize1 == 0 || nSize2 == 0 || nSize3 == 0, a NULL pointer will also be
    1126             :  returned.  CPLFree() or VSIFree() can be used to free memory allocated by this
    1127             :  function.
    1128             : */
    1129         839 : void CPL_DLL *VSIMalloc3(size_t nSize1, size_t nSize2, size_t nSize3)
    1130             : {
    1131         839 :     return VSIMalloc3Verbose(nSize1, nSize2, nSize3, nullptr, 0);
    1132             : }
    1133             : 
    1134             : /************************************************************************/
    1135             : /*                          VSIMallocVerbose()                          */
    1136             : /************************************************************************/
    1137             : 
    1138     8917650 : void *VSIMallocVerbose(size_t nSize, const char *pszFile, int nLine)
    1139             : {
    1140     8917650 :     void *pRet = VSIMalloc(nSize);
    1141     8917670 :     if (pRet == nullptr && nSize != 0)
    1142             :     {
    1143           2 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1144             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1145             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1146             :                  static_cast<GUIntBig>(nSize));
    1147             :     }
    1148     8917640 :     return pRet;
    1149             : }
    1150             : 
    1151             : /************************************************************************/
    1152             : /*                          VSIMalloc2Verbose()                         */
    1153             : /************************************************************************/
    1154             : 
    1155      308133 : void *VSIMalloc2Verbose(size_t nSize1, size_t nSize2, const char *pszFile,
    1156             :                         int nLine)
    1157             : {
    1158      308133 :     bool bOverflowFlag = false;
    1159             :     const size_t nSizeToAllocate =
    1160      308133 :         VSICheckMul2(nSize1, nSize2, &bOverflowFlag, pszFile, nLine);
    1161      308123 :     if (bOverflowFlag || nSizeToAllocate == 0)
    1162          44 :         return nullptr;
    1163             : 
    1164      308079 :     void *pRet = VSIMalloc(nSizeToAllocate);
    1165      308097 :     if (pRet == nullptr)
    1166             :     {
    1167           2 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1168             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1169             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1170             :                  static_cast<GUIntBig>(nSize1) * static_cast<GUIntBig>(nSize2));
    1171             :     }
    1172      308098 :     return pRet;
    1173             : }
    1174             : 
    1175             : /************************************************************************/
    1176             : /*                          VSIMalloc3Verbose()                         */
    1177             : /************************************************************************/
    1178             : 
    1179      193011 : void *VSIMalloc3Verbose(size_t nSize1, size_t nSize2, size_t nSize3,
    1180             :                         const char *pszFile, int nLine)
    1181             : {
    1182      193011 :     bool bOverflowFlag = false;
    1183             :     size_t nSizeToAllocate =
    1184      193011 :         VSICheckMul3(nSize1, nSize2, nSize3, &bOverflowFlag, pszFile, nLine);
    1185      193000 :     if (bOverflowFlag || nSizeToAllocate == 0)
    1186           5 :         return nullptr;
    1187             : 
    1188      192995 :     void *pRet = VSIMalloc(nSizeToAllocate);
    1189      193008 :     if (pRet == nullptr)
    1190             :     {
    1191           4 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1192             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1193             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1194           4 :                  static_cast<GUIntBig>(nSize1) * static_cast<GUIntBig>(nSize2) *
    1195             :                      static_cast<GUIntBig>(nSize3));
    1196             :     }
    1197      193009 :     return pRet;
    1198             : }
    1199             : 
    1200             : /************************************************************************/
    1201             : /*                          VSICallocVerbose()                          */
    1202             : /************************************************************************/
    1203             : 
    1204     9692290 : void *VSICallocVerbose(size_t nCount, size_t nSize, const char *pszFile,
    1205             :                        int nLine)
    1206             : {
    1207     9692290 :     void *pRet = VSICalloc(nCount, nSize);
    1208     9692200 :     if (pRet == nullptr && nCount != 0 && nSize != 0)
    1209             :     {
    1210           5 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1211             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB "x" CPL_FRMT_GUIB
    1212             :                  " bytes",
    1213             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1214             :                  static_cast<GUIntBig>(nCount), static_cast<GUIntBig>(nSize));
    1215             :     }
    1216     9692200 :     return pRet;
    1217             : }
    1218             : 
    1219             : /************************************************************************/
    1220             : /*                          VSIReallocVerbose()                         */
    1221             : /************************************************************************/
    1222             : 
    1223    17821800 : void *VSIReallocVerbose(void *pOldPtr, size_t nNewSize, const char *pszFile,
    1224             :                         int nLine)
    1225             : {
    1226    17821800 :     void *pRet = VSIRealloc(pOldPtr, nNewSize);
    1227    17821800 :     if (pRet == nullptr && nNewSize != 0)
    1228             :     {
    1229           2 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1230             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1231             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1232             :                  static_cast<GUIntBig>(nNewSize));
    1233             :     }
    1234    17821800 :     return pRet;
    1235             : }
    1236             : 
    1237             : /************************************************************************/
    1238             : /*                          VSIStrdupVerbose()                          */
    1239             : /************************************************************************/
    1240             : 
    1241    11713900 : char *VSIStrdupVerbose(const char *pszStr, const char *pszFile, int nLine)
    1242             : {
    1243    11713900 :     char *pRet = VSIStrdup(pszStr);
    1244    11713800 :     if (pRet == nullptr)
    1245             :     {
    1246           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1247             :                  "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
    1248             :                  pszFile ? pszFile : "(unknown file)", nLine,
    1249           0 :                  static_cast<GUIntBig>(strlen(pszStr) + 1));
    1250             :     }
    1251    11713800 :     return pRet;
    1252             : }
    1253             : 
    1254             : /************************************************************************/
    1255             : /*                              VSIStat()                               */
    1256             : /************************************************************************/
    1257             : 
    1258         945 : int VSIStat(const char *pszFilename, VSIStatBuf *pStatBuf)
    1259             : 
    1260             : {
    1261             : #if defined(_WIN32)
    1262             :     if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
    1263             :     {
    1264             :         wchar_t *pwszFilename =
    1265             :             CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
    1266             : 
    1267             :         int nResult =
    1268             :             _wstat(pwszFilename, reinterpret_cast<struct _stat *>(pStatBuf));
    1269             : 
    1270             :         CPLFree(pwszFilename);
    1271             : 
    1272             :         return nResult;
    1273             :     }
    1274             : 
    1275             : #endif
    1276         945 :     return stat(pszFilename, pStatBuf);
    1277             : }
    1278             : 
    1279             : /************************************************************************/
    1280             : /*                              VSITime()                               */
    1281             : /************************************************************************/
    1282             : 
    1283           0 : unsigned long VSITime(unsigned long *pnTimeToSet)
    1284             : 
    1285             : {
    1286             :     time_t tTime;
    1287             : 
    1288           0 :     tTime = time(nullptr);
    1289             : 
    1290           0 :     if (pnTimeToSet != nullptr)
    1291           0 :         *pnTimeToSet = static_cast<unsigned long>(tTime);
    1292             : 
    1293           0 :     return static_cast<unsigned long>(tTime);
    1294             : }
    1295             : 
    1296             : /************************************************************************/
    1297             : /*                              VSICTime()                              */
    1298             : /************************************************************************/
    1299             : 
    1300           6 : const char *VSICTime(unsigned long nTime)
    1301             : 
    1302             : {
    1303           6 :     time_t tTime = static_cast<time_t>(nTime);
    1304             : #if HAVE_CTIME_R
    1305             :     // Cf https://linux.die.net/man/3/ctime_r:
    1306             :     // "The reentrant version ctime_r() does the same, but stores the string in
    1307             :     // a user-supplied buffer which should have room for at least 26 bytes"
    1308             :     char buffer[26];
    1309           6 :     char *ret = ctime_r(&tTime, buffer);
    1310           6 :     return ret ? CPLSPrintf("%s", ret) : nullptr;
    1311             : #elif defined(_WIN32)
    1312             :     char buffer[26];
    1313             :     return ctime_s(buffer, sizeof(buffer), &tTime) == 0
    1314             :                ? CPLSPrintf("%s", buffer)
    1315             :                : nullptr;
    1316             : #else
    1317             :     return reinterpret_cast<const char *>(ctime(&tTime));
    1318             : #endif
    1319             : }
    1320             : 
    1321             : /************************************************************************/
    1322             : /*                             VSIGMTime()                              */
    1323             : /************************************************************************/
    1324             : 
    1325           0 : struct tm *VSIGMTime(const time_t *pnTime, struct tm *poBrokenTime)
    1326             : {
    1327             : 
    1328             : #if HAVE_GMTIME_R
    1329           0 :     gmtime_r(pnTime, poBrokenTime);
    1330           0 :     return poBrokenTime;
    1331             : #elif defined(_WIN32)
    1332             :     return gmtime_s(poBrokenTime, pnTime) == 0 ? poBrokenTime : nullptr;
    1333             : #else
    1334             :     struct tm *poTime = gmtime(pnTime);
    1335             :     memcpy(poBrokenTime, poTime, sizeof(tm));
    1336             :     return poBrokenTime;
    1337             : #endif
    1338             : }
    1339             : 
    1340             : /************************************************************************/
    1341             : /*                             VSILocalTime()                           */
    1342             : /************************************************************************/
    1343             : 
    1344         536 : struct tm *VSILocalTime(const time_t *pnTime, struct tm *poBrokenTime)
    1345             : {
    1346             : 
    1347             : #if HAVE_LOCALTIME_R
    1348         536 :     localtime_r(pnTime, poBrokenTime);
    1349         536 :     return poBrokenTime;
    1350             : #elif defined(_WIN32)
    1351             :     return localtime_s(poBrokenTime, pnTime) == 0 ? poBrokenTime : nullptr;
    1352             : #else
    1353             :     struct tm *poTime = localtime(pnTime);
    1354             :     memcpy(poBrokenTime, poTime, sizeof(tm));
    1355             :     return poBrokenTime;
    1356             : #endif
    1357             : }
    1358             : 
    1359             : /************************************************************************/
    1360             : /*                            VSIStrerror()                             */
    1361             : /************************************************************************/
    1362             : 
    1363             : /** Return the error string corresponding to the error number. Do not free it */
    1364         150 : char *VSIStrerror(int nErrno)
    1365             : 
    1366             : {
    1367         150 :     return strerror(nErrno);
    1368             : }
    1369             : 
    1370             : /************************************************************************/
    1371             : /*                        CPLGetPhysicalRAM()                           */
    1372             : /************************************************************************/
    1373             : 
    1374             : #if HAVE_SC_PHYS_PAGES
    1375             : 
    1376             : /** Return the total physical RAM in bytes.
    1377             :  *
    1378             :  * In the context of a container using cgroups (typically Docker), this
    1379             :  * will take into account that limitation (with extra fixes in GDAL 3.6.3)
    1380             :  *
    1381             :  * You should generally use CPLGetUsablePhysicalRAM() instead.
    1382             :  *
    1383             :  * @return the total physical RAM in bytes (or 0 in case of failure).
    1384             :  */
    1385        9153 : GIntBig CPLGetPhysicalRAM(void)
    1386             : {
    1387        1184 :     static const GIntBig nPhysicalRAM = []() -> GIntBig
    1388             :     {
    1389        1184 :         const long nPhysPages = sysconf(_SC_PHYS_PAGES);
    1390        1184 :         const long nPageSize = sysconf(_SC_PAGESIZE);
    1391        2368 :         if (nPhysPages <= 0 || nPageSize <= 0 ||
    1392        1184 :             nPhysPages > std::numeric_limits<GIntBig>::max() / nPageSize)
    1393             :         {
    1394           0 :             return 0;
    1395             :         }
    1396             : 
    1397        1184 :         GIntBig nVal = static_cast<GIntBig>(nPhysPages) * nPageSize;
    1398             : 
    1399             : #ifdef __linux
    1400             :         {
    1401             :             // Take into account MemTotal in /proc/meminfo
    1402             :             // which seems to be necessary for some container solutions
    1403             :             // Cf https://lists.osgeo.org/pipermail/gdal-dev/2023-January/056784.html
    1404        1184 :             FILE *f = fopen("/proc/meminfo", "rb");
    1405        1184 :             if (f)
    1406             :             {
    1407             :                 char szLine[256];
    1408        1184 :                 while (fgets(szLine, sizeof(szLine), f))
    1409             :                 {
    1410             :                     // Find line like "MemTotal:       32525176 kB"
    1411        1184 :                     if (strncmp(szLine, "MemTotal:", strlen("MemTotal:")) == 0)
    1412             :                     {
    1413        1184 :                         char *pszVal = szLine + strlen("MemTotal:");
    1414        1184 :                         pszVal += strspn(pszVal, " ");
    1415        1184 :                         char *pszEnd = strstr(pszVal, " kB");
    1416        1184 :                         if (pszEnd)
    1417             :                         {
    1418        1184 :                             *pszEnd = 0;
    1419        1184 :                             if (CPLGetValueType(pszVal) == CPL_VALUE_INTEGER)
    1420             :                             {
    1421             :                                 const GUIntBig nLimit =
    1422        2368 :                                     CPLScanUIntBig(
    1423             :                                         pszVal,
    1424        1184 :                                         static_cast<int>(strlen(pszVal))) *
    1425        1184 :                                     1024;
    1426        1184 :                                 nVal = static_cast<GIntBig>(std::min(
    1427        1184 :                                     static_cast<GUIntBig>(nVal), nLimit));
    1428             :                             }
    1429             :                         }
    1430        1184 :                         break;
    1431             :                     }
    1432             :                 }
    1433        1184 :                 fclose(f);
    1434             :             }
    1435             :         }
    1436             : 
    1437             :         char szGroupName[256];
    1438        1184 :         bool bFromMemory = false;
    1439        1184 :         szGroupName[0] = 0;
    1440             :         {
    1441        1184 :             FILE *f = fopen("/proc/self/cgroup", "rb");
    1442        1184 :             if (f)
    1443             :             {
    1444             :                 char szLine[256];
    1445             :                 // Find line like "6:memory:/user.slice/user-1000.slice/user@1000.service"
    1446             :                 // and store "/user.slice/user-1000.slice/user@1000.service" in
    1447             :                 // szMemoryPath for cgroup V1 or single line "0::/...." for cgroup V2.
    1448        1184 :                 while (fgets(szLine, sizeof(szLine), f))
    1449             :                 {
    1450        1184 :                     const char *pszMemory = strstr(szLine, ":memory:");
    1451        1184 :                     if (pszMemory)
    1452             :                     {
    1453           0 :                         bFromMemory = true;
    1454           0 :                         snprintf(szGroupName, sizeof(szGroupName), "%s",
    1455             :                                  pszMemory + strlen(":memory:"));
    1456           0 :                         char *pszEOL = strchr(szGroupName, '\n');
    1457           0 :                         if (pszEOL)
    1458           0 :                             *pszEOL = '\0';
    1459           0 :                         break;
    1460             :                     }
    1461        1184 :                     if (strncmp(szLine, "0::", strlen("0::")) == 0)
    1462             :                     {
    1463        1184 :                         snprintf(szGroupName, sizeof(szGroupName), "%s",
    1464             :                                  szLine + strlen("0::"));
    1465        1184 :                         char *pszEOL = strchr(szGroupName, '\n');
    1466        1184 :                         if (pszEOL)
    1467        1184 :                             *pszEOL = '\0';
    1468        1184 :                         break;
    1469             :                     }
    1470             :                 }
    1471        1184 :                 fclose(f);
    1472             :             }
    1473             :         }
    1474        1184 :         if (szGroupName[0])
    1475             :         {
    1476             :             char szFilename[256 + 64];
    1477        1184 :             if (bFromMemory)
    1478             :             {
    1479             :                 // cgroup V1
    1480             :                 // Read memory.limit_in_byte in the whole szGroupName hierarchy
    1481             :                 // Make sure to end up by reading
    1482             :                 // /sys/fs/cgroup/memory/memory.limit_in_bytes itself, for
    1483             :                 // scenarios like https://github.com/OSGeo/gdal/issues/8968
    1484             :                 while (true)
    1485             :                 {
    1486           0 :                     snprintf(szFilename, sizeof(szFilename),
    1487             :                              "/sys/fs/cgroup/memory/%s/memory.limit_in_bytes",
    1488             :                              szGroupName);
    1489           0 :                     FILE *f = fopen(szFilename, "rb");
    1490           0 :                     if (f)
    1491             :                     {
    1492             :                         // If no limitation, on 64 bit, 9223372036854771712 is returned.
    1493             :                         char szBuffer[32];
    1494             :                         const int nRead = static_cast<int>(
    1495           0 :                             fread(szBuffer, 1, sizeof(szBuffer) - 1, f));
    1496           0 :                         szBuffer[nRead] = 0;
    1497           0 :                         fclose(f);
    1498           0 :                         const GUIntBig nLimit = CPLScanUIntBig(szBuffer, nRead);
    1499           0 :                         nVal = static_cast<GIntBig>(
    1500           0 :                             std::min(static_cast<GUIntBig>(nVal), nLimit));
    1501             :                     }
    1502           0 :                     char *pszLastSlash = strrchr(szGroupName, '/');
    1503           0 :                     if (!pszLastSlash)
    1504           0 :                         break;
    1505           0 :                     *pszLastSlash = '\0';
    1506           0 :                 }
    1507             :             }
    1508             :             else
    1509             :             {
    1510             :                 // cgroup V2
    1511             :                 // Read memory.max in the whole szGroupName hierarchy
    1512             :                 while (true)
    1513             :                 {
    1514        1184 :                     snprintf(szFilename, sizeof(szFilename),
    1515             :                              "/sys/fs/cgroup/%s/memory.max", szGroupName);
    1516        1184 :                     FILE *f = fopen(szFilename, "rb");
    1517        1184 :                     if (f)
    1518             :                     {
    1519             :                         // If no limitation, "max" is returned.
    1520             :                         char szBuffer[32];
    1521             :                         int nRead = static_cast<int>(
    1522        1184 :                             fread(szBuffer, 1, sizeof(szBuffer) - 1, f));
    1523        1184 :                         szBuffer[nRead] = 0;
    1524        1184 :                         if (nRead > 0 && szBuffer[nRead - 1] == '\n')
    1525             :                         {
    1526        1184 :                             nRead--;
    1527        1184 :                             szBuffer[nRead] = 0;
    1528             :                         }
    1529        1184 :                         fclose(f);
    1530        1184 :                         if (CPLGetValueType(szBuffer) == CPL_VALUE_INTEGER)
    1531             :                         {
    1532             :                             const GUIntBig nLimit =
    1533           0 :                                 CPLScanUIntBig(szBuffer, nRead);
    1534           0 :                             nVal = static_cast<GIntBig>(
    1535           0 :                                 std::min(static_cast<GUIntBig>(nVal), nLimit));
    1536             :                         }
    1537             :                     }
    1538        1184 :                     char *pszLastSlash = strrchr(szGroupName, '/');
    1539        1184 :                     if (!pszLastSlash || pszLastSlash == szGroupName)
    1540             :                         break;
    1541           0 :                     *pszLastSlash = '\0';
    1542           0 :                 }
    1543             :             }
    1544             :         }
    1545             : #endif
    1546             : 
    1547        1184 :         return nVal;
    1548        9153 :     }();
    1549             : 
    1550        9153 :     return nPhysicalRAM;
    1551             : }
    1552             : 
    1553             : #elif defined(__MACH__) && defined(__APPLE__)
    1554             : 
    1555             : #include <sys/types.h>
    1556             : #include <sys/sysctl.h>
    1557             : 
    1558             : GIntBig CPLGetPhysicalRAM(void)
    1559             : {
    1560             :     GIntBig nPhysMem = 0;
    1561             : 
    1562             :     int mib[2] = {CTL_HW, HW_MEMSIZE};
    1563             :     size_t nLengthRes = sizeof(nPhysMem);
    1564             :     sysctl(mib, CPL_ARRAYSIZE(mib), &nPhysMem, &nLengthRes, nullptr, 0);
    1565             : 
    1566             :     return nPhysMem;
    1567             : }
    1568             : 
    1569             : #elif defined(_WIN32)
    1570             : 
    1571             : // GlobalMemoryStatusEx requires _WIN32_WINNT >= 0x0500.
    1572             : #ifndef _WIN32_WINNT
    1573             : #define _WIN32_WINNT 0x0500
    1574             : #endif
    1575             : #include <windows.h>
    1576             : 
    1577             : GIntBig CPLGetPhysicalRAM(void)
    1578             : {
    1579             :     MEMORYSTATUSEX statex;
    1580             :     statex.ullTotalPhys = 0;
    1581             :     statex.dwLength = sizeof(statex);
    1582             :     GlobalMemoryStatusEx(&statex);
    1583             :     return static_cast<GIntBig>(statex.ullTotalPhys);
    1584             : }
    1585             : 
    1586             : #else
    1587             : 
    1588             : GIntBig CPLGetPhysicalRAM(void)
    1589             : {
    1590             :     static bool bOnce = false;
    1591             :     if (!bOnce)
    1592             :     {
    1593             :         bOnce = true;
    1594             :         CPLDebug("PORT", "No implementation for CPLGetPhysicalRAM()");
    1595             :     }
    1596             :     return 0;
    1597             : }
    1598             : #endif
    1599             : 
    1600             : /************************************************************************/
    1601             : /*                       CPLGetUsablePhysicalRAM()                      */
    1602             : /************************************************************************/
    1603             : 
    1604             : /** Return the total physical RAM, usable by a process, in bytes.
    1605             :  *
    1606             :  * This is the same as CPLGetPhysicalRAM() except it will limit to 2 GB
    1607             :  * for 32 bit processes.
    1608             :  *
    1609             :  * Starting with GDAL 2.4.0, it will also take account resource limits (virtual
    1610             :  * memory) on Posix systems. Starting with GDAL 3.6.1, it will also take into
    1611             :  * account RLIMIT_RSS on Linux.
    1612             :  *
    1613             :  * Note: This memory may already be partly used by other processes.
    1614             :  *
    1615             :  * @return the total physical RAM, usable by a process, in bytes (or 0
    1616             :  * in case of failure).
    1617             :  */
    1618        9152 : GIntBig CPLGetUsablePhysicalRAM(void)
    1619             : {
    1620        9152 :     GIntBig nRAM = CPLGetPhysicalRAM();
    1621             : #if SIZEOF_VOIDP == 4
    1622             :     if (nRAM > INT_MAX)
    1623             :         nRAM = INT_MAX;
    1624             : #endif
    1625             : #if HAVE_GETRLIMIT
    1626             :     struct rlimit sLimit;
    1627             : #if HAVE_RLIMIT_AS
    1628        9152 :     const int res = RLIMIT_AS;
    1629             : #else
    1630             :     // OpenBSD currently doesn't support RLIMIT_AS (mandated by Posix though)
    1631             :     const int res = RLIMIT_DATA;
    1632             : #endif
    1633        9152 :     if (getrlimit(res, &sLimit) == 0 && sLimit.rlim_cur != RLIM_INFINITY &&
    1634           0 :         static_cast<GIntBig>(sLimit.rlim_cur) < nRAM)
    1635             :     {
    1636           0 :         nRAM = static_cast<GIntBig>(sLimit.rlim_cur);
    1637             :     }
    1638             : #ifdef RLIMIT_RSS
    1639             :     // Helps with RSS limit set by the srun utility. Cf
    1640             :     // https://github.com/OSGeo/gdal/issues/6669
    1641        9152 :     if (getrlimit(RLIMIT_RSS, &sLimit) == 0 &&
    1642        9152 :         sLimit.rlim_cur != RLIM_INFINITY &&
    1643           0 :         static_cast<GIntBig>(sLimit.rlim_cur) < nRAM)
    1644             :     {
    1645           0 :         nRAM = static_cast<GIntBig>(sLimit.rlim_cur);
    1646             :     }
    1647             : #endif
    1648             : #endif
    1649        9152 :     return nRAM;
    1650             : }

Generated by: LCOV version 1.14