xref: /reactos/sdk/lib/crt/wine/heap.c (revision c47506a5)
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 #define MSVCRT_size_t size_t
28 #define MSVCRT_intptr_t intptr_t
29 #define MSVCRT__HEAPBADNODE _HEAPBADNODE
30 #define MSVCRT__HEAPOK _HEAPOK
31 #define MSVCRT__HEAPEND _HEAPEND
32 #define MSVCRT__FREEENTRY _FREEENTRY
33 #define MSVCRT__USEDENTRY _USEDENTRY
34 #define MSVCRT__HEAPBADBEGIN _HEAPBADBEGIN
35 #define MSVCRT_EINVAL EINVAL
36 #define MSVCRT_ENOSYS ENOSYS
37 #define MSVCRT_ENOMEM ENOMEM
38 #define MSVCRT__TRUNCATE _TRUNCATE
39 #define MSVCRT__heapinfo _heapinfo
40 #define MSVCRT__errno _errno
41 #define MSVCRT_calloc calloc
42 #define MSVCRT_malloc malloc
43 #define MSVCRT_realloc realloc
44 #define MSVCRT_free free
45 #define MSVCRT_memmove_s memmove_s
46 #define MSVCRT_strncpy_s strncpy_s
47 
48 /* MT */
49 #define LOCK_HEAP   _mlock( _HEAP_LOCK )
50 #define UNLOCK_HEAP _munlock( _HEAP_LOCK )
51 
52 /* _aligned */
53 #define SAVED_PTR(x) ((void *)((DWORD_PTR)((char *)x - sizeof(void *)) & \
54                                ~(sizeof(void *) - 1)))
55 #define ALIGN_PTR(ptr, alignment, offset) ((void *) \
56     ((((DWORD_PTR)((char *)ptr + alignment + sizeof(void *) + offset)) & \
57       ~(alignment - 1)) - offset))
58 
59 #define SB_HEAP_ALIGN 16
60 
61 static HANDLE heap, sb_heap;
62 
63 typedef int (CDECL *MSVCRT_new_handler_func)(MSVCRT_size_t size);
64 
65 static MSVCRT_new_handler_func MSVCRT_new_handler;
66 static int MSVCRT_new_mode;
67 
68 /* FIXME - According to documentation it should be 8*1024, at runtime it returns 16 */
69 static unsigned int MSVCRT_amblksiz = 16;
70 /* FIXME - According to documentation it should be 480 bytes, at runtime default is 0 */
71 static MSVCRT_size_t MSVCRT_sbh_threshold = 0;
72 
73 static void* msvcrt_heap_alloc(DWORD flags, MSVCRT_size_t size)
74 {
75     if(size < MSVCRT_sbh_threshold)
76     {
77         void *memblock, *temp, **saved;
78 
79         temp = HeapAlloc(sb_heap, flags, size+sizeof(void*)+SB_HEAP_ALIGN);
80         if(!temp) return NULL;
81 
82         memblock = ALIGN_PTR(temp, SB_HEAP_ALIGN, 0);
83         saved = SAVED_PTR(memblock);
84         *saved = temp;
85         return memblock;
86     }
87 
88     return HeapAlloc(heap, flags, size);
89 }
90 
91 static void* msvcrt_heap_realloc(DWORD flags, void *ptr, MSVCRT_size_t size)
92 {
93     if(sb_heap && ptr && !HeapValidate(heap, 0, ptr))
94     {
95         /* TODO: move data to normal heap if it exceeds sbh_threshold limit */
96         void *memblock, *temp, **saved;
97         MSVCRT_size_t old_padding, new_padding, old_size;
98 
99         saved = SAVED_PTR(ptr);
100         old_padding = (char*)ptr - (char*)*saved;
101         old_size = HeapSize(sb_heap, 0, *saved);
102         if(old_size == -1)
103             return NULL;
104         old_size -= old_padding;
105 
106         temp = HeapReAlloc(sb_heap, flags, *saved, size+sizeof(void*)+SB_HEAP_ALIGN);
107         if(!temp) return NULL;
108 
109         memblock = ALIGN_PTR(temp, SB_HEAP_ALIGN, 0);
110         saved = SAVED_PTR(memblock);
111         new_padding = (char*)memblock - (char*)temp;
112 
113         if(new_padding != old_padding)
114             memmove(memblock, (char*)temp+old_padding, old_size>size ? size : old_size);
115 
116         *saved = temp;
117         return memblock;
118     }
119 
120     return HeapReAlloc(heap, flags, ptr, size);
121 }
122 
123 static BOOL msvcrt_heap_free(void *ptr)
124 {
125     if(sb_heap && ptr && !HeapValidate(heap, 0, ptr))
126     {
127         void **saved = SAVED_PTR(ptr);
128         return HeapFree(sb_heap, 0, *saved);
129     }
130 
131     return HeapFree(heap, 0, ptr);
132 }
133 
134 static MSVCRT_size_t msvcrt_heap_size(void *ptr)
135 {
136     if(sb_heap && ptr && !HeapValidate(heap, 0, ptr))
137     {
138         void **saved = SAVED_PTR(ptr);
139         return HeapSize(sb_heap, 0, *saved);
140     }
141 
142     return HeapSize(heap, 0, ptr);
143 }
144 
145 /*********************************************************************
146  *		??2@YAPAXI@Z (MSVCRT.@)
147  */
148 void* CDECL MSVCRT_operator_new(MSVCRT_size_t size)
149 {
150   void *retval;
151   int freed;
152   MSVCRT_new_handler_func handler;
153 
154   do
155   {
156     retval = msvcrt_heap_alloc(0, size);
157     if(retval)
158     {
159       TRACE("(%ld) returning %p\n", size, retval);
160       return retval;
161     }
162 
163     LOCK_HEAP;
164     handler = MSVCRT_new_handler;
165     if(handler)
166       freed = (*handler)(size);
167     else
168       freed = 0;
169     UNLOCK_HEAP;
170   } while(freed);
171 
172   TRACE("(%ld) out of memory\n", size);
173 #if _MSVCR_VER >= 80
174   throw_bad_alloc("bad allocation");
175 #endif
176   return NULL;
177 }
178 
179 
180 /*********************************************************************
181  *		??2@YAPAXIHPBDH@Z (MSVCRT.@)
182  */
183 void* CDECL MSVCRT_operator_new_dbg(MSVCRT_size_t size, int type, const char *file, int line)
184 {
185     return MSVCRT_operator_new( size );
186 }
187 
188 
189 /*********************************************************************
190  *		??3@YAXPAX@Z (MSVCRT.@)
191  */
192 void CDECL MSVCRT_operator_delete(void *mem)
193 {
194   TRACE("(%p)\n", mem);
195   msvcrt_heap_free(mem);
196 }
197 
198 
199 /*********************************************************************
200  *		?_query_new_handler@@YAP6AHI@ZXZ (MSVCRT.@)
201  */
202 MSVCRT_new_handler_func CDECL MSVCRT__query_new_handler(void)
203 {
204   return MSVCRT_new_handler;
205 }
206 
207 
208 /*********************************************************************
209  *		?_query_new_mode@@YAHXZ (MSVCRT.@)
210  */
211 int CDECL MSVCRT__query_new_mode(void)
212 {
213   return MSVCRT_new_mode;
214 }
215 
216 /*********************************************************************
217  *		?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z (MSVCRT.@)
218  */
219 MSVCRT_new_handler_func CDECL MSVCRT__set_new_handler(MSVCRT_new_handler_func func)
220 {
221   MSVCRT_new_handler_func old_handler;
222   LOCK_HEAP;
223   old_handler = MSVCRT_new_handler;
224   MSVCRT_new_handler = func;
225   UNLOCK_HEAP;
226   return old_handler;
227 }
228 
229 /*********************************************************************
230  *		?set_new_handler@@YAP6AXXZP6AXXZ@Z (MSVCRT.@)
231  */
232 MSVCRT_new_handler_func CDECL MSVCRT_set_new_handler(void *func)
233 {
234   TRACE("(%p)\n",func);
235   MSVCRT__set_new_handler(NULL);
236   return NULL;
237 }
238 
239 /*********************************************************************
240  *		?_set_new_mode@@YAHH@Z (MSVCRT.@)
241  */
242 int CDECL MSVCRT__set_new_mode(int mode)
243 {
244   int old_mode;
245   LOCK_HEAP;
246   old_mode = MSVCRT_new_mode;
247   MSVCRT_new_mode = mode;
248   UNLOCK_HEAP;
249   return old_mode;
250 }
251 
252 /*********************************************************************
253  *		_callnewh (MSVCRT.@)
254  */
255 int CDECL _callnewh(MSVCRT_size_t size)
256 {
257   int ret = 0;
258   MSVCRT_new_handler_func handler = MSVCRT_new_handler;
259   if(handler)
260     ret = (*handler)(size) ? 1 : 0;
261   return ret;
262 }
263 
264 /*********************************************************************
265  *		_expand (MSVCRT.@)
266  */
267 void* CDECL _expand(void* mem, MSVCRT_size_t size)
268 {
269   return msvcrt_heap_realloc(HEAP_REALLOC_IN_PLACE_ONLY, mem, size);
270 }
271 
272 /*********************************************************************
273  *		_heapchk (MSVCRT.@)
274  */
275 int CDECL _heapchk(void)
276 {
277   if (!HeapValidate(heap, 0, NULL) ||
278           (sb_heap && !HeapValidate(sb_heap, 0, NULL)))
279   {
280     _dosmaperr(GetLastError());
281     return MSVCRT__HEAPBADNODE;
282   }
283   return MSVCRT__HEAPOK;
284 }
285 
286 /*********************************************************************
287  *		_heapmin (MSVCRT.@)
288  */
289 int CDECL _heapmin(void)
290 {
291   if (!HeapCompact( heap, 0 ) ||
292           (sb_heap && !HeapCompact( sb_heap, 0 )))
293   {
294     if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
295       _dosmaperr(GetLastError());
296     return -1;
297   }
298   return 0;
299 }
300 
301 /*********************************************************************
302  *		_heapwalk (MSVCRT.@)
303  */
304 int CDECL _heapwalk(struct MSVCRT__heapinfo* next)
305 {
306   PROCESS_HEAP_ENTRY phe;
307 
308   if (sb_heap)
309       FIXME("small blocks heap not supported\n");
310 
311   LOCK_HEAP;
312   phe.lpData = next->_pentry;
313   phe.cbData = (DWORD)next->_size;
314   phe.wFlags = next->_useflag == MSVCRT__USEDENTRY ? PROCESS_HEAP_ENTRY_BUSY : 0;
315 
316   if (phe.lpData && phe.wFlags & PROCESS_HEAP_ENTRY_BUSY &&
317       !HeapValidate( heap, 0, phe.lpData ))
318   {
319     UNLOCK_HEAP;
320     _dosmaperr(GetLastError());
321     return MSVCRT__HEAPBADNODE;
322   }
323 
324   do
325   {
326     if (!HeapWalk( heap, &phe ))
327     {
328       UNLOCK_HEAP;
329       if (GetLastError() == ERROR_NO_MORE_ITEMS)
330          return MSVCRT__HEAPEND;
331       _dosmaperr(GetLastError());
332       if (!phe.lpData)
333         return MSVCRT__HEAPBADBEGIN;
334       return MSVCRT__HEAPBADNODE;
335     }
336   } while (phe.wFlags & (PROCESS_HEAP_REGION|PROCESS_HEAP_UNCOMMITTED_RANGE));
337 
338   UNLOCK_HEAP;
339   next->_pentry = phe.lpData;
340   next->_size = phe.cbData;
341   next->_useflag = phe.wFlags & PROCESS_HEAP_ENTRY_BUSY ? MSVCRT__USEDENTRY : MSVCRT__FREEENTRY;
342   return MSVCRT__HEAPOK;
343 }
344 
345 /*********************************************************************
346  *		_heapset (MSVCRT.@)
347  */
348 int CDECL _heapset(unsigned int value)
349 {
350   int retval;
351   struct MSVCRT__heapinfo heap;
352 
353   memset( &heap, 0, sizeof(heap) );
354   LOCK_HEAP;
355   while ((retval = _heapwalk(&heap)) == MSVCRT__HEAPOK)
356   {
357     if (heap._useflag == MSVCRT__FREEENTRY)
358       memset(heap._pentry, value, heap._size);
359   }
360   UNLOCK_HEAP;
361   return retval == MSVCRT__HEAPEND? MSVCRT__HEAPOK : retval;
362 }
363 
364 /*********************************************************************
365  *		_heapadd (MSVCRT.@)
366  */
367 int CDECL _heapadd(void* mem, MSVCRT_size_t size)
368 {
369   TRACE("(%p,%ld) unsupported in Win32\n", mem,size);
370   *MSVCRT__errno() = MSVCRT_ENOSYS;
371   return -1;
372 }
373 
374 /*********************************************************************
375  *		_get_heap_handle (MSVCRT.@)
376  */
377 MSVCRT_intptr_t CDECL _get_heap_handle(void)
378 {
379     return (MSVCRT_intptr_t)heap;
380 }
381 
382 /*********************************************************************
383  *		_msize (MSVCRT.@)
384  */
385 MSVCRT_size_t CDECL _msize(void* mem)
386 {
387   MSVCRT_size_t size = msvcrt_heap_size(mem);
388   if (size == ~(MSVCRT_size_t)0)
389   {
390     WARN(":Probably called with non wine-allocated memory, ret = -1\n");
391     /* At least the Win32 crtdll/msvcrt also return -1 in this case */
392   }
393   return size;
394 }
395 
396 /*********************************************************************
397  *		calloc (MSVCRT.@)
398  */
399 void* CDECL MSVCRT_calloc(MSVCRT_size_t count, MSVCRT_size_t size)
400 {
401   return msvcrt_heap_alloc(HEAP_ZERO_MEMORY, count*size);
402 }
403 
404 /*********************************************************************
405  *		free (MSVCRT.@)
406  */
407 void CDECL MSVCRT_free(void* ptr)
408 {
409   if(ptr == NULL) return;
410   msvcrt_heap_free(ptr);
411 }
412 
413 /*********************************************************************
414  *                  malloc (MSVCRT.@)
415  */
416 void* CDECL MSVCRT_malloc(MSVCRT_size_t size)
417 {
418   void *ret = msvcrt_heap_alloc(0, size);
419   if (!ret)
420       *MSVCRT__errno() = MSVCRT_ENOMEM;
421   return ret;
422 }
423 
424 /*********************************************************************
425  *		realloc (MSVCRT.@)
426  */
427 void* CDECL MSVCRT_realloc(void* ptr, MSVCRT_size_t size)
428 {
429   if (!ptr) return MSVCRT_malloc(size);
430   if (size) return msvcrt_heap_realloc(0, ptr, size);
431   MSVCRT_free(ptr);
432   return NULL;
433 }
434 
435 /*********************************************************************
436  *		__p__amblksiz (MSVCRT.@)
437  */
438 unsigned int* CDECL __p__amblksiz(void)
439 {
440   return &MSVCRT_amblksiz;
441 }
442 
443 /*********************************************************************
444  *		_get_sbh_threshold (MSVCRT.@)
445  */
446 MSVCRT_size_t CDECL _get_sbh_threshold(void)
447 {
448   return MSVCRT_sbh_threshold;
449 }
450 
451 /*********************************************************************
452  *		_set_sbh_threshold (MSVCRT.@)
453  */
454 int CDECL _set_sbh_threshold(MSVCRT_size_t threshold)
455 {
456 #ifdef _WIN64
457   return 0;
458 #else
459   if(threshold > 1016)
460      return 0;
461 
462   if(!sb_heap)
463   {
464       sb_heap = HeapCreate(0, 0, 0);
465       if(!sb_heap)
466           return 0;
467   }
468 
469   MSVCRT_sbh_threshold = (threshold+0xf) & ~0xf;
470   return 1;
471 #endif
472 }
473 
474 /*********************************************************************
475  *		_aligned_free (MSVCRT.@)
476  */
477 void CDECL _aligned_free(void *memblock)
478 {
479     TRACE("(%p)\n", memblock);
480 
481     if (memblock)
482     {
483         void **saved = SAVED_PTR(memblock);
484         MSVCRT_free(*saved);
485     }
486 }
487 
488 /*********************************************************************
489  *		_aligned_offset_malloc (MSVCRT.@)
490  */
491 void * CDECL _aligned_offset_malloc(MSVCRT_size_t size, MSVCRT_size_t alignment, MSVCRT_size_t offset)
492 {
493     void *memblock, *temp, **saved;
494     TRACE("(%lu, %lu, %lu)\n", size, alignment, offset);
495 
496     /* alignment must be a power of 2 */
497     if ((alignment & (alignment - 1)) != 0)
498     {
499         *MSVCRT__errno() = MSVCRT_EINVAL;
500         return NULL;
501     }
502 
503     /* offset must be less than size */
504     if (offset >= size)
505     {
506         *MSVCRT__errno() = MSVCRT_EINVAL;
507         return NULL;
508     }
509 
510     /* don't align to less than void pointer size */
511     if (alignment < sizeof(void *))
512         alignment = sizeof(void *);
513 
514     /* allocate enough space for void pointer and alignment */
515     temp = MSVCRT_malloc(size + alignment + sizeof(void *));
516 
517     if (!temp)
518         return NULL;
519 
520     /* adjust pointer for proper alignment and offset */
521     memblock = ALIGN_PTR(temp, alignment, offset);
522 
523     /* Save the real allocation address below returned address */
524     /* so it can be found later to free. */
525     saved = SAVED_PTR(memblock);
526     *saved = temp;
527 
528     return memblock;
529 }
530 
531 /*********************************************************************
532  *		_aligned_malloc (MSVCRT.@)
533  */
534 void * CDECL _aligned_malloc(MSVCRT_size_t size, MSVCRT_size_t alignment)
535 {
536     TRACE("(%lu, %lu)\n", size, alignment);
537     return _aligned_offset_malloc(size, alignment, 0);
538 }
539 
540 /*********************************************************************
541  *		_aligned_offset_realloc (MSVCRT.@)
542  */
543 void * CDECL _aligned_offset_realloc(void *memblock, MSVCRT_size_t size,
544                                      MSVCRT_size_t alignment, MSVCRT_size_t offset)
545 {
546     void * temp, **saved;
547     MSVCRT_size_t old_padding, new_padding, old_size;
548     TRACE("(%p, %lu, %lu, %lu)\n", memblock, size, alignment, offset);
549 
550     if (!memblock)
551         return _aligned_offset_malloc(size, alignment, offset);
552 
553     /* alignment must be a power of 2 */
554     if ((alignment & (alignment - 1)) != 0)
555     {
556         *MSVCRT__errno() = MSVCRT_EINVAL;
557         return NULL;
558     }
559 
560     /* offset must be less than size */
561     if (offset >= size)
562     {
563         *MSVCRT__errno() = MSVCRT_EINVAL;
564         return NULL;
565     }
566 
567     if (size == 0)
568     {
569         _aligned_free(memblock);
570         return NULL;
571     }
572 
573     /* don't align to less than void pointer size */
574     if (alignment < sizeof(void *))
575         alignment = sizeof(void *);
576 
577     /* make sure alignment and offset didn't change */
578     saved = SAVED_PTR(memblock);
579     if (memblock != ALIGN_PTR(*saved, alignment, offset))
580     {
581         *MSVCRT__errno() = MSVCRT_EINVAL;
582         return NULL;
583     }
584 
585     old_padding = (char *)memblock - (char *)*saved;
586 
587     /* Get previous size of block */
588     old_size = _msize(*saved);
589     if (old_size == -1)
590     {
591         /* It seems this function was called with an invalid pointer. Bail out. */
592         return NULL;
593     }
594 
595     /* Adjust old_size to get amount of actual data in old block. */
596     if (old_size < old_padding)
597     {
598         /* Shouldn't happen. Something's weird, so bail out. */
599         return NULL;
600     }
601     old_size -= old_padding;
602 
603     temp = MSVCRT_realloc(*saved, size + alignment + sizeof(void *));
604 
605     if (!temp)
606         return NULL;
607 
608     /* adjust pointer for proper alignment and offset */
609     memblock = ALIGN_PTR(temp, alignment, offset);
610 
611     /* Save the real allocation address below returned address */
612     /* so it can be found later to free. */
613     saved = SAVED_PTR(memblock);
614 
615     new_padding = (char *)memblock - (char *)temp;
616 
617 /*
618    Memory layout of old block is as follows:
619    +-------+---------------------+-+--------------------------+-----------+
620    |  ...  | "old_padding" bytes | | ... "old_size" bytes ... |    ...    |
621    +-------+---------------------+-+--------------------------+-----------+
622            ^                     ^ ^
623            |                     | |
624         *saved               saved memblock
625 
626    Memory layout of new block is as follows:
627    +-------+-----------------------------+-+----------------------+-------+
628    |  ...  |    "new_padding" bytes      | | ... "size" bytes ... |  ...  |
629    +-------+-----------------------------+-+----------------------+-------+
630            ^                             ^ ^
631            |                             | |
632           temp                       saved memblock
633 
634    However, in the new block, actual data is still written as follows
635    (because it was copied by MSVCRT_realloc):
636    +-------+---------------------+--------------------------------+-------+
637    |  ...  | "old_padding" bytes |   ... "old_size" bytes ...     |  ...  |
638    +-------+---------------------+--------------------------------+-------+
639            ^                             ^ ^
640            |                             | |
641           temp                       saved memblock
642 
643    Therefore, min(old_size,size) bytes of actual data have to be moved
644    from the offset they were at in the old block (temp + old_padding),
645    to the offset they have to be in the new block (temp + new_padding == memblock).
646 */
647     if (new_padding != old_padding)
648         memmove((char *)memblock, (char *)temp + old_padding, (old_size < size) ? old_size : size);
649 
650     *saved = temp;
651 
652     return memblock;
653 }
654 
655 /*********************************************************************
656  *		_aligned_realloc (MSVCRT.@)
657  */
658 void * CDECL _aligned_realloc(void *memblock, MSVCRT_size_t size, MSVCRT_size_t alignment)
659 {
660     TRACE("(%p, %lu, %lu)\n", memblock, size, alignment);
661     return _aligned_offset_realloc(memblock, size, alignment, 0);
662 }
663 
664 /*********************************************************************
665  *		memmove_s (MSVCRT.@)
666  */
667 int CDECL MSVCRT_memmove_s(void *dest, MSVCRT_size_t numberOfElements, const void *src, MSVCRT_size_t count)
668 {
669     TRACE("(%p %lu %p %lu)\n", dest, numberOfElements, src, count);
670 
671     if(!count)
672         return 0;
673 
674     if(!dest || !src) {
675         if(dest)
676             memset(dest, 0, numberOfElements);
677 
678         *_errno() = EINVAL;
679         return EINVAL;
680     }
681 
682     if(count > numberOfElements) {
683         memset(dest, 0, numberOfElements);
684 
685         *_errno() = ERANGE;
686         return ERANGE;
687     }
688 
689     memmove(dest, src, count);
690     return 0;
691 }
692 
693 /*********************************************************************
694  *		strncpy_s (MSVCRT.@)
695  */
696 int CDECL MSVCRT_strncpy_s(char *dest, MSVCRT_size_t numberOfElements,
697         const char *src, MSVCRT_size_t count)
698 {
699     MSVCRT_size_t i, end;
700 
701     TRACE("(%s %lu %s %lu)\n", dest, numberOfElements, src, count);
702 
703     if(!count)
704         return 0;
705 
706     if (!MSVCRT_CHECK_PMT(dest != NULL) || !MSVCRT_CHECK_PMT(src != NULL) ||
707         !MSVCRT_CHECK_PMT(numberOfElements != 0)) {
708         *_errno() = EINVAL;
709         return EINVAL;
710     }
711 
712     if(count!=MSVCRT__TRUNCATE && count<numberOfElements)
713         end = count;
714     else
715         end = numberOfElements-1;
716 
717     for(i=0; i<end && src[i]; i++)
718         dest[i] = src[i];
719 
720     if(!src[i] || end==count || count==MSVCRT__TRUNCATE) {
721         dest[i] = '\0';
722         return 0;
723     }
724 
725     MSVCRT_INVALID_PMT("dest[numberOfElements] is too small", MSVCRT_EINVAL);
726     dest[0] = '\0';
727     return MSVCRT_EINVAL;
728 }
729 
730 BOOL msvcrt_init_heap(void)
731 {
732 #ifdef __REACTOS__
733     heap = GetProcessHeap();
734 #else
735     heap = HeapCreate(0, 0, 0);
736 #endif
737     return heap != NULL;
738 }
739 
740 void msvcrt_destroy_heap(void)
741 {
742     HeapDestroy(heap);
743     if(sb_heap)
744         HeapDestroy(sb_heap);
745 }
746