LCOV - code coverage report
Current view: top level - port - cpl_vsisimple.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 174 257 67.7 %
Date: 2024-04-28 23:18:46 Functions: 27 42 64.3 %

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

Generated by: LCOV version 1.14