| /* Alloc.c -- Memory allocation functions |
| 2018-04-27 : Igor Pavlov : Public domain */ |
| |
| #include "Precomp.h" |
| |
| #include <stdio.h> |
| |
| #ifdef _WIN32 |
| #include <windows.h> |
| #endif |
| #include <stdlib.h> |
| |
| #include "Alloc.h" |
| |
| /* #define _SZ_ALLOC_DEBUG */ |
| |
| /* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ |
| #ifdef _SZ_ALLOC_DEBUG |
| |
| #include <stdio.h> |
| int g_allocCount = 0; |
| int g_allocCountMid = 0; |
| int g_allocCountBig = 0; |
| |
| |
| #define CONVERT_INT_TO_STR(charType, tempSize) \ |
| unsigned char temp[tempSize]; unsigned i = 0; \ |
| while (val >= 10) { temp[i++] = (unsigned char)('0' + (unsigned)(val % 10)); val /= 10; } \ |
| *s++ = (charType)('0' + (unsigned)val); \ |
| while (i != 0) { i--; *s++ = temp[i]; } \ |
| *s = 0; |
| |
| static void ConvertUInt64ToString(UInt64 val, char *s) |
| { |
| CONVERT_INT_TO_STR(char, 24); |
| } |
| |
| #define GET_HEX_CHAR(t) ((char)(((t < 10) ? ('0' + t) : ('A' + (t - 10))))) |
| |
| static void ConvertUInt64ToHex(UInt64 val, char *s) |
| { |
| UInt64 v = val; |
| unsigned i; |
| for (i = 1;; i++) |
| { |
| v >>= 4; |
| if (v == 0) |
| break; |
| } |
| s[i] = 0; |
| do |
| { |
| unsigned t = (unsigned)(val & 0xF); |
| val >>= 4; |
| s[--i] = GET_HEX_CHAR(t); |
| } |
| while (i); |
| } |
| |
| #define DEBUG_OUT_STREAM stderr |
| |
| static void Print(const char *s) |
| { |
| fputs(s, DEBUG_OUT_STREAM); |
| } |
| |
| static void PrintAligned(const char *s, size_t align) |
| { |
| size_t len = strlen(s); |
| for(;;) |
| { |
| fputc(' ', DEBUG_OUT_STREAM); |
| if (len >= align) |
| break; |
| ++len; |
| } |
| Print(s); |
| } |
| |
| static void PrintLn() |
| { |
| Print("\n"); |
| } |
| |
| static void PrintHex(UInt64 v, size_t align) |
| { |
| char s[32]; |
| ConvertUInt64ToHex(v, s); |
| PrintAligned(s, align); |
| } |
| |
| static void PrintDec(UInt64 v, size_t align) |
| { |
| char s[32]; |
| ConvertUInt64ToString(v, s); |
| PrintAligned(s, align); |
| } |
| |
| static void PrintAddr(void *p) |
| { |
| PrintHex((UInt64)(size_t)(ptrdiff_t)p, 12); |
| } |
| |
| |
| #define PRINT_ALLOC(name, cnt, size, ptr) \ |
| Print(name " "); \ |
| PrintDec(cnt++, 10); \ |
| PrintHex(size, 10); \ |
| PrintAddr(ptr); \ |
| PrintLn(); |
| |
| #define PRINT_FREE(name, cnt, ptr) if (ptr) { \ |
| Print(name " "); \ |
| PrintDec(--cnt, 10); \ |
| PrintAddr(ptr); \ |
| PrintLn(); } |
| |
| #else |
| |
| #define PRINT_ALLOC(name, cnt, size, ptr) |
| #define PRINT_FREE(name, cnt, ptr) |
| #define Print(s) |
| #define PrintLn() |
| #define PrintHex(v, align) |
| #define PrintDec(v, align) |
| #define PrintAddr(p) |
| |
| #endif |
| |
| |
| |
| void *MyAlloc(size_t size) |
| { |
| if (size == 0) |
| return NULL; |
| #ifdef _SZ_ALLOC_DEBUG |
| { |
| void *p = malloc(size); |
| PRINT_ALLOC("Alloc ", g_allocCount, size, p); |
| return p; |
| } |
| #else |
| return malloc(size); |
| #endif |
| } |
| |
| void MyFree(void *address) |
| { |
| PRINT_FREE("Free ", g_allocCount, address); |
| |
| free(address); |
| } |
| |
| #ifdef _WIN32 |
| |
| void *MidAlloc(size_t size) |
| { |
| if (size == 0) |
| return NULL; |
| |
| PRINT_ALLOC("Alloc-Mid", g_allocCountMid, size, NULL); |
| |
| return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); |
| } |
| |
| void MidFree(void *address) |
| { |
| PRINT_FREE("Free-Mid", g_allocCountMid, address); |
| |
| if (!address) |
| return; |
| VirtualFree(address, 0, MEM_RELEASE); |
| } |
| |
| #ifndef MEM_LARGE_PAGES |
| #undef _7ZIP_LARGE_PAGES |
| #endif |
| |
| #ifdef _7ZIP_LARGE_PAGES |
| SIZE_T g_LargePageSize = 0; |
| typedef SIZE_T (WINAPI *GetLargePageMinimumP)(); |
| #endif |
| |
| void SetLargePageSize() |
| { |
| #ifdef _7ZIP_LARGE_PAGES |
| SIZE_T size; |
| GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP) |
| GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum"); |
| if (!largePageMinimum) |
| return; |
| size = largePageMinimum(); |
| if (size == 0 || (size & (size - 1)) != 0) |
| return; |
| g_LargePageSize = size; |
| #endif |
| } |
| |
| |
| void *BigAlloc(size_t size) |
| { |
| if (size == 0) |
| return NULL; |
| |
| PRINT_ALLOC("Alloc-Big", g_allocCountBig, size, NULL); |
| |
| #ifdef _7ZIP_LARGE_PAGES |
| { |
| SIZE_T ps = g_LargePageSize; |
| if (ps != 0 && ps <= (1 << 30) && size > (ps / 2)) |
| { |
| size_t size2; |
| ps--; |
| size2 = (size + ps) & ~ps; |
| if (size2 >= size) |
| { |
| void *res = VirtualAlloc(NULL, size2, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); |
| if (res) |
| return res; |
| } |
| } |
| } |
| #endif |
| |
| return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); |
| } |
| |
| void BigFree(void *address) |
| { |
| PRINT_FREE("Free-Big", g_allocCountBig, address); |
| |
| if (!address) |
| return; |
| VirtualFree(address, 0, MEM_RELEASE); |
| } |
| |
| #endif |
| |
| |
| static void *SzAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MyAlloc(size); } |
| static void SzFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MyFree(address); } |
| const ISzAlloc g_Alloc = { SzAlloc, SzFree }; |
| |
| static void *SzMidAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MidAlloc(size); } |
| static void SzMidFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MidFree(address); } |
| const ISzAlloc g_MidAlloc = { SzMidAlloc, SzMidFree }; |
| |
| static void *SzBigAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return BigAlloc(size); } |
| static void SzBigFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); BigFree(address); } |
| const ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; |
| |
| |
| /* |
| uintptr_t : <stdint.h> C99 (optional) |
| : unsupported in VS6 |
| */ |
| |
| #ifdef _WIN32 |
| typedef UINT_PTR UIntPtr; |
| #else |
| /* |
| typedef uintptr_t UIntPtr; |
| */ |
| typedef ptrdiff_t UIntPtr; |
| #endif |
| |
| |
| #define ADJUST_ALLOC_SIZE 0 |
| /* |
| #define ADJUST_ALLOC_SIZE (sizeof(void *) - 1) |
| */ |
| /* |
| Use (ADJUST_ALLOC_SIZE = (sizeof(void *) - 1)), if |
| MyAlloc() can return address that is NOT multiple of sizeof(void *). |
| */ |
| |
| |
| /* |
| #define MY_ALIGN_PTR_DOWN(p, align) ((void *)((char *)(p) - ((size_t)(UIntPtr)(p) & ((align) - 1)))) |
| */ |
| #define MY_ALIGN_PTR_DOWN(p, align) ((void *)((((UIntPtr)(p)) & ~((UIntPtr)(align) - 1)))) |
| |
| #define MY_ALIGN_PTR_UP_PLUS(p, align) MY_ALIGN_PTR_DOWN(((char *)(p) + (align) + ADJUST_ALLOC_SIZE), align) |
| |
| |
| #if (_POSIX_C_SOURCE >= 200112L) && !defined(_WIN32) |
| #define USE_posix_memalign |
| #endif |
| |
| /* |
| This posix_memalign() is for test purposes only. |
| We also need special Free() function instead of free(), |
| if this posix_memalign() is used. |
| */ |
| |
| /* |
| static int posix_memalign(void **ptr, size_t align, size_t size) |
| { |
| size_t newSize = size + align; |
| void *p; |
| void *pAligned; |
| *ptr = NULL; |
| if (newSize < size) |
| return 12; // ENOMEM |
| p = MyAlloc(newSize); |
| if (!p) |
| return 12; // ENOMEM |
| pAligned = MY_ALIGN_PTR_UP_PLUS(p, align); |
| ((void **)pAligned)[-1] = p; |
| *ptr = pAligned; |
| return 0; |
| } |
| */ |
| |
| /* |
| ALLOC_ALIGN_SIZE >= sizeof(void *) |
| ALLOC_ALIGN_SIZE >= cache_line_size |
| */ |
| |
| #define ALLOC_ALIGN_SIZE ((size_t)1 << 7) |
| |
| static void *SzAlignedAlloc(ISzAllocPtr pp, size_t size) |
| { |
| #ifndef USE_posix_memalign |
| |
| void *p; |
| void *pAligned; |
| size_t newSize; |
| UNUSED_VAR(pp); |
| |
| /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned |
| block to prevent cache line sharing with another allocated blocks */ |
| |
| newSize = size + ALLOC_ALIGN_SIZE * 1 + ADJUST_ALLOC_SIZE; |
| if (newSize < size) |
| return NULL; |
| |
| p = MyAlloc(newSize); |
| |
| if (!p) |
| return NULL; |
| pAligned = MY_ALIGN_PTR_UP_PLUS(p, ALLOC_ALIGN_SIZE); |
| |
| Print(" size="); PrintHex(size, 8); |
| Print(" a_size="); PrintHex(newSize, 8); |
| Print(" ptr="); PrintAddr(p); |
| Print(" a_ptr="); PrintAddr(pAligned); |
| PrintLn(); |
| |
| ((void **)pAligned)[-1] = p; |
| |
| return pAligned; |
| |
| #else |
| |
| void *p; |
| UNUSED_VAR(pp); |
| if (posix_memalign(&p, ALLOC_ALIGN_SIZE, size)) |
| return NULL; |
| |
| Print(" posix_memalign="); PrintAddr(p); |
| PrintLn(); |
| |
| return p; |
| |
| #endif |
| } |
| |
| |
| static void SzAlignedFree(ISzAllocPtr pp, void *address) |
| { |
| UNUSED_VAR(pp); |
| #ifndef USE_posix_memalign |
| if (address) |
| MyFree(((void **)address)[-1]); |
| #else |
| free(address); |
| #endif |
| } |
| |
| |
| const ISzAlloc g_AlignedAlloc = { SzAlignedAlloc, SzAlignedFree }; |
| |
| |
| |
| #define MY_ALIGN_PTR_DOWN_1(p) MY_ALIGN_PTR_DOWN(p, sizeof(void *)) |
| |
| /* we align ptr to support cases where CAlignOffsetAlloc::offset is not multiply of sizeof(void *) */ |
| #define REAL_BLOCK_PTR_VAR(p) ((void **)MY_ALIGN_PTR_DOWN_1(p))[-1] |
| /* |
| #define REAL_BLOCK_PTR_VAR(p) ((void **)(p))[-1] |
| */ |
| |
| static void *AlignOffsetAlloc_Alloc(ISzAllocPtr pp, size_t size) |
| { |
| CAlignOffsetAlloc *p = CONTAINER_FROM_VTBL(pp, CAlignOffsetAlloc, vt); |
| void *adr; |
| void *pAligned; |
| size_t newSize; |
| size_t extra; |
| size_t alignSize = (size_t)1 << p->numAlignBits; |
| |
| if (alignSize < sizeof(void *)) |
| alignSize = sizeof(void *); |
| |
| if (p->offset >= alignSize) |
| return NULL; |
| |
| /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned |
| block to prevent cache line sharing with another allocated blocks */ |
| extra = p->offset & (sizeof(void *) - 1); |
| newSize = size + alignSize + extra + ADJUST_ALLOC_SIZE; |
| if (newSize < size) |
| return NULL; |
| |
| adr = ISzAlloc_Alloc(p->baseAlloc, newSize); |
| |
| if (!adr) |
| return NULL; |
| |
| pAligned = (char *)MY_ALIGN_PTR_DOWN((char *)adr + |
| alignSize - p->offset + extra + ADJUST_ALLOC_SIZE, alignSize) + p->offset; |
| |
| PrintLn(); |
| Print("- Aligned: "); |
| Print(" size="); PrintHex(size, 8); |
| Print(" a_size="); PrintHex(newSize, 8); |
| Print(" ptr="); PrintAddr(adr); |
| Print(" a_ptr="); PrintAddr(pAligned); |
| PrintLn(); |
| |
| REAL_BLOCK_PTR_VAR(pAligned) = adr; |
| |
| return pAligned; |
| } |
| |
| |
| static void AlignOffsetAlloc_Free(ISzAllocPtr pp, void *address) |
| { |
| if (address) |
| { |
| CAlignOffsetAlloc *p = CONTAINER_FROM_VTBL(pp, CAlignOffsetAlloc, vt); |
| PrintLn(); |
| Print("- Aligned Free: "); |
| PrintLn(); |
| ISzAlloc_Free(p->baseAlloc, REAL_BLOCK_PTR_VAR(address)); |
| } |
| } |
| |
| |
| void AlignOffsetAlloc_CreateVTable(CAlignOffsetAlloc *p) |
| { |
| p->vt.Alloc = AlignOffsetAlloc_Alloc; |
| p->vt.Free = AlignOffsetAlloc_Free; |
| } |