• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 16038479760

03 Jul 2025 12:12AM UTC coverage: 71.106% (-0.004%) from 71.11%
16038479760

Pull #12692

github

web-flow
Merge efeee3602 into b5d2a80d4
Pull Request #12692: C/C++/Python band algebra: add gdal.abs(), sqrt(), log(), log10() and pow()

80 of 87 new or added lines in 3 files covered. (91.95%)

8848 existing lines in 54 files now uncovered.

574863 of 808463 relevant lines covered (71.11%)

255001.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.68
/port/cpl_vsisimple.cpp
1
/******************************************************************************
2
 *
3
 * Project:  Common Portability Library
4
 * Purpose:  Simple implementation of POSIX VSI functions.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1998, Frank Warmerdam
9
 * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************
13
 *
14
 * NB: Note that in wrappers we are always saving the error state (errno
15
 * variable) to avoid side effects during debug prints or other possible
16
 * standard function calls (error states will be overwritten after such
17
 * a call).
18
 *
19
 ****************************************************************************/
20

21
// Must be done before including cpl_port.h
22
// We need __MSVCRT_VERSION__ >= 0x0700 to have "_aligned_malloc"
23
// Latest versions of mingw32 define it, but with older ones,
24
// we need to define it manually.
25
#if defined(__MINGW32__)
26
#include <windows.h>
27
#ifndef __MSVCRT_VERSION__
28
#define __MSVCRT_VERSION__ 0x0700
29
#endif
30
#endif
31

32
#include "cpl_port.h"
33
#include "cpl_vsi.h"
34

35
#include <algorithm>
36
#include <cerrno>
37
#include <cstdarg>
38
#include <cstddef>
39
#include <cstdio>
40
#include <cstdlib>
41
#include <cstring>
42
#include <ctime>
43
#include <limits>
44
#if HAVE_SYS_STAT_H
45
#include <sys/stat.h>
46
#endif
47
#if HAVE_GETRLIMIT
48
#include <sys/time.h>
49
#include <sys/resource.h>
50
#endif
51

52
#include "cpl_config.h"
53
#include "cpl_error.h"
54
#include "cpl_string.h"
55

56
#ifdef _WIN32
57
#include <malloc.h>  // For _aligned_malloc
58
#endif
59

60
// Uncomment to check consistent usage of VSIMalloc(), VSIRealloc(),
61
// VSICalloc(), VSIFree(), VSIStrdup().
62
// #define DEBUG_VSIMALLOC
63

64
// Uncomment to compute memory usage statistics.
65
// DEBUG_VSIMALLOC must also be defined.
66
// #define DEBUG_VSIMALLOC_STATS
67

68
// Highly experimental, and likely buggy. Do not use, except for fixing it!
69
// DEBUG_VSIMALLOC must also be defined.
70
// #define DEBUG_VSIMALLOC_MPROTECT
71

72
#ifdef DEBUG_VSIMALLOC_MPROTECT
73
#include <sys/mman.h>
74
#endif
75

76
// Uncomment to print every memory allocation or deallocation.
77
// DEBUG_VSIMALLOC must also be defined.
78
// #define DEBUG_VSIMALLOC_VERBOSE
79

80
// Number of bytes of the malloc/calloc/free that triggers a debug trace.
81
// Can be 0 for all allocs.
82
#define THRESHOLD_PRINT 10000
83

84
// Uncomment to print GDAL block cache use.
85
// Only used if DEBUG_VSIMALLOC_VERBOSE is enabled.
86
// #define DEBUG_BLOCK_CACHE_USE
87

88
#ifdef DEBUG_BLOCK_CACHE_USE
89
extern "C" GIntBig CPL_DLL CPL_STDCALL GDALGetCacheUsed64(void);
90
#endif
91

92
/* Unix or Windows NT/2000/XP */
93
#if !defined(_WIN32)
94
#include <unistd.h>
95
#else
96
#include <io.h>
97
#include <fcntl.h>
98
#include <direct.h>
99
#endif
100

101
/************************************************************************/
102
/*                              VSIFOpen()                              */
103
/************************************************************************/
104

105
FILE *VSIFOpen(const char *pszFilename, const char *pszAccess)
33✔
106

107
{
108
#if defined(_WIN32)
109
    FILE *fp = nullptr;
110
    if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
111
    {
112
        wchar_t *pwszFilename =
113
            CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
114
        wchar_t *pwszAccess =
115
            CPLRecodeToWChar(pszAccess, CPL_ENC_UTF8, CPL_ENC_UCS2);
116

117
        fp = _wfopen(pwszFilename, pwszAccess);
118

119
        CPLFree(pwszFilename);
120
        CPLFree(pwszAccess);
121
    }
122
    else
123
    {
124
        // Are the casts really necessary?
125
        fp = fopen(const_cast<char *>(pszFilename),
126
                   const_cast<char *>(pszAccess));
127
    }
128
#else
129
    FILE *fp = fopen(pszFilename, pszAccess);
33✔
130
#endif
131

132
#ifdef VSI_DEBUG
133
    // Capture the error from fopen to avoid being overwritten by errors
134
    // from VSIDebug3.
135
    const int nError = errno;
136
    VSIDebug3("VSIFOpen(%s,%s) = %p", pszFilename, pszAccess, fp);
137
    errno = nError;
138
#endif
139

140
    return fp;
33✔
141
}
142

143
/************************************************************************/
144
/*                             VSIFClose()                              */
145
/************************************************************************/
146

147
int VSIFClose(FILE *fp)
33✔
148

149
{
150
    VSIDebug1("VSIClose(%p)", fp);
151

152
    return fclose(fp);
33✔
153
}
154

155
/************************************************************************/
156
/*                              VSIFSeek()                              */
157
/************************************************************************/
158

159
int VSIFSeek(FILE *fp, long nOffset, int nWhence)
×
160

161
{
162
#ifdef DEBUG
163
    // To workaround Coverity strange warning about potential negative seek
164
    // CID 1340084 when called from dgnwrite.cpp.
165
    if (nWhence == SEEK_SET && nOffset < 0)
×
166
        return -1;
×
167
#endif
168
    int nResult = fseek(fp, nOffset, nWhence);
×
169

170
#ifdef VSI_DEBUG
171
    // Capture the error from fseek to avoid being overwritten by errors
172
    // from VSIDebug.
173
    const int nError = errno;
174

175
    if (nWhence == SEEK_SET)
176
    {
177
        VSIDebug3("VSIFSeek(%p,%ld,SEEK_SET) = %d", fp, nOffset, nResult);
178
    }
179
    else if (nWhence == SEEK_END)
180
    {
181
        VSIDebug3("VSIFSeek(%p,%ld,SEEK_END) = %d", fp, nOffset, nResult);
182
    }
183
    else if (nWhence == SEEK_CUR)
184
    {
185
        VSIDebug3("VSIFSeek(%p,%ld,SEEK_CUR) = %d", fp, nOffset, nResult);
186
    }
187
    else
188
    {
189
        VSIDebug4("VSIFSeek(%p,%ld,%d-Unknown) = %d", fp, nOffset, nWhence,
190
                  nResult);
191
    }
192

193
    errno = nError;
194
#endif
195

196
    return nResult;
×
197
}
198

199
/************************************************************************/
200
/*                              VSIFTell()                              */
201
/************************************************************************/
202

203
long VSIFTell(FILE *fp)
×
204

205
{
206
    const long nOffset = ftell(fp);
×
207

208
#ifdef VSI_DEBUG
209
    // Capture the error from ftell to avoid being overwritten by errors
210
    // from VSIDebug.
211
    const int nError = errno;
212
    VSIDebug2("VSIFTell(%p) = %ld", fp, nOffset);
213
    errno = nError;
214
#endif
215

216
    return nOffset;
×
217
}
218

219
/************************************************************************/
220
/*                             VSIRewind()                              */
221
/************************************************************************/
222

223
void VSIRewind(FILE *fp)
×
224

225
{
226
    VSIDebug1("VSIRewind(%p)", fp);
227
    rewind(fp);
×
228
#ifdef VSI_DEBUG
229
    // Capture the error rewind ftell to avoid being overwritten by errors
230
    // from VSIDebug.
231
    const int nError = errno;
232
    VSIDebug2("VSIRewind(%p) errno = %d", fp, nError);
233
    errno = nError;
234
#endif
235
}
×
236

237
/************************************************************************/
238
/*                              VSIFRead()                              */
239
/************************************************************************/
240

241
size_t VSIFRead(void *pBuffer, size_t nSize, size_t nCount, FILE *fp)
×
242

