xref: /reactos/sdk/lib/crt/wine/heap.c (revision c2c66aff)
1 /*
2  * msvcrt.dll heap functions
3  *
4  * Copyright 2000 Jon Griffiths
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Note: Win32 heap operations are MT safe. We only lock the new
21  *       handler and non atomic heap operations
22  */
23 
24 #include <precomp.h>
25 #include <malloc.h>
26 
27 /* MT */
28 #define LOCK_HEAP   _mlock( _HEAP_LOCK )
29 #define UNLOCK_HEAP _munlock( _HEAP_LOCK )
30 
31 /* _aligned */
32 #define SAVED_PTR(x) ((void *)((DWORD_PTR)((char *)x - sizeof(void *)) & \
33                                ~(sizeof(void *) - 1)))
34 #define ALIGN_PTR(ptr, alignment, offset) ((void *) \
35     ((((DWORD_PTR)((char *)ptr + alignment + sizeof(void *) + offset)) & \
36       ~(alignment - 1)) - offset))
37 
38 
39 typedef int (CDECL *MSVCRT_new_handler_func)(size_t size);
40 
41 static MSVCRT_new_handler_func MSVCRT_new_handler;
42 static int MSVCRT_new_mode;
43 
44 /* FIXME - According to documentation it should be 8*1024, at runtime it returns 16 */
45 static unsigned int MSVCRT_amblksiz = 16;
46 /* FIXME - According to documentation it should be 480 bytes, at runtime default is 0 */
47 static size_t MSVCRT_sbh_threshold = 0;
48 
49 /*********************************************************************
50  *		??2@YAPAXI@Z (MSVCRT.@)
51  */
52 void* CDECL MSVCRT_operator_new(size_t size)
53 {
54   void *retval;
55   int freed;
56 
57   do
58   {
59     retval = HeapAlloc(GetProcessHeap(), 0, size);
60     if(retval)
61     {
62       TRACE("(%ld) returning %p\n", size, retval);
63       return retval;
64     }
65 
66     LOCK_HEAP;
67     if(MSVCRT_new_handler)
68       freed = (*MSVCRT_new_handler)(size);
69     else
70       freed = 0;
71     UNLOCK_HEAP;
72   } while(freed);
73 
74   TRACE("(%ld) out of memory\n", size);
75   return NULL;
76 }
77 
78 
79 /*********************************************************************
80  *		??2@YAPAXIHPBDH@Z (MSVCRT.@)
81  */
82 void* CDECL MSVCRT_operator_new_dbg(size_t size, int type, const char *file, int line)
83 {
84     return MSVCRT_operator_new( size );
85 }
86 
87 
88 /*********************************************************************
89  *		??3@YAXPAX@Z (MSVCRT.@)
90  */
91 void CDECL MSVCRT_operator_delete(void *mem)
92 {
93   TRACE("(%p)\n", mem);
94   HeapFree(GetProcessHeap(), 0, mem);
95 }
96 
97 
98 /*********************************************************************
99  *		?_query_new_handler@@YAP6AHI@ZXZ (MSVCRT.@)
100  */
101 MSVCRT_new_handler_func CDECL MSVCRT__query_new_handler(void)
102 {
103   return MSVCRT_new_handler;
104 }
105 
106 
107 /*********************************************************************
108  *		?_query_new_mode@@YAHXZ (MSVCRT.@)
109  */
110 int CDECL MSVCRT__query_new_mode(void)
111 {
112   return MSVCRT_new_mode;
113 }
114 
115 /*********************************************************************
116  *		?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z (MSVCRT.@)
117  */
118 MSVCRT_new_handler_func CDECL MSVCRT__set_new_handler(MSVCRT_new_handler_func func)
119 {
120   MSVCRT_new_handler_func old_handler;
121   LOCK_HEAP;
122   old_handler = MSVCRT_new_handler;
123   MSVCRT_new_handler = func;
124   UNLOCK_HEAP;
125   return old_handler;
126 }
127 
128 /*********************************************************************
129  *		?set_new_handler@@YAP6AXXZP6AXXZ@Z (MSVCRT.@)
130  */
131 MSVCRT_new_handler_func CDECL MSVCRT_set_new_handler(void *func)
132 {
133   TRACE("(%p)\n",func);
134   MSVCRT__set_new_handler(NULL);
135   return NULL;
136 }
137 
138 /*********************************************************************
139  *		?_set_new_mode@@YAHH@Z (MSVCRT.@)
140  */
141 int CDECL MSVCRT__set_new_mode(int mode)
142 {
143   int old_mode;
144   LOCK_HEAP;
145   old_mode = MSVCRT_new_mode;
146   MSVCRT_new_mode = mode;
147   UNLOCK_HEAP;
148   return old_mode;
149 }
150 
151 /*********************************************************************
152  *		_callnewh (MSVCRT.@)
153  */
154 int CDECL _callnewh(size_t size)
155 {
156   if(MSVCRT_new_handler)
157     (*MSVCRT_new_handler)(size);
158   return 0;
159 }
160 
161 /*********************************************************************
162  *		_expand (MSVCRT.@)
163  */
164 void* CDECL _expand(void* mem, size_t size)
165 {
166   return HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, mem, size);
167 }
168 
169 /*********************************************************************
170  *		_heapchk (MSVCRT.@)
171  */
172 int CDECL _heapchk(void)
173 {
174   if (!HeapValidate( GetProcessHeap(), 0, NULL))
175   {
176     _dosmaperr(GetLastError());
177     return _HEAPBADNODE;
178   }
179   return _HEAPOK;
180 }
181 
182 /*********************************************************************
183  *		_heapmin (MSVCRT.@)
184  */
185 int CDECL _heapmin(void)
186 {
187   if (!HeapCompact( GetProcessHeap(), 0 ))
188   {
189     if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
190       _dosmaperr(GetLastError());
191     return -1;
192   }
193   return 0;
194 }
195 
196 /*********************************************************************
197  *		_heapwalk (MSVCRT.@)
198  */
199 int CDECL _heapwalk(_HEAPINFO* next)
200 {
201   PROCESS_HEAP_ENTRY phe;
202 
203   LOCK_HEAP;
204   phe.lpData = next->_pentry;
205   phe.cbData = (DWORD)next->_size;
206   phe.wFlags = next->_useflag == _USEDENTRY ? PROCESS_HEAP_ENTRY_BUSY : 0;
207 
208   if (phe.lpData && phe.wFlags & PROCESS_HEAP_ENTRY_BUSY &&
209       !HeapValidate( GetProcessHeap(), 0, phe.lpData ))
210   {
211     UNLOCK_HEAP;
212     _dosmaperr(GetLastError());
213     return _HEAPBADNODE;
214   }
215 
216   do
217   {
218     if (!HeapWalk( GetProcessHeap(), &phe ))
219     {
220       UNLOCK_HEAP;
221       if (GetLastError() == ERROR_NO_MORE_ITEMS)
222          return _HEAPEND;
223       _dosmaperr(GetLastError());
224       if (!phe.lpData)
225         return _HEAPBADBEGIN;
226       return _HEAPBADNODE;
227     }
228   } while (phe.wFlags & (PROCESS_HEAP_REGION|PROCESS_HEAP_UNCOMMITTED_RANGE));
229 
230   UNLOCK_HEAP;
231   next->_pentry = phe.lpData;
232   next->_size = phe.cbData;
233   next->_useflag = phe.wFlags & PROCESS_HEAP_ENTRY_BUSY ? _USEDENTRY : _FREEENTRY;
234   return _HEAPOK;
235 }
236 
237 /*********************************************************************
238  *		_heapset (MSVCRT.@)
239  */
240 int CDECL _heapset(unsigned int value)
241 {
242   int retval;
243   _HEAPINFO heap;
244 
245   memset( &heap, 0, sizeof(heap) );
246   LOCK_HEAP;
247   while ((retval = _heapwalk(&heap)) == _HEAPOK)
248   {
249     if (heap._useflag == _FREEENTRY)
250       memset(heap._pentry, value, heap._size);
251   }
252   UNLOCK_HEAP;
253   return retval == _HEAPEND? _HEAPOK : retval;
254 }
255 
256 /*********************************************************************
257  *		_heapadd (MSVCRT.@)
258  */
259 int CDECL _heapadd(void* mem, size_t size)
260 {
261   TRACE("(%p,%ld) unsupported in Win32\n", mem,size);
262   *_errno() = ENOSYS;
263   return -1;
264 }
265 
266 /*********************************************************************
267  *		_heapadd (MSVCRT.@)
268  */
269 intptr_t CDECL _get_heap_handle(void)
270 {
271     return (intptr_t)GetProcessHeap();
272 }
273 
274 /*********************************************************************
275  *		_msize (MSVCRT.@)
276  */
277 size_t CDECL _msize(void* mem)
278 {
279   size_t size = HeapSize(GetProcessHeap(),0,mem);
280   if (size == ~(size_t)0)
281   {
282     WARN(":Probably called with non wine-allocated memory, ret = -1\n");
283     /* At least the Win32 crtdll/msvcrt also return -1 in this case */
284   }
285   return size;
286 }
287 
288 /*********************************************************************
289  *		calloc (MSVCRT.@)
290  */
291 void* CDECL calloc(size_t count, size_t size)
292 {
293   return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, count * size );
294 }
295 
296 /*********************************************************************
297  *		free (MSVCRT.@)
298  */
299 void CDECL free(void* ptr)
300 {
301   if(ptr == NULL) return;
302   HeapFree(GetProcessHeap(),0,ptr);
303 }
304 
305 /*********************************************************************
306  *                  malloc (MSVCRT.@)
307  */
308 void* CDECL malloc(size_t size)
309 {
310   void *ret = HeapAlloc(GetProcessHeap(),0,size);
311   if (!ret)
312       *_errno() = ENOMEM;
313   return ret;
314 }
315 
316 /*********************************************************************
317  *		realloc (MSVCRT.@)
318  */
319 void* CDECL realloc(void* ptr, size_t size)
320 {
321   if (!ptr) return malloc(size);
322   if (size) return HeapReAlloc(GetProcessHeap(), 0, ptr, size);
323   free(ptr);
324   return NULL;
325 }
326 
327 /*********************************************************************
328  *		__p__amblksiz (MSVCRT.@)
329  */
330 unsigned int* CDECL __p__amblksiz(void)
331 {
332   return &MSVCRT_amblksiz;
333 }
334 
335 /*********************************************************************
336  *		_get_sbh_threshold (MSVCRT.@)
337  */
338 size_t CDECL _get_sbh_threshold(void)
339 {
340   return MSVCRT_sbh_threshold;
341 }
342 
343 /*********************************************************************
344  *		_set_sbh_threshold (MSVCRT.@)
345  */
346 int CDECL _set_sbh_threshold(size_t threshold)
347 {
348   if(threshold > 1016)
349      return 0;
350   else
351      MSVCRT_sbh_threshold = threshold;
352   return 1;
353 }
354 
355 /*********************************************************************
356  *		_aligned_free (MSVCRT.@)
357  */
358 void CDECL _aligned_free(void *memblock)
359 {
360     TRACE("(%p)\n", memblock);
361 
362     if (memblock)
363     {
364         void **saved = SAVED_PTR(memblock);
365         free(*saved);
366     }
367 }
368 
369 /*********************************************************************
370  *		_aligned_offset_malloc (MSVCRT.@)
371  */
372 void * CDECL _aligned_offset_malloc(size_t size, size_t alignment, size_t offset)
373 {
374     void *memblock, *temp, **saved;
375     TRACE("(%lu, %lu, %lu)\n", size, alignment, offset);
376 
377     /* alignment must be a power of 2 */
378     if ((alignment & (alignment - 1)) != 0)
379     {
380         *_errno() = EINVAL;
381         return NULL;
382     }
383 
384     /* offset must be less than size */
385     if (offset >= size)
386     {
387         *_errno() = EINVAL;
388         return NULL;
389     }
390 
391     /* don't align to less than void pointer size */
392     if (alignment < sizeof(void *))
393         alignment = sizeof(void *);
394 
395     /* allocate enough space for void pointer and alignment */
396     temp = malloc(size + alignment + sizeof(void *));
397 
398     if (!temp)
399         return NULL;
400 
401     /* adjust pointer for proper alignment and offset */
402     memblock = ALIGN_PTR(temp, alignment, offset);
403 
404     /* Save the real allocation address below returned address */
405     /* so it can be found later to free. */
406     saved = SAVED_PTR(memblock);
407     *saved = temp;
408 
409     return memblock;
410 }
411 
412 /*********************************************************************
413  *		_aligned_malloc (MSVCRT.@)
414  */
415 void * CDECL _aligned_malloc(size_t size, size_t alignment)
416 {
417     TRACE("(%lu, %lu)\n", size, alignment);
418     return _aligned_offset_malloc(size, alignment, 0);
419 }
420 
421 /*********************************************************************
422  *		_aligned_offset_realloc (MSVCRT.@)
423  */
424 void * CDECL _aligned_offset_realloc(void *memblock, size_t size,
425                                      size_t alignment, size_t offset)
426 {
427     void * temp, **saved;
428     size_t old_padding, new_padding, old_size;
429     TRACE("(%p, %lu, %lu, %lu)\n", memblock, size, alignment, offset);
430 
431     if (!memblock)
432         return _aligned_offset_malloc(size, alignment, offset);
433 
434     /* alignment must be a power of 2 */
435     if ((alignment & (alignment - 1)) != 0)
436     {
437         *_errno() = EINVAL;
438         return NULL;
439     }
440 
441     /* offset must be less than size */
442     if (offset >= size)
443     {
444         *_errno() = EINVAL;
445         return NULL;
446     }
447 
448     if (size == 0)
449     {
450         _aligned_free(memblock);
451         return NULL;
452     }
453 
454     /* don't align to less than void pointer size */
455     if (alignment < sizeof(void *))
456         alignment = sizeof(void *);
457 
458     /* make sure alignment and offset didn't change */
459     saved = SAVED_PTR(memblock);
460     if (memblock != ALIGN_PTR(*saved, alignment, offset))
461     {
462         *_errno() = EINVAL;
463         return NULL;
464     }
465 
466     old_padding = (char *)memblock - (char *)*saved;
467 
468     /* Get previous size of block */
469     old_size = _msize(*saved);
470     if (old_size == -1)
471     {
472         /* It seems this function was called with an invalid pointer. Bail out. */
473         return NULL;
474     }
475 
476     /* Adjust old_size to get amount of actual data in old block. */
477     if (old_size < old_padding)
478     {
479         /* Shouldn't happen. Something's weird, so bail out. */
480         return NULL;
481     }
482     old_size -= old_padding;
483 
484     temp = realloc(*saved, size + alignment + sizeof(void *));
485 
486     if (!temp)
487         return NULL;
488 
489     /* adjust pointer for proper alignment and offset */
490     memblock = ALIGN_PTR(temp, alignment, offset);
491 
492     /* Save the real allocation address below returned address */
493     /* so it can be found later to free. */
494     saved = SAVED_PTR(memblock);
495 
496     new_padding = (char *)memblock - (char *)temp;
497 
498 /*
499    Memory layout of old block is as follows:
500    +-------+---------------------+-+--------------------------+-----------+
501    |  ...  | "old_padding" bytes | | ... "old_size" bytes ... |    ...    |
502    +-------+---------------------+-+--------------------------+-----------+
503            ^                     ^ ^
504            |                     | |
505         *saved               saved memblock
506 
507    Memory layout of new block is as follows:
508    +-------+-----------------------------+-+----------------------+-------+
509    |  ...  |    "new_padding" bytes      | | ... "size" bytes ... |  ...  |
510    +-------+-----------------------------+-+----------------------+-------+
511            ^                             ^ ^
512            |                             | |
513           temp                       saved memblock
514 
515    However, in the new block, actual data is still written as follows
516    (because it was copied by MSVCRT_realloc):
517    +-------+---------------------+--------------------------------+-------+
518    |  ...  | "old_padding" bytes |   ... "old_size" bytes ...     |  ...  |
519    +-------+---------------------+--------------------------------+-------+
520            ^                             ^ ^
521            |                             | |
522           temp                       saved memblock
523 
524    Therefore, min(old_size,size) bytes of actual data have to be moved
525    from the offset they were at in the old block (temp + old_padding),
526    to the offset they have to be in the new block (temp + new_padding == memblock).
527 */
528     if (new_padding != old_padding)
529         memmove((char *)memblock, (char *)temp + old_padding, (old_size < size) ? old_size : size);
530 
531     *saved = temp;
532 
533     return memblock;
534 }
535 
536 /*********************************************************************
537  *		_aligned_realloc (MSVCRT.@)
538  */
539 void * CDECL _aligned_realloc(void *memblock, size_t size, size_t alignment)
540 {
541     TRACE("(%p, %lu, %lu)\n", memblock, size, alignment);
542     return _aligned_offset_realloc(memblock, size, alignment, 0);
543 }
544 
545 /*********************************************************************
546  *		memmove_s (MSVCRT.@)
547  */
548 int CDECL memmove_s(void *dest, size_t numberOfElements, const void *src, size_t count)
549 {
550     TRACE("(%p %lu %p %lu)\n", dest, numberOfElements, src, count);
551 
552     if(!count)
553         return 0;
554 
555     if(!dest || !src) {
556         if(dest)
557             memset(dest, 0, numberOfElements);
558 
559         *_errno() = EINVAL;
560         return EINVAL;
561     }
562 
563     if(count > numberOfElements) {
564         memset(dest, 0, numberOfElements);
565 
566         *_errno() = ERANGE;
567         return ERANGE;
568     }
569 
570     memmove(dest, src, count);
571     return 0;
572 }
573 
574 /*********************************************************************
575  *		strncpy_s (MSVCRT.@)
576  */
577 int CDECL strncpy_s(char *dest, size_t numberOfElements,
578         const char *src, size_t count)
579 {
580     size_t i, end;
581 
582     TRACE("(%s %lu %s %lu)\n", dest, numberOfElements, src, count);
583 
584     if(!count)
585         return 0;
586 
587     if (!MSVCRT_CHECK_PMT(dest != NULL) || !MSVCRT_CHECK_PMT(src != NULL) ||
588         !MSVCRT_CHECK_PMT(numberOfElements != 0)) {
589         *_errno() = EINVAL;
590         return EINVAL;
591     }
592 
593     if(count!=_TRUNCATE && count<numberOfElements)
594         end = count;
595     else
596         end = numberOfElements-1;
597 
598     for(i=0; i<end && src[i]; i++)
599         dest[i] = src[i];
600 
601     if(!src[i] || end==count || count==_TRUNCATE) {
602         dest[i] = '\0';
603         return 0;
604     }
605 
606     MSVCRT_INVALID_PMT("dest[numberOfElements] is too small", EINVAL);
607     dest[0] = '\0';
608     return EINVAL;
609 }
610