1 /* Alloc.c -- Memory allocation functions
2 2018-04-27 : Igor Pavlov : Public domain */
15 /* #define _SZ_ALLOC_DEBUG */
17 /* use _SZ_ALLOC_DEBUG to debug alloc/free operations */
18 #ifdef _SZ_ALLOC_DEBUG
22 int g_allocCountMid
= 0;
23 int g_allocCountBig
= 0;
26 #define CONVERT_INT_TO_STR(charType, tempSize) \
27 unsigned char temp[tempSize]; unsigned i = 0; \
28 while (val >= 10) { temp[i++] = (unsigned char)('0' + (unsigned)(val % 10)); val /= 10; } \
29 *s++ = (charType)('0' + (unsigned)val); \
30 while (i != 0) { i--; *s++ = temp[i]; } \
33 static void ConvertUInt64ToString(UInt64 val
, char *s
)
35 CONVERT_INT_TO_STR(char, 24);
38 #define GET_HEX_CHAR(t) ((char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))))
40 static void ConvertUInt64ToHex(UInt64 val
, char *s
)
53 unsigned t
= (unsigned)(val
& 0xF);
55 s
[--i
] = GET_HEX_CHAR(t
);
60 #define DEBUG_OUT_STREAM stderr
62 static void Print(const char *s
)
64 fputs(s
, DEBUG_OUT_STREAM
);
67 static void PrintAligned(const char *s
, size_t align
)
69 size_t len
= strlen(s
);
72 fputc(' ', DEBUG_OUT_STREAM
);
85 static void PrintHex(UInt64 v
, size_t align
)
88 ConvertUInt64ToHex(v
, s
);
89 PrintAligned(s
, align
);
92 static void PrintDec(UInt64 v
, size_t align
)
95 ConvertUInt64ToString(v
, s
);
96 PrintAligned(s
, align
);
99 static void PrintAddr(void *p
)
101 PrintHex((UInt64
)(size_t)(ptrdiff_t)p
, 12);
105 #define PRINT_ALLOC(name, cnt, size, ptr) \
107 PrintDec(cnt++, 10); \
108 PrintHex(size, 10); \
112 #define PRINT_FREE(name, cnt, ptr) if (ptr) { \
114 PrintDec(--cnt, 10); \
120 #define PRINT_ALLOC(name, cnt, size, ptr)
121 #define PRINT_FREE(name, cnt, ptr)
124 #define PrintHex(v, align)
125 #define PrintDec(v, align)
132 void *MyAlloc(size_t size
)
136 #ifdef _SZ_ALLOC_DEBUG
138 void *p
= malloc(size
);
139 PRINT_ALLOC("Alloc ", g_allocCount
, size
, p
);
147 void MyFree(void *address
)
149 PRINT_FREE("Free ", g_allocCount
, address
);
156 void *MidAlloc(size_t size
)
161 PRINT_ALLOC("Alloc-Mid", g_allocCountMid
, size
, NULL
);
163 return VirtualAlloc(NULL
, size
, MEM_COMMIT
, PAGE_READWRITE
);
166 void MidFree(void *address
)
168 PRINT_FREE("Free-Mid", g_allocCountMid
, address
);
172 VirtualFree(address
, 0, MEM_RELEASE
);
175 #ifndef MEM_LARGE_PAGES
176 #undef _7ZIP_LARGE_PAGES
179 #ifdef _7ZIP_LARGE_PAGES
180 SIZE_T g_LargePageSize
= 0;
181 typedef SIZE_T (WINAPI
*GetLargePageMinimumP
)();
184 void SetLargePageSize()
186 #ifdef _7ZIP_LARGE_PAGES
188 GetLargePageMinimumP largePageMinimum
= (GetLargePageMinimumP
)
189 GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum");
190 if (!largePageMinimum
)
192 size
= largePageMinimum();
193 if (size
== 0 || (size
& (size
- 1)) != 0)
195 g_LargePageSize
= size
;
200 void *BigAlloc(size_t size
)
205 PRINT_ALLOC("Alloc-Big", g_allocCountBig
, size
, NULL
);
207 #ifdef _7ZIP_LARGE_PAGES
209 SIZE_T ps
= g_LargePageSize
;
210 if (ps
!= 0 && ps
<= (1 << 30) && size
> (ps
/ 2))
214 size2
= (size
+ ps
) & ~ps
;
217 void *res
= VirtualAlloc(NULL
, size2
, MEM_COMMIT
| MEM_LARGE_PAGES
, PAGE_READWRITE
);
225 return VirtualAlloc(NULL
, size
, MEM_COMMIT
, PAGE_READWRITE
);
228 void BigFree(void *address
)
230 PRINT_FREE("Free-Big", g_allocCountBig
, address
);
234 VirtualFree(address
, 0, MEM_RELEASE
);
240 static void *SzAlloc(ISzAllocPtr p
, size_t size
) { UNUSED_VAR(p
); return MyAlloc(size
); }
241 static void SzFree(ISzAllocPtr p
, void *address
) { UNUSED_VAR(p
); MyFree(address
); }
242 const ISzAlloc g_Alloc
= { SzAlloc
, SzFree
};
244 static void *SzMidAlloc(ISzAllocPtr p
, size_t size
) { UNUSED_VAR(p
); return MidAlloc(size
); }
245 static void SzMidFree(ISzAllocPtr p
, void *address
) { UNUSED_VAR(p
); MidFree(address
); }
246 const ISzAlloc g_MidAlloc
= { SzMidAlloc
, SzMidFree
};
248 static void *SzBigAlloc(ISzAllocPtr p
, size_t size
) { UNUSED_VAR(p
); return BigAlloc(size
); }
249 static void SzBigFree(ISzAllocPtr p
, void *address
) { UNUSED_VAR(p
); BigFree(address
); }
250 const ISzAlloc g_BigAlloc
= { SzBigAlloc
, SzBigFree
};
254 uintptr_t : <stdint.h> C99 (optional)
259 typedef UINT_PTR UIntPtr
;
262 typedef uintptr_t UIntPtr;
264 typedef ptrdiff_t UIntPtr
;
268 #define ADJUST_ALLOC_SIZE 0
270 #define ADJUST_ALLOC_SIZE (sizeof(void *) - 1)
273 Use (ADJUST_ALLOC_SIZE = (sizeof(void *) - 1)), if
274 MyAlloc() can return address that is NOT multiple of sizeof(void *).
279 #define MY_ALIGN_PTR_DOWN(p, align) ((void *)((char *)(p) - ((size_t)(UIntPtr)(p) & ((align) - 1))))
281 #define MY_ALIGN_PTR_DOWN(p, align) ((void *)((((UIntPtr)(p)) & ~((UIntPtr)(align) - 1))))
283 #define MY_ALIGN_PTR_UP_PLUS(p, align) MY_ALIGN_PTR_DOWN(((char *)(p) + (align) + ADJUST_ALLOC_SIZE), align)
286 #if (_POSIX_C_SOURCE >= 200112L) && !defined(_WIN32)
287 #define USE_posix_memalign
291 This posix_memalign() is for test purposes only.
292 We also need special Free() function instead of free(),
293 if this posix_memalign() is used.
297 static int posix_memalign(void **ptr, size_t align, size_t size)
299 size_t newSize = size + align;
305 p = MyAlloc(newSize);
308 pAligned = MY_ALIGN_PTR_UP_PLUS(p, align);
309 ((void **)pAligned)[-1] = p;
316 ALLOC_ALIGN_SIZE >= sizeof(void *)
317 ALLOC_ALIGN_SIZE >= cache_line_size
320 #define ALLOC_ALIGN_SIZE ((size_t)1 << 7)
322 static void *SzAlignedAlloc(ISzAllocPtr pp
, size_t size
)
324 #ifndef USE_posix_memalign
331 /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned
332 block to prevent cache line sharing with another allocated blocks */
334 newSize
= size
+ ALLOC_ALIGN_SIZE
* 1 + ADJUST_ALLOC_SIZE
;
338 p
= MyAlloc(newSize
);
342 pAligned
= MY_ALIGN_PTR_UP_PLUS(p
, ALLOC_ALIGN_SIZE
);
344 Print(" size="); PrintHex(size
, 8);
345 Print(" a_size="); PrintHex(newSize
, 8);
346 Print(" ptr="); PrintAddr(p
);
347 Print(" a_ptr="); PrintAddr(pAligned
);
350 ((void **)pAligned
)[-1] = p
;
358 if (posix_memalign(&p
, ALLOC_ALIGN_SIZE
, size
))
361 Print(" posix_memalign="); PrintAddr(p
);
370 static void SzAlignedFree(ISzAllocPtr pp
, void *address
)
373 #ifndef USE_posix_memalign
375 MyFree(((void **)address
)[-1]);
382 const ISzAlloc g_AlignedAlloc
= { SzAlignedAlloc
, SzAlignedFree
};
386 #define MY_ALIGN_PTR_DOWN_1(p) MY_ALIGN_PTR_DOWN(p, sizeof(void *))
388 /* we align ptr to support cases where CAlignOffsetAlloc::offset is not multiply of sizeof(void *) */
389 #define REAL_BLOCK_PTR_VAR(p) ((void **)MY_ALIGN_PTR_DOWN_1(p))[-1]
391 #define REAL_BLOCK_PTR_VAR(p) ((void **)(p))[-1]
394 static void *AlignOffsetAlloc_Alloc(ISzAllocPtr pp
, size_t size
)
396 CAlignOffsetAlloc
*p
= CONTAINER_FROM_VTBL(pp
, CAlignOffsetAlloc
, vt
);
401 size_t alignSize
= (size_t)1 << p
->numAlignBits
;
403 if (alignSize
< sizeof(void *))
404 alignSize
= sizeof(void *);
406 if (p
->offset
>= alignSize
)
409 /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned
410 block to prevent cache line sharing with another allocated blocks */
411 extra
= p
->offset
& (sizeof(void *) - 1);
412 newSize
= size
+ alignSize
+ extra
+ ADJUST_ALLOC_SIZE
;
416 adr
= ISzAlloc_Alloc(p
->baseAlloc
, newSize
);
421 pAligned
= (char *)MY_ALIGN_PTR_DOWN((char *)adr
+
422 alignSize
- p
->offset
+ extra
+ ADJUST_ALLOC_SIZE
, alignSize
) + p
->offset
;
425 Print("- Aligned: ");
426 Print(" size="); PrintHex(size
, 8);
427 Print(" a_size="); PrintHex(newSize
, 8);
428 Print(" ptr="); PrintAddr(adr
);
429 Print(" a_ptr="); PrintAddr(pAligned
);
432 REAL_BLOCK_PTR_VAR(pAligned
) = adr
;
438 static void AlignOffsetAlloc_Free(ISzAllocPtr pp
, void *address
)
442 CAlignOffsetAlloc
*p
= CONTAINER_FROM_VTBL(pp
, CAlignOffsetAlloc
, vt
);
444 Print("- Aligned Free: ");
446 ISzAlloc_Free(p
->baseAlloc
, REAL_BLOCK_PTR_VAR(address
));
451 void AlignOffsetAlloc_CreateVTable(CAlignOffsetAlloc
*p
)
453 p
->vt
.Alloc
= AlignOffsetAlloc_Alloc
;
454 p
->vt
.Free
= AlignOffsetAlloc_Free
;