1 /* Alloc.c -- Memory allocation functions
\r
2 2021-07-13 : Igor Pavlov : Public domain */
\r
15 /* #define _SZ_ALLOC_DEBUG */
\r
17 /* use _SZ_ALLOC_DEBUG to debug alloc/free operations */
\r
18 #ifdef _SZ_ALLOC_DEBUG
\r
21 int g_allocCount = 0;
\r
22 int g_allocCountMid = 0;
\r
23 int g_allocCountBig = 0;
\r
26 #define CONVERT_INT_TO_STR(charType, tempSize) \
\r
27 unsigned char temp[tempSize]; unsigned i = 0; \
\r
28 while (val >= 10) { temp[i++] = (unsigned char)('0' + (unsigned)(val % 10)); val /= 10; } \
\r
29 *s++ = (charType)('0' + (unsigned)val); \
\r
30 while (i != 0) { i--; *s++ = temp[i]; } \
\r
33 static void ConvertUInt64ToString(UInt64 val, char *s)
\r
35 CONVERT_INT_TO_STR(char, 24);
\r
38 #define GET_HEX_CHAR(t) ((char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))))
\r
40 static void ConvertUInt64ToHex(UInt64 val, char *s)
\r
53 unsigned t = (unsigned)(val & 0xF);
\r
55 s[--i] = GET_HEX_CHAR(t);
\r
60 #define DEBUG_OUT_STREAM stderr
\r
62 static void Print(const char *s)
\r
64 fputs(s, DEBUG_OUT_STREAM);
\r
67 static void PrintAligned(const char *s, size_t align)
\r
69 size_t len = strlen(s);
\r
72 fputc(' ', DEBUG_OUT_STREAM);
\r
80 static void PrintLn()
\r
85 static void PrintHex(UInt64 v, size_t align)
\r
88 ConvertUInt64ToHex(v, s);
\r
89 PrintAligned(s, align);
\r
92 static void PrintDec(UInt64 v, size_t align)
\r
95 ConvertUInt64ToString(v, s);
\r
96 PrintAligned(s, align);
\r
99 static void PrintAddr(void *p)
\r
101 PrintHex((UInt64)(size_t)(ptrdiff_t)p, 12);
\r
105 #define PRINT_ALLOC(name, cnt, size, ptr) \
\r
107 PrintDec(cnt++, 10); \
\r
108 PrintHex(size, 10); \
\r
112 #define PRINT_FREE(name, cnt, ptr) if (ptr) { \
\r
114 PrintDec(--cnt, 10); \
\r
120 #define PRINT_ALLOC(name, cnt, size, ptr)
\r
121 #define PRINT_FREE(name, cnt, ptr)
\r
124 #define PrintHex(v, align)
\r
125 #define PrintAddr(p)
\r
131 void *MyAlloc(size_t size)
\r
135 PRINT_ALLOC("Alloc ", g_allocCount, size, NULL);
\r
136 #ifdef _SZ_ALLOC_DEBUG
\r
138 void *p = malloc(size);
\r
139 // PRINT_ALLOC("Alloc ", g_allocCount, size, p);
\r
143 return malloc(size);
\r
147 void MyFree(void *address)
\r
149 PRINT_FREE("Free ", g_allocCount, address);
\r
156 void *MidAlloc(size_t size)
\r
161 PRINT_ALLOC("Alloc-Mid", g_allocCountMid, size, NULL);
\r
163 return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
\r
166 void MidFree(void *address)
\r
168 PRINT_FREE("Free-Mid", g_allocCountMid, address);
\r
172 VirtualFree(address, 0, MEM_RELEASE);
\r
175 #ifdef _7ZIP_LARGE_PAGES
\r
177 #ifdef MEM_LARGE_PAGES
\r
178 #define MY__MEM_LARGE_PAGES MEM_LARGE_PAGES
\r
180 #define MY__MEM_LARGE_PAGES 0x20000000
\r
184 SIZE_T g_LargePageSize;
\r
185 SIZE_T g_LargePageSize = 0;
\r
186 typedef SIZE_T (WINAPI *GetLargePageMinimumP)(VOID);
\r
188 #endif // _7ZIP_LARGE_PAGES
\r
190 void SetLargePageSize()
\r
192 #ifdef _7ZIP_LARGE_PAGES
\r
194 GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP)
\r
195 GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum");
\r
196 if (!largePageMinimum)
\r
198 size = largePageMinimum();
\r
199 if (size == 0 || (size & (size - 1)) != 0)
\r
201 g_LargePageSize = size;
\r
206 void *BigAlloc(size_t size)
\r
211 PRINT_ALLOC("Alloc-Big", g_allocCountBig, size, NULL);
\r
213 #ifdef _7ZIP_LARGE_PAGES
\r
215 SIZE_T ps = g_LargePageSize;
\r
216 if (ps != 0 && ps <= (1 << 30) && size > (ps / 2))
\r
220 size2 = (size + ps) & ~ps;
\r
223 void *res = VirtualAlloc(NULL, size2, MEM_COMMIT | MY__MEM_LARGE_PAGES, PAGE_READWRITE);
\r
231 return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
\r
234 void BigFree(void *address)
\r
236 PRINT_FREE("Free-Big", g_allocCountBig, address);
\r
240 VirtualFree(address, 0, MEM_RELEASE);
\r
246 static void *SzAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MyAlloc(size); }
\r
247 static void SzFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MyFree(address); }
\r
248 const ISzAlloc g_Alloc = { SzAlloc, SzFree };
\r
251 static void *SzMidAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MidAlloc(size); }
\r
252 static void SzMidFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MidFree(address); }
\r
253 static void *SzBigAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return BigAlloc(size); }
\r
254 static void SzBigFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); BigFree(address); }
\r
255 const ISzAlloc g_MidAlloc = { SzMidAlloc, SzMidFree };
\r
256 const ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree };
\r
260 uintptr_t : <stdint.h> C99 (optional)
\r
261 : unsupported in VS6
\r
265 typedef UINT_PTR UIntPtr;
\r
268 typedef uintptr_t UIntPtr;
\r
270 typedef ptrdiff_t UIntPtr;
\r
274 #define ADJUST_ALLOC_SIZE 0
\r
276 #define ADJUST_ALLOC_SIZE (sizeof(void *) - 1)
\r
279 Use (ADJUST_ALLOC_SIZE = (sizeof(void *) - 1)), if
\r
280 MyAlloc() can return address that is NOT multiple of sizeof(void *).
\r
285 #define MY_ALIGN_PTR_DOWN(p, align) ((void *)((char *)(p) - ((size_t)(UIntPtr)(p) & ((align) - 1))))
\r
287 #define MY_ALIGN_PTR_DOWN(p, align) ((void *)((((UIntPtr)(p)) & ~((UIntPtr)(align) - 1))))
\r
290 #if !defined(_WIN32) && defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)
\r
291 #define USE_posix_memalign
\r
294 #ifndef USE_posix_memalign
\r
295 #define MY_ALIGN_PTR_UP_PLUS(p, align) MY_ALIGN_PTR_DOWN(((char *)(p) + (align) + ADJUST_ALLOC_SIZE), align)
\r
299 This posix_memalign() is for test purposes only.
\r
300 We also need special Free() function instead of free(),
\r
301 if this posix_memalign() is used.
\r
305 static int posix_memalign(void **ptr, size_t align, size_t size)
\r
307 size_t newSize = size + align;
\r
311 if (newSize < size)
\r
312 return 12; // ENOMEM
\r
313 p = MyAlloc(newSize);
\r
315 return 12; // ENOMEM
\r
316 pAligned = MY_ALIGN_PTR_UP_PLUS(p, align);
\r
317 ((void **)pAligned)[-1] = p;
\r
324 ALLOC_ALIGN_SIZE >= sizeof(void *)
\r
325 ALLOC_ALIGN_SIZE >= cache_line_size
\r
328 #define ALLOC_ALIGN_SIZE ((size_t)1 << 7)
\r
330 static void *SzAlignedAlloc(ISzAllocPtr pp, size_t size)
\r
332 #ifndef USE_posix_memalign
\r
339 /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned
\r
340 block to prevent cache line sharing with another allocated blocks */
\r
342 newSize = size + ALLOC_ALIGN_SIZE * 1 + ADJUST_ALLOC_SIZE;
\r
343 if (newSize < size)
\r
346 p = MyAlloc(newSize);
\r
350 pAligned = MY_ALIGN_PTR_UP_PLUS(p, ALLOC_ALIGN_SIZE);
\r
352 Print(" size="); PrintHex(size, 8);
\r
353 Print(" a_size="); PrintHex(newSize, 8);
\r
354 Print(" ptr="); PrintAddr(p);
\r
355 Print(" a_ptr="); PrintAddr(pAligned);
\r
358 ((void **)pAligned)[-1] = p;
\r
366 if (posix_memalign(&p, ALLOC_ALIGN_SIZE, size))
\r
369 Print(" posix_memalign="); PrintAddr(p);
\r
378 static void SzAlignedFree(ISzAllocPtr pp, void *address)
\r
381 #ifndef USE_posix_memalign
\r
383 MyFree(((void **)address)[-1]);
\r
390 const ISzAlloc g_AlignedAlloc = { SzAlignedAlloc, SzAlignedFree };
\r
394 #define MY_ALIGN_PTR_DOWN_1(p) MY_ALIGN_PTR_DOWN(p, sizeof(void *))
\r
396 /* we align ptr to support cases where CAlignOffsetAlloc::offset is not multiply of sizeof(void *) */
\r
397 #define REAL_BLOCK_PTR_VAR(p) ((void **)MY_ALIGN_PTR_DOWN_1(p))[-1]
\r
399 #define REAL_BLOCK_PTR_VAR(p) ((void **)(p))[-1]
\r
402 static void *AlignOffsetAlloc_Alloc(ISzAllocPtr pp, size_t size)
\r
404 CAlignOffsetAlloc *p = CONTAINER_FROM_VTBL(pp, CAlignOffsetAlloc, vt);
\r
409 size_t alignSize = (size_t)1 << p->numAlignBits;
\r
411 if (alignSize < sizeof(void *))
\r
412 alignSize = sizeof(void *);
\r
414 if (p->offset >= alignSize)
\r
417 /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned
\r
418 block to prevent cache line sharing with another allocated blocks */
\r
419 extra = p->offset & (sizeof(void *) - 1);
\r
420 newSize = size + alignSize + extra + ADJUST_ALLOC_SIZE;
\r
421 if (newSize < size)
\r
424 adr = ISzAlloc_Alloc(p->baseAlloc, newSize);
\r
429 pAligned = (char *)MY_ALIGN_PTR_DOWN((char *)adr +
\r
430 alignSize - p->offset + extra + ADJUST_ALLOC_SIZE, alignSize) + p->offset;
\r
433 Print("- Aligned: ");
\r
434 Print(" size="); PrintHex(size, 8);
\r
435 Print(" a_size="); PrintHex(newSize, 8);
\r
436 Print(" ptr="); PrintAddr(adr);
\r
437 Print(" a_ptr="); PrintAddr(pAligned);
\r
440 REAL_BLOCK_PTR_VAR(pAligned) = adr;
\r
446 static void AlignOffsetAlloc_Free(ISzAllocPtr pp, void *address)
\r
450 CAlignOffsetAlloc *p = CONTAINER_FROM_VTBL(pp, CAlignOffsetAlloc, vt);
\r
452 Print("- Aligned Free: ");
\r
454 ISzAlloc_Free(p->baseAlloc, REAL_BLOCK_PTR_VAR(address));
\r
459 void AlignOffsetAlloc_CreateVTable(CAlignOffsetAlloc *p)
\r
461 p->vt.Alloc = AlignOffsetAlloc_Alloc;
\r
462 p->vt.Free = AlignOffsetAlloc_Free;
\r