2
0

Alloc.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /* Alloc.c -- Memory allocation functions
  2. 2024-02-18 : Igor Pavlov : Public domain */
  3. #include "Precomp.h"
  4. #ifdef _WIN32
  5. #include "7zWindows.h"
  6. #endif
  7. #include <stdlib.h>
  8. #include "Alloc.h"
  9. #if defined(Z7_LARGE_PAGES) && defined(_WIN32) && \
  10. (!defined(Z7_WIN32_WINNT_MIN) || Z7_WIN32_WINNT_MIN < 0x0502) // < Win2003 (xp-64)
  11. #define Z7_USE_DYN_GetLargePageMinimum
  12. #endif
  13. // for debug:
  14. #if 0
  15. #if defined(__CHERI__) && defined(__SIZEOF_POINTER__) && (__SIZEOF_POINTER__ == 16)
  16. // #pragma message("=== Z7_ALLOC_NO_OFFSET_ALLOCATOR === ")
  17. #define Z7_ALLOC_NO_OFFSET_ALLOCATOR
  18. #endif
  19. #endif
  20. // #define SZ_ALLOC_DEBUG
  21. /* #define SZ_ALLOC_DEBUG */
  22. /* use SZ_ALLOC_DEBUG to debug alloc/free operations */
  23. #ifdef SZ_ALLOC_DEBUG
  24. #include <string.h>
  25. #include <stdio.h>
  26. static int g_allocCount = 0;
  27. #ifdef _WIN32
  28. static int g_allocCountMid = 0;
  29. static int g_allocCountBig = 0;
  30. #endif
  31. #define CONVERT_INT_TO_STR(charType, tempSize) \
  32. char temp[tempSize]; unsigned i = 0; \
  33. while (val >= 10) { temp[i++] = (char)('0' + (unsigned)(val % 10)); val /= 10; } \
  34. *s++ = (charType)('0' + (unsigned)val); \
  35. while (i != 0) { i--; *s++ = temp[i]; } \
  36. *s = 0;
  37. static void ConvertUInt64ToString(UInt64 val, char *s)
  38. {
  39. CONVERT_INT_TO_STR(char, 24)
  40. }
  41. #define GET_HEX_CHAR(t) ((char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))))
  42. static void ConvertUInt64ToHex(UInt64 val, char *s)
  43. {
  44. UInt64 v = val;
  45. unsigned i;
  46. for (i = 1;; i++)
  47. {
  48. v >>= 4;
  49. if (v == 0)
  50. break;
  51. }
  52. s[i] = 0;
  53. do
  54. {
  55. unsigned t = (unsigned)(val & 0xF);
  56. val >>= 4;
  57. s[--i] = GET_HEX_CHAR(t);
  58. }
  59. while (i);
  60. }
  61. #define DEBUG_OUT_STREAM stderr
  62. static void Print(const char *s)
  63. {
  64. fputs(s, DEBUG_OUT_STREAM);
  65. }
  66. static void PrintAligned(const char *s, size_t align)
  67. {
  68. size_t len = strlen(s);
  69. for(;;)
  70. {
  71. fputc(' ', DEBUG_OUT_STREAM);
  72. if (len >= align)
  73. break;
  74. ++len;
  75. }
  76. Print(s);
  77. }
  78. static void PrintLn(void)
  79. {
  80. Print("\n");
  81. }
  82. static void PrintHex(UInt64 v, size_t align)
  83. {
  84. char s[32];
  85. ConvertUInt64ToHex(v, s);
  86. PrintAligned(s, align);
  87. }
  88. static void PrintDec(int v, size_t align)
  89. {
  90. char s[32];
  91. ConvertUInt64ToString((unsigned)v, s);
  92. PrintAligned(s, align);
  93. }
  94. static void PrintAddr(void *p)
  95. {
  96. PrintHex((UInt64)(size_t)(ptrdiff_t)p, 12);
  97. }
  98. #define PRINT_REALLOC(name, cnt, size, ptr) { \
  99. Print(name " "); \
  100. if (!ptr) PrintDec(cnt++, 10); \
  101. PrintHex(size, 10); \
  102. PrintAddr(ptr); \
  103. PrintLn(); }
  104. #define PRINT_ALLOC(name, cnt, size, ptr) { \
  105. Print(name " "); \
  106. PrintDec(cnt++, 10); \
  107. PrintHex(size, 10); \
  108. PrintAddr(ptr); \
  109. PrintLn(); }
  110. #define PRINT_FREE(name, cnt, ptr) if (ptr) { \
  111. Print(name " "); \
  112. PrintDec(--cnt, 10); \
  113. PrintAddr(ptr); \
  114. PrintLn(); }
  115. #else
  116. #ifdef _WIN32
  117. #define PRINT_ALLOC(name, cnt, size, ptr)
  118. #endif
  119. #define PRINT_FREE(name, cnt, ptr)
  120. #define Print(s)
  121. #define PrintLn()
  122. #ifndef Z7_ALLOC_NO_OFFSET_ALLOCATOR
  123. #define PrintHex(v, align)
  124. #endif
  125. #define PrintAddr(p)
  126. #endif
  127. /*
  128. by specification:
  129. malloc(non_NULL, 0) : returns NULL or a unique pointer value that can later be successfully passed to free()
  130. realloc(NULL, size) : the call is equivalent to malloc(size)
  131. realloc(non_NULL, 0) : the call is equivalent to free(ptr)
  132. in main compilers:
  133. malloc(0) : returns non_NULL
  134. realloc(NULL, 0) : returns non_NULL
  135. realloc(non_NULL, 0) : returns NULL
  136. */
  137. void *MyAlloc(size_t size)
  138. {
  139. if (size == 0)
  140. return NULL;
  141. // PRINT_ALLOC("Alloc ", g_allocCount, size, NULL)
  142. #ifdef SZ_ALLOC_DEBUG
  143. {
  144. void *p = malloc(size);
  145. if (p)
  146. {
  147. PRINT_ALLOC("Alloc ", g_allocCount, size, p)
  148. }
  149. return p;
  150. }
  151. #else
  152. return malloc(size);
  153. #endif
  154. }
  155. void MyFree(void *address)
  156. {
  157. PRINT_FREE("Free ", g_allocCount, address)
  158. free(address);
  159. }
  160. void *MyRealloc(void *address, size_t size)
  161. {
  162. if (size == 0)
  163. {
  164. MyFree(address);
  165. return NULL;
  166. }
  167. // PRINT_REALLOC("Realloc ", g_allocCount, size, address)
  168. #ifdef SZ_ALLOC_DEBUG
  169. {
  170. void *p = realloc(address, size);
  171. if (p)
  172. {
  173. PRINT_REALLOC("Realloc ", g_allocCount, size, address)
  174. }
  175. return p;
  176. }
  177. #else
  178. return realloc(address, size);
  179. #endif
  180. }
  181. #ifdef _WIN32
  182. void *MidAlloc(size_t size)
  183. {
  184. if (size == 0)
  185. return NULL;
  186. #ifdef SZ_ALLOC_DEBUG
  187. {
  188. void *p = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
  189. if (p)
  190. {
  191. PRINT_ALLOC("Alloc-Mid", g_allocCountMid, size, p)
  192. }
  193. return p;
  194. }
  195. #else
  196. return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
  197. #endif
  198. }
  199. void MidFree(void *address)
  200. {
  201. PRINT_FREE("Free-Mid", g_allocCountMid, address)
  202. if (!address)
  203. return;
  204. VirtualFree(address, 0, MEM_RELEASE);
  205. }
  206. #ifdef Z7_LARGE_PAGES
  207. #ifdef MEM_LARGE_PAGES
  208. #define MY_MEM_LARGE_PAGES MEM_LARGE_PAGES
  209. #else
  210. #define MY_MEM_LARGE_PAGES 0x20000000
  211. #endif
  212. extern
  213. SIZE_T g_LargePageSize;
  214. SIZE_T g_LargePageSize = 0;
  215. typedef SIZE_T (WINAPI *Func_GetLargePageMinimum)(VOID);
  216. void SetLargePageSize(void)
  217. {
  218. SIZE_T size;
  219. #ifdef Z7_USE_DYN_GetLargePageMinimum
  220. Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
  221. const
  222. Func_GetLargePageMinimum fn =
  223. (Func_GetLargePageMinimum) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
  224. "GetLargePageMinimum");
  225. if (!fn)
  226. return;
  227. size = fn();
  228. #else
  229. size = GetLargePageMinimum();
  230. #endif
  231. if (size == 0 || (size & (size - 1)) != 0)
  232. return;
  233. g_LargePageSize = size;
  234. }
  235. #endif // Z7_LARGE_PAGES
  236. void *BigAlloc(size_t size)
  237. {
  238. if (size == 0)
  239. return NULL;
  240. PRINT_ALLOC("Alloc-Big", g_allocCountBig, size, NULL)
  241. #ifdef Z7_LARGE_PAGES
  242. {
  243. SIZE_T ps = g_LargePageSize;
  244. if (ps != 0 && ps <= (1 << 30) && size > (ps / 2))
  245. {
  246. size_t size2;
  247. ps--;
  248. size2 = (size + ps) & ~ps;
  249. if (size2 >= size)
  250. {
  251. void *p = VirtualAlloc(NULL, size2, MEM_COMMIT | MY_MEM_LARGE_PAGES, PAGE_READWRITE);
  252. if (p)
  253. {
  254. PRINT_ALLOC("Alloc-BM ", g_allocCountMid, size2, p)
  255. return p;
  256. }
  257. }
  258. }
  259. }
  260. #endif
  261. return MidAlloc(size);
  262. }
  263. void BigFree(void *address)
  264. {
  265. PRINT_FREE("Free-Big", g_allocCountBig, address)
  266. MidFree(address);
  267. }
  268. #endif // _WIN32
  269. static void *SzAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p) return MyAlloc(size); }
  270. static void SzFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p) MyFree(address); }
  271. const ISzAlloc g_Alloc = { SzAlloc, SzFree };
  272. #ifdef _WIN32
  273. static void *SzMidAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p) return MidAlloc(size); }
  274. static void SzMidFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p) MidFree(address); }
  275. static void *SzBigAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p) return BigAlloc(size); }
  276. static void SzBigFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p) BigFree(address); }
  277. const ISzAlloc g_MidAlloc = { SzMidAlloc, SzMidFree };
  278. const ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree };
  279. #endif
  280. #ifndef Z7_ALLOC_NO_OFFSET_ALLOCATOR
  281. #define ADJUST_ALLOC_SIZE 0
  282. /*
  283. #define ADJUST_ALLOC_SIZE (sizeof(void *) - 1)
  284. */
  285. /*
  286. Use (ADJUST_ALLOC_SIZE = (sizeof(void *) - 1)), if
  287. MyAlloc() can return address that is NOT multiple of sizeof(void *).
  288. */
  289. /*
  290. uintptr_t : <stdint.h> C99 (optional)
  291. : unsupported in VS6
  292. */
  293. typedef
  294. #ifdef _WIN32
  295. UINT_PTR
  296. #elif 1
  297. uintptr_t
  298. #else
  299. ptrdiff_t
  300. #endif
  301. MY_uintptr_t;
  302. #if 0 \
  303. || (defined(__CHERI__) \
  304. || defined(__SIZEOF_POINTER__) && (__SIZEOF_POINTER__ > 8))
  305. // for 128-bit pointers (cheri):
  306. #define MY_ALIGN_PTR_DOWN(p, align) \
  307. ((void *)((char *)(p) - ((size_t)(MY_uintptr_t)(p) & ((align) - 1))))
  308. #else
  309. #define MY_ALIGN_PTR_DOWN(p, align) \
  310. ((void *)((((MY_uintptr_t)(p)) & ~((MY_uintptr_t)(align) - 1))))
  311. #endif
  312. #endif
  313. #if !defined(_WIN32) \
  314. && (defined(Z7_ALLOC_NO_OFFSET_ALLOCATOR) \
  315. || defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L))
  316. #define USE_posix_memalign
  317. #endif
  318. #ifndef USE_posix_memalign
  319. #define MY_ALIGN_PTR_UP_PLUS(p, align) MY_ALIGN_PTR_DOWN(((char *)(p) + (align) + ADJUST_ALLOC_SIZE), align)
  320. #endif
  321. /*
  322. This posix_memalign() is for test purposes only.
  323. We also need special Free() function instead of free(),
  324. if this posix_memalign() is used.
  325. */
  326. /*
  327. static int posix_memalign(void **ptr, size_t align, size_t size)
  328. {
  329. size_t newSize = size + align;
  330. void *p;
  331. void *pAligned;
  332. *ptr = NULL;
  333. if (newSize < size)
  334. return 12; // ENOMEM
  335. p = MyAlloc(newSize);
  336. if (!p)
  337. return 12; // ENOMEM
  338. pAligned = MY_ALIGN_PTR_UP_PLUS(p, align);
  339. ((void **)pAligned)[-1] = p;
  340. *ptr = pAligned;
  341. return 0;
  342. }
  343. */
  344. /*
  345. ALLOC_ALIGN_SIZE >= sizeof(void *)
  346. ALLOC_ALIGN_SIZE >= cache_line_size
  347. */
  348. #define ALLOC_ALIGN_SIZE ((size_t)1 << 7)
  349. void *z7_AlignedAlloc(size_t size)
  350. {
  351. #ifndef USE_posix_memalign
  352. void *p;
  353. void *pAligned;
  354. size_t newSize;
  355. /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned
  356. block to prevent cache line sharing with another allocated blocks */
  357. newSize = size + ALLOC_ALIGN_SIZE * 1 + ADJUST_ALLOC_SIZE;
  358. if (newSize < size)
  359. return NULL;
  360. p = MyAlloc(newSize);
  361. if (!p)
  362. return NULL;
  363. pAligned = MY_ALIGN_PTR_UP_PLUS(p, ALLOC_ALIGN_SIZE);
  364. Print(" size="); PrintHex(size, 8);
  365. Print(" a_size="); PrintHex(newSize, 8);
  366. Print(" ptr="); PrintAddr(p);
  367. Print(" a_ptr="); PrintAddr(pAligned);
  368. PrintLn();
  369. ((void **)pAligned)[-1] = p;
  370. return pAligned;
  371. #else
  372. void *p;
  373. if (posix_memalign(&p, ALLOC_ALIGN_SIZE, size))
  374. return NULL;
  375. Print(" posix_memalign="); PrintAddr(p);
  376. PrintLn();
  377. return p;
  378. #endif
  379. }
  380. void z7_AlignedFree(void *address)
  381. {
  382. #ifndef USE_posix_memalign
  383. if (address)
  384. MyFree(((void **)address)[-1]);
  385. #else
  386. free(address);
  387. #endif
  388. }
  389. static void *SzAlignedAlloc(ISzAllocPtr pp, size_t size)
  390. {
  391. UNUSED_VAR(pp)
  392. return z7_AlignedAlloc(size);
  393. }
  394. static void SzAlignedFree(ISzAllocPtr pp, void *address)
  395. {
  396. UNUSED_VAR(pp)
  397. #ifndef USE_posix_memalign
  398. if (address)
  399. MyFree(((void **)address)[-1]);
  400. #else
  401. free(address);
  402. #endif
  403. }
  404. const ISzAlloc g_AlignedAlloc = { SzAlignedAlloc, SzAlignedFree };
  405. /* we align ptr to support cases where CAlignOffsetAlloc::offset is not multiply of sizeof(void *) */
  406. #ifndef Z7_ALLOC_NO_OFFSET_ALLOCATOR
  407. #if 1
  408. #define MY_ALIGN_PTR_DOWN_1(p) MY_ALIGN_PTR_DOWN(p, sizeof(void *))
  409. #define REAL_BLOCK_PTR_VAR(p) ((void **)MY_ALIGN_PTR_DOWN_1(p))[-1]
  410. #else
  411. // we can use this simplified code,
  412. // if (CAlignOffsetAlloc::offset == (k * sizeof(void *))
  413. #define REAL_BLOCK_PTR_VAR(p) (((void **)(p))[-1])
  414. #endif
  415. #endif
  416. #if 0
  417. #ifndef Z7_ALLOC_NO_OFFSET_ALLOCATOR
  418. #include <stdio.h>
  419. static void PrintPtr(const char *s, const void *p)
  420. {
  421. const Byte *p2 = (const Byte *)&p;
  422. unsigned i;
  423. printf("%s %p ", s, p);
  424. for (i = sizeof(p); i != 0;)
  425. {
  426. i--;
  427. printf("%02x", p2[i]);
  428. }
  429. printf("\n");
  430. }
  431. #endif
  432. #endif
  433. static void *AlignOffsetAlloc_Alloc(ISzAllocPtr pp, size_t size)
  434. {
  435. #if defined(Z7_ALLOC_NO_OFFSET_ALLOCATOR)
  436. UNUSED_VAR(pp)
  437. return z7_AlignedAlloc(size);
  438. #else
  439. const CAlignOffsetAlloc *p = Z7_CONTAINER_FROM_VTBL_CONST(pp, CAlignOffsetAlloc, vt);
  440. void *adr;
  441. void *pAligned;
  442. size_t newSize;
  443. size_t extra;
  444. size_t alignSize = (size_t)1 << p->numAlignBits;
  445. if (alignSize < sizeof(void *))
  446. alignSize = sizeof(void *);
  447. if (p->offset >= alignSize)
  448. return NULL;
  449. /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned
  450. block to prevent cache line sharing with another allocated blocks */
  451. extra = p->offset & (sizeof(void *) - 1);
  452. newSize = size + alignSize + extra + ADJUST_ALLOC_SIZE;
  453. if (newSize < size)
  454. return NULL;
  455. adr = ISzAlloc_Alloc(p->baseAlloc, newSize);
  456. if (!adr)
  457. return NULL;
  458. pAligned = (char *)MY_ALIGN_PTR_DOWN((char *)adr +
  459. alignSize - p->offset + extra + ADJUST_ALLOC_SIZE, alignSize) + p->offset;
  460. #if 0
  461. printf("\nalignSize = %6x, offset=%6x, size=%8x \n", (unsigned)alignSize, (unsigned)p->offset, (unsigned)size);
  462. PrintPtr("base", adr);
  463. PrintPtr("alig", pAligned);
  464. #endif
  465. PrintLn();
  466. Print("- Aligned: ");
  467. Print(" size="); PrintHex(size, 8);
  468. Print(" a_size="); PrintHex(newSize, 8);
  469. Print(" ptr="); PrintAddr(adr);
  470. Print(" a_ptr="); PrintAddr(pAligned);
  471. PrintLn();
  472. REAL_BLOCK_PTR_VAR(pAligned) = adr;
  473. return pAligned;
  474. #endif
  475. }
  476. static void AlignOffsetAlloc_Free(ISzAllocPtr pp, void *address)
  477. {
  478. #if defined(Z7_ALLOC_NO_OFFSET_ALLOCATOR)
  479. UNUSED_VAR(pp)
  480. z7_AlignedFree(address);
  481. #else
  482. if (address)
  483. {
  484. const CAlignOffsetAlloc *p = Z7_CONTAINER_FROM_VTBL_CONST(pp, CAlignOffsetAlloc, vt);
  485. PrintLn();
  486. Print("- Aligned Free: ");
  487. PrintLn();
  488. ISzAlloc_Free(p->baseAlloc, REAL_BLOCK_PTR_VAR(address));
  489. }
  490. #endif
  491. }
  492. void AlignOffsetAlloc_CreateVTable(CAlignOffsetAlloc *p)
  493. {
  494. p->vt.Alloc = AlignOffsetAlloc_Alloc;
  495. p->vt.Free = AlignOffsetAlloc_Free;
  496. }