243
{
244
    const size_t nResult = fread(pBuffer, nSize, nCount, fp);
×
245

246
#ifdef VSI_DEBUG
247
    // Capture the error from fread to avoid being overwritten by errors
248
    // from VSIDebug.
249
    const int nError = errno;
250
    VSIDebug4("VSIFRead(%p,%ld,%ld) = %ld", fp, static_cast<long>(nSize),
251
              static_cast<long>(nCount), static_cast<long>(nResult));
252
    errno = nError;
253
#endif
254

255
    return nResult;
×
256
}
257

258
/************************************************************************/
259
/*                             VSIFWrite()                              */
260
/************************************************************************/
261

262
size_t VSIFWrite(const void *pBuffer, size_t nSize, size_t nCount, FILE *fp)
33✔
263

264
{
265
    const size_t nResult = fwrite(pBuffer, nSize, nCount, fp);
33✔
266

267
#ifdef VSI_DEBUG
268
    // Capture the error from fwrite to avoid being overwritten by errors
269
    // from VSIDebug.
270
    const int nError = errno;
271
    VSIDebug4("VSIFWrite(%p,%ld,%ld) = %ld", fp, static_cast<long>(nSize),
272
              static_cast<long>(nCount), static_cast<long>(nResult));
273
    errno = nError;
274
#endif
275

276
    return nResult;
33✔
277
}
278

279
/************************************************************************/
280
/*                             VSIFFlush()                              */
281
/************************************************************************/
282

283
void VSIFFlush(FILE *fp)
×
284

285
{
286
#ifdef VSI_DEBUG
287
    VSIDebug1("VSIFFlush(%p)", fp);
288
    const int result =
289
#endif
290
        fflush(fp);
×
291

292
#ifdef VSI_DEBUG
293
    // Capture the error rewind ftell to avoid being overwritten by errors
294
    // from VSIDebug.
295
    const int nError = errno;
296
    VSIDebug2("VSIRewind(%p) errno = %d", fp, nError);
297
    if (result != 0)
298
    {
299
        CPLError(CE_Failure, CPLE_FileIO, "Flush failed.  errno = %d", nError);
300
    }
301
    errno = nError;
302
#endif
303
}
×
304

305
/************************************************************************/
306
/*                              VSIFGets()                              */
307
/************************************************************************/
308

309
char *VSIFGets(char *pszBuffer, int nBufferSize, FILE *fp)
×
310

311
{
312
    return fgets(pszBuffer, nBufferSize, fp);
×
313
}
314

315
/************************************************************************/
316
/*                              VSIFGetc()                              */
317
/************************************************************************/
318

319
int VSIFGetc(FILE *fp)
×
320

321
{
322
    return fgetc(fp);
×
323
}
324

325
/************************************************************************/
326
/*                             VSIUngetc()                              */
327
/************************************************************************/
328

329
int VSIUngetc(int c, FILE *fp)
×
330

331
{
332
    return ungetc(c, fp);
×
333
}
334

335
/************************************************************************/
336
/*                             VSIFPrintf()                             */
337
/*                                                                      */
338
/*      This is a little more complicated than just calling             */
339
/*      fprintf() because of the variable arguments.  Instead we        */
340
/*      have to use vfprintf().                                         */
341
/************************************************************************/
342

343
int VSIFPrintf(FILE *fp, CPL_FORMAT_STRING(const char *pszFormat), ...)
×
344

345
{
346
    va_list args;
347

348
    va_start(args, pszFormat);
×
349
    const int nReturn = vfprintf(fp, pszFormat, args);
×
350
    va_end(args);
×
351

352
    return nReturn;
×
353
}
354

355
/************************************************************************/
356
/*                              VSIFEof()                               */
357
/************************************************************************/
358

359
int VSIFEof(FILE *fp)
×
360

361
{
362
    return feof(fp);
×
363
}
364

365
/************************************************************************/
366
/*                              VSIFPuts()                              */
367
/************************************************************************/
368

369
int VSIFPuts(const char *pszString, FILE *fp)
×
370

371
{
372
    return fputs(pszString, fp);
×
373
}
374

375
/************************************************************************/
376
/*                              VSIFPutc()                              */
377
/************************************************************************/
378

379
int VSIFPutc(int nChar, FILE *fp)
×
380

381
{
382
    return fputc(nChar, fp);
×
383
}
384

385
#ifdef DEBUG_VSIMALLOC_STATS
386
#include "cpl_multiproc.h"
387

388
static CPLMutex *hMemStatMutex = nullptr;
389
static size_t nCurrentTotalAllocs = 0;
390
static size_t nMaxTotalAllocs = 0;
391
static GUIntBig nVSIMallocs = 0;
392
static GUIntBig nVSICallocs = 0;
393
static GUIntBig nVSIReallocs = 0;
394
static GUIntBig nVSIFrees = 0;
395

396
/************************************************************************/
397
/*                         VSIShowMemStats()                            */
398
/************************************************************************/
399

400
void VSIShowMemStats();
401

402
void VSIShowMemStats()
403
{
404
    char *pszShowMemStats = getenv("CPL_SHOW_MEM_STATS");
405
    if (pszShowMemStats == nullptr || pszShowMemStats[0] == '\0')
406
        return;
407
    printf("Current VSI memory usage        : " CPL_FRMT_GUIB " bytes\n", /*ok*/
408
           static_cast<GUIntBig>(nCurrentTotalAllocs));
409
    printf("Maximum VSI memory usage        : " CPL_FRMT_GUIB " bytes\n", /*ok*/
410
           static_cast<GUIntBig>(nMaxTotalAllocs));
411
    printf("Number of calls to VSIMalloc()  : " CPL_FRMT_GUIB "\n", /*ok*/
412
           nVSIMallocs);
413
    printf("Number of calls to VSICalloc()  : " CPL_FRMT_GUIB "\n", /*ok*/
414
           nVSICallocs);
415
    printf("Number of calls to VSIRealloc() : " CPL_FRMT_GUIB "\n", /*ok*/
416
           nVSIReallocs);
417
    printf("Number of calls to VSIFree()    : " CPL_FRMT_GUIB "\n", /*ok*/
418
           nVSIFrees);
419
    printf("VSIMalloc + VSICalloc - VSIFree : " CPL_FRMT_GUIB "\n", /*ok*/
420
           nVSIMallocs + nVSICallocs - nVSIFrees);
421
}
422
#endif
423

424
#ifdef DEBUG_VSIMALLOC
425
static GIntBig nMaxPeakAllocSize = -1;
426
static GIntBig nMaxCumulAllocSize = -1;
427
#endif
428

429
/************************************************************************/
430
/*                             VSICalloc()                              */
431
/************************************************************************/
432

433
#ifndef DEBUG_VSIMALLOC
434

435
/** Analog of calloc(). Use VSIFree() to free */
436
void *VSICalloc(size_t nCount, size_t nSize)
35,674,300✔
437
{
438
    // cppcheck-suppress invalidFunctionArg
439
    return calloc(nCount, nSize);
35,674,300✔
440
}
441

442
#else  // DEBUG_VSIMALLOC
443

