msvcrt: Added memmove_s and memcpy_s implementation.
[wine] / dlls / msvcrt / heap.c
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 "msvcrt.h"
25 #include "mtdll.h"
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
29
30 /* MT */
31 #define LOCK_HEAP   _mlock( _HEAP_LOCK )
32 #define UNLOCK_HEAP _munlock( _HEAP_LOCK )
33
34 /* _aligned */
35 #define SAVED_PTR(x) ((void *)((DWORD_PTR)((char *)x - sizeof(void *)) & \
36                                ~(sizeof(void *) - 1)))
37 #define ALIGN_PTR(ptr, alignment, offset) ((void *) \
38     ((((DWORD_PTR)((char *)ptr + alignment + sizeof(void *) + offset)) & \
39       ~(alignment - 1)) - offset))
40
41
42 typedef void (*MSVCRT_new_handler_func)(MSVCRT_size_t size);
43
44 static MSVCRT_new_handler_func MSVCRT_new_handler;
45 static int MSVCRT_new_mode;
46
47 /* FIXME - According to documentation it should be 8*1024, at runtime it returns 16 */ 
48 static unsigned int MSVCRT_amblksiz = 16;
49 /* FIXME - According to documentation it should be 480 bytes, at runtime default is 0 */
50 static MSVCRT_size_t MSVCRT_sbh_threshold = 0;
51
52 /*********************************************************************
53  *              ??2@YAPAXI@Z (MSVCRT.@)
54  */
55 void* CDECL MSVCRT_operator_new(MSVCRT_size_t size)
56 {
57   void *retval = HeapAlloc(GetProcessHeap(), 0, size);
58   TRACE("(%ld) returning %p\n", size, retval);
59   if(retval) return retval;
60   LOCK_HEAP;
61   if(MSVCRT_new_handler)
62     (*MSVCRT_new_handler)(size);
63   UNLOCK_HEAP;
64   return retval;
65 }
66
67 /*********************************************************************
68  *              ??3@YAXPAX@Z (MSVCRT.@)
69  */
70 void CDECL MSVCRT_operator_delete(void *mem)
71 {
72   TRACE("(%p)\n", mem);
73   HeapFree(GetProcessHeap(), 0, mem);
74 }
75
76
77 /*********************************************************************
78  *              ?_query_new_handler@@YAP6AHI@ZXZ (MSVCRT.@)
79  */
80 MSVCRT_new_handler_func CDECL MSVCRT__query_new_handler(void)
81 {
82   return MSVCRT_new_handler;
83 }
84
85
86 /*********************************************************************
87  *              ?_query_new_mode@@YAHXZ (MSVCRT.@)
88  */
89 int CDECL MSVCRT__query_new_mode(void)
90 {
91   return MSVCRT_new_mode;
92 }
93
94 /*********************************************************************
95  *              ?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z (MSVCRT.@)
96  */
97 MSVCRT_new_handler_func CDECL MSVCRT__set_new_handler(MSVCRT_new_handler_func func)
98 {
99   MSVCRT_new_handler_func old_handler;
100   LOCK_HEAP;
101   old_handler = MSVCRT_new_handler;
102   MSVCRT_new_handler = func;
103   UNLOCK_HEAP;
104   return old_handler;
105 }
106
107 /*********************************************************************
108  *              ?set_new_handler@@YAP6AXXZP6AXXZ@Z (MSVCRT.@)
109  */
110 MSVCRT_new_handler_func CDECL MSVCRT_set_new_handler(void *func)
111 {
112   TRACE("(%p)\n",func);
113   MSVCRT__set_new_handler(NULL);
114   return NULL;
115 }
116
117 /*********************************************************************
118  *              ?_set_new_mode@@YAHH@Z (MSVCRT.@)
119  */
120 int CDECL MSVCRT__set_new_mode(int mode)
121 {
122   int old_mode;
123   LOCK_HEAP;
124   old_mode = MSVCRT_new_mode;
125   MSVCRT_new_mode = mode;
126   UNLOCK_HEAP;
127   return old_mode;
128 }
129
130 /*********************************************************************
131  *              _callnewh (MSVCRT.@)
132  */
133 int CDECL _callnewh(MSVCRT_size_t size)
134 {
135   if(MSVCRT_new_handler)
136     (*MSVCRT_new_handler)(size);
137   return 0;
138 }
139
140 /*********************************************************************
141  *              _expand (MSVCRT.@)
142  */
143 void* CDECL _expand(void* mem, MSVCRT_size_t size)
144 {
145   return HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, mem, size);
146 }
147
148 /*********************************************************************
149  *              _heapchk (MSVCRT.@)
150  */
151 int CDECL _heapchk(void)
152 {
153   if (!HeapValidate( GetProcessHeap(), 0, NULL))
154   {
155     msvcrt_set_errno(GetLastError());
156     return MSVCRT__HEAPBADNODE;
157   }
158   return MSVCRT__HEAPOK;
159 }
160
161 /*********************************************************************
162  *              _heapmin (MSVCRT.@)
163  */
164 int CDECL _heapmin(void)
165 {
166   if (!HeapCompact( GetProcessHeap(), 0 ))
167   {
168     if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
169       msvcrt_set_errno(GetLastError());
170     return -1;
171   }
172   return 0;
173 }
174
175 /*********************************************************************
176  *              _heapwalk (MSVCRT.@)
177  */
178 int CDECL _heapwalk(struct MSVCRT__heapinfo* next)
179 {
180   PROCESS_HEAP_ENTRY phe;
181
182   LOCK_HEAP;
183   phe.lpData = next->_pentry;
184   phe.cbData = next->_size;
185   phe.wFlags = next->_useflag == MSVCRT__USEDENTRY ? PROCESS_HEAP_ENTRY_BUSY : 0;
186
187   if (phe.lpData && phe.wFlags & PROCESS_HEAP_ENTRY_BUSY &&
188       !HeapValidate( GetProcessHeap(), 0, phe.lpData ))
189   {
190     UNLOCK_HEAP;
191     msvcrt_set_errno(GetLastError());
192     return MSVCRT__HEAPBADNODE;
193   }
194
195   do
196   {
197     if (!HeapWalk( GetProcessHeap(), &phe ))
198     {
199       UNLOCK_HEAP;
200       if (GetLastError() == ERROR_NO_MORE_ITEMS)
201          return MSVCRT__HEAPEND;
202       msvcrt_set_errno(GetLastError());
203       if (!phe.lpData)
204         return MSVCRT__HEAPBADBEGIN;
205       return MSVCRT__HEAPBADNODE;
206     }
207   } while (phe.wFlags & (PROCESS_HEAP_REGION|PROCESS_HEAP_UNCOMMITTED_RANGE));
208
209   UNLOCK_HEAP;
210   next->_pentry = phe.lpData;
211   next->_size = phe.cbData;
212   next->_useflag = phe.wFlags & PROCESS_HEAP_ENTRY_BUSY ? MSVCRT__USEDENTRY : MSVCRT__FREEENTRY;
213   return MSVCRT__HEAPOK;
214 }
215
216 /*********************************************************************
217  *              _heapset (MSVCRT.@)
218  */
219 int CDECL _heapset(unsigned int value)
220 {
221   int retval;
222   struct MSVCRT__heapinfo heap;
223
224   memset( &heap, 0, sizeof(heap) );
225   LOCK_HEAP;
226   while ((retval = _heapwalk(&heap)) == MSVCRT__HEAPOK)
227   {
228     if (heap._useflag == MSVCRT__FREEENTRY)
229       memset(heap._pentry, value, heap._size);
230   }
231   UNLOCK_HEAP;
232   return retval == MSVCRT__HEAPEND? MSVCRT__HEAPOK : retval;
233 }
234
235 /*********************************************************************
236  *              _heapadd (MSVCRT.@)
237  */
238 int CDECL _heapadd(void* mem, MSVCRT_size_t size)
239 {
240   TRACE("(%p,%ld) unsupported in Win32\n", mem,size);
241   *MSVCRT__errno() = MSVCRT_ENOSYS;
242   return -1;
243 }
244
245 /*********************************************************************
246  *              _msize (MSVCRT.@)
247  */
248 MSVCRT_size_t CDECL _msize(void* mem)
249 {
250   MSVCRT_size_t size = HeapSize(GetProcessHeap(),0,mem);
251   if (size == ~(MSVCRT_size_t)0)
252   {
253     WARN(":Probably called with non wine-allocated memory, ret = -1\n");
254     /* At least the Win32 crtdll/msvcrt also return -1 in this case */
255   }
256   return size;
257 }
258
259 /*********************************************************************
260  *              calloc (MSVCRT.@)
261  */
262 void* CDECL MSVCRT_calloc(MSVCRT_size_t size, MSVCRT_size_t count)
263 {
264   return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size * count );
265 }
266
267 /*********************************************************************
268  *              free (MSVCRT.@)
269  */
270 void CDECL MSVCRT_free(void* ptr)
271 {
272   HeapFree(GetProcessHeap(),0,ptr);
273 }
274
275 /*********************************************************************
276  *                  malloc (MSVCRT.@)
277  */
278 void* CDECL MSVCRT_malloc(MSVCRT_size_t size)
279 {
280   void *ret = HeapAlloc(GetProcessHeap(),0,size);
281   if (!ret)
282       *MSVCRT__errno() = MSVCRT_ENOMEM;
283   return ret;
284 }
285
286 /*********************************************************************
287  *              realloc (MSVCRT.@)
288  */
289 void* CDECL MSVCRT_realloc(void* ptr, MSVCRT_size_t size)
290 {
291   if (!ptr) return MSVCRT_malloc(size);
292   if (size) return HeapReAlloc(GetProcessHeap(), 0, ptr, size);
293   MSVCRT_free(ptr);
294   return NULL;
295 }
296
297 /*********************************************************************
298  *              __p__amblksiz (MSVCRT.@)
299  */
300 unsigned int* CDECL __p__amblksiz(void)
301 {
302   return &MSVCRT_amblksiz;
303 }
304
305 /*********************************************************************
306  *              _get_sbh_threshold (MSVCRT.@)
307  */
308 MSVCRT_size_t CDECL _get_sbh_threshold(void)
309 {
310   return MSVCRT_sbh_threshold;
311 }
312
313 /*********************************************************************
314  *              _set_sbh_threshold (MSVCRT.@)
315  */
316 int CDECL _set_sbh_threshold(MSVCRT_size_t threshold)
317 {
318   if(threshold > 1016)
319      return 0;
320   else
321      MSVCRT_sbh_threshold = threshold;
322   return 1;
323 }
324
325 /*********************************************************************
326  *              _aligned_free (MSVCRT.@)
327  */
328 void CDECL _aligned_free(void *memblock)
329 {
330     TRACE("(%p)\n", memblock);
331
332     if (memblock)
333     {
334         void **saved = SAVED_PTR(memblock);
335         MSVCRT_free(*saved);
336     }
337 }
338
339 /*********************************************************************
340  *              _aligned_offset_malloc (MSVCRT.@)
341  */
342 void * CDECL _aligned_offset_malloc(MSVCRT_size_t size, MSVCRT_size_t alignment, MSVCRT_size_t offset)
343 {
344     void *memblock, *temp, **saved;
345     TRACE("(%lu, %lu, %lu)\n", size, alignment, offset);
346
347     /* alignment must be a power of 2 */
348     if ((alignment & (alignment - 1)) != 0)
349     {
350         *MSVCRT__errno() = MSVCRT_EINVAL;
351         return NULL;
352     }
353
354     /* offset must be less than size */
355     if (offset >= size)
356     {
357         *MSVCRT__errno() = MSVCRT_EINVAL;
358         return NULL;
359     }
360
361     /* don't align to less than void pointer size */
362     if (alignment < sizeof(void *))
363         alignment = sizeof(void *);
364
365     /* allocate enough space for void pointer and alignment */
366     temp = MSVCRT_malloc(size + alignment + sizeof(void *));
367
368     if (!temp)
369         return NULL;
370
371     /* adjust pointer for proper alignment and offset */
372     memblock = ALIGN_PTR(temp, alignment, offset);
373
374     /* Save the real allocation address below returned address */
375     /* so it can be found later to free. */
376     saved = SAVED_PTR(memblock);
377     *saved = temp;
378
379     return memblock;
380 }
381
382 /*********************************************************************
383  *              _aligned_malloc (MSVCRT.@)
384  */
385 void * CDECL _aligned_malloc(MSVCRT_size_t size, MSVCRT_size_t alignment)
386 {
387     TRACE("(%lu, %lu)\n", size, alignment);
388     return _aligned_offset_malloc(size, alignment, 0);
389 }
390
391 /*********************************************************************
392  *              _aligned_offset_realloc (MSVCRT.@)
393  */
394 void * CDECL _aligned_offset_realloc(void *memblock, MSVCRT_size_t size,
395                                      MSVCRT_size_t alignment, MSVCRT_size_t offset)
396 {
397     void * temp, **saved;
398     MSVCRT_size_t old_padding, new_padding, old_size;
399     TRACE("(%p, %lu, %lu, %lu)\n", memblock, size, alignment, offset);
400
401     if (!memblock)
402         return _aligned_offset_malloc(size, alignment, offset);
403
404     /* alignment must be a power of 2 */
405     if ((alignment & (alignment - 1)) != 0)
406     {
407         *MSVCRT__errno() = MSVCRT_EINVAL;
408         return NULL;
409     }
410
411     /* offset must be less than size */
412     if (offset >= size)
413     {
414         *MSVCRT__errno() = MSVCRT_EINVAL;
415         return NULL;
416     }
417
418     if (size == 0)
419     {
420         _aligned_free(memblock);
421         return NULL;
422     }
423
424     /* don't align to less than void pointer size */
425     if (alignment < sizeof(void *))
426         alignment = sizeof(void *);
427
428     /* make sure alignment and offset didn't change */
429     saved = SAVED_PTR(memblock);
430     if (memblock != ALIGN_PTR(*saved, alignment, offset))
431     {
432         *MSVCRT__errno() = MSVCRT_EINVAL;
433         return NULL;
434     }
435
436     old_padding = (char *)memblock - (char *)*saved;
437
438     /* Get previous size of block */
439     old_size = _msize(*saved);
440     if (old_size == -1)
441     {
442         /* It seems this function was called with an invalid pointer. Bail out. */
443         return NULL;
444     }
445
446     /* Adjust old_size to get amount of actual data in old block. */
447     if (old_size < old_padding)
448     {
449         /* Shouldn't happen. Something's weird, so bail out. */
450         return NULL;
451     }
452     old_size -= old_padding;
453
454     temp = MSVCRT_realloc(*saved, size + alignment + sizeof(void *));
455
456     if (!temp)
457         return NULL;
458
459     /* adjust pointer for proper alignment and offset */
460     memblock = ALIGN_PTR(temp, alignment, offset);
461
462     /* Save the real allocation address below returned address */
463     /* so it can be found later to free. */
464     saved = SAVED_PTR(memblock);
465
466     new_padding = (char *)memblock - (char *)temp;
467
468 /*
469    Memory layout of old block is as follows:
470    +-------+---------------------+-+--------------------------+-----------+
471    |  ...  | "old_padding" bytes | | ... "old_size" bytes ... |    ...    |
472    +-------+---------------------+-+--------------------------+-----------+
473            ^                     ^ ^
474            |                     | |
475         *saved               saved memblock
476
477    Memory layout of new block is as follows:
478    +-------+-----------------------------+-+----------------------+-------+
479    |  ...  |    "new_padding" bytes      | | ... "size" bytes ... |  ...  |
480    +-------+-----------------------------+-+----------------------+-------+
481            ^                             ^ ^
482            |                             | |
483           temp                       saved memblock
484
485    However, in the new block, actual data is still written as follows
486    (because it was copied by MSVCRT_realloc):
487    +-------+---------------------+--------------------------------+-------+
488    |  ...  | "old_padding" bytes |   ... "old_size" bytes ...     |  ...  |
489    +-------+---------------------+--------------------------------+-------+
490            ^                             ^ ^
491            |                             | |
492           temp                       saved memblock
493
494    Therefore, min(old_size,size) bytes of actual data have to be moved
495    from the offset they were at in the old block (temp + old_padding),
496    to the offset they have to be in the new block (temp + new_padding == memblock).
497 */
498     if (new_padding != old_padding)
499         memmove((char *)memblock, (char *)temp + old_padding, (old_size < size) ? old_size : size);
500
501     *saved = temp;
502
503     return memblock;
504 }
505
506 /*********************************************************************
507  *              _aligned_realloc (MSVCRT.@)
508  */
509 void * CDECL _aligned_realloc(void *memblock, MSVCRT_size_t size, MSVCRT_size_t alignment)
510 {
511     TRACE("(%p, %lu, %lu)\n", memblock, size, alignment);
512     return _aligned_offset_realloc(memblock, size, alignment, 0);
513 }
514
515 /*********************************************************************
516  *              memmove_s (MSVCRT.@)
517  */
518 int CDECL memmove_s(void *dest, MSVCRT_size_t numberOfElements, const void *src, MSVCRT_size_t count)
519 {
520     TRACE("(%p %lu %p %lu)\n", dest, numberOfElements, src, count);
521
522     if(!count)
523         return 0;
524
525     if(!dest || !src) {
526         if(dest)
527             memset(dest, 0, numberOfElements);
528
529         *MSVCRT__errno() = MSVCRT_EINVAL;
530         return MSVCRT_EINVAL;
531     }
532
533     if(count > numberOfElements) {
534         memset(dest, 0, numberOfElements);
535
536         *MSVCRT__errno() = MSVCRT_ERANGE;
537         return MSVCRT_ERANGE;
538     }
539
540     memmove(dest, src, count);
541     return 0;
542 }