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