444
void *VSICalloc(size_t nCount, size_t nSize)
445
{
446
    size_t nMul = nCount * nSize;
447
    if (nCount != 0 && nMul / nCount != nSize)
448
    {
449
        fprintf(stderr, "Overflow in VSICalloc(%d, %d)\n", /*ok*/
450
                static_cast<int>(nCount), static_cast<int>(nSize));
451
        return nullptr;
452
    }
453
    if (nMaxPeakAllocSize < 0)
454
    {
455
        char *pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE");
456
        nMaxPeakAllocSize = pszMaxPeakAllocSize ? atoi(pszMaxPeakAllocSize) : 0;
457
        char *pszMaxCumulAllocSize = getenv("CPL_MAX_CUMUL_ALLOC_SIZE");
458
        nMaxCumulAllocSize =
459
            pszMaxCumulAllocSize ? atoi(pszMaxCumulAllocSize) : 0;
460
    }
461
    if (nMaxPeakAllocSize > 0 && static_cast<GIntBig>(nMul) > nMaxPeakAllocSize)
462
        return nullptr;
463
#ifdef DEBUG_VSIMALLOC_STATS
464
    if (nMaxCumulAllocSize > 0 &&
465
        static_cast<GIntBig>(nCurrentTotalAllocs) + static_cast<GIntBig>(nMul) >
466
            nMaxCumulAllocSize)
467
        return nullptr;
468
#endif
469

470
#ifdef DEBUG_VSIMALLOC_MPROTECT
471
    char *ptr = nullptr;
472
    const size_t nPageSize = getpagesize();
473
    const size_t nRequestedSize =
474
        (3 * sizeof(void *) + nMul + nPageSize - 1) & ~(nPageSize - 1);
475
    if (nRequestedSize < nMul)
476
        return nullptr;
477
    posix_memalign((void **)&ptr, nPageSize, nRequestedSize);
478
    if (ptr == nullptr)
479
        return nullptr;
480
    memset(ptr + 2 * sizeof(void *), 0, nMul);
481
#else
482
    const size_t nRequestedSize = 3 * sizeof(void *) + nMul;
483
    if (nRequestedSize < nMul)
484
        return nullptr;
485
    char *ptr = static_cast<char *>(calloc(1, nRequestedSize));
486
    if (ptr == nullptr)
487
        return nullptr;
488
#endif
489

490
    ptr[0] = 'V';
491
    ptr[1] = 'S';
492
    ptr[2] = 'I';
493
    ptr[3] = 'M';
494
    // cppcheck-suppress pointerSize
495
    memcpy(ptr + sizeof(void *), &nMul, sizeof(void *));
496
    ptr[2 * sizeof(void *) + nMul + 0] = 'E';
497
    ptr[2 * sizeof(void *) + nMul + 1] = 'V';
498
    ptr[2 * sizeof(void *) + nMul + 2] = 'S';
499
    ptr[2 * sizeof(void *) + nMul + 3] = 'I';
500
#if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
501
    {
502
        CPLMutexHolderD(&hMemStatMutex);
503
#ifdef DEBUG_VSIMALLOC_VERBOSE
504
        if (nMul > THRESHOLD_PRINT)
505
        {
506
            fprintf(stderr, /*ok*/
507
                    "Thread[%p] VSICalloc(%d,%d) = %p"
508
#ifdef DEBUG_VSIMALLOC_STATS
509
                    ", current_cumul = " CPL_FRMT_GUIB
510
#ifdef DEBUG_BLOCK_CACHE_USE
511
                    ", block_cache_used = " CPL_FRMT_GIB
512
#endif
513
                    ", mal+cal-free = %d"
514
#endif
515
                    "\n",
516
                    (void *)CPLGetPID(), static_cast<int>(nCount),
517
                    static_cast<int>(nSize), ptr + 2 * sizeof(void *)
518
#ifdef DEBUG_VSIMALLOC_STATS
519
                                                 ,
520
                    static_cast<GUIntBig>(nCurrentTotalAllocs + nMul)
521
#ifdef DEBUG_BLOCK_CACHE_USE
522
                        ,
523
                    GDALGetCacheUsed64()
524
#endif
525
                        ,
526
                    static_cast<int>(nVSIMallocs + nVSICallocs - nVSIFrees)
527
#endif
528
            );
529
        }
530
#endif
531
#ifdef DEBUG_VSIMALLOC_STATS
532
        nVSICallocs++;
533
        if (nMaxTotalAllocs == 0)
534
            atexit(VSIShowMemStats);
535
        nCurrentTotalAllocs += nMul;
536
        if (nCurrentTotalAllocs > nMaxTotalAllocs)
537
            nMaxTotalAllocs = nCurrentTotalAllocs;
538
#endif
539
    }
540
#endif
541
    // cppcheck-suppress memleak
542
    return ptr + 2 * sizeof(void *);
543
}
544
#endif  // DEBUG_VSIMALLOC
545

546
/************************************************************************/
547
/*                             VSIMalloc()                              */
548
/************************************************************************/
549

550
#ifndef DEBUG_VSIMALLOC
551
/** Analog of malloc(). Use VSIFree() to free */
552
void *VSIMalloc(size_t nSize)
92,126,500✔
553

554
{
555
    return malloc(nSize);
92,126,500✔
556
}
557

558
#else  // DEBUG_VSIMALLOC
559

560
void *VSIMalloc(size_t nSize)
561

562
{
563
    if (nMaxPeakAllocSize < 0)
564
    {
565
        char *pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE");
566
        nMaxPeakAllocSize = pszMaxPeakAllocSize ? atoi(pszMaxPeakAllocSize) : 0;
567
        char *pszMaxCumulAllocSize = getenv("CPL_MAX_CUMUL_ALLOC_SIZE");
568
        nMaxCumulAllocSize =
569
            pszMaxCumulAllocSize ? atoi(pszMaxCumulAllocSize) : 0;
570
    }
571
    if (nMaxPeakAllocSize > 0 &&
572
        static_cast<GIntBig>(nSize) > nMaxPeakAllocSize)
573
        return nullptr;
574
#ifdef DEBUG_VSIMALLOC_STATS
575
    if (nMaxCumulAllocSize > 0 && static_cast<GIntBig>(nCurrentTotalAllocs) +
576
                                          static_cast<GIntBig>(nSize) >
577
                                      nMaxCumulAllocSize)
578
        return nullptr;
579
#endif  // DEBUG_VSIMALLOC_STATS
580

581
#ifdef DEBUG_VSIMALLOC_MPROTECT
582
    char *ptr = nullptr;
583
    const size_t nPageSize = getpagesize();
584
    const size_t nRequestedSize =
585
        (3 * sizeof(void *) + nSize + nPageSize - 1) & ~(nPageSize - 1);
586
    if (nRequestedSize < nSize)
587
        return nullptr;
588
    posix_memalign((void **)&ptr, nPageSize, nRequestedSize);
589
#else
590
    const size_t nRequestedSize = 3 * sizeof(void *) + nSize;
591
    if (nRequestedSize < nSize)
592
        return nullptr;
593
    char *ptr = static_cast<char *>(malloc(nRequestedSize));
594
#endif  // DEBUG_VSIMALLOC_MPROTECT
595
    if (ptr == nullptr)
596
        return nullptr;
597
    ptr[0] = 'V';
598
    ptr[1] = 'S';
599
    ptr[2] = 'I';
600
    ptr[3] = 'M';
601
    // cppcheck-suppress pointerSize
602
    memcpy(ptr + sizeof(void *), &nSize, sizeof(void *));
603
    ptr[2 * sizeof(void *) + nSize + 0] = 'E';
604
    ptr[2 * sizeof(void *) + nSize + 1] = 'V';
605
    ptr[2 * sizeof(void *) + nSize + 2] = 'S';
606
    ptr[2 * sizeof(void *) + nSize + 3] = 'I';
607
#if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
608
    {
609
        CPLMutexHolderD(&hMemStatMutex);
610
#ifdef DEBUG_VSIMALLOC_VERBOSE
611
        if (nSize > THRESHOLD_PRINT)
612
        {
613
            fprintf(stderr, /*ok*/
614
                    "Thread[%p] VSIMalloc(%d) = %p"
615
#ifdef DEBUG_VSIMALLOC_STATS
616
                    ", current_cumul = " CPL_FRMT_GUIB
617
#ifdef DEBUG_BLOCK_CACHE_USE
618
                    ", block_cache_used = " CPL_FRMT_GIB
619
#endif
620
                    ", mal+cal-free = %d"
621
#endif
622
                    "\n",
623
                    (void *)CPLGetPID(), static_cast<int>(nSize),
624
                    ptr + 2 * sizeof(void *)
625
#ifdef DEBUG_VSIMALLOC_STATS
626
                        ,
627
                    static_cast<GUIntBig>(nCurrentTotalAllocs + nSize)
628
#ifdef DEBUG_BLOCK_CACHE_USE
629
                        ,
630
                    GDALGetCacheUsed64()
631
#endif
632
                        ,
633
                    static_cast<int>(nVSIMallocs + nVSICallocs - nVSIFrees)
634
#endif
635
            );
636
        }
637
#endif  // DEBUG_VSIMALLOC_VERBOSE
638
#ifdef DEBUG_VSIMALLOC_STATS
639
        nVSIMallocs++;
640
        if (nMaxTotalAllocs == 0)
641
            atexit(VSIShowMemStats);
642
        nCurrentTotalAllocs += nSize;
643
        if (nCurrentTotalAllocs > nMaxTotalAllocs)
644
            nMaxTotalAllocs = nCurrentTotalAllocs;
645
#endif  // DEBUG_VSIMALLOC_STATS
646
    }
647
#endif  // DEBUG_VSIMALLOC_STATS || DEBUG_VSIMALLOC_VERBOSE
648
    // cppcheck-suppress memleak
649
    return ptr + 2 * sizeof(void *);
650
}
651

