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