/* 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; }