652
static void VSICheckMarkerBegin(char *ptr)
653
{
654
    if (memcmp(ptr, "VSIM", 4) != 0)
655
    {
656
        CPLError(CE_Fatal, CPLE_AppDefined,
657
                 "Inconsistent use of VSI memory allocation primitives "
658
                 "for %p : %c%c%c%c",
659
                 ptr, ptr[0], ptr[1], ptr[2], ptr[3]);
660
    }
661
}
662

663
static void VSICheckMarkerEnd(char *ptr, size_t nEnd)
664
{
665
    if (memcmp(ptr + nEnd, "EVSI", 4) != 0)
666
    {
667
        CPLError(CE_Fatal, CPLE_AppDefined,
668
                 "Memory has been written after the end of %p", ptr);
669
    }
670
}
671

672
#endif  // DEBUG_VSIMALLOC
673

674
/************************************************************************/
675
/*                             VSIRealloc()                             */
676
/************************************************************************/
677

678
/** Analog of realloc(). Use VSIFree() to free.
679
 *
680
 * If the pointer is NULL, VSIRealloc is equivalent to VSIMalloc.
681
 */
682
void *VSIRealloc(void *pData, size_t nNewSize)
22,671,000✔
683

684
{
685
#ifdef DEBUG_VSIMALLOC
686
    if (pData == nullptr)
687
        return VSIMalloc(nNewSize);
688

689
    char *ptr = ((char *)pData) - 2 * sizeof(void *);
690
    VSICheckMarkerBegin(ptr);
691

692
    size_t nOldSize = 0;
693
    // cppcheck-suppress pointerSize
694
    memcpy(&nOldSize, ptr + sizeof(void *), sizeof(void *));
695
    VSICheckMarkerEnd(ptr, 2 * sizeof(void *) + nOldSize);
696

697
    if (nMaxPeakAllocSize < 0)
698
    {
699
        char *pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE");
700
        nMaxPeakAllocSize = pszMaxPeakAllocSize ? atoi(pszMaxPeakAllocSize) : 0;
701
    }
702
    if (nMaxPeakAllocSize > 0 &&
703
        static_cast<GIntBig>(nNewSize) > nMaxPeakAllocSize)
704
        return nullptr;
705
#ifdef DEBUG_VSIMALLOC_STATS
706
    if (nMaxCumulAllocSize > 0 && static_cast<GIntBig>(nCurrentTotalAllocs) +
707
                                          static_cast<GIntBig>(nNewSize) -
708
                                          static_cast<GIntBig>(nOldSize) >
709
                                      nMaxCumulAllocSize)
710
        return nullptr;
711
#endif
712

713
    ptr[2 * sizeof(void *) + nOldSize + 0] = 'I';
714
    ptr[2 * sizeof(void *) + nOldSize + 1] = 'S';
715
    ptr[2 * sizeof(void *) + nOldSize + 2] = 'V';
716
    ptr[2 * sizeof(void *) + nOldSize + 3] = 'E';
717

718
#ifdef DEBUG_VSIMALLOC_MPROTECT
719
    char *newptr = nullptr;
720
    const size_t nPageSize = getpagesize();
721
    const size_t nRequestedSize =
722
        (nNewSize + 3 * sizeof(void *) + nPageSize - 1) & ~(nPageSize - 1);
723
    if (nRequestedSize < nNewSize)
724
    {
725
        ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
726
        ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
727
        ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
728
        ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
729
        return nullptr;
730
    }
731
    posix_memalign((void **)&newptr, nPageSize, nRequestedSize);
732
    if (newptr == nullptr)
733
    {
734
        ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
735
        ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
736
        ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
737
        ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
738
        return nullptr;
739
    }
740
    memcpy(newptr + 2 * sizeof(void *), pData, nOldSize);
741
    ptr[0] = 'M';
742
    ptr[1] = 'I';
743
    ptr[2] = 'S';
744
    ptr[3] = 'V';
745
    free(ptr);
746
    newptr[0] = 'V';
747
    newptr[1] = 'S';
748
    newptr[2] = 'I';
749
    newptr[3] = 'M';
750
#else
751
    const size_t nRequestedSize = 3 * sizeof(void *) + nNewSize;
752
    if (nRequestedSize < nNewSize)
753
    {
754
        ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
755
        ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
756
        ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
757
        ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
758
        return nullptr;
759
    }
760
    void *newptr = realloc(ptr, nRequestedSize);
761
    if (newptr == nullptr)
762
    {
763
        ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
764
        ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
765
        ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
766
        ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
767
        return nullptr;
768
    }
769
#endif
770
    ptr = static_cast<char *>(newptr);
771
    // cppcheck-suppress pointerSize
772
    memcpy(ptr + sizeof(void *), &nNewSize, sizeof(void *));
773
    ptr[2 * sizeof(void *) + nNewSize + 0] = 'E';
774
    ptr[2 * sizeof(void *) + nNewSize + 1] = 'V';
775
    ptr[2 * sizeof(void *) + nNewSize + 2] = 'S';
776
    ptr[2 * sizeof(void *) + nNewSize + 3] = 'I';
777

778
#if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
779
    {
780
        CPLMutexHolderD(&hMemStatMutex);
781
#ifdef DEBUG_VSIMALLOC_VERBOSE
782
        if (nNewSize > THRESHOLD_PRINT)
783
        {
784
            fprintf(
785
                stderr,
786
                "Thread[%p] VSIRealloc(%p, %d) = %p" /*ok*/
787
#ifdef DEBUG_VSIMALLOC_STATS
788
                ", current_cumul = " CPL_FRMT_GUIB
789
#ifdef DEBUG_BLOCK_CACHE_USE
790
                ", block_cache_used = " CPL_FRMT_GIB
791
#endif
792
                ", mal+cal-free = %d"
793
#endif
794
                "\n",
795
                (void *)CPLGetPID(), pData, static_cast<int>(nNewSize),
796
                ptr + 2 * sizeof(void *)
797
#ifdef DEBUG_VSIMALLOC_STATS
798
                    ,
799
                static_cast<GUIntBig>(nCurrentTotalAllocs - nOldSize + nNewSize)
800
#ifdef DEBUG_BLOCK_CACHE_USE
801
                    ,
802
                GDALGetCacheUsed64()
803
#endif
804
                    ,
805
                static_cast<int>(nVSIMallocs + nVSICallocs - nVSIFrees)
806
#endif
807
            );
808
        }
809
#endif
810
#ifdef DEBUG_VSIMALLOC_STATS
811
        nVSIReallocs++;
812
        nCurrentTotalAllocs -= nOldSize;
813
        nCurrentTotalAllocs += nNewSize;
814
        if (nCurrentTotalAllocs > nMaxTotalAllocs)
815
            nMaxTotalAllocs = nCurrentTotalAllocs;
816
#endif
817
    }
818
#endif
819
    return ptr + 2 * sizeof(void *);
820
#else
821
    return realloc(pData, nNewSize);
22,671,000✔
822
#endif
823
}
824

825
/************************************************************************/
826
/*                              VSIFree()                               */
827
/************************************************************************/
828

829
/** Analog of free() for data allocated with VSIMalloc(), VSICalloc(),
830
 * VSIRealloc().
831
 *
832
 * It is not an error to call VSIFree with a NULL pointer, and it will
833
 * have no effect.
834
 */
835
void VSIFree(void *pData)
148,161,000✔
836

837
{
838
#ifdef DEBUG_VSIMALLOC
839
    if (pData == nullptr)
840
        return;
841

842
    char *ptr = ((char *)pData) - 2 * sizeof(void *);
843
    VSICheckMarkerBegin(ptr);
844
    size_t nOldSize = 0;
845
    // cppcheck-suppress pointerSize
846
    memcpy(&nOldSize, ptr + sizeof(void *), sizeof(void *));
847
    VSICheckMarkerEnd(ptr, 2 * sizeof(void *) + nOldSize);
848
    ptr[0] = 'M';
849
    ptr[1] = 'I';
850
    ptr[2] = 'S';
851
    ptr[3] = 'V';
852
    ptr[2 * sizeof(void *) + nOldSize + 0] = 'I';
853
    ptr[2 * sizeof(void *) + nOldSize + 1] = 'S';
854
    ptr[2 * sizeof(void *) + nOldSize + 2] = 'V';
855
    ptr[2 * sizeof(void *) + nOldSize + 3] = 'E';
856
#if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
857
    {
858
        CPLMutexHolderD(&hMemStatMutex);
859
#ifdef DEBUG_VSIMALLOC_VERBOSE
860
        if (nOldSize > THRESHOLD_PRINT)
861
        {
862
            fprintf(stderr, "Thread[%p] VSIFree(%p, (%d bytes))\n", /*ok*/
863
                    (void *)CPLGetPID(), pData, static_cast<int>(nOldSize));
864
        }
865
#endif
866
#ifdef DEBUG_VSIMALLOC_STATS
867
        nVSIFrees++;
868
        nCurrentTotalAllocs -= nOldSize;
869
#endif
870
    }
871
#endif
872

873
#ifdef DEBUG_VSIMALLOC_MPROTECT
874
    mprotect(ptr, nOldSize + 2 * sizeof(void *), PROT_NONE);
875
#else
876
    free(ptr);
877
#endif
878

879
#else
880
    if (pData != nullptr)
148,161,000✔
881
        free(pData);
125,359,000✔
882
#endif
883
}
148,161,000✔
884

