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