ok() does not support '%S'. Store the Ansi version, convert to Unicode
[wine] / dlls / ntdll / heap.c
1 /*
2  * Win32 heap functions
3  *
4  * Copyright 1996 Alexandre Julliard
5  * Copyright 1998 Ulrich Weigand
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28
29 #define NONAMELESSUNION
30 #define NONAMELESSSTRUCT
31 #include "winternl.h"
32 #include "wine/winbase16.h"
33 #include "winbase.h"
34 #include "winerror.h"
35 #include "winnt.h"
36 #include "heap.h"
37 #include "thread.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(heap);
41
42 /* Note: the heap data structures are based on what Pietrek describes in his
43  * book 'Windows 95 System Programming Secrets'. The layout is not exactly
44  * the same, but could be easily adapted if it turns out some programs
45  * require it.
46  */
47
48 typedef struct tagARENA_INUSE
49 {
50     DWORD  size;                    /* Block size; must be the first field */
51     DWORD  magic;                   /* Magic number */
52 } ARENA_INUSE;
53
54 typedef struct tagARENA_FREE
55 {
56     DWORD                 size;     /* Block size; must be the first field */
57     DWORD                 magic;    /* Magic number */
58     struct tagARENA_FREE *next;     /* Next free arena */
59     struct tagARENA_FREE *prev;     /* Prev free arena */
60 } ARENA_FREE;
61
62 #define ARENA_FLAG_FREE        0x00000001  /* flags OR'ed with arena size */
63 #define ARENA_FLAG_PREV_FREE   0x00000002
64 #define ARENA_SIZE_MASK        (~3)
65 #define ARENA_INUSE_MAGIC      0x44455355      /* Value for arena 'magic' field */
66 #define ARENA_FREE_MAGIC       0x45455246      /* Value for arena 'magic' field */
67
68 #define ARENA_INUSE_FILLER     0x55
69 #define ARENA_FREE_FILLER      0xaa
70
71 #define ALIGNMENT              8   /* everything is aligned on 8 byte boundaries */
72 #define ROUND_SIZE(size)       (((size) + ALIGNMENT - 1) & ~(ALIGNMENT-1))
73
74 #define QUIET                  1           /* Suppress messages  */
75 #define NOISY                  0           /* Report all errors  */
76
77 #define HEAP_NB_FREE_LISTS   4   /* Number of free lists */
78
79 /* Max size of the blocks on the free lists */
80 static const DWORD HEAP_freeListSizes[HEAP_NB_FREE_LISTS] =
81 {
82     0x20, 0x80, 0x200, ~0UL
83 };
84
85 typedef struct
86 {
87     DWORD       size;
88     ARENA_FREE  arena;
89 } FREE_LIST_ENTRY;
90
91 struct tagHEAP;
92
93 typedef struct tagSUBHEAP
94 {
95     DWORD               size;       /* Size of the whole sub-heap */
96     DWORD               commitSize; /* Committed size of the sub-heap */
97     DWORD               headerSize; /* Size of the heap header */
98     struct tagSUBHEAP  *next;       /* Next sub-heap */
99     struct tagHEAP     *heap;       /* Main heap structure */
100     DWORD               magic;      /* Magic number */
101 } SUBHEAP;
102
103 #define SUBHEAP_MAGIC    ((DWORD)('S' | ('U'<<8) | ('B'<<16) | ('H'<<24)))
104
105 typedef struct tagHEAP
106 {
107     SUBHEAP          subheap;       /* First sub-heap */
108     struct tagHEAP  *next;          /* Next heap for this process */
109     CRITICAL_SECTION critSection;   /* Critical section for serialization */
110     FREE_LIST_ENTRY  freeList[HEAP_NB_FREE_LISTS];  /* Free lists */
111     DWORD            flags;         /* Heap flags */
112     DWORD            magic;         /* Magic number */
113 } HEAP;
114
115 #define HEAP_MAGIC       ((DWORD)('H' | ('E'<<8) | ('A'<<16) | ('P'<<24)))
116
117 #define HEAP_DEF_SIZE        0x110000   /* Default heap size = 1Mb + 64Kb */
118 #define HEAP_MIN_BLOCK_SIZE  (8+sizeof(ARENA_FREE))  /* Min. heap block size */
119 #define COMMIT_MASK          0xffff  /* bitmask for commit/decommit granularity */
120
121 static HANDLE processHeap;  /* main process heap */
122
123 static HEAP *firstHeap;     /* head of secondary heaps list */
124
125 static BOOL HEAP_IsRealArena( HEAP *heapPtr, DWORD flags, LPCVOID block, BOOL quiet );
126
127 /* SetLastError for ntdll */
128 inline static void set_status( NTSTATUS status )
129 {
130 #if defined(__i386__) && defined(__GNUC__)
131     /* in this case SetLastError is an inline function so we can use it */
132     SetLastError( RtlNtStatusToDosError( status ) );
133 #else
134     /* cannot use SetLastError, do it manually */
135     NtCurrentTeb()->last_error = RtlNtStatusToDosError( status );
136 #endif
137 }
138
139 /* set the process main heap */
140 static void set_process_heap( HANDLE heap )
141 {
142     HANDLE *pdb = (HANDLE *)NtCurrentTeb()->process;
143     pdb[0x18 / sizeof(HANDLE)] = heap;  /* heap is at offset 0x18 in pdb */
144     processHeap = heap;
145 }
146
147
148 /***********************************************************************
149  *           HEAP_Dump
150  */
151 void HEAP_Dump( HEAP *heap )
152 {
153     int i;
154     SUBHEAP *subheap;
155     char *ptr;
156
157     DPRINTF( "Heap: %08lx\n", (DWORD)heap );
158     DPRINTF( "Next: %08lx  Sub-heaps: %08lx",
159           (DWORD)heap->next, (DWORD)&heap->subheap );
160     subheap = &heap->subheap;
161     while (subheap->next)
162     {
163         DPRINTF( " -> %08lx", (DWORD)subheap->next );
164         subheap = subheap->next;
165     }
166
167     DPRINTF( "\nFree lists:\n Block   Stat   Size    Id\n" );
168     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
169         DPRINTF( "%08lx free %08lx prev=%08lx next=%08lx\n",
170               (DWORD)&heap->freeList[i].arena, heap->freeList[i].arena.size,
171               (DWORD)heap->freeList[i].arena.prev,
172               (DWORD)heap->freeList[i].arena.next );
173
174     subheap = &heap->subheap;
175     while (subheap)
176     {
177         DWORD freeSize = 0, usedSize = 0, arenaSize = subheap->headerSize;
178         DPRINTF( "\n\nSub-heap %08lx: size=%08lx committed=%08lx\n",
179               (DWORD)subheap, subheap->size, subheap->commitSize );
180
181         DPRINTF( "\n Block   Stat   Size    Id\n" );
182         ptr = (char*)subheap + subheap->headerSize;
183         while (ptr < (char *)subheap + subheap->size)
184         {
185             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
186             {
187                 ARENA_FREE *pArena = (ARENA_FREE *)ptr;
188                 DPRINTF( "%08lx free %08lx prev=%08lx next=%08lx\n",
189                       (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
190                       (DWORD)pArena->prev, (DWORD)pArena->next);
191                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
192                 arenaSize += sizeof(ARENA_FREE);
193                 freeSize += pArena->size & ARENA_SIZE_MASK;
194             }
195             else if (*(DWORD *)ptr & ARENA_FLAG_PREV_FREE)
196             {
197                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
198                 DPRINTF( "%08lx Used %08lx back=%08lx\n",
199                          (DWORD)pArena, pArena->size & ARENA_SIZE_MASK, *((DWORD *)pArena - 1) );
200                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
201                 arenaSize += sizeof(ARENA_INUSE);
202                 usedSize += pArena->size & ARENA_SIZE_MASK;
203             }
204             else
205             {
206                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
207                 DPRINTF( "%08lx used %08lx\n",
208                       (DWORD)pArena, pArena->size & ARENA_SIZE_MASK );
209                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
210                 arenaSize += sizeof(ARENA_INUSE);
211                 usedSize += pArena->size & ARENA_SIZE_MASK;
212             }
213         }
214         DPRINTF( "\nTotal: Size=%08lx Committed=%08lx Free=%08lx Used=%08lx Arenas=%08lx (%ld%%)\n\n",
215               subheap->size, subheap->commitSize, freeSize, usedSize,
216               arenaSize, (arenaSize * 100) / subheap->size );
217         subheap = subheap->next;
218     }
219 }
220
221
222 /***********************************************************************
223  *           HEAP_GetPtr
224  * RETURNS
225  *      Pointer to the heap
226  *      NULL: Failure
227  */
228 static HEAP *HEAP_GetPtr(
229              HANDLE heap /* [in] Handle to the heap */
230 ) {
231     HEAP *heapPtr = (HEAP *)heap;
232     if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
233     {
234         ERR("Invalid heap %p!\n", heap );
235         return NULL;
236     }
237     if (TRACE_ON(heap) && !HEAP_IsRealArena( heapPtr, 0, NULL, NOISY ))
238     {
239         HEAP_Dump( heapPtr );
240         assert( FALSE );
241         return NULL;
242     }
243     return heapPtr;
244 }
245
246
247 /***********************************************************************
248  *           HEAP_InsertFreeBlock
249  *
250  * Insert a free block into the free list.
251  */
252 static void HEAP_InsertFreeBlock( HEAP *heap, ARENA_FREE *pArena )
253 {
254     FREE_LIST_ENTRY *pEntry = heap->freeList;
255     while (pEntry->size < pArena->size) pEntry++;
256     pArena->size      |= ARENA_FLAG_FREE;
257     pArena->next       = pEntry->arena.next;
258     pArena->next->prev = pArena;
259     pArena->prev       = &pEntry->arena;
260     pEntry->arena.next = pArena;
261 }
262
263
264 /***********************************************************************
265  *           HEAP_FindSubHeap
266  * Find the sub-heap containing a given address.
267  *
268  * RETURNS
269  *      Pointer: Success
270  *      NULL: Failure
271  */
272 static SUBHEAP *HEAP_FindSubHeap(
273                 HEAP *heap, /* [in] Heap pointer */
274                 LPCVOID ptr /* [in] Address */
275 ) {
276     SUBHEAP *sub = &heap->subheap;
277     while (sub)
278     {
279         if (((char *)ptr >= (char *)sub) &&
280             ((char *)ptr < (char *)sub + sub->size)) return sub;
281         sub = sub->next;
282     }
283     return NULL;
284 }
285
286
287 /***********************************************************************
288  *           HEAP_Commit
289  *
290  * Make sure the heap storage is committed up to (not including) ptr.
291  */
292 static inline BOOL HEAP_Commit( SUBHEAP *subheap, void *ptr )
293 {
294     DWORD size = (DWORD)((char *)ptr - (char *)subheap);
295     size = (size + COMMIT_MASK) & ~COMMIT_MASK;
296     if (size > subheap->size) size = subheap->size;
297     if (size <= subheap->commitSize) return TRUE;
298     size -= subheap->commitSize;
299     if (NtAllocateVirtualMemory( GetCurrentProcess(), &ptr, (char *)subheap + subheap->commitSize,
300                                  &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
301     {
302         WARN("Could not commit %08lx bytes at %08lx for heap %08lx\n",
303                  size, (DWORD)((char *)subheap + subheap->commitSize),
304                  (DWORD)subheap->heap );
305         return FALSE;
306     }
307     subheap->commitSize += size;
308     return TRUE;
309 }
310
311
312 /***********************************************************************
313  *           HEAP_Decommit
314  *
315  * If possible, decommit the heap storage from (including) 'ptr'.
316  */
317 static inline BOOL HEAP_Decommit( SUBHEAP *subheap, void *ptr )
318 {
319     void *addr;
320     ULONG decommit_size;
321
322     DWORD size = (DWORD)((char *)ptr - (char *)subheap);
323     /* round to next block and add one full block */
324     size = ((size + COMMIT_MASK) & ~COMMIT_MASK) + COMMIT_MASK + 1;
325     if (size >= subheap->commitSize) return TRUE;
326     decommit_size = subheap->commitSize - size;
327     addr = (char *)subheap + size;
328
329     if (NtFreeVirtualMemory( GetCurrentProcess(), &addr, &decommit_size, MEM_DECOMMIT ))
330     {
331         WARN("Could not decommit %08lx bytes at %08lx for heap %p\n",
332                  decommit_size, (DWORD)((char *)subheap + size), subheap->heap );
333         return FALSE;
334     }
335     subheap->commitSize -= decommit_size;
336     return TRUE;
337 }
338
339
340 /***********************************************************************
341  *           HEAP_CreateFreeBlock
342  *
343  * Create a free block at a specified address. 'size' is the size of the
344  * whole block, including the new arena.
345  */
346 static void HEAP_CreateFreeBlock( SUBHEAP *subheap, void *ptr, DWORD size )
347 {
348     ARENA_FREE *pFree;
349
350     /* Create a free arena */
351
352     pFree = (ARENA_FREE *)ptr;
353     pFree->magic = ARENA_FREE_MAGIC;
354
355     /* If debugging, erase the freed block content */
356
357     if (TRACE_ON(heap))
358     {
359         char *pEnd = (char *)ptr + size;
360         if (pEnd > (char *)subheap + subheap->commitSize)
361             pEnd = (char *)subheap + subheap->commitSize;
362         if (pEnd > (char *)(pFree + 1))
363             memset( pFree + 1, ARENA_FREE_FILLER, pEnd - (char *)(pFree + 1) );
364     }
365
366     /* Check if next block is free also */
367
368     if (((char *)ptr + size < (char *)subheap + subheap->size) &&
369         (*(DWORD *)((char *)ptr + size) & ARENA_FLAG_FREE))
370     {
371         /* Remove the next arena from the free list */
372         ARENA_FREE *pNext = (ARENA_FREE *)((char *)ptr + size);
373         pNext->next->prev = pNext->prev;
374         pNext->prev->next = pNext->next;
375         size += (pNext->size & ARENA_SIZE_MASK) + sizeof(*pNext);
376         if (TRACE_ON(heap))
377             memset( pNext, ARENA_FREE_FILLER, sizeof(ARENA_FREE) );
378     }
379
380     /* Set the next block PREV_FREE flag and pointer */
381
382     if ((char *)ptr + size < (char *)subheap + subheap->size)
383     {
384         DWORD *pNext = (DWORD *)((char *)ptr + size);
385         *pNext |= ARENA_FLAG_PREV_FREE;
386         *(ARENA_FREE **)(pNext - 1) = pFree;
387     }
388
389     /* Last, insert the new block into the free list */
390
391     pFree->size = size - sizeof(*pFree);
392     HEAP_InsertFreeBlock( subheap->heap, pFree );
393 }
394
395
396 /***********************************************************************
397  *           HEAP_MakeInUseBlockFree
398  *
399  * Turn an in-use block into a free block. Can also decommit the end of
400  * the heap, and possibly even free the sub-heap altogether.
401  */
402 static void HEAP_MakeInUseBlockFree( SUBHEAP *subheap, ARENA_INUSE *pArena )
403 {
404     ARENA_FREE *pFree;
405     DWORD size = (pArena->size & ARENA_SIZE_MASK) + sizeof(*pArena);
406
407     /* Check if we can merge with previous block */
408
409     if (pArena->size & ARENA_FLAG_PREV_FREE)
410     {
411         pFree = *((ARENA_FREE **)pArena - 1);
412         size += (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
413         /* Remove it from the free list */
414         pFree->next->prev = pFree->prev;
415         pFree->prev->next = pFree->next;
416     }
417     else pFree = (ARENA_FREE *)pArena;
418
419     /* Create a free block */
420
421     HEAP_CreateFreeBlock( subheap, pFree, size );
422     size = (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
423     if ((char *)pFree + size < (char *)subheap + subheap->size)
424         return;  /* Not the last block, so nothing more to do */
425
426     /* Free the whole sub-heap if it's empty and not the original one */
427
428     if (((char *)pFree == (char *)subheap + subheap->headerSize) &&
429         (subheap != &subheap->heap->subheap))
430     {
431         ULONG size = 0;
432         SUBHEAP *pPrev = &subheap->heap->subheap;
433         /* Remove the free block from the list */
434         pFree->next->prev = pFree->prev;
435         pFree->prev->next = pFree->next;
436         /* Remove the subheap from the list */
437         while (pPrev && (pPrev->next != subheap)) pPrev = pPrev->next;
438         if (pPrev) pPrev->next = subheap->next;
439         /* Free the memory */
440         subheap->magic = 0;
441         NtFreeVirtualMemory( GetCurrentProcess(), (void **)&subheap, &size, MEM_RELEASE );
442         return;
443     }
444
445     /* Decommit the end of the heap */
446
447     if (!(subheap->heap->flags & HEAP_SHARED)) HEAP_Decommit( subheap, pFree + 1 );
448 }
449
450
451 /***********************************************************************
452  *           HEAP_ShrinkBlock
453  *
454  * Shrink an in-use block.
455  */
456 static void HEAP_ShrinkBlock(SUBHEAP *subheap, ARENA_INUSE *pArena, DWORD size)
457 {
458     if ((pArena->size & ARENA_SIZE_MASK) >= size + HEAP_MIN_BLOCK_SIZE)
459     {
460         HEAP_CreateFreeBlock( subheap, (char *)(pArena + 1) + size,
461                               (pArena->size & ARENA_SIZE_MASK) - size );
462         /* assign size plus previous arena flags */
463         pArena->size = size | (pArena->size & ~ARENA_SIZE_MASK);
464     }
465     else
466     {
467         /* Turn off PREV_FREE flag in next block */
468         char *pNext = (char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK);
469         if (pNext < (char *)subheap + subheap->size)
470             *(DWORD *)pNext &= ~ARENA_FLAG_PREV_FREE;
471     }
472 }
473
474 /***********************************************************************
475  *           HEAP_InitSubHeap
476  */
477 static BOOL HEAP_InitSubHeap( HEAP *heap, LPVOID address, DWORD flags,
478                                 DWORD commitSize, DWORD totalSize )
479 {
480     SUBHEAP *subheap;
481     FREE_LIST_ENTRY *pEntry;
482     int i;
483
484     /* Commit memory */
485
486     if (flags & HEAP_SHARED)
487         commitSize = totalSize;  /* always commit everything in a shared heap */
488     if (NtAllocateVirtualMemory( GetCurrentProcess(), &address, address,
489                                  &commitSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
490     {
491         WARN("Could not commit %08lx bytes for sub-heap %p\n", commitSize, address );
492         return FALSE;
493     }
494
495     /* Fill the sub-heap structure */
496
497     subheap = (SUBHEAP *)address;
498     subheap->heap       = heap;
499     subheap->size       = totalSize;
500     subheap->commitSize = commitSize;
501     subheap->magic      = SUBHEAP_MAGIC;
502
503     if ( subheap != (SUBHEAP *)heap )
504     {
505         /* If this is a secondary subheap, insert it into list */
506
507         subheap->headerSize = ROUND_SIZE( sizeof(SUBHEAP) );
508         subheap->next       = heap->subheap.next;
509         heap->subheap.next  = subheap;
510     }
511     else
512     {
513         /* If this is a primary subheap, initialize main heap */
514
515         subheap->headerSize = ROUND_SIZE( sizeof(HEAP) );
516         subheap->next       = NULL;
517         heap->next          = NULL;
518         heap->flags         = flags;
519         heap->magic         = HEAP_MAGIC;
520
521         /* Build the free lists */
522
523         for (i = 0, pEntry = heap->freeList; i < HEAP_NB_FREE_LISTS; i++, pEntry++)
524         {
525             pEntry->size       = HEAP_freeListSizes[i];
526             pEntry->arena.size = 0 | ARENA_FLAG_FREE;
527             pEntry->arena.next = i < HEAP_NB_FREE_LISTS-1 ?
528                                  &heap->freeList[i+1].arena : &heap->freeList[0].arena;
529             pEntry->arena.prev = i ? &heap->freeList[i-1].arena :
530                                   &heap->freeList[HEAP_NB_FREE_LISTS-1].arena;
531             pEntry->arena.magic = ARENA_FREE_MAGIC;
532         }
533
534         /* Initialize critical section */
535
536         RtlInitializeCriticalSection( &heap->critSection );
537         if (flags & HEAP_SHARED)
538             MakeCriticalSectionGlobal( &heap->critSection );  /* FIXME: dll separation */
539     }
540
541     /* Create the first free block */
542
543     HEAP_CreateFreeBlock( subheap, (LPBYTE)subheap + subheap->headerSize,
544                           subheap->size - subheap->headerSize );
545
546     return TRUE;
547 }
548
549 /***********************************************************************
550  *           HEAP_CreateSubHeap
551  *
552  * Create a sub-heap of the given size.
553  * If heap == NULL, creates a main heap.
554  */
555 static SUBHEAP *HEAP_CreateSubHeap( HEAP *heap, void *base, DWORD flags,
556                                     DWORD commitSize, DWORD totalSize )
557 {
558     LPVOID address = base;
559
560     /* round-up sizes on a 64K boundary */
561     totalSize  = (totalSize + 0xffff) & 0xffff0000;
562     commitSize = (commitSize + 0xffff) & 0xffff0000;
563     if (!commitSize) commitSize = 0x10000;
564     if (totalSize < commitSize) totalSize = commitSize;
565
566     if (!address)
567     {
568         /* allocate the memory block */
569         if (NtAllocateVirtualMemory( GetCurrentProcess(), &address, NULL, &totalSize,
570                                      MEM_RESERVE, PAGE_EXECUTE_READWRITE ))
571         {
572             WARN("Could not allocate %08lx bytes\n", totalSize );
573             return NULL;
574         }
575     }
576
577     /* Initialize subheap */
578
579     if (!HEAP_InitSubHeap( heap ? heap : (HEAP *)address,
580                            address, flags, commitSize, totalSize ))
581     {
582         ULONG size = 0;
583         if (!base) NtFreeVirtualMemory( GetCurrentProcess(), &address, &size, MEM_RELEASE );
584         return NULL;
585     }
586
587     return (SUBHEAP *)address;
588 }
589
590
591 /***********************************************************************
592  *           HEAP_FindFreeBlock
593  *
594  * Find a free block at least as large as the requested size, and make sure
595  * the requested size is committed.
596  */
597 static ARENA_FREE *HEAP_FindFreeBlock( HEAP *heap, DWORD size,
598                                        SUBHEAP **ppSubHeap )
599 {
600     SUBHEAP *subheap;
601     ARENA_FREE *pArena;
602     FREE_LIST_ENTRY *pEntry = heap->freeList;
603
604     /* Find a suitable free list, and in it find a block large enough */
605
606     while (pEntry->size < size) pEntry++;
607     pArena = pEntry->arena.next;
608     while (pArena != &heap->freeList[0].arena)
609     {
610         DWORD arena_size = (pArena->size & ARENA_SIZE_MASK) +
611                             sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
612         if (arena_size >= size)
613         {
614             subheap = HEAP_FindSubHeap( heap, pArena );
615             if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
616                                                + size + HEAP_MIN_BLOCK_SIZE))
617                 return NULL;
618             *ppSubHeap = subheap;
619             return pArena;
620         }
621         pArena = pArena->next;
622     }
623
624     /* If no block was found, attempt to grow the heap */
625
626     if (!(heap->flags & HEAP_GROWABLE))
627     {
628         WARN("Not enough space in heap %08lx for %08lx bytes\n",
629                  (DWORD)heap, size );
630         return NULL;
631     }
632     /* make sure that we have a big enough size *committed* to fit another
633      * last free arena in !
634      * So just one heap struct, one first free arena which will eventually
635      * get inuse, and HEAP_MIN_BLOCK_SIZE for the second free arena that
636      * might get assigned all remaining free space in HEAP_ShrinkBlock() */
637     size += ROUND_SIZE(sizeof(SUBHEAP)) + sizeof(ARENA_INUSE) + HEAP_MIN_BLOCK_SIZE;
638     if (!(subheap = HEAP_CreateSubHeap( heap, NULL, heap->flags, size,
639                                         max( HEAP_DEF_SIZE, size ) )))
640         return NULL;
641
642     TRACE("created new sub-heap %08lx of %08lx bytes for heap %08lx\n",
643                 (DWORD)subheap, size, (DWORD)heap );
644
645     *ppSubHeap = subheap;
646     return (ARENA_FREE *)(subheap + 1);
647 }
648
649
650 /***********************************************************************
651  *           HEAP_IsValidArenaPtr
652  *
653  * Check that the pointer is inside the range possible for arenas.
654  */
655 static BOOL HEAP_IsValidArenaPtr( HEAP *heap, void *ptr )
656 {
657     int i;
658     SUBHEAP *subheap = HEAP_FindSubHeap( heap, ptr );
659     if (!subheap) return FALSE;
660     if ((char *)ptr >= (char *)subheap + subheap->headerSize) return TRUE;
661     if (subheap != &heap->subheap) return FALSE;
662     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
663         if (ptr == (void *)&heap->freeList[i].arena) return TRUE;
664     return FALSE;
665 }
666
667
668 /***********************************************************************
669  *           HEAP_ValidateFreeArena
670  */
671 static BOOL HEAP_ValidateFreeArena( SUBHEAP *subheap, ARENA_FREE *pArena )
672 {
673     char *heapEnd = (char *)subheap + subheap->size;
674
675     /* Check for unaligned pointers */
676     if ( (long)pArena % ALIGNMENT != 0 )
677     {
678         ERR( "Heap %08lx: unaligned arena pointer %08lx\n",
679              (DWORD)subheap->heap, (DWORD)pArena );
680         return FALSE;
681     }
682
683     /* Check magic number */
684     if (pArena->magic != ARENA_FREE_MAGIC)
685     {
686         ERR("Heap %08lx: invalid free arena magic for %08lx\n",
687                  (DWORD)subheap->heap, (DWORD)pArena );
688         return FALSE;
689     }
690     /* Check size flags */
691     if (!(pArena->size & ARENA_FLAG_FREE) ||
692         (pArena->size & ARENA_FLAG_PREV_FREE))
693     {
694         ERR("Heap %08lx: bad flags %lx for free arena %08lx\n",
695                  (DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
696     }
697     /* Check arena size */
698     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
699     {
700         ERR("Heap %08lx: bad size %08lx for free arena %08lx\n",
701                  (DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
702         return FALSE;
703     }
704     /* Check that next pointer is valid */
705     if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->next ))
706     {
707         ERR("Heap %08lx: bad next ptr %08lx for arena %08lx\n",
708                  (DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
709         return FALSE;
710     }
711     /* Check that next arena is free */
712     if (!(pArena->next->size & ARENA_FLAG_FREE) ||
713         (pArena->next->magic != ARENA_FREE_MAGIC))
714     {
715         ERR("Heap %08lx: next arena %08lx invalid for %08lx\n",
716                  (DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
717         return FALSE;
718     }
719     /* Check that prev pointer is valid */
720     if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->prev ))
721     {
722         ERR("Heap %08lx: bad prev ptr %08lx for arena %08lx\n",
723                  (DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
724         return FALSE;
725     }
726     /* Check that prev arena is free */
727     if (!(pArena->prev->size & ARENA_FLAG_FREE) ||
728         (pArena->prev->magic != ARENA_FREE_MAGIC))
729     {
730         /* this often means that the prev arena got overwritten
731          * by a memory write before that prev arena */
732         ERR("Heap %08lx: prev arena %08lx invalid for %08lx\n",
733                  (DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
734         return FALSE;
735     }
736     /* Check that next block has PREV_FREE flag */
737     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd)
738     {
739         if (!(*(DWORD *)((char *)(pArena + 1) +
740             (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
741         {
742             ERR("Heap %08lx: free arena %08lx next block has no PREV_FREE flag\n",
743                      (DWORD)subheap->heap, (DWORD)pArena );
744             return FALSE;
745         }
746         /* Check next block back pointer */
747         if (*((ARENA_FREE **)((char *)(pArena + 1) +
748             (pArena->size & ARENA_SIZE_MASK)) - 1) != pArena)
749         {
750             ERR("Heap %08lx: arena %08lx has wrong back ptr %08lx\n",
751                      (DWORD)subheap->heap, (DWORD)pArena,
752                      *((DWORD *)((char *)(pArena+1)+ (pArena->size & ARENA_SIZE_MASK)) - 1));
753             return FALSE;
754         }
755     }
756     return TRUE;
757 }
758
759
760 /***********************************************************************
761  *           HEAP_ValidateInUseArena
762  */
763 static BOOL HEAP_ValidateInUseArena( SUBHEAP *subheap, ARENA_INUSE *pArena, BOOL quiet )
764 {
765     char *heapEnd = (char *)subheap + subheap->size;
766
767     /* Check for unaligned pointers */
768     if ( (long)pArena % ALIGNMENT != 0 )
769     {
770         if ( quiet == NOISY )
771         {
772             ERR( "Heap %08lx: unaligned arena pointer %08lx\n",
773                   (DWORD)subheap->heap, (DWORD)pArena );
774             if ( TRACE_ON(heap) )
775                 HEAP_Dump( subheap->heap );
776         }
777         else if ( WARN_ON(heap) )
778         {
779             WARN( "Heap %08lx: unaligned arena pointer %08lx\n",
780                   (DWORD)subheap->heap, (DWORD)pArena );
781             if ( TRACE_ON(heap) )
782                 HEAP_Dump( subheap->heap );
783         }
784         return FALSE;
785     }
786
787     /* Check magic number */
788     if (pArena->magic != ARENA_INUSE_MAGIC)
789     {
790         if (quiet == NOISY) {
791         ERR("Heap %08lx: invalid in-use arena magic for %08lx\n",
792                  (DWORD)subheap->heap, (DWORD)pArena );
793             if (TRACE_ON(heap))
794                HEAP_Dump( subheap->heap );
795         }  else if (WARN_ON(heap)) {
796             WARN("Heap %08lx: invalid in-use arena magic for %08lx\n",
797                  (DWORD)subheap->heap, (DWORD)pArena );
798             if (TRACE_ON(heap))
799                HEAP_Dump( subheap->heap );
800         }
801         return FALSE;
802     }
803     /* Check size flags */
804     if (pArena->size & ARENA_FLAG_FREE)
805     {
806         ERR("Heap %08lx: bad flags %lx for in-use arena %08lx\n",
807                  (DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
808     }
809     /* Check arena size */
810     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
811     {
812         ERR("Heap %08lx: bad size %08lx for in-use arena %08lx\n",
813                  (DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
814         return FALSE;
815     }
816     /* Check next arena PREV_FREE flag */
817     if (((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd) &&
818         (*(DWORD *)((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
819     {
820         ERR("Heap %08lx: in-use arena %08lx next block has PREV_FREE flag\n",
821                  (DWORD)subheap->heap, (DWORD)pArena );
822         return FALSE;
823     }
824     /* Check prev free arena */
825     if (pArena->size & ARENA_FLAG_PREV_FREE)
826     {
827         ARENA_FREE *pPrev = *((ARENA_FREE **)pArena - 1);
828         /* Check prev pointer */
829         if (!HEAP_IsValidArenaPtr( subheap->heap, pPrev ))
830         {
831             ERR("Heap %08lx: bad back ptr %08lx for arena %08lx\n",
832                     (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
833             return FALSE;
834         }
835         /* Check that prev arena is free */
836         if (!(pPrev->size & ARENA_FLAG_FREE) ||
837             (pPrev->magic != ARENA_FREE_MAGIC))
838         {
839             ERR("Heap %08lx: prev arena %08lx invalid for in-use %08lx\n",
840                      (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
841             return FALSE;
842         }
843         /* Check that prev arena is really the previous block */
844         if ((char *)(pPrev + 1) + (pPrev->size & ARENA_SIZE_MASK) != (char *)pArena)
845         {
846             ERR("Heap %08lx: prev arena %08lx is not prev for in-use %08lx\n",
847                      (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
848             return FALSE;
849         }
850     }
851     return TRUE;
852 }
853
854
855 /***********************************************************************
856  *           HEAP_IsRealArena  [Internal]
857  * Validates a block is a valid arena.
858  *
859  * RETURNS
860  *      TRUE: Success
861  *      FALSE: Failure
862  */
863 static BOOL HEAP_IsRealArena( HEAP *heapPtr,   /* [in] ptr to the heap */
864               DWORD flags,   /* [in] Bit flags that control access during operation */
865               LPCVOID block, /* [in] Optional pointer to memory block to validate */
866               BOOL quiet )   /* [in] Flag - if true, HEAP_ValidateInUseArena
867                               *             does not complain    */
868 {
869     SUBHEAP *subheap;
870     BOOL ret = TRUE;
871
872     if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
873     {
874         ERR("Invalid heap %p!\n", heapPtr );
875         return FALSE;
876     }
877
878     flags &= HEAP_NO_SERIALIZE;
879     flags |= heapPtr->flags;
880     /* calling HeapLock may result in infinite recursion, so do the critsect directly */
881     if (!(flags & HEAP_NO_SERIALIZE))
882         RtlEnterCriticalSection( &heapPtr->critSection );
883
884     if (block)
885     {
886         /* Only check this single memory block */
887
888         if (!(subheap = HEAP_FindSubHeap( heapPtr, block )) ||
889             ((char *)block < (char *)subheap + subheap->headerSize
890                               + sizeof(ARENA_INUSE)))
891         {
892             if (quiet == NOISY)
893                 ERR("Heap %p: block %p is not inside heap\n", heapPtr, block );
894             else if (WARN_ON(heap))
895                 WARN("Heap %p: block %p is not inside heap\n", heapPtr, block );
896             ret = FALSE;
897         } else
898             ret = HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)block - 1, quiet );
899
900         if (!(flags & HEAP_NO_SERIALIZE))
901             RtlLeaveCriticalSection( &heapPtr->critSection );
902         return ret;
903     }
904
905     subheap = &heapPtr->subheap;
906     while (subheap && ret)
907     {
908         char *ptr = (char *)subheap + subheap->headerSize;
909         while (ptr < (char *)subheap + subheap->size)
910         {
911             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
912             {
913                 if (!HEAP_ValidateFreeArena( subheap, (ARENA_FREE *)ptr )) {
914                     ret = FALSE;
915                     break;
916                 }
917                 ptr += sizeof(ARENA_FREE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
918             }
919             else
920             {
921                 if (!HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)ptr, NOISY )) {
922                     ret = FALSE;
923                     break;
924                 }
925                 ptr += sizeof(ARENA_INUSE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
926             }
927         }
928         subheap = subheap->next;
929     }
930
931     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
932     return ret;
933 }
934
935
936 /***********************************************************************
937  *           RtlCreateHeap   (NTDLL.@)
938  */
939 HANDLE WINAPI RtlCreateHeap( ULONG flags, PVOID addr, ULONG totalSize, ULONG commitSize,
940                              PVOID unknown, PRTL_HEAP_DEFINITION definition )
941 {
942     SUBHEAP *subheap;
943
944     /* Allocate the heap block */
945
946     if (!totalSize)
947     {
948         totalSize = HEAP_DEF_SIZE;
949         flags |= HEAP_GROWABLE;
950     }
951
952     if (!(subheap = HEAP_CreateSubHeap( NULL, addr, flags, commitSize, totalSize ))) return 0;
953
954     /* link it into the per-process heap list */
955     if (processHeap)
956     {
957         HEAP *heapPtr = subheap->heap;
958         RtlLockHeap( processHeap );
959         heapPtr->next = firstHeap;
960         firstHeap = heapPtr;
961         RtlUnlockHeap( processHeap );
962     }
963     else  /* assume the first heap we create is the process main heap */
964     {
965         set_process_heap( (HANDLE)subheap->heap );
966     }
967     return (HANDLE)subheap;
968 }
969
970
971 /***********************************************************************
972  *           RtlDestroyHeap   (NTDLL.@)
973  */
974 HANDLE WINAPI RtlDestroyHeap( HANDLE heap )
975 {
976     HEAP *heapPtr = HEAP_GetPtr( heap );
977     SUBHEAP *subheap;
978
979     TRACE("%p\n", heap );
980     if (!heapPtr) return heap;
981
982     if (heap == processHeap) return heap; /* cannot delete the main process heap */
983     else /* remove it from the per-process list */
984     {
985         HEAP **pptr;
986         RtlLockHeap( processHeap );
987         pptr = &firstHeap;
988         while (*pptr && *pptr != heapPtr) pptr = &(*pptr)->next;
989         if (*pptr) *pptr = (*pptr)->next;
990         RtlUnlockHeap( processHeap );
991     }
992
993     RtlDeleteCriticalSection( &heapPtr->critSection );
994     subheap = &heapPtr->subheap;
995     while (subheap)
996     {
997         SUBHEAP *next = subheap->next;
998         ULONG size = 0;
999         void *addr = subheap;
1000         NtFreeVirtualMemory( GetCurrentProcess(), &addr, &size, MEM_RELEASE );
1001         subheap = next;
1002     }
1003     return 0;
1004 }
1005
1006
1007 /***********************************************************************
1008  *           RtlAllocateHeap   (NTDLL.@)
1009  *
1010  * NOTE: does not set last error.
1011  */
1012 PVOID WINAPI RtlAllocateHeap( HANDLE heap, ULONG flags, ULONG size )
1013 {
1014     ARENA_FREE *pArena;
1015     ARENA_INUSE *pInUse;
1016     SUBHEAP *subheap;
1017     HEAP *heapPtr = HEAP_GetPtr( heap );
1018
1019     /* Validate the parameters */
1020
1021     if (!heapPtr) return NULL;
1022     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY;
1023     flags |= heapPtr->flags;
1024     size = ROUND_SIZE(size);
1025     if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
1026
1027     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1028     /* Locate a suitable free block */
1029
1030     if (!(pArena = HEAP_FindFreeBlock( heapPtr, size, &subheap )))
1031     {
1032         TRACE("(%p,%08lx,%08lx): returning NULL\n",
1033                   heap, flags, size  );
1034         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1035         if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1036         return NULL;
1037     }
1038
1039     /* Remove the arena from the free list */
1040
1041     pArena->next->prev = pArena->prev;
1042     pArena->prev->next = pArena->next;
1043
1044     /* Build the in-use arena */
1045
1046     pInUse = (ARENA_INUSE *)pArena;
1047
1048     /* in-use arena is smaller than free arena,
1049      * so we have to add the difference to the size */
1050     pInUse->size  = (pInUse->size & ~ARENA_FLAG_FREE) + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1051     pInUse->magic = ARENA_INUSE_MAGIC;
1052
1053     /* Shrink the block */
1054
1055     HEAP_ShrinkBlock( subheap, pInUse, size );
1056
1057     if (flags & HEAP_ZERO_MEMORY)
1058         memset( pInUse + 1, 0, pInUse->size & ARENA_SIZE_MASK );
1059     else if (TRACE_ON(heap))
1060         memset( pInUse + 1, ARENA_INUSE_FILLER, pInUse->size & ARENA_SIZE_MASK );
1061
1062     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1063
1064     TRACE("(%p,%08lx,%08lx): returning %08lx\n",
1065                   heap, flags, size, (DWORD)(pInUse + 1) );
1066     return (LPVOID)(pInUse + 1);
1067 }
1068
1069
1070 /***********************************************************************
1071  *           RtlFreeHeap   (NTDLL.@)
1072  */
1073 BOOLEAN WINAPI RtlFreeHeap( HANDLE heap, ULONG flags, PVOID ptr )
1074 {
1075     ARENA_INUSE *pInUse;
1076     SUBHEAP *subheap;
1077     HEAP *heapPtr = HEAP_GetPtr( heap );
1078
1079     /* Validate the parameters */
1080
1081     if (!ptr) return TRUE;  /* freeing a NULL ptr isn't an error in Win2k */
1082     if (!heapPtr)
1083     {
1084         set_status( STATUS_INVALID_HANDLE );
1085         return FALSE;
1086     }
1087
1088     flags &= HEAP_NO_SERIALIZE;
1089     flags |= heapPtr->flags;
1090     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1091     if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
1092     {
1093         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1094         set_status( STATUS_INVALID_PARAMETER );
1095         TRACE("(%p,%08lx,%08lx): returning FALSE\n",
1096                       heap, flags, (DWORD)ptr );
1097         return FALSE;
1098     }
1099
1100     /* Turn the block into a free block */
1101
1102     pInUse  = (ARENA_INUSE *)ptr - 1;
1103     subheap = HEAP_FindSubHeap( heapPtr, pInUse );
1104     HEAP_MakeInUseBlockFree( subheap, pInUse );
1105
1106     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1107
1108     TRACE("(%p,%08lx,%08lx): returning TRUE\n",
1109                   heap, flags, (DWORD)ptr );
1110     return TRUE;
1111 }
1112
1113
1114 /***********************************************************************
1115  *           RtlReAllocateHeap   (NTDLL.@)
1116  */
1117 PVOID WINAPI RtlReAllocateHeap( HANDLE heap, ULONG flags, PVOID ptr, ULONG size )
1118 {
1119     ARENA_INUSE *pArena;
1120     DWORD oldSize;
1121     HEAP *heapPtr;
1122     SUBHEAP *subheap;
1123
1124     if (!ptr) return RtlAllocateHeap( heap, flags, size );  /* FIXME: correct? */
1125     if (!(heapPtr = HEAP_GetPtr( heap )))
1126     {
1127         set_status( STATUS_INVALID_HANDLE );
1128         return FALSE;
1129     }
1130
1131     /* Validate the parameters */
1132
1133     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY |
1134              HEAP_REALLOC_IN_PLACE_ONLY;
1135     flags |= heapPtr->flags;
1136     size = ROUND_SIZE(size);
1137     if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
1138
1139     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1140     if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
1141     {
1142         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1143         set_status( STATUS_INVALID_PARAMETER );
1144         TRACE("(%p,%08lx,%08lx,%08lx): returning NULL\n",
1145                       heap, flags, (DWORD)ptr, size );
1146         return NULL;
1147     }
1148
1149     /* Check if we need to grow the block */
1150
1151     pArena = (ARENA_INUSE *)ptr - 1;
1152     subheap = HEAP_FindSubHeap( heapPtr, pArena );
1153     oldSize = (pArena->size & ARENA_SIZE_MASK);
1154     if (size > oldSize)
1155     {
1156         char *pNext = (char *)(pArena + 1) + oldSize;
1157         if ((pNext < (char *)subheap + subheap->size) &&
1158             (*(DWORD *)pNext & ARENA_FLAG_FREE) &&
1159             (oldSize + (*(DWORD *)pNext & ARENA_SIZE_MASK) + sizeof(ARENA_FREE) >= size))
1160         {
1161             /* The next block is free and large enough */
1162             ARENA_FREE *pFree = (ARENA_FREE *)pNext;
1163             pFree->next->prev = pFree->prev;
1164             pFree->prev->next = pFree->next;
1165             pArena->size += (pFree->size & ARENA_SIZE_MASK) + sizeof(*pFree);
1166             if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
1167                                                + size + HEAP_MIN_BLOCK_SIZE))
1168             {
1169                 if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1170                 if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1171                 set_status( STATUS_NO_MEMORY );
1172                 return NULL;
1173             }
1174             HEAP_ShrinkBlock( subheap, pArena, size );
1175         }
1176         else  /* Do it the hard way */
1177         {
1178             ARENA_FREE *pNew;
1179             ARENA_INUSE *pInUse;
1180             SUBHEAP *newsubheap;
1181
1182             if ((flags & HEAP_REALLOC_IN_PLACE_ONLY) ||
1183                 !(pNew = HEAP_FindFreeBlock( heapPtr, size, &newsubheap )))
1184             {
1185                 if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1186                 if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1187                 set_status( STATUS_NO_MEMORY );
1188                 return NULL;
1189             }
1190
1191             /* Build the in-use arena */
1192
1193             pNew->next->prev = pNew->prev;
1194             pNew->prev->next = pNew->next;
1195             pInUse = (ARENA_INUSE *)pNew;
1196             pInUse->size = (pInUse->size & ~ARENA_FLAG_FREE)
1197                            + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1198             pInUse->magic = ARENA_INUSE_MAGIC;
1199             HEAP_ShrinkBlock( newsubheap, pInUse, size );
1200             memcpy( pInUse + 1, pArena + 1, oldSize );
1201
1202             /* Free the previous block */
1203
1204             HEAP_MakeInUseBlockFree( subheap, pArena );
1205             subheap = newsubheap;
1206             pArena  = pInUse;
1207         }
1208     }
1209     else HEAP_ShrinkBlock( subheap, pArena, size );  /* Shrink the block */
1210
1211     /* Clear the extra bytes if needed */
1212
1213     if (size > oldSize)
1214     {
1215         if (flags & HEAP_ZERO_MEMORY)
1216             memset( (char *)(pArena + 1) + oldSize, 0,
1217                     (pArena->size & ARENA_SIZE_MASK) - oldSize );
1218         else if (TRACE_ON(heap))
1219             memset( (char *)(pArena + 1) + oldSize, ARENA_INUSE_FILLER,
1220                     (pArena->size & ARENA_SIZE_MASK) - oldSize );
1221     }
1222
1223     /* Return the new arena */
1224
1225     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1226
1227     TRACE("(%p,%08lx,%08lx,%08lx): returning %08lx\n",
1228                   heap, flags, (DWORD)ptr, size, (DWORD)(pArena + 1) );
1229     return (LPVOID)(pArena + 1);
1230 }
1231
1232
1233 /***********************************************************************
1234  *           RtlCompactHeap   (NTDLL.@)
1235  */
1236 ULONG WINAPI RtlCompactHeap( HANDLE heap, ULONG flags )
1237 {
1238     FIXME( "stub\n" );
1239     return 0;
1240 }
1241
1242
1243 /***********************************************************************
1244  *           RtlLockHeap   (NTDLL.@)
1245  */
1246 BOOLEAN WINAPI RtlLockHeap( HANDLE heap )
1247 {
1248     HEAP *heapPtr = HEAP_GetPtr( heap );
1249     if (!heapPtr) return FALSE;
1250     RtlEnterCriticalSection( &heapPtr->critSection );
1251     return TRUE;
1252 }
1253
1254
1255 /***********************************************************************
1256  *           RtlUnlockHeap   (NTDLL.@)
1257  */
1258 BOOLEAN WINAPI RtlUnlockHeap( HANDLE heap )
1259 {
1260     HEAP *heapPtr = HEAP_GetPtr( heap );
1261     if (!heapPtr) return FALSE;
1262     RtlLeaveCriticalSection( &heapPtr->critSection );
1263     return TRUE;
1264 }
1265
1266
1267 /***********************************************************************
1268  *           RtlSizeHeap   (NTDLL.@)
1269  */
1270 ULONG WINAPI RtlSizeHeap( HANDLE heap, ULONG flags, PVOID ptr )
1271 {
1272     DWORD ret;
1273     HEAP *heapPtr = HEAP_GetPtr( heap );
1274
1275     if (!heapPtr)
1276     {
1277         set_status( STATUS_INVALID_HANDLE );
1278         return (ULONG)-1;
1279     }
1280     flags &= HEAP_NO_SERIALIZE;
1281     flags |= heapPtr->flags;
1282     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1283     if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
1284     {
1285         set_status( STATUS_INVALID_PARAMETER );
1286         ret = (ULONG)-1;
1287     }
1288     else
1289     {
1290         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr - 1;
1291         ret = pArena->size & ARENA_SIZE_MASK;
1292     }
1293     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1294
1295     TRACE("(%p,%08lx,%08lx): returning %08lx\n",
1296                   heap, flags, (DWORD)ptr, ret );
1297     return ret;
1298 }
1299
1300
1301 /***********************************************************************
1302  *           RtlValidateHeap   (NTDLL.@)
1303  */
1304 BOOLEAN WINAPI RtlValidateHeap( HANDLE heap, ULONG flags, LPCVOID block )
1305 {
1306     HEAP *heapPtr = HEAP_GetPtr( heap );
1307     if (!heapPtr) return FALSE;
1308     return HEAP_IsRealArena( heapPtr, flags, block, QUIET );
1309 }
1310
1311
1312 /***********************************************************************
1313  *           RtlWalkHeap    (NTDLL.@)
1314  *
1315  * FIXME: the PROCESS_HEAP_ENTRY flag values seem different between this
1316  *        function and HeapWalk. To be checked.
1317  */
1318 NTSTATUS WINAPI RtlWalkHeap( HANDLE heap, PVOID entry_ptr )
1319 {
1320     LPPROCESS_HEAP_ENTRY entry = entry_ptr; /* FIXME */
1321     HEAP *heapPtr = HEAP_GetPtr(heap);
1322     SUBHEAP *sub, *currentheap = NULL;
1323     NTSTATUS ret;
1324     char *ptr;
1325     int region_index = 0;
1326
1327     FIXME( "not fully compatible\n" );
1328
1329     if (!heapPtr || !entry) return STATUS_INVALID_PARAMETER;
1330
1331     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1332
1333     /* set ptr to the next arena to be examined */
1334
1335     if (!entry->lpData) /* first call (init) ? */
1336     {
1337         TRACE("begin walking of heap %p.\n", heap);
1338         currentheap = &heapPtr->subheap;
1339         ptr = (char*)currentheap + currentheap->headerSize;
1340     }
1341     else
1342     {
1343         ptr = entry->lpData;
1344         sub = &heapPtr->subheap;
1345         while (sub)
1346         {
1347             if (((char *)ptr >= (char *)sub) &&
1348                 ((char *)ptr < (char *)sub + sub->size))
1349             {
1350                 currentheap = sub;
1351                 break;
1352             }
1353             sub = sub->next;
1354             region_index++;
1355         }
1356         if (currentheap == NULL)
1357         {
1358             ERR("no matching subheap found, shouldn't happen !\n");
1359             ret = STATUS_NO_MORE_ENTRIES;
1360             goto HW_end;
1361         }
1362
1363         ptr += entry->cbData; /* point to next arena */
1364         if (ptr > (char *)currentheap + currentheap->size - 1)
1365         {   /* proceed with next subheap */
1366             if (!(currentheap = currentheap->next))
1367             {  /* successfully finished */
1368                 TRACE("end reached.\n");
1369                 ret = STATUS_NO_MORE_ENTRIES;
1370                 goto HW_end;
1371             }
1372             ptr = (char*)currentheap + currentheap->headerSize;
1373         }
1374     }
1375
1376     entry->wFlags = 0;
1377     if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1378     {
1379         ARENA_FREE *pArena = (ARENA_FREE *)ptr;
1380
1381         /*TRACE("free, magic: %04x\n", pArena->magic);*/
1382
1383         entry->lpData = pArena + 1;
1384         entry->cbData = pArena->size & ARENA_SIZE_MASK;
1385         entry->cbOverhead = sizeof(ARENA_FREE);
1386         entry->wFlags = PROCESS_HEAP_UNCOMMITTED_RANGE;
1387     }
1388     else
1389     {
1390         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
1391
1392         /*TRACE("busy, magic: %04x\n", pArena->magic);*/
1393
1394         entry->lpData = pArena + 1;
1395         entry->cbData = pArena->size & ARENA_SIZE_MASK;
1396         entry->cbOverhead = sizeof(ARENA_INUSE);
1397         entry->wFlags = PROCESS_HEAP_ENTRY_BUSY;
1398         /* FIXME: can't handle PROCESS_HEAP_ENTRY_MOVEABLE
1399         and PROCESS_HEAP_ENTRY_DDESHARE yet */
1400     }
1401
1402     entry->iRegionIndex = region_index;
1403
1404     /* first element of heap ? */
1405     if (ptr == (char *)(currentheap + currentheap->headerSize))
1406     {
1407         entry->wFlags |= PROCESS_HEAP_REGION;
1408         entry->u.Region.dwCommittedSize = currentheap->commitSize;
1409         entry->u.Region.dwUnCommittedSize =
1410                 currentheap->size - currentheap->commitSize;
1411         entry->u.Region.lpFirstBlock = /* first valid block */
1412                 currentheap + currentheap->headerSize;
1413         entry->u.Region.lpLastBlock  = /* first invalid block */
1414                 currentheap + currentheap->size;
1415     }
1416     ret = STATUS_SUCCESS;
1417
1418 HW_end:
1419     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1420     return ret;
1421 }
1422
1423
1424 /***********************************************************************
1425  *           RtlGetProcessHeaps    (NTDLL.@)
1426  */
1427 ULONG WINAPI RtlGetProcessHeaps( ULONG count, HANDLE *heaps )
1428 {
1429     DWORD total;
1430     HEAP *ptr;
1431
1432     if (!processHeap) return 0;  /* should never happen */
1433     total = 1;  /* main heap */
1434     RtlLockHeap( processHeap );
1435     for (ptr = firstHeap; ptr; ptr = ptr->next) total++;
1436     if (total <= count)
1437     {
1438         *heaps++ = (HANDLE)processHeap;
1439         for (ptr = firstHeap; ptr; ptr = ptr->next) *heaps++ = (HANDLE)ptr;
1440     }
1441     RtlUnlockHeap( processHeap );
1442     return total;
1443 }