885
/************************************************************************/
886
/*                      VSIMallocAligned()                              */
887
/************************************************************************/
888

889
/** Allocates a buffer with an alignment constraint.
890
 *
891
 * The return value must be freed with VSIFreeAligned().
892
 *
893
 * @param nAlignment Must be a power of 2, multiple of sizeof(void*), and
894
 *                   lesser than 256.
895
 * @param nSize Size of the buffer to allocate.
896
 * @return a buffer aligned on nAlignment and of size nSize, or NULL
897
 * @since GDAL 2.2
898
 */
899

900
void *VSIMallocAligned(size_t nAlignment, size_t nSize)
3,393,610✔
901
{
902
    // In particular for posix_memalign() where behavior when passing
903
    // nSize == 0 is technically implementation defined (Valgrind complains),
904
    // so let's always return NULL.
905
    if (nSize == 0)
3,393,610✔
906
        return nullptr;
×
907
#if defined(HAVE_POSIX_MEMALIGN) && !defined(DEBUG_VSIMALLOC)
908
    void *pRet = nullptr;
3,393,610✔
909
    if (posix_memalign(&pRet, nAlignment, nSize) != 0)
3,393,610✔
910
    {
911
        pRet = nullptr;
4✔
912
    }
913
    return pRet;
3,393,610✔
914
#elif defined(_WIN32) && !defined(DEBUG_VSIMALLOC)
915
    return _aligned_malloc(nSize, nAlignment);
916
#else
917
    // Check constraints on alignment.
918
    if (nAlignment < sizeof(void *) || nAlignment >= 256 ||
919
        (nAlignment & (nAlignment - 1)) != 0)
920
        return nullptr;
921
    // Detect overflow.
922
    if (nSize + nAlignment < nSize)
923
        return nullptr;
924
    GByte *pabyData = static_cast<GByte *>(VSIMalloc(nSize + nAlignment));
925
    if (pabyData == nullptr)
926
        return nullptr;
927
    size_t nShift =
928
        nAlignment - (reinterpret_cast<size_t>(pabyData) % nAlignment);
929
    GByte *pabyAligned = pabyData + nShift;
930
    // Guaranteed to fit on a byte since nAlignment < 256.
931
    pabyAligned[-1] = static_cast<GByte>(nShift);
932
    return pabyAligned;
933
#endif
934
}
935

936
/************************************************************************/
937
/*                     VSIMallocAlignedAuto()                           */
938
/************************************************************************/
939

940
/** Allocates a buffer with an alignment constraint such that it can be
941
 * used by the most demanding vector instruction set on that platform.
942
 *
943
 * The return value must be freed with VSIFreeAligned().
944
 *
945
 * @param nSize Size of the buffer to allocate.
946
 * @return an aligned buffer of size nSize, or NULL
947
 * @since GDAL 2.2
948
 */
949

950
void *VSIMallocAlignedAuto(size_t nSize)
3,393,600✔
951
{
952
    // We could potentially dynamically detect the capability of the CPU
953
    // but to simplify use 64 for AVX512 requirements (we use only AVX256
954
    // currently).
955
    return VSIMallocAligned(64, nSize);
3,393,600✔
956
}
957

958
/************************************************************************/
959
/*                        VSIMallocAlignedAutoVerbose()                 */
960
/************************************************************************/
961

962
/** See VSIMallocAlignedAuto() */
963
void *VSIMallocAlignedAutoVerbose(size_t nSize, const char *pszFile, int nLine)
3,393,610✔
964
{
965
    void *pRet = VSIMallocAlignedAuto(nSize);
3,393,610✔
966
    if (pRet == nullptr && nSize != 0)
3,393,580✔
967
    {
968
        CPLError(CE_Failure, CPLE_OutOfMemory,
×
969
                 "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
970
                 pszFile ? pszFile : "(unknown file)", nLine,
971
                 static_cast<GUIntBig>(nSize));
972
    }
973
    return pRet;
3,393,580✔
974
}
975

976
/************************************************************************/
977
/*                        VSIFreeAligned()                              */
978
/************************************************************************/
979

980
/** Free a buffer allocated with VSIMallocAligned().
981
 *
982
 * @param ptr Buffer to free.
983
 * @since GDAL 2.2
984
 */
985

986
void VSIFreeAligned(void *ptr)
3,416,150✔
987
{
988
#if defined(HAVE_POSIX_MEMALIGN) && !defined(DEBUG_VSIMALLOC)
989
    free(ptr);
3,416,150✔
990
#elif defined(_WIN32) && !defined(DEBUG_VSIMALLOC)
991
    _aligned_free(ptr);
992
#else
993
    if (ptr == nullptr)
994
        return;
995
    GByte *pabyAligned = static_cast<GByte *>(ptr);
996
    size_t nShift = pabyAligned[-1];
997
    VSIFree(pabyAligned - nShift);
998
#endif
999
}
3,416,150✔
1000

1001
/************************************************************************/
1002
/*                             VSIStrdup()                              */
1003
/************************************************************************/
1004

1005
/** Analog of strdup(). Use VSIFree() to free */
1006
char *VSIStrdup(const char *pszString)
37,067,300✔
1007

1008
{
1009
    const size_t nSize = strlen(pszString) + 1;
37,067,300✔
1010
    char *ptr = static_cast<char *>(VSIMalloc(nSize));
37,067,300✔
1011
    if (ptr == nullptr)
37,067,400✔
1012
        return nullptr;
×
1013
    memcpy(ptr, pszString, nSize);
37,067,400✔
1014
    return ptr;
37,067,400✔
1015
}
1016

1017
/************************************************************************/
1018
/*                          VSICheckMul2()                              */
1019
/************************************************************************/
1020

1021
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
1022
static size_t VSICheckMul2(size_t mul1, size_t mul2, bool *pbOverflowFlag,
296,954✔
1023
                           const char *pszFile, int nLine)
1024
{
1025
    const size_t res = mul1 * mul2;
296,954✔
1026
    if (mul1 != 0)
296,954✔
1027
    {
1028
        if (res / mul1 == mul2)
296,942✔
1029
        {
1030
            if (pbOverflowFlag)
296,942✔
1031
                *pbOverflowFlag = FALSE;
296,941✔
1032
            return res;
296,942✔
1033
        }
1034
        else
1035
        {
UNCOV
1036
            if (pbOverflowFlag)
×
1037
                *pbOverflowFlag = TRUE;
1✔
UNCOV
1038
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
1039
                     "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
1040
                     " * " CPL_FRMT_GUIB,
1041
                     pszFile ? pszFile : "(unknown file)", nLine,
1042
                     static_cast<GUIntBig>(mul1), static_cast<GUIntBig>(mul2));
1043
        }
1044
    }
1045
    else
1046
    {
1047
        if (pbOverflowFlag)
12✔
1048
            *pbOverflowFlag = FALSE;
8✔
1049
    }
1050
    return 0;
9✔
1051
}
1052

1053
/************************************************************************/
1054
/*                          VSICheckMul3()                              */
1055
/************************************************************************/
1056

1057
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
1058
static size_t VSICheckMul3(size_t mul1, size_t mul2, size_t mul3,
185,710✔
1059
                           bool *pbOverflowFlag, const char *pszFile, int nLine)
