Alloc.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /* Alloc.c -- Memory allocation functions
  2. 2018-04-27 : Igor Pavlov : Public domain */
  3. #include "Precomp.h"
  4. #include <stdio.h>
  5. #ifdef _WIN32
  6. #include <windows.h>
  7. #endif
  8. #include <stdlib.h>
  9. #include "Alloc.h"
  10. /* #define _SZ_ALLOC_DEBUG */
  11. /* use _SZ_ALLOC_DEBUG to debug alloc/free operations */
  12. #ifdef _SZ_ALLOC_DEBUG
  13. #include <stdio.h>
  14. int g_allocCount = 0;
  15. int g_allocCountMid = 0;
  16. int g_allocCountBig = 0;
  17. #define CONVERT_INT_TO_STR(charType, tempSize) \
  18. unsigned char temp[tempSize]; unsigned i = 0; \
  19. while (val >= 10) { temp[i++] = (unsigned char)('0' + (unsigned)(val % 10)); val /= 10; } \
  20. *s++ = (charType)('0' + (unsigned)val); \
  21. while (i != 0) { i--; *s++ = temp[i]; } \
  22. *s = 0;
  23. static void ConvertUInt64ToString(UInt64 val, char *s)
  24. {
  25. CONVERT_INT_TO_STR(char, 24);
  26. }
  27. #define GET_HEX_CHAR(t) ((char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))))
  28. static void ConvertUInt64ToHex(UInt64 val, char *s)
  29. {
  30. UInt64 v = val;
  31. unsigned i;
  32. for (i = 1;; i++)
  33. {
  34. v >>= 4;
  35. if (v == 0)
  36. break;
  37. }
  38. s[i] = 0;
  39. do
  40. {
  41. unsigned t = (unsigned)(val & 0xF);
  42. val >>= 4;
  43. s[--i] = GET_HEX_CHAR(t);
  44. }
  45. while (i);
  46. }
  47. #define DEBUG_OUT_STREAM stderr
  48. static void Print(const char *s)
  49. {
  50. fputs(s, DEBUG_OUT_STREAM);
  51. }
  52. static void PrintAligned(const char *s, size_t align)
  53. {
  54. size_t len = strlen(s);
  55. for(;;)
  56. {
  57. fputc(' ', DEBUG_OUT_STREAM);
  58. if (len >= align)
  59. break;
  60. ++len;
  61. }
  62. Print(s);
  63. }
  64. static void PrintLn()
  65. {
  66. Print("\n");
  67. }
  68. static void PrintHex(UInt64 v, size_t align)
  69. {
  70. char s[32];
  71. ConvertUInt64ToHex(v, s);
  72. PrintAligned(s, align);
  73. }
  74. static void PrintDec(UInt64 v, size_t align)
  75. {
  76. char s[32];
  77. ConvertUInt64ToString(v, s);
  78. PrintAligned(s, align);
  79. }
  80. static void PrintAddr(void *p)
  81. {
  82. PrintHex((UInt64)(size_t)(ptrdiff_t)p, 12);
  83. }
  84. #define PRINT_ALLOC(name, cnt, size, ptr) \
  85. Print(name " "); \
  86. PrintDec(cnt++, 10); \
  87. PrintHex(size, 10); \
  88. PrintAddr(ptr); \
  89. PrintLn();
  90. #define PRINT_FREE(name, cnt, ptr) if (ptr) { \
  91. Print(name " "); \
  92. PrintDec(--cnt, 10); \
  93. PrintAddr(ptr); \
  94. PrintLn(); }
  95. #else
  96. #define PRINT_ALLOC(name, cnt, size, ptr)
  97. #define PRINT_FREE(name, cnt, ptr)
  98. #define Print(s)
  99. #define PrintLn()
  100. #define PrintHex(v, align)
  101. #define PrintDec(v, align)
  102. #define PrintAddr(p)
  103. #endif
  104. void *MyAlloc(size_t size)
  105. {
  106. if (size == 0)
  107. return NULL;
  108. #ifdef _SZ_ALLOC_DEBUG
  109. {
  110. void *p = malloc(size);
  111. PRINT_ALLOC("Alloc ", g_allocCount, size, p);
  112. return p;
  113. }
  114. #else
  115. return malloc(size);
  116. #endif
  117. }
  118. void MyFree(void *address)
  119. {
  120. PRINT_FREE("Free ", g_allocCount, address);
  121. free(address);
  122. }
  123. #ifdef _WIN32
  124. void *MidAlloc(size_t size)
  125. {
  126. if (size == 0)
  127. return NULL;
  128. PRINT_ALLOC("Alloc-Mid", g_allocCountMid, size, NULL);
  129. return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
  130. }
  131. void MidFree(void *address)
  132. {
  133. PRINT_FREE("Free-Mid", g_allocCountMid, address);
  134. if (!address)
  135. return;
  136. VirtualFree(address, 0, MEM_RELEASE);
  137. }
  138. #ifndef MEM_LARGE_PAGES
  139. #undef _7ZIP_LARGE_PAGES
  140. #endif
  141. #ifdef _7ZIP_LARGE_PAGES
  142. SIZE_T g_LargePageSize = 0;
  143. typedef SIZE_T (WINAPI *GetLargePageMinimumP)();
  144. #endif
  145. void SetLargePageSize()
  146. {
  147. #ifdef _7ZIP_LARGE_PAGES
  148. SIZE_T size;
  149. GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP)
  150. GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum");
  151. if (!largePageMinimum)
  152. return;
  153. size = largePageMinimum();
  154. if (size == 0 || (size & (size - 1)) != 0)
  155. return;
  156. g_LargePageSize = size;
  157. #endif
  158. }
  159. void *BigAlloc(size_t size)
  160. {
  161. if (size == 0)
  162. return NULL;
  163. PRINT_ALLOC("Alloc-Big", g_allocCountBig, size, NULL);
  164. #ifdef _7ZIP_LARGE_PAGES
  165. {
  166. SIZE_T ps = g_LargePageSize;
  167. if (ps != 0 && ps <= (1 << 30) && size > (ps / 2))
  168. {
  169. size_t size2;
  170. ps--;
  171. size2 = (size + ps) & ~ps;
  172. if (size2 >= size)
  173. {
  174. void *res = VirtualAlloc(NULL, size2, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
  175. if (res)
  176. return res;
  177. }
  178. }
  179. }
  180. #endif
  181. return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
  182. }
  183. void BigFree(void *address)
  184. {
  185. PRINT_FREE("Free-Big", g_allocCountBig, address);
  186. if (!address)
  187. return;
  188. VirtualFree(address, 0, MEM_RELEASE);
  189. }
  190. #endif
  191. static void *SzAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MyAlloc(size); }
  192. static void SzFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MyFree(address); }
  193. const ISzAlloc g_Alloc = { SzAlloc, SzFree };
  194. static void *SzMidAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MidAlloc(size); }
  195. static void SzMidFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MidFree(address); }
  196. const ISzAlloc g_MidAlloc = { SzMidAlloc, SzMidFree };
  197. static void *SzBigAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return BigAlloc(size); }
  198. static void SzBigFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); BigFree(address); }
  199. const ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree };
  200. /*
  201. uintptr_t : <stdint.h> C99 (optional)
  202. : unsupported in VS6
  203. */
  204. #ifdef _WIN32
  205. typedef UINT_PTR UIntPtr;
  206. #else
  207. /*
  208. typedef uintptr_t UIntPtr;
  209. */
  210. typedef ptrdiff_t UIntPtr;
  211. #endif
  212. #define ADJUST_ALLOC_SIZE 0
  213. /*
  214. #define ADJUST_ALLOC_SIZE (sizeof(void *) - 1)
  215. */
  216. /*
  217. Use (ADJUST_ALLOC_SIZE = (sizeof(void *) - 1)), if
  218. MyAlloc() can return address that is NOT multiple of sizeof(void *).
  219. */
  220. /*
  221. #define MY_ALIGN_PTR_DOWN(p, align) ((void *)((char *)(p) - ((size_t)(UIntPtr)(p) & ((align) - 1))))
  222. */
  223. #define MY_ALIGN_PTR_DOWN(p, align) ((void *)((((UIntPtr)(p)) & ~((UIntPtr)(align) - 1))))
  224. #define MY_ALIGN_PTR_UP_PLUS(p, align) MY_ALIGN_PTR_DOWN(((char *)(p) + (align) + ADJUST_ALLOC_SIZE), align)
  225. #if (_POSIX_C_SOURCE >= 200112L) && !defined(_WIN32)
  226. #define USE_posix_memalign
  227. #endif
  228. /*
  229. This posix_memalign() is for test purposes only.
  230. We also need special Free() function instead of free(),
  231. if this posix_memalign() is used.
  232. */
  233. /*
  234. static int posix_memalign(void **ptr, size_t align, size_t size)
  235. {
  236. size_t newSize = size + align;
  237. void *p;
  238. void *pAligned;
  239. *ptr = NULL;
  240. if (newSize < size)
  241. return 12; // ENOMEM
  242. p = MyAlloc(newSize);
  243. if (!p)
  244. return 12; // ENOMEM
  245. pAligned = MY_ALIGN_PTR_UP_PLUS(p, align);
  246. ((void **)pAligned)[-1] = p;
  247. *ptr = pAligned;
  248. return 0;
  249. }
  250. */
  251. /*
  252. ALLOC_ALIGN_SIZE >= sizeof(void *)
  253. ALLOC_ALIGN_SIZE >= cache_line_size
  254. */
  255. #define ALLOC_ALIGN_SIZE ((size_t)1 << 7)
  256. static void *SzAlignedAlloc(ISzAllocPtr pp, size_t size)
  257. {
  258. #ifndef USE_posix_memalign
  259. void *p;
  260. void *pAligned;
  261. size_t newSize;
  262. UNUSED_VAR(pp);
  263. /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned
  264. block to prevent cache line sharing with another allocated blocks */
  265. newSize = size + ALLOC_ALIGN_SIZE * 1 + ADJUST_ALLOC_SIZE;
  266. if (newSize < size)
  267. return NULL;
  268. p = MyAlloc(newSize);
  269. if (!p)
  270. return NULL;
  271. pAligned = MY_ALIGN_PTR_UP_PLUS(p, ALLOC_ALIGN_SIZE);
  272. Print(" size="); PrintHex(size, 8);
  273. Print(" a_size="); PrintHex(newSize, 8);
  274. Print(" ptr="); PrintAddr(p);
  275. Print(" a_ptr="); PrintAddr(pAligned);
  276. PrintLn();
  277. ((void **)pAligned)[-1] = p;
  278. return pAligned;
  279. #else
  280. void *p;
  281. UNUSED_VAR(pp);
  282. if (posix_memalign(&p, ALLOC_ALIGN_SIZE, size))
  283. return NULL;
  284. Print(" posix_memalign="); PrintAddr(p);
  285. PrintLn();
  286. return p;
  287. #endif
  288. }
  289. static void SzAlignedFree(ISzAllocPtr pp, void *address)
  290. {
  291. UNUSED_VAR(pp);
  292. #ifndef USE_posix_memalign
  293. if (address)
  294. MyFree(((void **)address)[-1]);
  295. #else
  296. free(address);
  297. #endif
  298. }
  299. const ISzAlloc g_AlignedAlloc = { SzAlignedAlloc, SzAlignedFree };
  300. #define MY_ALIGN_PTR_DOWN_1(p) MY_ALIGN_PTR_DOWN(p, sizeof(void *))
  301. /* we align ptr to support cases where CAlignOffsetAlloc::offset is not multiply of sizeof(void *) */
  302. #define REAL_BLOCK_PTR_VAR(p) ((void **)MY_ALIGN_PTR_DOWN_1(p))[-1]
  303. /*
  304. #define REAL_BLOCK_PTR_VAR(p) ((void **)(p))[-1]
  305. */
  306. static void *AlignOffsetAlloc_Alloc(ISzAllocPtr pp, size_t size)
  307. {
  308. CAlignOffsetAlloc *p = CONTAINER_FROM_VTBL(pp, CAlignOffsetAlloc, vt);
  309. void *adr;
  310. void *pAligned;
  311. size_t newSize;
  312. size_t extra;
  313. size_t alignSize = (size_t)1 << p->numAlignBits;
  314. if (alignSize < sizeof(void *))
  315. alignSize = sizeof(void *);
  316. if (p->offset >= alignSize)
  317. return NULL;
  318. /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned
  319. block to prevent cache line sharing with another allocated blocks */
  320. extra = p->offset & (sizeof(void *) - 1);
  321. newSize = size + alignSize + extra + ADJUST_ALLOC_SIZE;
  322. if (newSize < size)
  323. return NULL;
  324. adr = ISzAlloc_Alloc(p->baseAlloc, newSize);
  325. if (!adr)
  326. return NULL;
  327. pAligned = (char *)MY_ALIGN_PTR_DOWN((char *)adr +
  328. alignSize - p->offset + extra + ADJUST_ALLOC_SIZE, alignSize) + p->offset;
  329. PrintLn();
  330. Print("- Aligned: ");
  331. Print(" size="); PrintHex(size, 8);
  332. Print(" a_size="); PrintHex(newSize, 8);
  333. Print(" ptr="); PrintAddr(adr);
  334. Print(" a_ptr="); PrintAddr(pAligned);
  335. PrintLn();
  336. REAL_BLOCK_PTR_VAR(pAligned) = adr;
  337. return pAligned;
  338. }
  339. static void AlignOffsetAlloc_Free(ISzAllocPtr pp, void *address)
  340. {
  341. if (address)
  342. {
  343. CAlignOffsetAlloc *p = CONTAINER_FROM_VTBL(pp, CAlignOffsetAlloc, vt);
  344. PrintLn();
  345. Print("- Aligned Free: ");
  346. PrintLn();
  347. ISzAlloc_Free(p->baseAlloc, REAL_BLOCK_PTR_VAR(address));
  348. }
  349. }
  350. void AlignOffsetAlloc_CreateVTable(CAlignOffsetAlloc *p)
  351. {
  352. p->vt.Alloc = AlignOffsetAlloc_Alloc;
  353. p->vt.Free = AlignOffsetAlloc_Free;
  354. }