Source code
Revision control
Copy as Markdown
Other Tools
/* 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;↩
}↩