1060
{
1061
    if (mul1 != 0)
185,710✔
1062
    {
1063
        const size_t res = mul1 * mul2;
185,709✔
1064
        if (res / mul1 == mul2)
185,709✔
1065
        {
1066
            const size_t res2 = res * mul3;
185,710✔
1067
            if (mul3 != 0)
185,710✔
1068
            {
1069
                if (res2 / mul3 == res)
185,709✔
1070
                {
1071
                    if (pbOverflowFlag)
185,710✔
1072
                        *pbOverflowFlag = false;
185,709✔
1073
                    return res2;
185,710✔
1074
                }
1075
                else
1076
                {
1077
                    if (pbOverflowFlag)
×
1078
                        *pbOverflowFlag = true;
2✔
1079
                    CPLError(CE_Failure, CPLE_OutOfMemory,
×
1080
                             "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
1081
                             " * " CPL_FRMT_GUIB " * " CPL_FRMT_GUIB,
1082
                             pszFile ? pszFile : "(unknown file)", nLine,
1083
                             static_cast<GUIntBig>(mul1),
1084
                             static_cast<GUIntBig>(mul2),
1085
                             static_cast<GUIntBig>(mul3));
1086
                }
1087
            }
1088
            else
1089
            {
1090
                if (pbOverflowFlag)
1✔
1091
                    *pbOverflowFlag = false;
1✔
1092
            }
1093
        }
1094
        else
1095
        {
UNCOV
1096
            if (pbOverflowFlag)
×
1097
                *pbOverflowFlag = true;
1✔
UNCOV
1098
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
1099
                     "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
1100
                     " * " CPL_FRMT_GUIB " * " CPL_FRMT_GUIB,
1101
                     pszFile ? pszFile : "(unknown file)", nLine,
1102
                     static_cast<GUIntBig>(mul1), static_cast<GUIntBig>(mul2),
1103
                     static_cast<GUIntBig>(mul3));
1104
        }
1105
    }
1106
    else
1107
    {
1108
        if (pbOverflowFlag)
1✔
1109
            *pbOverflowFlag = false;
1✔
1110
    }
1111
    return 0;
5✔
1112
}
1113

1114
/**
1115
 VSIMalloc2 allocates (nSize1 * nSize2) bytes.
1116
 In case of overflow of the multiplication, or if memory allocation fails, a
1117
 NULL pointer is returned and a CE_Failure error is raised with CPLError().
1118
 If nSize1 == 0 || nSize2 == 0, a NULL pointer will also be returned.
1119
 CPLFree() or VSIFree() can be used to free memory allocated by this function.
1120
*/
1121
void CPL_DLL *VSIMalloc2(size_t nSize1, size_t nSize2)
153✔
1122
{
1123
    return VSIMalloc2Verbose(nSize1, nSize2, nullptr, 0);
153✔
1124
}
1125

1126
/**
1127
 VSIMalloc3 allocates (nSize1 * nSize2 * nSize3) bytes.
1128
 In case of overflow of the multiplication, or if memory allocation fails, a
1129
 NULL pointer is returned and a CE_Failure error is raised with CPLError().
1130
 If nSize1 == 0 || nSize2 == 0 || nSize3 == 0, a NULL pointer will also be
1131
 returned.  CPLFree() or VSIFree() can be used to free memory allocated by this
1132
 function.
1133
*/
1134
void CPL_DLL *VSIMalloc3(size_t nSize1, size_t nSize2, size_t nSize3)
944✔
1135
{
1136
    return VSIMalloc3Verbose(nSize1, nSize2, nSize3, nullptr, 0);
944✔
1137
}
1138

1139
/************************************************************************/
1140
/*                          VSIMallocVerbose()                          */
1141
/************************************************************************/
1142

1143
void *VSIMallocVerbose(size_t nSize, const char *pszFile, int nLine)
8,341,660✔
1144
{
1145
    void *pRet = VSIMalloc(nSize);
8,341,660✔
1146
    if (pRet == nullptr && nSize != 0)
8,341,640✔
1147
    {
1148
        CPLError(CE_Failure, CPLE_OutOfMemory,
2✔
1149
                 "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1150
                 pszFile ? pszFile : "(unknown file)", nLine,
1151
                 static_cast<GUIntBig>(nSize));
1152
    }
1153
    return pRet;
8,341,620✔
1154
}
1155

1156
/************************************************************************/
1157
/*                          VSIMalloc2Verbose()                         */
1158
/************************************************************************/
1159

1160
void *VSIMalloc2Verbose(size_t nSize1, size_t nSize2, const char *pszFile,
296,961✔
1161
                        int nLine)
1162
{
1163
    bool bOverflowFlag = false;
296,961✔
1164
    const size_t nSizeToAllocate =
1165
        VSICheckMul2(nSize1, nSize2, &bOverflowFlag, pszFile, nLine);
296,961✔
1166
    if (bOverflowFlag || nSizeToAllocate == 0)
296,945✔
1167
        return nullptr;
35✔
1168

1169
    void *pRet = VSIMalloc(nSizeToAllocate);
296,910✔
1170
    if (pRet == nullptr)
296,930✔
1171
    {
1172
        CPLError(CE_Failure, CPLE_OutOfMemory,
2✔
1173
                 "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1174
                 pszFile ? pszFile : "(unknown file)", nLine,
1175
                 static_cast<GUIntBig>(nSize1) * static_cast<GUIntBig>(nSize2));
1176
    }
1177
    return pRet;
296,930✔
1178
}
1179

1180
/************************************************************************/
1181
/*                          VSIMalloc3Verbose()                         */
1182
/************************************************************************/
1183

1184
void *VSIMalloc3Verbose(size_t nSize1, size_t nSize2, size_t nSize3,
185,710✔
1185
                        const char *pszFile, int nLine)
1186
{
1187
    bool bOverflowFlag = false;
185,710✔
1188
    size_t nSizeToAllocate =
1189
        VSICheckMul3(nSize1, nSize2, nSize3, &bOverflowFlag, pszFile, nLine);
185,710✔
1190
    if (bOverflowFlag || nSizeToAllocate == 0)
185,712✔
1191
        return nullptr;
16✔
1192

1193
    void *pRet = VSIMalloc(nSizeToAllocate);
185,696✔
1194
    if (pRet == nullptr)
185,722✔
1195
    {
1196
        CPLError(CE_Failure, CPLE_OutOfMemory,
4✔
1197
                 "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1198
                 pszFile ? pszFile : "(unknown file)", nLine,
1199
                 static_cast<GUIntBig>(nSize1) * static_cast<GUIntBig>(nSize2) *
4✔
1200
                     static_cast<GUIntBig>(nSize3));
1201
    }
1202
    return pRet;
185,726✔
1203
}
1204

1205
/************************************************************************/
1206
/*                          VSICallocVerbose()                          */
1207
/************************************************************************/
1208

1209
void *VSICallocVerbose(size_t nCount, size_t nSize, const char *pszFile,
9,397,900✔
1210
                       int nLine)
1211
{
1212
    void *pRet = VSICalloc(nCount, nSize);
9,397,900✔
1213
    if (pRet == nullptr && nCount != 0 && nSize != 0)
9,397,870✔
1214
    {
1215
        CPLError(CE_Failure, CPLE_OutOfMemory,
5✔
1216
                 "%s, %d: cannot allocate " CPL_FRMT_GUIB "x" CPL_FRMT_GUIB
1217
                 " bytes",
1218
                 pszFile ? pszFile : "(unknown file)", nLine,
1219
                 static_cast<GUIntBig>(nCount), static_cast<GUIntBig>(nSize));
1220
    }
1221
    return pRet;
9,397,850✔
1222
}
1223

1224
/************************************************************************/
1225
/*                          VSIReallocVerbose()                         */
1226
/************************************************************************/
1227

1228
void *VSIReallocVerbose(void *pOldPtr, size_t nNewSize, const char *pszFile,
17,412,800✔
1229
                        int nLine)
1230
{
1231
    void *pRet = VSIRealloc(pOldPtr, nNewSize);
17,412,800✔
1232
    if (pRet == nullptr && nNewSize != 0)
17,412,800✔
1233
    {
1234
        CPLError(CE_Failure, CPLE_OutOfMemory,
2✔
1235
                 "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1236
                 pszFile ? pszFile : "(unknown file)", nLine,
1237
                 static_cast<GUIntBig>(nNewSize));
1238
    }
1239
    return pRet;
17,412,800✔
1240
}
1241

1242
/************************************************************************/
1243
/*                          VSIStrdupVerbose()                          */
1244
/************************************************************************/
1245

1246
char *VSIStrdupVerbose(const char *pszStr, const char *pszFile, int nLine)
11,225,100✔
1247
{
1248
    char *pRet = VSIStrdup(pszStr);
11,225,100✔
1249
    if (pRet == nullptr)
11,225,200✔
1250
    {
1251
        CPLError(CE_Failure, CPLE_OutOfMemory,
×
1252
                 "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1253
                 pszFile ? pszFile : "(unknown file)", nLine,
1254
                 static_cast<GUIntBig>(strlen(pszStr) + 1));
×
1255
    }
1256
    return pRet;
11,225,200✔
1257
}
1258

