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 31921800 : void *VSICalloc(size_t nCount, size_t nSize)
437 : {
438 : // cppcheck-suppress invalidFunctionArg
439 31921800 : 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 84572400 : void *VSIMalloc(size_t nSize)
553 :
554 : {
555 84572400 : 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 : *
680 : * If the pointer is NULL, VSIRealloc is equivalent to VSIMalloc.
681 : */
682 21625400 : void *VSIRealloc(void *pData, size_t nNewSize)
683 :
684 : {
685 : #ifdef DEBUG_VSIMALLOC
686 : if (pData == nullptr)
687 : return VSIMalloc(nNewSize);
688 :
689 : char *ptr = ((char *)pData) - 2 * sizeof(void *);
690 : VSICheckMarkerBegin(ptr);
691 :
692 : size_t nOldSize = 0;
693 : // cppcheck-suppress pointerSize
694 : memcpy(&nOldSize, ptr + sizeof(void *), sizeof(void *));
695 : VSICheckMarkerEnd(ptr, 2 * sizeof(void *) + nOldSize);
696 :
697 : if (nMaxPeakAllocSize < 0)
698 : {
699 : char *pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE");
700 : nMaxPeakAllocSize = pszMaxPeakAllocSize ? atoi(pszMaxPeakAllocSize) : 0;
701 : }
702 : if (nMaxPeakAllocSize > 0 &&
703 : static_cast<GIntBig>(nNewSize) > nMaxPeakAllocSize)
704 : return nullptr;
705 : #ifdef DEBUG_VSIMALLOC_STATS
706 : if (nMaxCumulAllocSize > 0 && static_cast<GIntBig>(nCurrentTotalAllocs) +
707 : static_cast<GIntBig>(nNewSize) -
708 : static_cast<GIntBig>(nOldSize) >
709 : nMaxCumulAllocSize)
710 : return nullptr;
711 : #endif
712 :
713 : ptr[2 * sizeof(void *) + nOldSize + 0] = 'I';
714 : ptr[2 * sizeof(void *) + nOldSize + 1] = 'S';
715 : ptr[2 * sizeof(void *) + nOldSize + 2] = 'V';
716 : ptr[2 * sizeof(void *) + nOldSize + 3] = 'E';
717 :
718 : #ifdef DEBUG_VSIMALLOC_MPROTECT
719 : char *newptr = nullptr;
720 : const size_t nPageSize = getpagesize();
721 : const size_t nRequestedSize =
722 : (nNewSize + 3 * sizeof(void *) + nPageSize - 1) & ~(nPageSize - 1);
723 : if (nRequestedSize < nNewSize)
724 : {
725 : ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
726 : ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
727 : ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
728 : ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
729 : return nullptr;
730 : }
731 : posix_memalign((void **)&newptr, nPageSize, nRequestedSize);
732 : if (newptr == nullptr)
733 : {
734 : ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
735 : ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
736 : ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
737 : ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
738 : return nullptr;
739 : }
740 : memcpy(newptr + 2 * sizeof(void *), pData, nOldSize);
741 : ptr[0] = 'M';
742 : ptr[1] = 'I';
743 : ptr[2] = 'S';
744 : ptr[3] = 'V';
745 : free(ptr);
746 : newptr[0] = 'V';
747 : newptr[1] = 'S';
748 : newptr[2] = 'I';
749 : newptr[3] = 'M';
750 : #else
751 : const size_t nRequestedSize = 3 * sizeof(void *) + nNewSize;
752 : if (nRequestedSize < nNewSize)
753 : {
754 : ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
755 : ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
756 : ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
757 : ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
758 : return nullptr;
759 : }
760 : void *newptr = realloc(ptr, nRequestedSize);
761 : if (newptr == nullptr)
762 : {
763 : ptr[2 * sizeof(void *) + nOldSize + 0] = 'E';
764 : ptr[2 * sizeof(void *) + nOldSize + 1] = 'V';
765 : ptr[2 * sizeof(void *) + nOldSize + 2] = 'S';
766 : ptr[2 * sizeof(void *) + nOldSize + 3] = 'I';
767 : return nullptr;
768 : }
769 : #endif
770 : ptr = static_cast<char *>(newptr);
771 : // cppcheck-suppress pointerSize
772 : memcpy(ptr + sizeof(void *), &nNewSize, sizeof(void *));
773 : ptr[2 * sizeof(void *) + nNewSize + 0] = 'E';
774 : ptr[2 * sizeof(void *) + nNewSize + 1] = 'V';
775 : ptr[2 * sizeof(void *) + nNewSize + 2] = 'S';
776 : ptr[2 * sizeof(void *) + nNewSize + 3] = 'I';
777 :
778 : #if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
779 : {
780 : CPLMutexHolderD(&hMemStatMutex);
781 : #ifdef DEBUG_VSIMALLOC_VERBOSE
782 : if (nNewSize > THRESHOLD_PRINT)
783 : {
784 : fprintf(
785 : stderr,
786 : "Thread[%p] VSIRealloc(%p, %d) = %p" /*ok*/
787 : #ifdef DEBUG_VSIMALLOC_STATS
788 : ", current_cumul = " CPL_FRMT_GUIB
789 : #ifdef DEBUG_BLOCK_CACHE_USE
790 : ", block_cache_used = " CPL_FRMT_GIB
791 : #endif
792 : ", mal+cal-free = %d"
793 : #endif
794 : "\n",
795 : (void *)CPLGetPID(), pData, static_cast<int>(nNewSize),
796 : ptr + 2 * sizeof(void *)
797 : #ifdef DEBUG_VSIMALLOC_STATS
798 : ,
799 : static_cast<GUIntBig>(nCurrentTotalAllocs - nOldSize + nNewSize)
800 : #ifdef DEBUG_BLOCK_CACHE_USE
801 : ,
802 : GDALGetCacheUsed64()
803 : #endif
804 : ,
805 : static_cast<int>(nVSIMallocs + nVSICallocs - nVSIFrees)
806 : #endif
807 : );
808 : }
809 : #endif
810 : #ifdef DEBUG_VSIMALLOC_STATS
811 : nVSIReallocs++;
812 : nCurrentTotalAllocs -= nOldSize;
813 : nCurrentTotalAllocs += nNewSize;
814 : if (nCurrentTotalAllocs > nMaxTotalAllocs)
815 : nMaxTotalAllocs = nCurrentTotalAllocs;
816 : #endif
817 : }
818 : #endif
819 : return ptr + 2 * sizeof(void *);
820 : #else
821 21625400 : return realloc(pData, nNewSize);
822 : #endif
823 : }
824 :
825 : /************************************************************************/
826 : /* VSIFree() */
827 : /************************************************************************/
828 :
829 : /** Analog of free() for data allocated with VSIMalloc(), VSICalloc(),
830 : * VSIRealloc().
831 : *
832 : * It is not an error to call VSIFree with a NULL pointer, and it will
833 : * have no effect.
834 : */
835 135281000 : void VSIFree(void *pData)
836 :
837 : {
838 : #ifdef DEBUG_VSIMALLOC
839 : if (pData == nullptr)
840 : return;
841 :
842 : char *ptr = ((char *)pData) - 2 * sizeof(void *);
843 : VSICheckMarkerBegin(ptr);
844 : size_t nOldSize = 0;
845 : // cppcheck-suppress pointerSize
846 : memcpy(&nOldSize, ptr + sizeof(void *), sizeof(void *));
847 : VSICheckMarkerEnd(ptr, 2 * sizeof(void *) + nOldSize);
848 : ptr[0] = 'M';
849 : ptr[1] = 'I';
850 : ptr[2] = 'S';
851 : ptr[3] = 'V';
852 : ptr[2 * sizeof(void *) + nOldSize + 0] = 'I';
853 : ptr[2 * sizeof(void *) + nOldSize + 1] = 'S';
854 : ptr[2 * sizeof(void *) + nOldSize + 2] = 'V';
855 : ptr[2 * sizeof(void *) + nOldSize + 3] = 'E';
856 : #if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE)
857 : {
858 : CPLMutexHolderD(&hMemStatMutex);
859 : #ifdef DEBUG_VSIMALLOC_VERBOSE
860 : if (nOldSize > THRESHOLD_PRINT)
861 : {
862 : fprintf(stderr, "Thread[%p] VSIFree(%p, (%d bytes))\n", /*ok*/
863 : (void *)CPLGetPID(), pData, static_cast<int>(nOldSize));
864 : }
865 : #endif
866 : #ifdef DEBUG_VSIMALLOC_STATS
867 : nVSIFrees++;
868 : nCurrentTotalAllocs -= nOldSize;
869 : #endif
870 : }
871 : #endif
872 :
873 : #ifdef DEBUG_VSIMALLOC_MPROTECT
874 : mprotect(ptr, nOldSize + 2 * sizeof(void *), PROT_NONE);
875 : #else
876 : free(ptr);
877 : #endif
878 :
879 : #else
880 135281000 : if (pData != nullptr)
881 114146000 : free(pData);
882 : #endif
883 135281000 : }
884 :
885 : /************************************************************************/
886 : /* VSIMallocAligned() */
887 : /************************************************************************/
888 :
889 : /** Allocates a buffer with an alignment constraint.
890 : *
891 : * The return value must be freed with VSIFreeAligned().
892 : *
893 : * @param nAlignment Must be a power of 2, multiple of sizeof(void*), and
894 : * lesser than 256.
895 : * @param nSize Size of the buffer to allocate.
896 : * @return a buffer aligned on nAlignment and of size nSize, or NULL
897 : * @since GDAL 2.2
898 : */
899 :
900 3180340 : void *VSIMallocAligned(size_t nAlignment, size_t nSize)
901 : {
902 : // In particular for posix_memalign() where behavior when passing
903 : // nSize == 0 is technically implementation defined (Valgrind complains),
904 : // so let's always return NULL.
905 3180340 : if (nSize == 0)
906 0 : return nullptr;
907 : #if defined(HAVE_POSIX_MEMALIGN) && !defined(DEBUG_VSIMALLOC)
908 3180340 : void *pRet = nullptr;
909 3180340 : if (posix_memalign(&pRet, nAlignment, nSize) != 0)
910 : {
911 4 : pRet = nullptr;
912 : }
913 3180340 : return pRet;
914 : #elif defined(_WIN32) && !defined(DEBUG_VSIMALLOC)
915 : return _aligned_malloc(nSize, nAlignment);
916 : #else
917 : // Check constraints on alignment.
918 : if (nAlignment < sizeof(void *) || nAlignment >= 256 ||
919 : (nAlignment & (nAlignment - 1)) != 0)
920 : return nullptr;
921 : // Detect overflow.
922 : if (nSize + nAlignment < nSize)
923 : return nullptr;
924 : GByte *pabyData = static_cast<GByte *>(VSIMalloc(nSize + nAlignment));
925 : if (pabyData == nullptr)
926 : return nullptr;
927 : size_t nShift =
928 : nAlignment - (reinterpret_cast<size_t>(pabyData) % nAlignment);
929 : GByte *pabyAligned = pabyData + nShift;
930 : // Guaranteed to fit on a byte since nAlignment < 256.
931 : pabyAligned[-1] = static_cast<GByte>(nShift);
932 : return pabyAligned;
933 : #endif
934 : }
935 :
936 : /************************************************************************/
937 : /* VSIMallocAlignedAuto() */
938 : /************************************************************************/
939 :
940 : /** Allocates a buffer with an alignment constraint such that it can be
941 : * used by the most demanding vector instruction set on that platform.
942 : *
943 : * The return value must be freed with VSIFreeAligned().
944 : *
945 : * @param nSize Size of the buffer to allocate.
946 : * @return an aligned buffer of size nSize, or NULL
947 : * @since GDAL 2.2
948 : */
949 :
950 3180340 : void *VSIMallocAlignedAuto(size_t nSize)
951 : {
952 : // We could potentially dynamically detect the capability of the CPU
953 : // but to simplify use 64 for AVX512 requirements (we use only AVX256
954 : // currently).
955 3180340 : return VSIMallocAligned(64, nSize);
956 : }
957 :
958 : /************************************************************************/
959 : /* VSIMallocAlignedAutoVerbose() */
960 : /************************************************************************/
961 :
962 : /** See VSIMallocAlignedAuto() */
963 3180330 : void *VSIMallocAlignedAutoVerbose(size_t nSize, const char *pszFile, int nLine)
964 : {
965 3180330 : void *pRet = VSIMallocAlignedAuto(nSize);
966 3180270 : if (pRet == nullptr && nSize != 0)
967 : {
968 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
969 : "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
970 : pszFile ? pszFile : "(unknown file)", nLine,
971 : static_cast<GUIntBig>(nSize));
972 : }
973 3180270 : return pRet;
974 : }
975 :
976 : /************************************************************************/
977 : /* VSIFreeAligned() */
978 : /************************************************************************/
979 :
980 : /** Free a buffer allocated with VSIMallocAligned().
981 : *
982 : * @param ptr Buffer to free.
983 : * @since GDAL 2.2
984 : */
985 :
986 3202760 : void VSIFreeAligned(void *ptr)
987 : {
988 : #if defined(HAVE_POSIX_MEMALIGN) && !defined(DEBUG_VSIMALLOC)
989 3202760 : free(ptr);
990 : #elif defined(_WIN32) && !defined(DEBUG_VSIMALLOC)
991 : _aligned_free(ptr);
992 : #else
993 : if (ptr == nullptr)
994 : return;
995 : GByte *pabyAligned = static_cast<GByte *>(ptr);
996 : size_t nShift = pabyAligned[-1];
997 : VSIFree(pabyAligned - nShift);
998 : #endif
999 3202760 : }
1000 :
1001 : /************************************************************************/
1002 : /* VSIStrdup() */
1003 : /************************************************************************/
1004 :
1005 : /** Analog of strdup(). Use VSIFree() to free */
1006 33077900 : char *VSIStrdup(const char *pszString)
1007 :
1008 : {
1009 33077900 : const size_t nSize = strlen(pszString) + 1;
1010 33077900 : char *ptr = static_cast<char *>(VSIMalloc(nSize));
1011 33077800 : if (ptr == nullptr)
1012 0 : return nullptr;
1013 33077800 : memcpy(ptr, pszString, nSize);
1014 33077800 : return ptr;
1015 : }
1016 :
1017 : /************************************************************************/
1018 : /* VSICheckMul2() */
1019 : /************************************************************************/
1020 :
1021 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
1022 283085 : static size_t VSICheckMul2(size_t mul1, size_t mul2, bool *pbOverflowFlag,
1023 : const char *pszFile, int nLine)
1024 : {
1025 283085 : const size_t res = mul1 * mul2;
1026 283085 : if (mul1 != 0)
1027 : {
1028 283082 : if (res / mul1 == mul2)
1029 : {
1030 283081 : if (pbOverflowFlag)
1031 283081 : *pbOverflowFlag = FALSE;
1032 283081 : return res;
1033 : }
1034 : else
1035 : {
1036 1 : if (pbOverflowFlag)
1037 1 : *pbOverflowFlag = TRUE;
1038 1 : CPLError(CE_Failure, CPLE_OutOfMemory,
1039 : "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
1040 : " * " CPL_FRMT_GUIB,
1041 : pszFile ? pszFile : "(unknown file)", nLine,
1042 : static_cast<GUIntBig>(mul1), static_cast<GUIntBig>(mul2));
1043 : }
1044 : }
1045 : else
1046 : {
1047 3 : if (pbOverflowFlag)
1048 8 : *pbOverflowFlag = FALSE;
1049 : }
1050 9 : return 0;
1051 : }
1052 :
1053 : /************************************************************************/
1054 : /* VSICheckMul3() */
1055 : /************************************************************************/
1056 :
1057 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
1058 97561 : static size_t VSICheckMul3(size_t mul1, size_t mul2, size_t mul3,
1059 : bool *pbOverflowFlag, const char *pszFile, int nLine)
1060 : {
1061 97561 : if (mul1 != 0)
1062 : {
1063 97556 : const size_t res = mul1 * mul2;
1064 97556 : if (res / mul1 == mul2)
1065 : {
1066 97550 : const size_t res2 = res * mul3;
1067 97550 : if (mul3 != 0)
1068 : {
1069 97549 : if (res2 / mul3 == res)
1070 : {
1071 97549 : if (pbOverflowFlag)
1072 97548 : *pbOverflowFlag = false;
1073 97549 : return res2;
1074 : }
1075 : else
1076 : {
1077 0 : if (pbOverflowFlag)
1078 2 : *pbOverflowFlag = true;
1079 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1080 : "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
1081 : " * " CPL_FRMT_GUIB " * " CPL_FRMT_GUIB,
1082 : pszFile ? pszFile : "(unknown file)", nLine,
1083 : static_cast<GUIntBig>(mul1),
1084 : static_cast<GUIntBig>(mul2),
1085 : static_cast<GUIntBig>(mul3));
1086 : }
1087 : }
1088 : else
1089 : {
1090 1 : if (pbOverflowFlag)
1091 1 : *pbOverflowFlag = false;
1092 : }
1093 : }
1094 : else
1095 : {
1096 6 : if (pbOverflowFlag)
1097 1 : *pbOverflowFlag = true;
1098 6 : CPLError(CE_Failure, CPLE_OutOfMemory,
1099 : "%s: %d: Multiplication overflow : " CPL_FRMT_GUIB
1100 : " * " CPL_FRMT_GUIB " * " CPL_FRMT_GUIB,
1101 : pszFile ? pszFile : "(unknown file)", nLine,
1102 : static_cast<GUIntBig>(mul1), static_cast<GUIntBig>(mul2),
1103 : static_cast<GUIntBig>(mul3));
1104 : }
1105 : }
1106 : else
1107 : {
1108 5 : if (pbOverflowFlag)
1109 1 : *pbOverflowFlag = false;
1110 : }
1111 5 : return 0;
1112 : }
1113 :
1114 : /**
1115 : VSIMalloc2 allocates (nSize1 * nSize2) bytes.
1116 : In case of overflow of the multiplication, or if memory allocation fails, a
1117 : NULL pointer is returned and a CE_Failure error is raised with CPLError().
1118 : If nSize1 == 0 || nSize2 == 0, a NULL pointer will also be returned.
1119 : CPLFree() or VSIFree() can be used to free memory allocated by this function.
1120 : */
1121 153 : void CPL_DLL *VSIMalloc2(size_t nSize1, size_t nSize2)
1122 : {
1123 153 : return VSIMalloc2Verbose(nSize1, nSize2, nullptr, 0);
1124 : }
1125 :
1126 : /**
1127 : VSIMalloc3 allocates (nSize1 * nSize2 * nSize3) bytes.
1128 : In case of overflow of the multiplication, or if memory allocation fails, a
1129 : NULL pointer is returned and a CE_Failure error is raised with CPLError().
1130 : If nSize1 == 0 || nSize2 == 0 || nSize3 == 0, a NULL pointer will also be
1131 : returned. CPLFree() or VSIFree() can be used to free memory allocated by this
1132 : function.
1133 : */
1134 884 : void CPL_DLL *VSIMalloc3(size_t nSize1, size_t nSize2, size_t nSize3)
1135 : {
1136 884 : return VSIMalloc3Verbose(nSize1, nSize2, nSize3, nullptr, 0);
1137 : }
1138 :
1139 : /************************************************************************/
1140 : /* VSIMallocVerbose() */
1141 : /************************************************************************/
1142 :
1143 7530340 : void *VSIMallocVerbose(size_t nSize, const char *pszFile, int nLine)
1144 : {
1145 7530340 : void *pRet = VSIMalloc(nSize);
1146 7530350 : if (pRet == nullptr && nSize != 0)
1147 : {
1148 2 : CPLError(CE_Failure, CPLE_OutOfMemory,
1149 : "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1150 : pszFile ? pszFile : "(unknown file)", nLine,
1151 : static_cast<GUIntBig>(nSize));
1152 : }
1153 7530300 : return pRet;
1154 : }
1155 :
1156 : /************************************************************************/
1157 : /* VSIMalloc2Verbose() */
1158 : /************************************************************************/
1159 :
1160 283091 : void *VSIMalloc2Verbose(size_t nSize1, size_t nSize2, const char *pszFile,
1161 : int nLine)
1162 : {
1163 283091 : bool bOverflowFlag = false;
1164 : const size_t nSizeToAllocate =
1165 283091 : VSICheckMul2(nSize1, nSize2, &bOverflowFlag, pszFile, nLine);
1166 283087 : if (bOverflowFlag || nSizeToAllocate == 0)
1167 39 : return nullptr;
1168 :
1169 283048 : void *pRet = VSIMalloc(nSizeToAllocate);
1170 283061 : if (pRet == nullptr)
1171 : {
1172 2 : CPLError(CE_Failure, CPLE_OutOfMemory,
1173 : "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1174 : pszFile ? pszFile : "(unknown file)", nLine,
1175 : static_cast<GUIntBig>(nSize1) * static_cast<GUIntBig>(nSize2));
1176 : }
1177 283061 : return pRet;
1178 : }
1179 :
1180 : /************************************************************************/
1181 : /* VSIMalloc3Verbose() */
1182 : /************************************************************************/
1183 :
1184 97560 : void *VSIMalloc3Verbose(size_t nSize1, size_t nSize2, size_t nSize3,
1185 : const char *pszFile, int nLine)
1186 : {
1187 97560 : bool bOverflowFlag = false;
1188 : size_t nSizeToAllocate =
1189 97560 : VSICheckMul3(nSize1, nSize2, nSize3, &bOverflowFlag, pszFile, nLine);
1190 97564 : if (bOverflowFlag || nSizeToAllocate == 0)
1191 24 : return nullptr;
1192 :
1193 97540 : void *pRet = VSIMalloc(nSizeToAllocate);
1194 97570 : if (pRet == nullptr)
1195 : {
1196 4 : CPLError(CE_Failure, CPLE_OutOfMemory,
1197 : "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1198 : pszFile ? pszFile : "(unknown file)", nLine,
1199 4 : static_cast<GUIntBig>(nSize1) * static_cast<GUIntBig>(nSize2) *
1200 : static_cast<GUIntBig>(nSize3));
1201 : }
1202 97570 : return pRet;
1203 : }
1204 :
1205 : /************************************************************************/
1206 : /* VSICallocVerbose() */
1207 : /************************************************************************/
1208 :
1209 8951200 : void *VSICallocVerbose(size_t nCount, size_t nSize, const char *pszFile,
1210 : int nLine)
1211 : {
1212 8951200 : void *pRet = VSICalloc(nCount, nSize);
1213 8951180 : if (pRet == nullptr && nCount != 0 && nSize != 0)
1214 : {
1215 4 : CPLError(CE_Failure, CPLE_OutOfMemory,
1216 : "%s, %d: cannot allocate " CPL_FRMT_GUIB "x" CPL_FRMT_GUIB
1217 : " bytes",
1218 : pszFile ? pszFile : "(unknown file)", nLine,
1219 : static_cast<GUIntBig>(nCount), static_cast<GUIntBig>(nSize));
1220 : }
1221 8951140 : return pRet;
1222 : }
1223 :
1224 : /************************************************************************/
1225 : /* VSIReallocVerbose() */
1226 : /************************************************************************/
1227 :
1228 17209000 : void *VSIReallocVerbose(void *pOldPtr, size_t nNewSize, const char *pszFile,
1229 : int nLine)
1230 : {
1231 17209000 : void *pRet = VSIRealloc(pOldPtr, nNewSize);
1232 17208900 : if (pRet == nullptr && nNewSize != 0)
1233 : {
1234 2 : CPLError(CE_Failure, CPLE_OutOfMemory,
1235 : "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1236 : pszFile ? pszFile : "(unknown file)", nLine,
1237 : static_cast<GUIntBig>(nNewSize));
1238 : }
1239 17208900 : return pRet;
1240 : }
1241 :
1242 : /************************************************************************/
1243 : /* VSIStrdupVerbose() */
1244 : /************************************************************************/
1245 :
1246 10308100 : char *VSIStrdupVerbose(const char *pszStr, const char *pszFile, int nLine)
1247 : {
1248 10308100 : char *pRet = VSIStrdup(pszStr);
1249 10308000 : if (pRet == nullptr)
1250 : {
1251 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1252 : "%s, %d: cannot allocate " CPL_FRMT_GUIB " bytes",
1253 : pszFile ? pszFile : "(unknown file)", nLine,
1254 0 : static_cast<GUIntBig>(strlen(pszStr) + 1));
1255 : }
1256 10307800 : return pRet;
1257 : }
1258 :
1259 : /************************************************************************/
1260 : /* VSIStat() */
1261 : /************************************************************************/
1262 :
1263 986 : int VSIStat(const char *pszFilename, VSIStatBuf *pStatBuf)
1264 :
1265 : {
1266 : #if defined(_WIN32)
1267 : if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
1268 : {
1269 : wchar_t *pwszFilename =
1270 : CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
1271 :
1272 : int nResult =
1273 : _wstat(pwszFilename, reinterpret_cast<struct _stat *>(pStatBuf));
1274 :
1275 : CPLFree(pwszFilename);
1276 :
1277 : return nResult;
1278 : }
1279 :
1280 : #endif
1281 986 : return stat(pszFilename, pStatBuf);
1282 : }
1283 :
1284 : /************************************************************************/
1285 : /* VSITime() */
1286 : /************************************************************************/
1287 :
1288 0 : unsigned long VSITime(unsigned long *pnTimeToSet)
1289 :
1290 : {
1291 : time_t tTime;
1292 :
1293 0 : tTime = time(nullptr);
1294 :
1295 0 : if (pnTimeToSet != nullptr)
1296 0 : *pnTimeToSet = static_cast<unsigned long>(tTime);
1297 :
1298 0 : return static_cast<unsigned long>(tTime);
1299 : }
1300 :
1301 : /************************************************************************/
1302 : /* VSICTime() */
1303 : /************************************************************************/
1304 :
1305 4 : const char *VSICTime(unsigned long nTime)
1306 :
1307 : {
1308 4 : time_t tTime = static_cast<time_t>(nTime);
1309 : #if HAVE_CTIME_R
1310 : // Cf https://linux.die.net/man/3/ctime_r:
1311 : // "The reentrant version ctime_r() does the same, but stores the string in
1312 : // a user-supplied buffer which should have room for at least 26 bytes"
1313 : char buffer[26];
1314 4 : char *ret = ctime_r(&tTime, buffer);
1315 4 : return ret ? CPLSPrintf("%s", ret) : nullptr;
1316 : #elif defined(_WIN32)
1317 : char buffer[26];
1318 : return ctime_s(buffer, sizeof(buffer), &tTime) == 0
1319 : ? CPLSPrintf("%s", buffer)
1320 : : nullptr;
1321 : #else
1322 : return reinterpret_cast<const char *>(ctime(&tTime));
1323 : #endif
1324 : }
1325 :
1326 : /************************************************************************/
1327 : /* VSIGMTime() */
1328 : /************************************************************************/
1329 :
1330 0 : struct tm *VSIGMTime(const time_t *pnTime, struct tm *poBrokenTime)
1331 : {
1332 :
1333 : #if HAVE_GMTIME_R
1334 0 : gmtime_r(pnTime, poBrokenTime);
1335 0 : return poBrokenTime;
1336 : #elif defined(_WIN32)
1337 : return gmtime_s(poBrokenTime, pnTime) == 0 ? poBrokenTime : nullptr;
1338 : #else
1339 : struct tm *poTime = gmtime(pnTime);
1340 : memcpy(poBrokenTime, poTime, sizeof(tm));
1341 : return poBrokenTime;
1342 : #endif
1343 : }
1344 :
1345 : /************************************************************************/
1346 : /* VSILocalTime() */
1347 : /************************************************************************/
1348 :
1349 527 : struct tm *VSILocalTime(const time_t *pnTime, struct tm *poBrokenTime)
1350 : {
1351 :
1352 : #if HAVE_LOCALTIME_R
1353 527 : localtime_r(pnTime, poBrokenTime);
1354 527 : return poBrokenTime;
1355 : #elif defined(_WIN32)
1356 : return localtime_s(poBrokenTime, pnTime) == 0 ? poBrokenTime : nullptr;
1357 : #else
1358 : struct tm *poTime = localtime(pnTime);
1359 : memcpy(poBrokenTime, poTime, sizeof(tm));
1360 : return poBrokenTime;
1361 : #endif
1362 : }
1363 :
1364 : /************************************************************************/
1365 : /* VSIStrerror() */
1366 : /************************************************************************/
1367 :
1368 : /** Return the error string corresponding to the error number. Do not free it */
1369 153 : char *VSIStrerror(int nErrno)
1370 :
1371 : {
1372 153 : return strerror(nErrno);
1373 : }
1374 :
1375 : /************************************************************************/
1376 : /* CPLGetPhysicalRAM() */
1377 : /************************************************************************/
1378 :
1379 : #if HAVE_SC_PHYS_PAGES
1380 :
1381 : /** Return the total physical RAM in bytes.
1382 : *
1383 : * In the context of a container using cgroups (typically Docker), this
1384 : * will take into account that limitation (starting with GDAL 2.4.0 and
1385 : * with extra fixes in GDAL 3.6.3)
1386 : *
1387 : * You should generally use CPLGetUsablePhysicalRAM() instead.
1388 : *
1389 : * @return the total physical RAM in bytes (or 0 in case of failure).
1390 : * @since GDAL 2.0
1391 : */
1392 5902 : GIntBig CPLGetPhysicalRAM(void)
1393 : {
1394 5902 : const long nPhysPages = sysconf(_SC_PHYS_PAGES);
1395 5902 : const long nPageSize = sysconf(_SC_PAGESIZE);
1396 11804 : if (nPhysPages <= 0 || nPageSize <= 0 ||
1397 5902 : nPhysPages > std::numeric_limits<GIntBig>::max() / nPageSize)
1398 : {
1399 0 : return 0;
1400 : }
1401 5902 : GIntBig nVal = static_cast<GIntBig>(nPhysPages) * nPageSize;
1402 :
1403 : #ifdef __linux
1404 : {
1405 : // Take into account MemTotal in /proc/meminfo
1406 : // which seems to be necessary for some container solutions
1407 : // Cf https://lists.osgeo.org/pipermail/gdal-dev/2023-January/056784.html
1408 5902 : FILE *f = fopen("/proc/meminfo", "rb");
1409 5902 : if (f)
1410 : {
1411 : char szLine[256];
1412 5902 : while (fgets(szLine, sizeof(szLine), f))
1413 : {
1414 : // Find line like "MemTotal: 32525176 kB"
1415 5902 : if (strncmp(szLine, "MemTotal:", strlen("MemTotal:")) == 0)
1416 : {
1417 5902 : char *pszVal = szLine + strlen("MemTotal:");
1418 5902 : pszVal += strspn(pszVal, " ");
1419 5902 : char *pszEnd = strstr(pszVal, " kB");
1420 5902 : if (pszEnd)
1421 : {
1422 5902 : *pszEnd = 0;
1423 5902 : if (CPLGetValueType(pszVal) == CPL_VALUE_INTEGER)
1424 : {
1425 : const GUIntBig nLimit =
1426 11804 : CPLScanUIntBig(
1427 5902 : pszVal, static_cast<int>(strlen(pszVal))) *
1428 5902 : 1024;
1429 5902 : nVal = static_cast<GIntBig>(
1430 5902 : std::min(static_cast<GUIntBig>(nVal), nLimit));
1431 : }
1432 : }
1433 5902 : break;
1434 : }
1435 : }
1436 5902 : fclose(f);
1437 : }
1438 : }
1439 :
1440 : char szGroupName[256];
1441 5902 : bool bFromMemory = false;
1442 5902 : szGroupName[0] = 0;
1443 : {
1444 5902 : FILE *f = fopen("/proc/self/cgroup", "rb");
1445 5902 : if (f)
1446 : {
1447 : char szLine[256];
1448 : // Find line like "6:memory:/user.slice/user-1000.slice/user@1000.service"
1449 : // and store "/user.slice/user-1000.slice/user@1000.service" in
1450 : // szMemoryPath for cgroup V1 or single line "0::/...." for cgroup V2.
1451 5902 : while (fgets(szLine, sizeof(szLine), f))
1452 : {
1453 5902 : const char *pszMemory = strstr(szLine, ":memory:");
1454 5902 : if (pszMemory)
1455 : {
1456 0 : bFromMemory = true;
1457 0 : snprintf(szGroupName, sizeof(szGroupName), "%s",
1458 : pszMemory + strlen(":memory:"));
1459 0 : char *pszEOL = strchr(szGroupName, '\n');
1460 0 : if (pszEOL)
1461 0 : *pszEOL = '\0';
1462 0 : break;
1463 : }
1464 5902 : if (strncmp(szLine, "0::", strlen("0::")) == 0)
1465 : {
1466 5902 : snprintf(szGroupName, sizeof(szGroupName), "%s",
1467 : szLine + strlen("0::"));
1468 5902 : char *pszEOL = strchr(szGroupName, '\n');
1469 5902 : if (pszEOL)
1470 5902 : *pszEOL = '\0';
1471 5902 : break;
1472 : }
1473 : }
1474 5902 : fclose(f);
1475 : }
1476 : }
1477 5902 : if (szGroupName[0])
1478 : {
1479 : char szFilename[256 + 64];
1480 5902 : if (bFromMemory)
1481 : {
1482 : // cgroup V1
1483 : // Read memory.limit_in_byte in the whole szGroupName hierarchy
1484 : // Make sure to end up by reading
1485 : // /sys/fs/cgroup/memory/memory.limit_in_bytes itself, for
1486 : // scenarios like https://github.com/OSGeo/gdal/issues/8968
1487 : while (true)
1488 : {
1489 0 : snprintf(szFilename, sizeof(szFilename),
1490 : "/sys/fs/cgroup/memory/%s/memory.limit_in_bytes",
1491 : szGroupName);
1492 0 : FILE *f = fopen(szFilename, "rb");
1493 0 : if (f)
1494 : {
1495 : // If no limitation, on 64 bit, 9223372036854771712 is returned.
1496 : char szBuffer[32];
1497 : const int nRead = static_cast<int>(
1498 0 : fread(szBuffer, 1, sizeof(szBuffer) - 1, f));
1499 0 : szBuffer[nRead] = 0;
1500 0 : fclose(f);
1501 0 : const GUIntBig nLimit = CPLScanUIntBig(szBuffer, nRead);
1502 0 : nVal = static_cast<GIntBig>(
1503 0 : std::min(static_cast<GUIntBig>(nVal), nLimit));
1504 : }
1505 0 : char *pszLastSlash = strrchr(szGroupName, '/');
1506 0 : if (!pszLastSlash)
1507 0 : break;
1508 0 : *pszLastSlash = '\0';
1509 0 : }
1510 : }
1511 : else
1512 : {
1513 : // cgroup V2
1514 : // Read memory.max in the whole szGroupName hierarchy
1515 : while (true)
1516 : {
1517 5902 : snprintf(szFilename, sizeof(szFilename),
1518 : "/sys/fs/cgroup/%s/memory.max", szGroupName);
1519 5902 : FILE *f = fopen(szFilename, "rb");
1520 5902 : if (f)
1521 : {
1522 : // If no limitation, "max" is returned.
1523 : char szBuffer[32];
1524 : int nRead = static_cast<int>(
1525 5902 : fread(szBuffer, 1, sizeof(szBuffer) - 1, f));
1526 5902 : szBuffer[nRead] = 0;
1527 5902 : if (nRead > 0 && szBuffer[nRead - 1] == '\n')
1528 : {
1529 5902 : nRead--;
1530 5902 : szBuffer[nRead] = 0;
1531 : }
1532 5902 : fclose(f);
1533 5902 : if (CPLGetValueType(szBuffer) == CPL_VALUE_INTEGER)
1534 : {
1535 0 : const GUIntBig nLimit = CPLScanUIntBig(szBuffer, nRead);
1536 0 : nVal = static_cast<GIntBig>(
1537 0 : std::min(static_cast<GUIntBig>(nVal), nLimit));
1538 : }
1539 : }
1540 5902 : char *pszLastSlash = strrchr(szGroupName, '/');
1541 5902 : if (!pszLastSlash || pszLastSlash == szGroupName)
1542 : break;
1543 0 : *pszLastSlash = '\0';
1544 0 : }
1545 : }
1546 : }
1547 : #endif
1548 :
1549 5902 : return nVal;
1550 : }
1551 :
1552 : #elif defined(__MACH__) && defined(__APPLE__)
1553 :
1554 : #include <sys/types.h>
1555 : #include <sys/sysctl.h>
1556 :
1557 : GIntBig CPLGetPhysicalRAM(void)
1558 : {
1559 : GIntBig nPhysMem = 0;
1560 :
1561 : int mib[2] = {CTL_HW, HW_MEMSIZE};
1562 : size_t nLengthRes = sizeof(nPhysMem);
1563 : sysctl(mib, CPL_ARRAYSIZE(mib), &nPhysMem, &nLengthRes, nullptr, 0);
1564 :
1565 : return nPhysMem;
1566 : }
1567 :
1568 : #elif defined(_WIN32)
1569 :
1570 : // GlobalMemoryStatusEx requires _WIN32_WINNT >= 0x0500.
1571 : #ifndef _WIN32_WINNT
1572 : #define _WIN32_WINNT 0x0500
1573 : #endif
1574 : #include <windows.h>
1575 :
1576 : GIntBig CPLGetPhysicalRAM(void)
1577 : {
1578 : MEMORYSTATUSEX statex;
1579 : statex.ullTotalPhys = 0;
1580 : statex.dwLength = sizeof(statex);
1581 : GlobalMemoryStatusEx(&statex);
1582 : return static_cast<GIntBig>(statex.ullTotalPhys);
1583 : }
1584 :
1585 : #else
1586 :
1587 : GIntBig CPLGetPhysicalRAM(void)
1588 : {
1589 : static bool bOnce = false;
1590 : if (!bOnce)
1591 : {
1592 : bOnce = true;
1593 : CPLDebug("PORT", "No implementation for CPLGetPhysicalRAM()");
1594 : }
1595 : return 0;
1596 : }
1597 : #endif
1598 :
1599 : /************************************************************************/
1600 : /* CPLGetUsablePhysicalRAM() */
1601 : /************************************************************************/
1602 :
1603 : /** Return the total physical RAM, usable by a process, in bytes.
1604 : *
1605 : * This is the same as CPLGetPhysicalRAM() except it will limit to 2 GB
1606 : * for 32 bit processes.
1607 : *
1608 : * Starting with GDAL 2.4.0, it will also take account resource limits (virtual
1609 : * memory) on Posix systems. Starting with GDAL 3.6.1, it will also take into
1610 : * account RLIMIT_RSS on Linux.
1611 : *
1612 : * Note: This memory may already be partly used by other processes.
1613 : *
1614 : * @return the total physical RAM, usable by a process, in bytes (or 0
1615 : * in case of failure).
1616 : * @since GDAL 2.0
1617 : */
1618 5901 : GIntBig CPLGetUsablePhysicalRAM(void)
1619 : {
1620 5901 : GIntBig nRAM = CPLGetPhysicalRAM();
1621 : #if SIZEOF_VOIDP == 4
1622 : if (nRAM > INT_MAX)
1623 : nRAM = INT_MAX;
1624 : #endif
1625 : #if HAVE_GETRLIMIT
1626 : struct rlimit sLimit;
1627 : #if HAVE_RLIMIT_AS
1628 5901 : const int res = RLIMIT_AS;
1629 : #else
1630 : // OpenBSD currently doesn't support RLIMIT_AS (mandated by Posix though)
1631 : const int res = RLIMIT_DATA;
1632 : #endif
1633 5901 : if (getrlimit(res, &sLimit) == 0 && sLimit.rlim_cur != RLIM_INFINITY &&
1634 0 : static_cast<GIntBig>(sLimit.rlim_cur) < nRAM)
1635 : {
1636 0 : nRAM = static_cast<GIntBig>(sLimit.rlim_cur);
1637 : }
1638 : #ifdef RLIMIT_RSS
1639 : // Helps with RSS limit set by the srun utility. Cf
1640 : // https://github.com/OSGeo/gdal/issues/6669
1641 5901 : if (getrlimit(RLIMIT_RSS, &sLimit) == 0 &&
1642 5901 : sLimit.rlim_cur != RLIM_INFINITY &&
1643 0 : static_cast<GIntBig>(sLimit.rlim_cur) < nRAM)
1644 : {
1645 0 : nRAM = static_cast<GIntBig>(sLimit.rlim_cur);
1646 : }
1647 : #endif
1648 : #endif
1649 5901 : return nRAM;
1650 : }
|