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