1259
/************************************************************************/
1260
/*                              VSIStat()                               */
1261
/************************************************************************/
1262

1263
int VSIStat(const char *pszFilename, VSIStatBuf *pStatBuf)
935✔
1264

1265
{
1266
#if defined(_WIN32)
1267
    if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
1268
    {
1269
        wchar_t *pwszFilename =
1270
            CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
1271

1272
        int nResult =
1273
            _wstat(pwszFilename, reinterpret_cast<struct _stat *>(pStatBuf));
1274

1275
        CPLFree(pwszFilename);
1276

1277
        return nResult;
1278
    }
1279

1280
#endif
1281
    return stat(pszFilename, pStatBuf);
935✔
1282
}
1283

1284
/************************************************************************/
1285
/*                              VSITime()                               */
1286
/************************************************************************/
1287

1288
unsigned long VSITime(unsigned long *pnTimeToSet)
×
1289

1290
{
1291
    time_t tTime;
1292

1293
    tTime = time(nullptr);
×
1294

1295
    if (pnTimeToSet != nullptr)
×
1296
        *pnTimeToSet = static_cast<unsigned long>(tTime);
×
1297

1298
    return static_cast<unsigned long>(tTime);
×
1299
}
1300

1301
/************************************************************************/
1302
/*                              VSICTime()                              */
1303
/************************************************************************/
1304

1305
const char *VSICTime(unsigned long nTime)
4✔
1306

1307
{
1308
    time_t tTime = static_cast<time_t>(nTime);
4✔
1309
#if HAVE_CTIME_R
1310
    // Cf https://linux.die.net/man/3/ctime_r:
1311
    // "The reentrant version ctime_r() does the same, but stores the string in
1312
    // a user-supplied buffer which should have room for at least 26 bytes"
1313
    char buffer[26];
1314
    char *ret = ctime_r(&tTime, buffer);
4✔
1315
    return ret ? CPLSPrintf("%s", ret) : nullptr;
4✔
1316
#elif defined(_WIN32)
1317
    char buffer[26];
1318
    return ctime_s(buffer, sizeof(buffer), &tTime) == 0
1319
               ? CPLSPrintf("%s", buffer)
1320
               : nullptr;
1321
#else
1322
    return reinterpret_cast<const char *>(ctime(&tTime));
1323
#endif
1324
}
1325

1326
/************************************************************************/
1327
/*                             VSIGMTime()                              */
1328
/************************************************************************/
1329

1330
struct tm *VSIGMTime(const time_t *pnTime, struct tm *poBrokenTime)
×
1331
{
1332

1333
#if HAVE_GMTIME_R
1334
    gmtime_r(pnTime, poBrokenTime);
×
1335
    return poBrokenTime;
×
1336
#elif defined(_WIN32)
1337
    return gmtime_s(poBrokenTime, pnTime) == 0 ? poBrokenTime : nullptr;
1338
#else
1339
    struct tm *poTime = gmtime(pnTime);
1340
    memcpy(poBrokenTime, poTime, sizeof(tm));
1341
    return poBrokenTime;
1342
#endif
1343
}
1344

1345
/************************************************************************/
1346
/*                             VSILocalTime()                           */
1347
/************************************************************************/
1348

1349
struct tm *VSILocalTime(const time_t *pnTime, struct tm *poBrokenTime)
532✔
1350
{
1351

1352
#if HAVE_LOCALTIME_R
1353
    localtime_r(pnTime, poBrokenTime);
532✔
1354
    return poBrokenTime;
532✔
1355
#elif defined(_WIN32)
1356
    return localtime_s(poBrokenTime, pnTime) == 0 ? poBrokenTime : nullptr;
1357
#else
1358
    struct tm *poTime = localtime(pnTime);
1359
    memcpy(poBrokenTime, poTime, sizeof(tm));
1360
    return poBrokenTime;
1361
#endif
1362
}
1363

1364
/************************************************************************/
1365
/*                            VSIStrerror()                             */
1366
/************************************************************************/
1367

1368
/** Return the error string corresponding to the error number. Do not free it */
1369
char *VSIStrerror(int nErrno)
122✔
1370

1371
{
1372
    return strerror(nErrno);
122✔
1373
}
1374

1375
/************************************************************************/
1376
/*                        CPLGetPhysicalRAM()                           */
1377
/************************************************************************/
1378

1379
#if HAVE_SC_PHYS_PAGES
1380

1381
/** Return the total physical RAM in bytes.
1382
 *
1383
 * In the context of a container using cgroups (typically Docker), this
1384
 * will take into account that limitation (starting with GDAL 2.4.0 and
1385
 * with extra fixes in GDAL 3.6.3)
1386
 *
1387
 * You should generally use CPLGetUsablePhysicalRAM() instead.
1388
 *
1389
 * @return the total physical RAM in bytes (or 0 in case of failure).
1390
 * @since GDAL 2.0
1391
 */
