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