Added some stubs.
[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     }
529
530     /* Create the first free block */
531
532     HEAP_CreateFreeBlock( subheap, (LPBYTE)subheap + subheap->headerSize, 
533                           subheap->size - subheap->headerSize );
534
535     return TRUE;
536 }
537
538 /***********************************************************************
539  *           HEAP_CreateSubHeap
540  *
541  * Create a sub-heap of the given size.
542  * If heap == NULL, creates a main heap.
543  */
544 static SUBHEAP *HEAP_CreateSubHeap( HEAP *heap, void *base, DWORD flags,
545                                     DWORD commitSize, DWORD totalSize )
546 {
547     LPVOID address = base;
548
549     if (!address)
550     {
551         /* round-up sizes on a 64K boundary */
552         totalSize  = (totalSize + 0xffff) & 0xffff0000;
553         commitSize = (commitSize + 0xffff) & 0xffff0000;
554         if (!commitSize) commitSize = 0x10000;
555         if (totalSize < commitSize) totalSize = commitSize;
556
557         /* allocate the memory block */
558         if (!(address = VirtualAlloc( NULL, totalSize, MEM_RESERVE, PAGE_EXECUTE_READWRITE )))
559         {
560             WARN("Could not VirtualAlloc %08lx bytes\n",
561                  totalSize );
562             return NULL;
563         }
564     }
565
566     /* Initialize subheap */
567
568     if (!HEAP_InitSubHeap( heap ? heap : (HEAP *)address,
569                            address, flags, commitSize, totalSize ))
570     {
571         if (!base) VirtualFree( address, 0, MEM_RELEASE );
572         return NULL;
573     }
574
575     return (SUBHEAP *)address;
576 }
577
578
579 /***********************************************************************
580  *           HEAP_FindFreeBlock
581  *
582  * Find a free block at least as large as the requested size, and make sure
583  * the requested size is committed.
584  */
585 static ARENA_FREE *HEAP_FindFreeBlock( HEAP *heap, DWORD size,
586                                        SUBHEAP **ppSubHeap )
587 {
588     SUBHEAP *subheap;
589     ARENA_FREE *pArena;
590     FREE_LIST_ENTRY *pEntry = heap->freeList;
591
592     /* Find a suitable free list, and in it find a block large enough */
593
594     while (pEntry->size < size) pEntry++;
595     pArena = pEntry->arena.next;
596     while (pArena != &heap->freeList[0].arena)
597     {
598         DWORD arena_size = (pArena->size & ARENA_SIZE_MASK) +
599                             sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
600         if (arena_size >= size)
601         {
602             subheap = HEAP_FindSubHeap( heap, pArena );
603             if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
604                                                + size + HEAP_MIN_BLOCK_SIZE))
605                 return NULL;
606             *ppSubHeap = subheap;
607             return pArena;
608         }
609         pArena = pArena->next;
610     }
611
612     /* If no block was found, attempt to grow the heap */
613
614     if (!(heap->flags & HEAP_GROWABLE))
615     {
616         WARN("Not enough space in heap %08lx for %08lx bytes\n",
617                  (DWORD)heap, size );
618         return NULL;
619     }
620     /* make sure that we have a big enough size *committed* to fit another
621      * last free arena in !
622      * So just one heap struct, one first free arena which will eventually
623      * get inuse, and HEAP_MIN_BLOCK_SIZE for the second free arena that
624      * might get assigned all remaining free space in HEAP_ShrinkBlock() */
625     size += sizeof(SUBHEAP) + sizeof(ARENA_INUSE) + HEAP_MIN_BLOCK_SIZE;
626     if (!(subheap = HEAP_CreateSubHeap( heap, NULL, heap->flags, size,
627                                         max( HEAP_DEF_SIZE, size ) )))
628         return NULL;
629
630     TRACE("created new sub-heap %08lx of %08lx bytes for heap %08lx\n",
631                 (DWORD)subheap, size, (DWORD)heap );
632
633     *ppSubHeap = subheap;
634     return (ARENA_FREE *)(subheap + 1);
635 }
636
637
638 /***********************************************************************
639  *           HEAP_IsValidArenaPtr
640  *
641  * Check that the pointer is inside the range possible for arenas.
642  */
643 static BOOL HEAP_IsValidArenaPtr( HEAP *heap, void *ptr )
644 {
645     int i;
646     SUBHEAP *subheap = HEAP_FindSubHeap( heap, ptr );
647     if (!subheap) return FALSE;
648     if ((char *)ptr >= (char *)subheap + subheap->headerSize) return TRUE;
649     if (subheap != &heap->subheap) return FALSE;
650     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
651         if (ptr == (void *)&heap->freeList[i].arena) return TRUE;
652     return FALSE;
653 }
654
655
656 /***********************************************************************
657  *           HEAP_ValidateFreeArena
658  */
659 static BOOL HEAP_ValidateFreeArena( SUBHEAP *subheap, ARENA_FREE *pArena )
660 {
661     char *heapEnd = (char *)subheap + subheap->size;
662
663 #if !defined(ALLOW_UNALIGNED_ACCESS)
664     /* Check for unaligned pointers */
665     if ( (long)pArena % sizeof(void *) != 0 )
666     {
667         ERR( "Heap %08lx: unaligned arena pointer %08lx\n",
668              (DWORD)subheap->heap, (DWORD)pArena );
669         return FALSE;
670     }
671 #endif
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 #if !defined(ALLOW_UNALIGNED_ACCESS)
758     /* Check for unaligned pointers */
759     if ( (long)pArena % sizeof(void *) != 0 )
760     {
761         if ( quiet == NOISY )
762         {
763             ERR( "Heap %08lx: unaligned arena pointer %08lx\n",
764                   (DWORD)subheap->heap, (DWORD)pArena );
765             if ( TRACE_ON(heap) )
766                 HEAP_Dump( subheap->heap );
767         }
768         else if ( WARN_ON(heap) )
769         {
770             WARN( "Heap %08lx: unaligned arena pointer %08lx\n",
771                   (DWORD)subheap->heap, (DWORD)pArena );
772             if ( TRACE_ON(heap) )
773                 HEAP_Dump( subheap->heap );
774         }
775         return FALSE;
776     }
777 #endif
778
779     /* Check magic number */
780     if (pArena->magic != ARENA_INUSE_MAGIC)
781     {
782         if (quiet == NOISY) {
783         ERR("Heap %08lx: invalid in-use arena magic for %08lx\n",
784                  (DWORD)subheap->heap, (DWORD)pArena );
785             if (TRACE_ON(heap))
786                HEAP_Dump( subheap->heap );
787         }  else if (WARN_ON(heap)) {
788             WARN("Heap %08lx: invalid in-use arena magic for %08lx\n",
789                  (DWORD)subheap->heap, (DWORD)pArena );
790             if (TRACE_ON(heap))
791                HEAP_Dump( subheap->heap );
792         }
793         return FALSE;
794     }
795     /* Check size flags */
796     if (pArena->size & ARENA_FLAG_FREE) 
797     {
798         ERR("Heap %08lx: bad flags %lx for in-use arena %08lx\n",
799                  (DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
800     }
801     /* Check arena size */
802     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
803     {
804         ERR("Heap %08lx: bad size %08lx for in-use arena %08lx\n",
805                  (DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
806         return FALSE;
807     }
808     /* Check next arena PREV_FREE flag */
809     if (((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd) &&
810         (*(DWORD *)((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
811     {
812         ERR("Heap %08lx: in-use arena %08lx next block has PREV_FREE flag\n",
813                  (DWORD)subheap->heap, (DWORD)pArena );
814         return FALSE;
815     }
816     /* Check prev free arena */
817     if (pArena->size & ARENA_FLAG_PREV_FREE)
818     {
819         ARENA_FREE *pPrev = *((ARENA_FREE **)pArena - 1);
820         /* Check prev pointer */
821         if (!HEAP_IsValidArenaPtr( subheap->heap, pPrev ))
822         {
823             ERR("Heap %08lx: bad back ptr %08lx for arena %08lx\n",
824                     (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
825             return FALSE;
826         }
827         /* Check that prev arena is free */
828         if (!(pPrev->size & ARENA_FLAG_FREE) ||
829             (pPrev->magic != ARENA_FREE_MAGIC))
830         { 
831             ERR("Heap %08lx: prev arena %08lx invalid for in-use %08lx\n", 
832                      (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
833             return FALSE;
834         }
835         /* Check that prev arena is really the previous block */
836         if ((char *)(pPrev + 1) + (pPrev->size & ARENA_SIZE_MASK) != (char *)pArena)
837         {
838             ERR("Heap %08lx: prev arena %08lx is not prev for in-use %08lx\n",
839                      (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
840             return FALSE;
841         }
842     }
843     return TRUE;
844 }
845
846
847 /***********************************************************************
848  *           HEAP_IsRealArena  [Internal]
849  * Validates a block is a valid arena.
850  *
851  * RETURNS
852  *      TRUE: Success
853  *      FALSE: Failure
854  */
855 static BOOL HEAP_IsRealArena( HEAP *heapPtr,   /* [in] ptr to the heap */
856               DWORD flags,   /* [in] Bit flags that control access during operation */
857               LPCVOID block, /* [in] Optional pointer to memory block to validate */
858               BOOL quiet )   /* [in] Flag - if true, HEAP_ValidateInUseArena
859                               *             does not complain    */
860 {
861     SUBHEAP *subheap;
862     BOOL ret = TRUE;
863
864     if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
865     {
866         ERR("Invalid heap %p!\n", heapPtr );
867         return FALSE;
868     }
869
870     flags &= HEAP_NO_SERIALIZE;
871     flags |= heapPtr->flags;
872     /* calling HeapLock may result in infinite recursion, so do the critsect directly */
873     if (!(flags & HEAP_NO_SERIALIZE))
874         RtlEnterCriticalSection( &heapPtr->critSection );
875
876     if (block)
877     {
878         /* Only check this single memory block */
879
880         if (!(subheap = HEAP_FindSubHeap( heapPtr, block )) ||
881             ((char *)block < (char *)subheap + subheap->headerSize
882                               + sizeof(ARENA_INUSE)))
883         {
884             if (quiet == NOISY) 
885                 ERR("Heap %p: block %p is not inside heap\n", heapPtr, block );
886             else if (WARN_ON(heap)) 
887                 WARN("Heap %p: block %p is not inside heap\n", heapPtr, block );
888             ret = FALSE;
889         } else
890             ret = HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)block - 1, quiet );
891
892         if (!(flags & HEAP_NO_SERIALIZE))
893             RtlLeaveCriticalSection( &heapPtr->critSection );
894         return ret;
895     }
896
897     subheap = &heapPtr->subheap;
898     while (subheap && ret)
899     {
900         char *ptr = (char *)subheap + subheap->headerSize;
901         while (ptr < (char *)subheap + subheap->size)
902         {
903             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
904             {
905                 if (!HEAP_ValidateFreeArena( subheap, (ARENA_FREE *)ptr )) {
906                     ret = FALSE;
907                     break;
908                 }
909                 ptr += sizeof(ARENA_FREE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
910             }
911             else
912             {
913                 if (!HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)ptr, NOISY )) {
914                     ret = FALSE;
915                     break;
916                 }
917                 ptr += sizeof(ARENA_INUSE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
918             }
919         }
920         subheap = subheap->next;
921     }
922
923     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
924     return ret;
925 }
926
927
928 /***********************************************************************
929  *           RtlCreateHeap   (NTDLL.@)
930  */
931 HANDLE WINAPI RtlCreateHeap( ULONG flags, PVOID addr, ULONG totalSize, ULONG commitSize,
932                              PVOID unknown, PRTL_HEAP_DEFINITION definition )
933 {
934     SUBHEAP *subheap;
935
936     /* Allocate the heap block */
937
938     if (!totalSize)
939     {
940         totalSize = HEAP_DEF_SIZE;
941         flags |= HEAP_GROWABLE;
942     }
943     /* round up sizes */
944     totalSize  = (totalSize + 0xffff) & 0xffff0000;
945     commitSize = (commitSize + 0xffff) & 0xffff0000;
946     if (!commitSize) commitSize = 0x10000;
947     if (totalSize < commitSize) totalSize = commitSize;
948
949     if (!(subheap = HEAP_CreateSubHeap( NULL, addr, flags, commitSize, totalSize ))) return 0;
950
951     /* link it into the per-process heap list */
952     if (processHeap)
953     {
954         HEAP *heapPtr = subheap->heap;
955         RtlLockHeap( processHeap );
956         heapPtr->next = firstHeap;
957         firstHeap = heapPtr;
958         RtlUnlockHeap( processHeap );
959     }
960     else  /* assume the first heap we create is the process main heap */
961     {
962         set_process_heap( (HANDLE)subheap->heap );
963     }
964     return (HANDLE)subheap;
965 }
966
967
968 /***********************************************************************
969  *           RtlDestroyHeap   (NTDLL.@)
970  */
971 HANDLE WINAPI RtlDestroyHeap( HANDLE heap )
972 {
973     HEAP *heapPtr = HEAP_GetPtr( heap );
974     SUBHEAP *subheap;
975
976     TRACE("%08x\n", heap );
977     if (!heapPtr) return heap;
978
979     if (heap == processHeap) return heap; /* cannot delete the main process heap */
980     else /* remove it from the per-process list */
981     {
982         HEAP **pptr;
983         RtlLockHeap( processHeap );
984         pptr = &firstHeap;
985         while (*pptr && *pptr != heapPtr) pptr = &(*pptr)->next;
986         if (*pptr) *pptr = (*pptr)->next;
987         RtlUnlockHeap( processHeap );
988     }
989
990     RtlDeleteCriticalSection( &heapPtr->critSection );
991     subheap = &heapPtr->subheap;
992     while (subheap)
993     {
994         SUBHEAP *next = subheap->next;
995         VirtualFree( subheap, 0, MEM_RELEASE );
996         subheap = next;
997     }
998     return 0;
999 }
1000
1001
1002 /***********************************************************************
1003  *           RtlAllocateHeap   (NTDLL.@)
1004  *
1005  * NOTE: does not set last error.
1006  */
1007 PVOID WINAPI RtlAllocateHeap( HANDLE heap, ULONG flags, ULONG size )
1008 {
1009     ARENA_FREE *pArena;
1010     ARENA_INUSE *pInUse;
1011     SUBHEAP *subheap;
1012     HEAP *heapPtr = HEAP_GetPtr( heap );
1013
1014     /* Validate the parameters */
1015
1016     if (!heapPtr) return NULL;
1017     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY;
1018     flags |= heapPtr->flags;
1019     size = (size + 3) & ~3;
1020     if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
1021
1022     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1023     /* Locate a suitable free block */
1024
1025     if (!(pArena = HEAP_FindFreeBlock( heapPtr, size, &subheap )))
1026     {
1027         TRACE("(%08x,%08lx,%08lx): returning NULL\n",
1028                   heap, flags, size  );
1029         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1030         if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1031         return NULL;
1032     }
1033
1034     /* Remove the arena from the free list */
1035
1036     pArena->next->prev = pArena->prev;
1037     pArena->prev->next = pArena->next;
1038
1039     /* Build the in-use arena */
1040
1041     pInUse = (ARENA_INUSE *)pArena;
1042
1043     /* in-use arena is smaller than free arena,
1044      * so we have to add the difference to the size */
1045     pInUse->size  = (pInUse->size & ~ARENA_FLAG_FREE) + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1046     pInUse->magic = ARENA_INUSE_MAGIC;
1047
1048     /* Shrink the block */
1049
1050     HEAP_ShrinkBlock( subheap, pInUse, size );
1051
1052     if (flags & HEAP_ZERO_MEMORY)
1053         memset( pInUse + 1, 0, pInUse->size & ARENA_SIZE_MASK );
1054     else if (TRACE_ON(heap))
1055         memset( pInUse + 1, ARENA_INUSE_FILLER, pInUse->size & ARENA_SIZE_MASK );
1056
1057     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1058
1059     TRACE("(%08x,%08lx,%08lx): returning %08lx\n",
1060                   heap, flags, size, (DWORD)(pInUse + 1) );
1061     return (LPVOID)(pInUse + 1);
1062 }
1063
1064
1065 /***********************************************************************
1066  *           RtlFreeHeap   (NTDLL.@)
1067  */
1068 BOOLEAN WINAPI RtlFreeHeap( HANDLE heap, ULONG flags, PVOID ptr )
1069 {
1070     ARENA_INUSE *pInUse;
1071     SUBHEAP *subheap;
1072     HEAP *heapPtr = HEAP_GetPtr( heap );
1073
1074     /* Validate the parameters */
1075
1076     if (!ptr) return TRUE;  /* freeing a NULL ptr isn't an error in Win2k */
1077     if (!heapPtr)
1078     {
1079         set_status( STATUS_INVALID_HANDLE );
1080         return FALSE;
1081     }
1082
1083     flags &= HEAP_NO_SERIALIZE;
1084     flags |= heapPtr->flags;
1085     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1086     if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
1087     {
1088         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1089         set_status( STATUS_INVALID_PARAMETER );
1090         TRACE("(%08x,%08lx,%08lx): returning FALSE\n",
1091                       heap, flags, (DWORD)ptr );
1092         return FALSE;
1093     }
1094
1095     /* Turn the block into a free block */
1096
1097     pInUse  = (ARENA_INUSE *)ptr - 1;
1098     subheap = HEAP_FindSubHeap( heapPtr, pInUse );
1099     HEAP_MakeInUseBlockFree( subheap, pInUse );
1100
1101     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1102
1103     TRACE("(%08x,%08lx,%08lx): returning TRUE\n",
1104                   heap, flags, (DWORD)ptr );
1105     return TRUE;
1106 }
1107
1108
1109 /***********************************************************************
1110  *           RtlReAllocateHeap   (NTDLL.@)
1111  */
1112 PVOID WINAPI RtlReAllocateHeap( HANDLE heap, ULONG flags, PVOID ptr, ULONG size )
1113 {
1114     ARENA_INUSE *pArena;
1115     DWORD oldSize;
1116     HEAP *heapPtr;
1117     SUBHEAP *subheap;
1118
1119     if (!ptr) return RtlAllocateHeap( heap, flags, size );  /* FIXME: correct? */
1120     if (!(heapPtr = HEAP_GetPtr( heap )))
1121     {
1122         set_status( STATUS_INVALID_HANDLE );
1123         return FALSE;
1124     }
1125
1126     /* Validate the parameters */
1127
1128     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY |
1129              HEAP_REALLOC_IN_PLACE_ONLY;
1130     flags |= heapPtr->flags;
1131     size = (size + 3) & ~3;
1132     if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
1133
1134     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1135     if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
1136     {
1137         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1138         set_status( STATUS_INVALID_PARAMETER );
1139         TRACE("(%08x,%08lx,%08lx,%08lx): returning NULL\n",
1140                       heap, flags, (DWORD)ptr, size );
1141         return NULL;
1142     }
1143
1144     /* Check if we need to grow the block */
1145
1146     pArena = (ARENA_INUSE *)ptr - 1;
1147     subheap = HEAP_FindSubHeap( heapPtr, pArena );
1148     oldSize = (pArena->size & ARENA_SIZE_MASK);
1149     if (size > oldSize)
1150     {
1151         char *pNext = (char *)(pArena + 1) + oldSize;
1152         if ((pNext < (char *)subheap + subheap->size) &&
1153             (*(DWORD *)pNext & ARENA_FLAG_FREE) &&
1154             (oldSize + (*(DWORD *)pNext & ARENA_SIZE_MASK) + sizeof(ARENA_FREE) >= size))
1155         {
1156             /* The next block is free and large enough */
1157             ARENA_FREE *pFree = (ARENA_FREE *)pNext;
1158             pFree->next->prev = pFree->prev;
1159             pFree->prev->next = pFree->next;
1160             pArena->size += (pFree->size & ARENA_SIZE_MASK) + sizeof(*pFree);
1161             if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
1162                                                + size + HEAP_MIN_BLOCK_SIZE))
1163             {
1164                 if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1165                 if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1166                 set_status( STATUS_NO_MEMORY );
1167                 return NULL;
1168             }
1169             HEAP_ShrinkBlock( subheap, pArena, size );
1170         }
1171         else  /* Do it the hard way */
1172         {
1173             ARENA_FREE *pNew;
1174             ARENA_INUSE *pInUse;
1175             SUBHEAP *newsubheap;
1176
1177             if ((flags & HEAP_REALLOC_IN_PLACE_ONLY) ||
1178                 !(pNew = HEAP_FindFreeBlock( heapPtr, size, &newsubheap )))
1179             {
1180                 if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1181                 if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1182                 set_status( STATUS_NO_MEMORY );
1183                 return NULL;
1184             }
1185
1186             /* Build the in-use arena */
1187
1188             pNew->next->prev = pNew->prev;
1189             pNew->prev->next = pNew->next;
1190             pInUse = (ARENA_INUSE *)pNew;
1191             pInUse->size = (pInUse->size & ~ARENA_FLAG_FREE)
1192                            + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1193             pInUse->magic = ARENA_INUSE_MAGIC;
1194             HEAP_ShrinkBlock( newsubheap, pInUse, size );
1195             memcpy( pInUse + 1, pArena + 1, oldSize );
1196
1197             /* Free the previous block */
1198
1199             HEAP_MakeInUseBlockFree( subheap, pArena );
1200             subheap = newsubheap;
1201             pArena  = pInUse;
1202         }
1203     }
1204     else HEAP_ShrinkBlock( subheap, pArena, size );  /* Shrink the block */
1205
1206     /* Clear the extra bytes if needed */
1207
1208     if (size > oldSize)
1209     {
1210         if (flags & HEAP_ZERO_MEMORY)
1211             memset( (char *)(pArena + 1) + oldSize, 0,
1212                     (pArena->size & ARENA_SIZE_MASK) - oldSize );
1213         else if (TRACE_ON(heap))
1214             memset( (char *)(pArena + 1) + oldSize, ARENA_INUSE_FILLER,
1215                     (pArena->size & ARENA_SIZE_MASK) - oldSize );
1216     }
1217
1218     /* Return the new arena */
1219
1220     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1221
1222     TRACE("(%08x,%08lx,%08lx,%08lx): returning %08lx\n",
1223                   heap, flags, (DWORD)ptr, size, (DWORD)(pArena + 1) );
1224     return (LPVOID)(pArena + 1);
1225 }
1226
1227
1228 /***********************************************************************
1229  *           RtlCompactHeap   (NTDLL.@)
1230  */
1231 ULONG WINAPI RtlCompactHeap( HANDLE heap, ULONG flags )
1232 {
1233     FIXME( "stub\n" );
1234     return 0;
1235 }
1236
1237
1238 /***********************************************************************
1239  *           RtlLockHeap   (NTDLL.@)
1240  */
1241 BOOLEAN WINAPI RtlLockHeap( HANDLE heap )
1242 {
1243     HEAP *heapPtr = HEAP_GetPtr( heap );
1244     if (!heapPtr) return FALSE;
1245     RtlEnterCriticalSection( &heapPtr->critSection );
1246     return TRUE;
1247 }
1248
1249
1250 /***********************************************************************
1251  *           RtlUnlockHeap   (NTDLL.@)
1252  */
1253 BOOLEAN WINAPI RtlUnlockHeap( HANDLE heap )
1254 {
1255     HEAP *heapPtr = HEAP_GetPtr( heap );
1256     if (!heapPtr) return FALSE;
1257     RtlLeaveCriticalSection( &heapPtr->critSection );
1258     return TRUE;
1259 }
1260
1261
1262 /***********************************************************************
1263  *           RtlSizeHeap   (NTDLL.@)
1264  */
1265 ULONG WINAPI RtlSizeHeap( HANDLE heap, ULONG flags, PVOID ptr )
1266 {
1267     DWORD ret;
1268     HEAP *heapPtr = HEAP_GetPtr( heap );
1269
1270     if (!heapPtr)
1271     {
1272         set_status( STATUS_INVALID_HANDLE );
1273         return (ULONG)-1;
1274     }
1275     flags &= HEAP_NO_SERIALIZE;
1276     flags |= heapPtr->flags;
1277     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1278     if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
1279     {
1280         set_status( STATUS_INVALID_PARAMETER );
1281         ret = (ULONG)-1;
1282     }
1283     else
1284     {
1285         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr - 1;
1286         ret = pArena->size & ARENA_SIZE_MASK;
1287     }
1288     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1289
1290     TRACE("(%08x,%08lx,%08lx): returning %08lx\n",
1291                   heap, flags, (DWORD)ptr, ret );
1292     return ret;
1293 }
1294
1295
1296 /***********************************************************************
1297  *           RtlValidateHeap   (NTDLL.@)
1298  */
1299 BOOLEAN WINAPI RtlValidateHeap( HANDLE heap, ULONG flags, PCVOID block )
1300 {
1301     HEAP *heapPtr = HEAP_GetPtr( heap );
1302     if (!heapPtr) return FALSE;
1303     return HEAP_IsRealArena( heapPtr, flags, block, QUIET );
1304 }
1305
1306
1307 /***********************************************************************
1308  *           RtlWalkHeap    (NTDLL.@)
1309  *
1310  * FIXME: the PROCESS_HEAP_ENTRY flag values seem different between this
1311  *        function and HeapWalk. To be checked.
1312  */
1313 NTSTATUS WINAPI RtlWalkHeap( HANDLE heap, PVOID entry_ptr )
1314 {
1315     LPPROCESS_HEAP_ENTRY entry = entry_ptr; /* FIXME */
1316     HEAP *heapPtr = HEAP_GetPtr(heap);
1317     SUBHEAP *sub, *currentheap = NULL;
1318     NTSTATUS ret;
1319     char *ptr;
1320     int region_index = 0;
1321
1322     FIXME( "not fully compatible\n" );
1323
1324     if (!heapPtr || !entry) return STATUS_INVALID_PARAMETER;
1325
1326     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) EnterCriticalSection( &heapPtr->critSection );
1327
1328     /* set ptr to the next arena to be examined */
1329
1330     if (!entry->lpData) /* first call (init) ? */
1331     {
1332         TRACE("begin walking of heap 0x%08x.\n", heap);
1333         currentheap = &heapPtr->subheap;
1334         ptr = (char*)currentheap + currentheap->headerSize;
1335     }
1336     else
1337     {
1338         ptr = entry->lpData;
1339         sub = &heapPtr->subheap;
1340         while (sub)
1341         {
1342             if (((char *)ptr >= (char *)sub) &&
1343                 ((char *)ptr < (char *)sub + sub->size))
1344             {
1345                 currentheap = sub;
1346                 break;
1347             }
1348             sub = sub->next;
1349             region_index++;
1350         }
1351         if (currentheap == NULL)
1352         {
1353             ERR("no matching subheap found, shouldn't happen !\n");
1354             ret = STATUS_NO_MORE_ENTRIES;
1355             goto HW_end;
1356         }
1357
1358         ptr += entry->cbData; /* point to next arena */
1359         if (ptr > (char *)currentheap + currentheap->size - 1)
1360         {   /* proceed with next subheap */
1361             if (!(currentheap = currentheap->next))
1362             {  /* successfully finished */
1363                 TRACE("end reached.\n");
1364                 ret = STATUS_NO_MORE_ENTRIES;
1365                 goto HW_end;
1366             }
1367             ptr = (char*)currentheap + currentheap->headerSize;
1368         }
1369     }
1370
1371     entry->wFlags = 0;
1372     if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1373     {
1374         ARENA_FREE *pArena = (ARENA_FREE *)ptr;
1375
1376         /*TRACE("free, magic: %04x\n", pArena->magic);*/
1377
1378         entry->lpData = pArena + 1;
1379         entry->cbData = pArena->size & ARENA_SIZE_MASK;
1380         entry->cbOverhead = sizeof(ARENA_FREE);
1381         entry->wFlags = PROCESS_HEAP_UNCOMMITTED_RANGE;
1382     }
1383     else
1384     {
1385         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
1386
1387         /*TRACE("busy, magic: %04x\n", pArena->magic);*/
1388
1389         entry->lpData = pArena + 1;
1390         entry->cbData = pArena->size & ARENA_SIZE_MASK;
1391         entry->cbOverhead = sizeof(ARENA_INUSE);
1392         entry->wFlags = PROCESS_HEAP_ENTRY_BUSY;
1393         /* FIXME: can't handle PROCESS_HEAP_ENTRY_MOVEABLE
1394         and PROCESS_HEAP_ENTRY_DDESHARE yet */
1395     }
1396
1397     entry->iRegionIndex = region_index;
1398
1399     /* first element of heap ? */
1400     if (ptr == (char *)(currentheap + currentheap->headerSize))
1401     {
1402         entry->wFlags |= PROCESS_HEAP_REGION;
1403         entry->u.Region.dwCommittedSize = currentheap->commitSize;
1404         entry->u.Region.dwUnCommittedSize =
1405                 currentheap->size - currentheap->commitSize;
1406         entry->u.Region.lpFirstBlock = /* first valid block */
1407                 currentheap + currentheap->headerSize;
1408         entry->u.Region.lpLastBlock  = /* first invalid block */
1409                 currentheap + currentheap->size;
1410     }
1411     ret = STATUS_SUCCESS;
1412
1413 HW_end:
1414     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) LeaveCriticalSection( &heapPtr->critSection );
1415     return ret;
1416 }
1417
1418
1419 /***********************************************************************
1420  *           RtlGetProcessHeaps    (NTDLL.@)
1421  */
1422 ULONG WINAPI RtlGetProcessHeaps( ULONG count, HANDLE *heaps )
1423 {
1424     DWORD total;
1425     HEAP *ptr;
1426
1427     if (!processHeap) return 0;  /* should never happen */
1428     total = 1;  /* main heap */
1429     RtlLockHeap( processHeap );
1430     for (ptr = firstHeap; ptr; ptr = ptr->next) total++;
1431     if (total <= count)
1432     {
1433         *heaps++ = (HANDLE)processHeap;
1434         for (ptr = firstHeap; ptr; ptr = ptr->next) *heaps++ = (HANDLE)ptr;
1435     }
1436     RtlUnlockHeap( processHeap );
1437     return total;
1438 }