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