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