1392
GIntBig CPLGetPhysicalRAM(void)
8,724✔
1393
{
1394
    static const GIntBig nPhysicalRAM = []() -> GIntBig
1,127✔
1395
    {
1396
        const long nPhysPages = sysconf(_SC_PHYS_PAGES);
1,127✔
1397
        const long nPageSize = sysconf(_SC_PAGESIZE);
1,127✔
1398
        if (nPhysPages <= 0 || nPageSize <= 0 ||
2,254✔
1399
            nPhysPages > std::numeric_limits<GIntBig>::max() / nPageSize)
1,127✔
1400
        {
1401
            return 0;
×
1402
        }
1403

1404
        GIntBig nVal = static_cast<GIntBig>(nPhysPages) * nPageSize;
1,127✔
1405

1406
#ifdef __linux
1407
        {
1408
            // Take into account MemTotal in /proc/meminfo
1409
            // which seems to be necessary for some container solutions
1410
            // Cf https://lists.osgeo.org/pipermail/gdal-dev/2023-January/056784.html
1411
            FILE *f = fopen("/proc/meminfo", "rb");
1,127✔
1412
            if (f)
1,127✔
1413
            {
1414
                char szLine[256];
1415
                while (fgets(szLine, sizeof(szLine), f))
1,127✔
1416
                {
1417
                    // Find line like "MemTotal:       32525176 kB"
1418
                    if (strncmp(szLine, "MemTotal:", strlen("MemTotal:")) == 0)
1,127✔
1419
                    {
1420
                        char *pszVal = szLine + strlen("MemTotal:");
1,127✔
1421
                        pszVal += strspn(pszVal, " ");
1,127✔
1422
                        char *pszEnd = strstr(pszVal, " kB");
1,127✔
1423
                        if (pszEnd)
1,127✔
1424
                        {
1425
                            *pszEnd = 0;
1,127✔
1426
                            if (CPLGetValueType(pszVal) == CPL_VALUE_INTEGER)
1,127✔
1427
                            {
1428
                                const GUIntBig nLimit =
1429
                                    CPLScanUIntBig(
2,254✔
1430
                                        pszVal,
1431
                                        static_cast<int>(strlen(pszVal))) *
1,127✔
1432
                                    1024;
1,127✔
1433
                                nVal = static_cast<GIntBig>(std::min(
1,127✔
1434
                                    static_cast<GUIntBig>(nVal), nLimit));
1,127✔
1435
                            }
1436
                        }
1437
                        break;
1,127✔
1438
                    }
1439
                }
1440
                fclose(f);
1,127✔
1441
            }
1442
        }
1443

1444
        char szGroupName[256];
1445
        bool bFromMemory = false;
1,127✔
1446
        szGroupName[0] = 0;
1,127✔
1447
        {
1448
            FILE *f = fopen("/proc/self/cgroup", "rb");
1,127✔
1449
            if (f)
1,127✔
1450
            {
1451
                char szLine[256];
1452
                // Find line like "6:memory:/user.slice/user-1000.slice/user@1000.service"
1453
                // and store "/user.slice/user-1000.slice/user@1000.service" in
1454
                // szMemoryPath for cgroup V1 or single line "0::/...." for cgroup V2.
1455
                while (fgets(szLine, sizeof(szLine), f))
1,127✔
1456
                {
1457
                    const char *pszMemory = strstr(szLine, ":memory:");
1,127✔
1458
                    if (pszMemory)
1,127✔
1459
                    {
1460
                        bFromMemory = true;
×
1461
                        snprintf(szGroupName, sizeof(szGroupName), "%s",
×
1462
                                 pszMemory + strlen(":memory:"));
1463
                        char *pszEOL = strchr(szGroupName, '\n');
×
1464
                        if (pszEOL)
×
1465
                            *pszEOL = '\0';
×
1466
                        break;
×
1467
                    }
1468
                    if (strncmp(szLine, "0::", strlen("0::")) == 0)
1,127✔
1469
                    {
1470
                        snprintf(szGroupName, sizeof(szGroupName), "%s",
1,127✔
1471
                                 szLine + strlen("0::"));
1472
                        char *pszEOL = strchr(szGroupName, '\n');
1,127✔
1473
                        if (pszEOL)
1,127✔
1474
                            *pszEOL = '\0';
1,127✔
1475
                        break;
1,127✔
1476
                    }
1477
                }
1478
                fclose(f);
1,127✔
1479
            }
1480
        }
1481
        if (szGroupName[0])
1,127✔
1482
        {
1483
            char szFilename[256 + 64];
1484
            if (bFromMemory)
1,127✔
1485
            {
1486
                // cgroup V1
1487
                // Read memory.limit_in_byte in the whole szGroupName hierarchy
1488
                // Make sure to end up by reading
1489
                // /sys/fs/cgroup/memory/memory.limit_in_bytes itself, for
1490
                // scenarios like https://github.com/OSGeo/gdal/issues/8968
1491
                while (true)
1492
                {
1493
                    snprintf(szFilename, sizeof(szFilename),
×
1494
                             "/sys/fs/cgroup/memory/%s/memory.limit_in_bytes",
1495
                             szGroupName);
1496
                    FILE *f = fopen(szFilename, "rb");
×
1497
                    if (f)
×
1498
                    {
1499
                        // If no limitation, on 64 bit, 9223372036854771712 is returned.
1500
                        char szBuffer[32];
1501
                        const int nRead = static_cast<int>(
1502
                            fread(szBuffer, 1, sizeof(szBuffer) - 1, f));
×
1503
                        szBuffer[nRead] = 0;
×
1504
                        fclose(f);
×
1505
                        const GUIntBig nLimit = CPLScanUIntBig(szBuffer, nRead);
×
1506
                        nVal = static_cast<GIntBig>(
×
1507
                            std::min(static_cast<GUIntBig>(nVal), nLimit));
×
1508
                    }
1509
                    char *pszLastSlash = strrchr(szGroupName, '/');
×
1510
                    if (!pszLastSlash)
×
1511
                        break;
×
1512
                    *pszLastSlash = '\0';
×
1513
                }
×
1514
            }
1515
            else
1516
            {
1517
                // cgroup V2
1518
                // Read memory.max in the whole szGroupName hierarchy
1519
                while (true)
1520
                {
1521
                    snprintf(szFilename, sizeof(szFilename),
1,127✔
1522
                             "/sys/fs/cgroup/%s/memory.max", szGroupName);
1523
                    FILE *f = fopen(szFilename, "rb");
1,127✔
1524
                    if (f)
1,127✔
1525
                    {
1526
                        // If no limitation, "max" is returned.
1527
                        char szBuffer[32];
1528
                        int nRead = static_cast<int>(
1529
                            fread(szBuffer, 1, sizeof(szBuffer) - 1, f));
1,127✔
1530
                        szBuffer[nRead] = 0;
1,127✔
1531
                        if (nRead > 0 && szBuffer[nRead - 1] == '\n')
1,127✔
1532
                        {
1533
                            nRead--;
1,127✔
1534
                            szBuffer[nRead] = 0;
1,127✔
1535
                        }
1536
                        fclose(f);
1,127✔
1537
                        if (CPLGetValueType(szBuffer) == CPL_VALUE_INTEGER)
1,127✔
1538
                        {
1539
                            const GUIntBig nLimit =
1540
                                CPLScanUIntBig(szBuffer, nRead);
×
1541
                            nVal = static_cast<GIntBig>(
×
1542
                                std::min(static_cast<GUIntBig>(nVal), nLimit));
×
1543
                        }
1544
                    }
1545
                    char *pszLastSlash = strrchr(szGroupName, '/');
1,127✔
1546
                    if (!pszLastSlash || pszLastSlash == szGroupName)
1,127✔
1547
                        break;
1548
                    *pszLastSlash = '\0';
×
1549
                }
×
1550
            }
1551
        }
1552
#endif
1553

1554
        return nVal;
1,127✔
1555
    }();
8,724✔
1556

1557
    return nPhysicalRAM;
8,724✔
1558
}
1559

1560
#elif defined(__MACH__) && defined(__APPLE__)
1561

1562
#include <sys/types.h>
1563
#include <sys/sysctl.h>
1564

1565
GIntBig CPLGetPhysicalRAM(void)
1566
{
1567
    GIntBig nPhysMem = 0;
1568

1569
    int mib[2] = {CTL_HW, HW_MEMSIZE};
1570
    size_t nLengthRes = sizeof(nPhysMem);
1571
    sysctl(mib, CPL_ARRAYSIZE(mib), &nPhysMem, &nLengthRes, nullptr, 0);
1572

1573
    return nPhysMem;
1574
}
1575

1576
#elif defined(_WIN32)
1577

1578
// GlobalMemoryStatusEx requires _WIN32_WINNT >= 0x0500.
1579
#ifndef _WIN32_WINNT
1580
#define _WIN32_WINNT 0x0500
1581
#endif
1582
#include <windows.h>
1583

1584
GIntBig CPLGetPhysicalRAM(void)
1585
{
1586
    MEMORYSTATUSEX statex;
1587
    statex.ullTotalPhys = 0;
1588
    statex.dwLength = sizeof(statex);
1589
    GlobalMemoryStatusEx(&statex);
1590
    return static_cast<GIntBig>(statex.ullTotalPhys);
1591
}
1592

1593
#else
1594

1595
GIntBig CPLGetPhysicalRAM(void)
1596
{
1597
    static bool bOnce = false;
1598
    if (!bOnce)
1599
    {
1600
        bOnce = true;
1601
        CPLDebug("PORT", "No implementation for CPLGetPhysicalRAM()");
1602
    }
1603
    return 0;
1604
}
1605
#endif
1606

1607
/************************************************************************/
1608
/*                       CPLGetUsablePhysicalRAM()                      */
1609
/************************************************************************/
1610

1611
/** Return the total physical RAM, usable by a process, in bytes.
1612
 *
1613
 * This is the same as CPLGetPhysicalRAM() except it will limit to 2 GB
1614
 * for 32 bit processes.
1615
 *
1616
 * Starting with GDAL 2.4.0, it will also take account resource limits (virtual
1617
 * memory) on Posix systems. Starting with GDAL 3.6.1, it will also take into
1618
 * account RLIMIT_RSS on Linux.
1619
 *
1620
 * Note: This memory may already be partly used by other processes.
1621
 *
1622
 * @return the total physical RAM, usable by a process, in bytes (or 0
1623
 * in case of failure).
1624
 * @since GDAL 2.0
1625
 */
1626
GIntBig CPLGetUsablePhysicalRAM(void)
8,723✔
1627
{
1628
    GIntBig nRAM = CPLGetPhysicalRAM();
8,723✔
1629
#if SIZEOF_VOIDP == 4
1630
    if (nRAM > INT_MAX)
1631
        nRAM = INT_MAX;
1632
#endif
1633
#if HAVE_GETRLIMIT
1634
    struct rlimit sLimit;
1635
#if HAVE_RLIMIT_AS
1636
    const int res = RLIMIT_AS;
8,723✔
1637
#else
1638
    // OpenBSD currently doesn't support RLIMIT_AS (mandated by Posix though)
1639
    const int res = RLIMIT_DATA;
1640
#endif
1641
    if (getrlimit(res, &sLimit) == 0 && sLimit.rlim_cur != RLIM_INFINITY &&
8,723✔
1642
        static_cast<GIntBig>(sLimit.rlim_cur) < nRAM)
×
1643
    {
1644
        nRAM = static_cast<GIntBig>(sLimit.rlim_cur);
×
1645
    }
1646
#ifdef RLIMIT_RSS
1647
    // Helps with RSS limit set by the srun utility. Cf
1648
    // https://github.com/OSGeo/gdal/issues/6669
1649
    if (getrlimit(RLIMIT_RSS, &sLimit) == 0 &&
8,723✔
1650
        sLimit.rlim_cur != RLIM_INFINITY &&
8,723✔
1651
        static_cast<GIntBig>(sLimit.rlim_cur) < nRAM)
×
1652
    {
1653
        nRAM = static_cast<GIntBig>(sLimit.rlim_cur);
×
1654
    }
1655
#endif
1656
#endif
1657
    return nRAM;
8,723✔
1658
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc