update for HEAD-2003091401
[reactos.git] / lib / ntdll / rtl / heap.c
1 /*
2  * Win32 heap functions
3  *
4  * Copyright 1996 Alexandre Julliard
5  * Copyright 1998 Ulrich Weigand
6  */
7
8
9 /* Note: the heap data structures are based on what Pietrek describes in his
10  * book 'Windows 95 System Programming Secrets'. The layout is not exactly
11  * the same, but could be easily adapted if it turns out some programs
12  * require it.
13  */
14
15 #include <string.h>
16 #include <ddk/ntddk.h>
17 #include <ntdll/rtl.h>
18 #include <ntos/heap.h>
19 #include <ntos/minmax.h>
20
21 #define NDEBUG
22 #include <ntdll/ntdll.h>
23
24 #define DPRINTF DPRINT
25 #define ERR DPRINT
26 #define SetLastError(x)
27 #define WARN DPRINT
28 #define TRACE DPRINT
29 #define WARN_ON(x) (1)
30
31 #undef assert
32 #ifdef NDEBUG
33 #define TRACE_ON(x) (0)
34 #define assert(x)
35 #else
36 #define TRACE_ON(x) (1)
37 #define assert(x)
38 #endif
39
40
41 static CRITICAL_SECTION RtlpProcessHeapsListLock;
42
43
44 typedef struct tagARENA_INUSE
45 {
46     DWORD  size;                    /* Block size; must be the first field */
47     WORD   threadId;                /* Allocating thread id */
48     WORD   magic;                   /* Magic number */
49     void  *callerEIP;               /* EIP of caller upon allocation */
50 } ARENA_INUSE;
51
52 typedef struct tagARENA_FREE
53 {
54     DWORD                 size;     /* Block size; must be the first field */
55     WORD                  threadId; /* Freeing thread id */
56     WORD                  magic;    /* Magic number */
57     struct tagARENA_FREE *next;     /* Next free arena */
58     struct tagARENA_FREE *prev;     /* Prev free arena */
59 } ARENA_FREE;
60
61 #define ARENA_FLAG_FREE        0x00000001  /* flags OR'ed with arena size */
62 #define ARENA_FLAG_PREV_FREE   0x00000002
63 #define ARENA_SIZE_MASK        0xfffffffc
64 #define ARENA_INUSE_MAGIC      0x4842      /* Value for arena 'magic' field */
65 #define ARENA_FREE_MAGIC       0x4846      /* Value for arena 'magic' field */
66
67 #define ARENA_INUSE_FILLER     0x55
68 #define ARENA_FREE_FILLER      0xaa
69
70 #define QUIET                  1           /* Suppress messages  */
71 #define NOISY                  0           /* Report all errors  */
72
73 #define HEAP_NB_FREE_LISTS   4   /* Number of free lists */
74
75 /* Max size of the blocks on the free lists */
76 static const DWORD HEAP_freeListSizes[HEAP_NB_FREE_LISTS] =
77 {
78     0x20, 0x80, 0x200, 0xffffffff
79 };
80
81 typedef struct
82 {
83     DWORD       size;
84     ARENA_FREE  arena;
85 } FREE_LIST_ENTRY;
86
87 struct tagHEAP;
88
89 typedef struct tagSUBHEAP
90 {
91     DWORD               size;       /* Size of the whole sub-heap */
92     DWORD               commitSize; /* Committed size of the sub-heap */
93     DWORD               headerSize; /* Size of the heap header */
94     struct tagSUBHEAP  *next;       /* Next sub-heap */
95     struct tagHEAP     *heap;       /* Main heap structure */
96     DWORD               magic;      /* Magic number */
97     WORD                selector;   /* Selector for HEAP_WINE_SEGPTR heaps */
98 } SUBHEAP, *PSUBHEAP;
99
100 #define SUBHEAP_MAGIC    ((DWORD)('S' | ('U'<<8) | ('B'<<16) | ('H'<<24)))
101
102 typedef struct tagHEAP
103 {
104     SUBHEAP          subheap;       /* First sub-heap */
105     struct tagHEAP  *next;          /* Next heap for this process */
106     FREE_LIST_ENTRY  freeList[HEAP_NB_FREE_LISTS];  /* Free lists */
107     CRITICAL_SECTION critSection;   /* Critical section for serialization */
108     DWORD            flags;         /* Heap flags */
109     DWORD            magic;         /* Magic number */
110     void            *private;       /* Private pointer for the user of the heap */
111 } HEAP, *PHEAP;
112
113 #define HEAP_MAGIC       ((DWORD)('H' | ('E'<<8) | ('A'<<16) | ('P'<<24)))
114
115 #define HEAP_DEF_SIZE        0x110000   /* Default heap size = 1Mb + 64Kb */
116 #define HEAP_MIN_BLOCK_SIZE  (8+sizeof(ARENA_FREE))  /* Min. heap block size */
117 #define COMMIT_MASK          0xffff  /* bitmask for commit/decommit granularity */
118
119
120 static BOOL HEAP_IsRealArena( HANDLE heap, DWORD flags, LPCVOID block, BOOL quiet );
121
122 #ifdef __GNUC__
123 #define GET_EIP()    (__builtin_return_address(0))
124 #define SET_EIP(ptr) ((ARENA_INUSE*)(ptr) - 1)->callerEIP = GET_EIP()
125 #else
126 #define GET_EIP()    0
127 #define SET_EIP(ptr) /* nothing */
128 #endif  /* __GNUC__ */
129
130
131 /***********************************************************************
132  *           HEAP_Dump
133  */
134 void
135 HEAP_Dump(PHEAP heap)
136 {
137     int i;
138     SUBHEAP *subheap;
139     char *ptr;
140
141     DPRINTF( "Heap: %08lx\n", (DWORD)heap );
142     DPRINTF( "Next: %08lx  Sub-heaps: %08lx",
143           (DWORD)heap->next, (DWORD)&heap->subheap );
144     subheap = &heap->subheap;
145     while (subheap->next)
146     {
147         DPRINTF( " -> %08lx", (DWORD)subheap->next );
148         subheap = subheap->next;
149     }
150
151     DPRINTF( "\nFree lists:\n Block   Stat   Size    Id\n" );
152     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
153         DPRINTF( "%08lx free %08lx %04x prev=%08lx next=%08lx\n",
154               (DWORD)&heap->freeList[i].arena, heap->freeList[i].arena.size,
155               heap->freeList[i].arena.threadId,
156               (DWORD)heap->freeList[i].arena.prev,
157               (DWORD)heap->freeList[i].arena.next );
158
159     subheap = &heap->subheap;
160     while (subheap)
161     {
162         DWORD freeSize = 0, usedSize = 0, arenaSize = subheap->headerSize;
163         DPRINTF( "\n\nSub-heap %08lx: size=%08lx committed=%08lx\n",
164               (DWORD)subheap, subheap->size, subheap->commitSize );
165         
166         DPRINTF( "\n Block   Stat   Size    Id\n" );
167         ptr = (char*)subheap + subheap->headerSize;
168         while (ptr < (char *)subheap + subheap->size)
169         {
170             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
171             {
172                 ARENA_FREE *pArena = (ARENA_FREE *)ptr;
173                 DPRINTF( "%08lx free %08lx %04x prev=%08lx next=%08lx\n",
174                       (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
175                       pArena->threadId, (DWORD)pArena->prev,
176                       (DWORD)pArena->next);
177                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
178                 arenaSize += sizeof(ARENA_FREE);
179                 freeSize += pArena->size & ARENA_SIZE_MASK;
180             }
181             else if (*(DWORD *)ptr & ARENA_FLAG_PREV_FREE)
182             {
183                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
184                 DPRINTF( "%08lx Used %08lx %04x back=%08lx EIP=%p\n",
185                       (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
186                       pArena->threadId, *((DWORD *)pArena - 1),
187                       pArena->callerEIP );
188                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
189                 arenaSize += sizeof(ARENA_INUSE);
190                 usedSize += pArena->size & ARENA_SIZE_MASK;
191             }
192             else
193             {
194                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
195                 DPRINTF( "%08lx used %08lx %04x EIP=%p\n",
196                       (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
197                       pArena->threadId, pArena->callerEIP );
198                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
199                 arenaSize += sizeof(ARENA_INUSE);
200                 usedSize += pArena->size & ARENA_SIZE_MASK;
201             }
202         }
203         DPRINTF( "\nTotal: Size=%08lx Committed=%08lx Free=%08lx Used=%08lx Arenas=%08lx (%ld%%)\n\n",
204               subheap->size, subheap->commitSize, freeSize, usedSize,
205               arenaSize, (arenaSize * 100) / subheap->size );
206         subheap = subheap->next;
207     }
208 }
209
210
211 /***********************************************************************
212  *           HEAP_GetPtr
213  * RETURNS
214  *      Pointer to the heap
215  *      NULL: Failure
216  */
217 static PHEAP
218 HEAP_GetPtr(HANDLE heap) /* [in] Handle to the heap */
219 {
220     HEAP *heapPtr = (HEAP *)heap;
221     if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
222     {
223         ERR("Invalid heap %08x!\n", heap );
224         return NULL;
225     }
226     if (TRACE_ON(heap) && !HEAP_IsRealArena( heap, 0, NULL, NOISY ))
227     {
228         HEAP_Dump( heapPtr );
229         assert( FALSE );
230         return NULL;
231     }
232     return heapPtr;
233 }
234
235
236 /***********************************************************************
237  *           HEAP_InsertFreeBlock
238  *
239  * Insert a free block into the free list.
240  */
241 static VOID
242 HEAP_InsertFreeBlock(PHEAP heap,
243                      ARENA_FREE *pArena,
244                      BOOL last)
245 {
246     FREE_LIST_ENTRY *pEntry = heap->freeList;
247     while (pEntry->size < pArena->size) pEntry++;
248     if (last)
249     {
250         /* insert at end of free list, i.e. before next free list entry */
251         pEntry++;
252         if (pEntry == &heap->freeList[HEAP_NB_FREE_LISTS])
253         {
254             pEntry = heap->freeList;
255         }
256         pArena->prev       = pEntry->arena.prev;
257         pArena->prev->next = pArena;
258         pArena->next       = &pEntry->arena;
259         pEntry->arena.prev = pArena;
260     }
261     else
262     {
263         /* insert at head of free list */
264         pArena->next       = pEntry->arena.next;
265         pArena->next->prev = pArena;
266         pArena->prev       = &pEntry->arena;
267         pEntry->arena.next = pArena;
268     }
269     pArena->size |= ARENA_FLAG_FREE;
270 }
271
272
273 /***********************************************************************
274  *           HEAP_FindSubHeap
275  * Find the sub-heap containing a given address.
276  *
277  * RETURNS
278  *      Pointer: Success
279  *      NULL: Failure
280  */
281 static PSUBHEAP
282 HEAP_FindSubHeap(HEAP *heap,  /* [in] Heap pointer */
283                  LPCVOID ptr) /* [in] Address */
284 {
285     SUBHEAP *sub = &heap->subheap;
286     while (sub)
287     {
288         if (((char *)ptr >= (char *)sub) &&
289             ((char *)ptr < (char *)sub + sub->size)) return sub;
290         sub = sub->next;
291     }
292     return NULL;
293 }
294
295
296 /***********************************************************************
297  *           HEAP_Commit
298  *
299  * Make sure the heap storage is committed up to (not including) ptr.
300  */
301 static inline BOOL
302 HEAP_Commit(SUBHEAP *subheap,
303             void *ptr,
304             DWORD flags)
305 {
306     DWORD size = (DWORD)((char *)ptr - (char *)subheap);
307     NTSTATUS Status;
308     PVOID address;
309     ULONG commitsize;
310    
311     size = (size + COMMIT_MASK) & ~COMMIT_MASK;
312     if (size > subheap->size) size = subheap->size;
313     if (size <= subheap->commitSize) return TRUE;
314    
315     address = (PVOID)((char *)subheap + subheap->commitSize);
316     commitsize = size - subheap->commitSize;
317
318     if (!(flags & HEAP_NO_VALLOC))
319       {
320         Status = NtAllocateVirtualMemory(NtCurrentProcess(),
321                                          &address,
322                                          0,
323                                          &commitsize,
324                                          MEM_COMMIT,
325                                          PAGE_EXECUTE_READWRITE);
326         if (!NT_SUCCESS(Status))
327           {
328             WARN("Could not commit %08lx bytes at %08lx for heap %08lx\n",
329                  size - subheap->commitSize,
330                  (DWORD)((char *)subheap + subheap->commitSize),
331                  (DWORD)subheap->heap );
332             return FALSE;
333           }
334       }
335     subheap->commitSize += commitsize;
336     return TRUE;
337 }
338
339
340 /***********************************************************************
341  *           HEAP_Decommit
342  *
343  * If possible, decommit the heap storage from (including) 'ptr'.
344  */
345 static inline BOOL HEAP_Decommit( SUBHEAP *subheap, void *ptr, DWORD flags )
346 {
347     DWORD size = (DWORD)((char *)ptr - (char *)subheap);
348     PVOID address;
349     ULONG decommitsize;
350     NTSTATUS Status;
351     /* round to next block and add one full block */
352     size = ((size + COMMIT_MASK) & ~COMMIT_MASK) + COMMIT_MASK + 1;
353     if (size >= subheap->commitSize) return TRUE;
354    
355     address = (PVOID)((char *)subheap + size);
356     decommitsize = subheap->commitSize - size;
357
358     if (!(flags & HEAP_NO_VALLOC))
359       {
360         Status = ZwFreeVirtualMemory(NtCurrentProcess(),
361                                      &address,
362                                      &decommitsize,
363                                      MEM_DECOMMIT);
364         if (!NT_SUCCESS(Status));
365         {
366           WARN("Could not decommit %08lx bytes at %08lx for heap %08lx\n",
367                subheap->commitSize - size,
368                (DWORD)((char *)subheap + size),
369                (DWORD)subheap->heap );
370           return FALSE;
371         }
372       }
373     subheap->commitSize -= decommitsize;
374     return TRUE;
375 }
376
377
378 /***********************************************************************
379  *           HEAP_CreateFreeBlock
380  *
381  * Create a free block at a specified address. 'size' is the size of the
382  * whole block, including the new arena.
383  */
384 static void HEAP_CreateFreeBlock( SUBHEAP *subheap, void *ptr, DWORD size )
385 {
386     ARENA_FREE *pFree;
387     BOOL last;
388
389     /* Create a free arena */
390
391     pFree = (ARENA_FREE *)ptr;
392     pFree->threadId = (DWORD)NtCurrentTeb()->Cid.UniqueThread;
393     pFree->magic = ARENA_FREE_MAGIC;
394
395     /* If debugging, erase the freed block content */
396
397     if (TRACE_ON(heap))
398     {
399         char *pEnd = (char *)ptr + size;
400         if (pEnd > (char *)subheap + subheap->commitSize)
401             pEnd = (char *)subheap + subheap->commitSize;
402         if (pEnd > (char *)(pFree + 1))
403             memset( pFree + 1, ARENA_FREE_FILLER, pEnd - (char *)(pFree + 1) );
404     }
405
406     /* Check if next block is free also */
407
408     if (((char *)ptr + size < (char *)subheap + subheap->size) &&
409         (*(DWORD *)((char *)ptr + size) & ARENA_FLAG_FREE))
410     {
411         /* Remove the next arena from the free list */
412         ARENA_FREE *pNext = (ARENA_FREE *)((char *)ptr + size);
413         pNext->next->prev = pNext->prev;
414         pNext->prev->next = pNext->next;
415         size += (pNext->size & ARENA_SIZE_MASK) + sizeof(*pNext);
416         if (TRACE_ON(heap))
417             memset( pNext, ARENA_FREE_FILLER, sizeof(ARENA_FREE) );
418     }
419
420     /* Set the next block PREV_FREE flag and pointer */
421
422     last = ((char *)ptr + size >= (char *)subheap + subheap->size);
423     if (!last)
424     {
425         DWORD *pNext = (DWORD *)((char *)ptr + size);
426         *pNext |= ARENA_FLAG_PREV_FREE;
427         *(ARENA_FREE **)(pNext - 1) = pFree;
428     }
429
430     /* Last, insert the new block into the free list */
431
432     pFree->size = size - sizeof(*pFree);
433     HEAP_InsertFreeBlock( subheap->heap, pFree, last );
434 }
435
436
437 /***********************************************************************
438  *           HEAP_MakeInUseBlockFree
439  *
440  * Turn an in-use block into a free block. Can also decommit the end of
441  * the heap, and possibly even free the sub-heap altogether.
442  */
443 static void HEAP_MakeInUseBlockFree( SUBHEAP *subheap, ARENA_INUSE *pArena,
444                                      DWORD flags)
445 {
446     ARENA_FREE *pFree;
447     DWORD size = (pArena->size & ARENA_SIZE_MASK) + sizeof(*pArena);
448
449     /* Check if we can merge with previous block */
450
451     if (pArena->size & ARENA_FLAG_PREV_FREE)
452     {
453         pFree = *((ARENA_FREE **)pArena - 1);
454         size += (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
455         /* Remove it from the free list */
456         pFree->next->prev = pFree->prev;
457         pFree->prev->next = pFree->next;
458     }
459     else pFree = (ARENA_FREE *)pArena;
460
461     /* Create a free block */
462
463     HEAP_CreateFreeBlock( subheap, pFree, size );
464     size = (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
465     if ((char *)pFree + size < (char *)subheap + subheap->size)
466         return;  /* Not the last block, so nothing more to do */
467
468     /* Free the whole sub-heap if it's empty and not the original one */
469
470     if (((char *)pFree == (char *)subheap + subheap->headerSize) &&
471         (subheap != &subheap->heap->subheap))
472     {
473         SUBHEAP *pPrev = &subheap->heap->subheap;
474         /* Remove the free block from the list */
475         pFree->next->prev = pFree->prev;
476         pFree->prev->next = pFree->next;
477         /* Remove the subheap from the list */
478         while (pPrev && (pPrev->next != subheap)) pPrev = pPrev->next;
479         if (pPrev) pPrev->next = subheap->next;
480         /* Free the memory */
481         subheap->magic = 0;
482         if (!(flags & HEAP_NO_VALLOC))
483           {
484             ULONG dummySize = 0;
485             ZwFreeVirtualMemory(NtCurrentProcess(),
486                                 (PVOID*)&subheap,
487                                 &dummySize,
488                                 MEM_RELEASE);
489           }
490         return;
491     }
492     
493     /* Decommit the end of the heap */
494 }
495
496
497 /***********************************************************************
498  *           HEAP_ShrinkBlock
499  *
500  * Shrink an in-use block.
501  */
502 static void HEAP_ShrinkBlock(SUBHEAP *subheap, ARENA_INUSE *pArena, DWORD size)
503 {
504     if ((pArena->size & ARENA_SIZE_MASK) >= size + HEAP_MIN_BLOCK_SIZE)
505     {
506         HEAP_CreateFreeBlock( subheap, (char *)(pArena + 1) + size,
507                               (pArena->size & ARENA_SIZE_MASK) - size );
508         /* assign size plus previous arena flags */
509         pArena->size = size | (pArena->size & ~ARENA_SIZE_MASK);
510     }
511     else
512     {
513         /* Turn off PREV_FREE flag in next block */
514         char *pNext = (char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK);
515         if (pNext < (char *)subheap + subheap->size)
516             *(DWORD *)pNext &= ~ARENA_FLAG_PREV_FREE;
517     }
518 }
519
520 /***********************************************************************
521  *           HEAP_InitSubHeap
522  */
523 static BOOL HEAP_InitSubHeap( HEAP *heap, LPVOID address, DWORD flags,
524                                 DWORD commitSize, DWORD totalSize )
525 {
526     SUBHEAP *subheap = (SUBHEAP *)address;
527     WORD selector = 0;
528     FREE_LIST_ENTRY *pEntry;
529     int i;
530     NTSTATUS Status;
531    
532     /* Commit memory */
533     if (!(flags & HEAP_NO_VALLOC))
534       {
535         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
536                                          &address,
537                                          0,
538                                          (PULONG)&commitSize,
539                                          MEM_COMMIT,
540                                          PAGE_EXECUTE_READWRITE);
541         if (!NT_SUCCESS(Status))
542           {
543             WARN("Could not commit %08lx bytes for sub-heap %08lx\n",
544                  commitSize, (DWORD)address );
545             return FALSE;
546           }
547       }
548
549     /* Fill the sub-heap structure */
550
551     subheap = (SUBHEAP *)address;
552     subheap->heap       = heap;
553     subheap->selector   = selector;
554     subheap->size       = totalSize;
555     subheap->commitSize = commitSize;
556     subheap->magic      = SUBHEAP_MAGIC;
557
558     if ( subheap != (SUBHEAP *)heap )
559     {
560         /* If this is a secondary subheap, insert it into list */
561
562         subheap->headerSize = sizeof(SUBHEAP);
563         subheap->next       = heap->subheap.next;
564         heap->subheap.next  = subheap;
565     }
566     else
567     {
568         /* If this is a primary subheap, initialize main heap */
569
570         subheap->headerSize = sizeof(HEAP);
571         subheap->next       = NULL;
572         heap->next          = NULL;
573         heap->flags         = flags;
574         heap->magic         = HEAP_MAGIC;
575
576         /* Build the free lists */
577
578         for (i = 0, pEntry = heap->freeList; i < HEAP_NB_FREE_LISTS; i++, pEntry++)
579         {
580             pEntry->size           = HEAP_freeListSizes[i];
581             pEntry->arena.size     = 0 | ARENA_FLAG_FREE;
582             pEntry->arena.next     = i < HEAP_NB_FREE_LISTS-1 ?
583                          &heap->freeList[i+1].arena : &heap->freeList[0].arena;
584             pEntry->arena.prev     = i ? &heap->freeList[i-1].arena : 
585                                    &heap->freeList[HEAP_NB_FREE_LISTS-1].arena;
586             pEntry->arena.threadId = 0;
587             pEntry->arena.magic    = ARENA_FREE_MAGIC;
588         }
589
590         /* Initialize critical section */
591
592         RtlInitializeCriticalSection( &heap->critSection );
593     }
594
595     /* Create the first free block */
596
597     HEAP_CreateFreeBlock( subheap, (LPBYTE)subheap + subheap->headerSize, 
598                           subheap->size - subheap->headerSize );
599
600     return TRUE;
601 }
602
603 /***********************************************************************
604  *           HEAP_CreateSubHeap
605  *
606  * Create a sub-heap of the given size.
607  * If heap == NULL, creates a main heap.
608  */
609 static SUBHEAP *HEAP_CreateSubHeap(PVOID BaseAddress,  
610                                    HEAP *heap, DWORD flags, 
611                                    DWORD commitSize, DWORD totalSize )
612 {
613     LPVOID address;
614     NTSTATUS Status;
615    
616     /* Round-up sizes on a 64K boundary */
617
618     totalSize  = (totalSize + 0xffff) & 0xffff0000;
619     commitSize = (commitSize + 0xffff) & 0xffff0000;
620     if (!commitSize) commitSize = 0x10000;
621     if (totalSize < commitSize) totalSize = commitSize;
622
623     /* Allocate the memory block */    
624     address = BaseAddress;
625     if (!(flags & HEAP_NO_VALLOC))
626       {
627         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
628                                          &address,
629                                          0,
630                                          (PULONG)&totalSize,
631                                          MEM_RESERVE | MEM_COMMIT,
632                                          PAGE_EXECUTE_READWRITE);
633         if (!NT_SUCCESS(Status))
634           {
635             WARN("Could not VirtualAlloc %08lx bytes\n",
636                  totalSize );
637             return NULL;
638           }
639       }
640
641     /* Initialize subheap */
642
643     if (!HEAP_InitSubHeap( heap? heap : (HEAP *)address, 
644                            address, flags, commitSize, totalSize ))
645     {
646       if (address && !(flags & HEAP_NO_VALLOC))
647         {
648           ULONG dummySize = 0;
649           ZwFreeVirtualMemory(NtCurrentProcess(),
650                               &address,
651                               &dummySize,
652                               MEM_RELEASE);
653         }
654       return NULL;
655     }
656
657     return (SUBHEAP *)address;
658 }
659
660
661 /***********************************************************************
662  *           HEAP_FindFreeBlock
663  *
664  * Find a free block at least as large as the requested size, and make sure
665  * the requested size is committed.
666  */
667 static ARENA_FREE *HEAP_FindFreeBlock( HEAP *heap, DWORD size,
668                                        SUBHEAP **ppSubHeap )
669 {
670     SUBHEAP *subheap;
671     ARENA_FREE *pArena;
672     FREE_LIST_ENTRY *pEntry = heap->freeList;
673
674     /* Find a suitable free list, and in it find a block large enough */
675
676     while (pEntry->size < size) pEntry++;
677     pArena = pEntry->arena.next;
678     while (pArena != &heap->freeList[0].arena)
679     {
680         DWORD arena_size = (pArena->size & ARENA_SIZE_MASK) +
681                            sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
682         if (arena_size >= size)
683         {
684             subheap = HEAP_FindSubHeap( heap, pArena );
685             if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
686                                                + size + HEAP_MIN_BLOCK_SIZE,
687                               heap->flags))
688                 return NULL;
689             *ppSubHeap = subheap;
690             return pArena;
691         }
692
693         pArena = pArena->next;
694     }
695
696     /* If no block was found, attempt to grow the heap */
697
698     if (!(heap->flags & HEAP_GROWABLE))
699     {
700         WARN("Not enough space in heap %08lx for %08lx bytes\n",
701                  (DWORD)heap, size );
702         return NULL;
703     }
704     /* make sure that we have a big enough size *committed* to fit another
705      * last free arena in !
706      * So just one heap struct, one first free arena which will eventually
707      * get inuse, and HEAP_MIN_BLOCK_SIZE for the second free arena that
708      * might get assigned all remaining free space in HEAP_ShrinkBlock() */
709     size += sizeof(SUBHEAP) + sizeof(ARENA_FREE) + HEAP_MIN_BLOCK_SIZE;
710     if (!(subheap = HEAP_CreateSubHeap( NULL, heap, heap->flags, size,
711                                         max( HEAP_DEF_SIZE, size ) )))
712         return NULL;
713
714     TRACE("created new sub-heap %08lx of %08lx bytes for heap %08lx\n",
715                 (DWORD)subheap, size, (DWORD)heap );
716
717     *ppSubHeap = subheap;
718     return (ARENA_FREE *)(subheap + 1);
719 }
720
721
722 /***********************************************************************
723  *           HEAP_IsValidArenaPtr
724  *
725  * Check that the pointer is inside the range possible for arenas.
726  */
727 static BOOL HEAP_IsValidArenaPtr( HEAP *heap, void *ptr )
728 {
729     int i;
730     SUBHEAP *subheap = HEAP_FindSubHeap( heap, ptr );
731     if (!subheap) return FALSE;
732     if ((char *)ptr >= (char *)subheap + subheap->headerSize) return TRUE;
733     if (subheap != &heap->subheap) return FALSE;
734     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
735         if (ptr == (void *)&heap->freeList[i].arena) return TRUE;
736     return FALSE;
737 }
738
739
740 /***********************************************************************
741  *           HEAP_ValidateFreeArena
742  */
743 static BOOL HEAP_ValidateFreeArena( SUBHEAP *subheap, ARENA_FREE *pArena )
744 {
745     char *heapEnd = (char *)subheap + subheap->size;
746
747     /* Check magic number */
748     if (pArena->magic != ARENA_FREE_MAGIC)
749     {
750         ERR("Heap %08lx: invalid free arena magic for %08lx\n",
751                  (DWORD)subheap->heap, (DWORD)pArena );
752         return FALSE;
753     }
754     /* Check size flags */
755     if (!(pArena->size & ARENA_FLAG_FREE) ||
756         (pArena->size & ARENA_FLAG_PREV_FREE))
757     {
758         ERR("Heap %08lx: bad flags %lx for free arena %08lx\n",
759                  (DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
760     }
761     /* Check arena size */
762     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
763     {
764         ERR("Heap %08lx: bad size %08lx for free arena %08lx\n",
765                  (DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
766         return FALSE;
767     }
768     /* Check that next pointer is valid */
769     if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->next ))
770     {
771         ERR("Heap %08lx: bad next ptr %08lx for arena %08lx\n",
772                  (DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
773         return FALSE;
774     }
775     /* Check that next arena is free */
776     if (!(pArena->next->size & ARENA_FLAG_FREE) ||
777         (pArena->next->magic != ARENA_FREE_MAGIC))
778     { 
779         ERR("Heap %08lx: next arena %08lx invalid for %08lx\n", 
780                  (DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
781         return FALSE;
782     }
783     /* Check that prev pointer is valid */
784     if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->prev ))
785     {
786         ERR("Heap %08lx: bad prev ptr %08lx for arena %08lx\n",
787                  (DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
788         return FALSE;
789     }
790     /* Check that prev arena is free */
791     if (!(pArena->prev->size & ARENA_FLAG_FREE) ||
792         (pArena->prev->magic != ARENA_FREE_MAGIC))
793     { 
794         ERR("Heap %08lx: prev arena %08lx invalid for %08lx\n", 
795                  (DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
796         return FALSE;
797     }
798     /* Check that next block has PREV_FREE flag */
799     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd)
800     {
801         if (!(*(DWORD *)((char *)(pArena + 1) +
802             (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
803         {
804             ERR("Heap %08lx: free arena %08lx next block has no PREV_FREE flag\n",
805                      (DWORD)subheap->heap, (DWORD)pArena );
806             return FALSE;
807         }
808         /* Check next block back pointer */
809         if (*((ARENA_FREE **)((char *)(pArena + 1) +
810             (pArena->size & ARENA_SIZE_MASK)) - 1) != pArena)
811         {
812             ERR("Heap %08lx: arena %08lx has wrong back ptr %08lx\n",
813                      (DWORD)subheap->heap, (DWORD)pArena,
814                      *((DWORD *)((char *)(pArena+1)+ (pArena->size & ARENA_SIZE_MASK)) - 1));
815             return FALSE;
816         }
817     }
818     return TRUE;
819 }
820
821
822 /***********************************************************************
823  *           HEAP_ValidateInUseArena
824  */
825 static BOOL HEAP_ValidateInUseArena( SUBHEAP *subheap, ARENA_INUSE *pArena, BOOL quiet )
826 {
827     char *heapEnd = (char *)subheap + subheap->size;
828
829     /* Check magic number */
830     if (pArena->magic != ARENA_INUSE_MAGIC)
831     {
832         if (quiet == NOISY) {
833         ERR("Heap %08lx: invalid in-use arena magic for %08lx\n",
834                  (DWORD)subheap->heap, (DWORD)pArena );
835             if (TRACE_ON(heap))
836                HEAP_Dump( subheap->heap );
837         }  else if (WARN_ON(heap)) {
838             WARN("Heap %08lx: invalid in-use arena magic for %08lx\n",
839                  (DWORD)subheap->heap, (DWORD)pArena );
840             if (TRACE_ON(heap))
841                HEAP_Dump( subheap->heap );
842         }
843         return FALSE;
844     }
845     /* Check size flags */
846     if (pArena->size & ARENA_FLAG_FREE) 
847     {
848         ERR("Heap %08lx: bad flags %lx for in-use arena %08lx\n",
849                  (DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
850         return FALSE;
851     }
852     /* Check arena size */
853     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
854     {
855         ERR("Heap %08lx: bad size %08lx for in-use arena %08lx\n",
856                  (DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
857         return FALSE;
858     }
859     /* Check next arena PREV_FREE flag */
860     if (((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd) &&
861         (*(DWORD *)((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
862     {
863         ERR("Heap %08lx: in-use arena %08lx next block has PREV_FREE flag\n",
864                  (DWORD)subheap->heap, (DWORD)pArena );
865         return FALSE;
866     }
867     /* Check prev free arena */
868     if (pArena->size & ARENA_FLAG_PREV_FREE)
869     {
870         ARENA_FREE *pPrev = *((ARENA_FREE **)pArena - 1);
871         /* Check prev pointer */
872         if (!HEAP_IsValidArenaPtr( subheap->heap, pPrev ))
873         {
874             ERR("Heap %08lx: bad back ptr %08lx for arena %08lx\n",
875                     (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
876             return FALSE;
877         }
878         /* Check that prev arena is free */
879         if (!(pPrev->size & ARENA_FLAG_FREE) ||
880             (pPrev->magic != ARENA_FREE_MAGIC))
881         { 
882             ERR("Heap %08lx: prev arena %08lx invalid for in-use %08lx\n", 
883                      (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
884             return FALSE;
885         }
886         /* Check that prev arena is really the previous block */
887         if ((char *)(pPrev + 1) + (pPrev->size & ARENA_SIZE_MASK) != (char *)pArena)
888         {
889             ERR("Heap %08lx: prev arena %08lx is not prev for in-use %08lx\n",
890                      (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
891             return FALSE;
892         }
893     }
894     return TRUE;
895 }
896
897
898 /***********************************************************************
899  *           HEAP_IsInsideHeap
900  * Checks whether the pointer points to a block inside a given heap.
901  *
902  * NOTES
903  *      Should this return BOOL32?
904  *
905  * RETURNS
906  *      !0: Success
907  *      0: Failure
908  */
909 int HEAP_IsInsideHeap(
910     HANDLE heap, /* [in] Heap */
911     DWORD flags,   /* [in] Flags */
912     LPCVOID ptr    /* [in] Pointer */
913 ) {
914     HEAP *heapPtr = HEAP_GetPtr( heap );
915     SUBHEAP *subheap;
916     int ret;
917
918     /* Validate the parameters */
919
920     if (!heapPtr) return 0;
921     flags |= heapPtr->flags;
922     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
923     ret = (((subheap = HEAP_FindSubHeap( heapPtr, ptr )) != NULL) &&
924            (((char *)ptr >= (char *)subheap + subheap->headerSize
925                               + sizeof(ARENA_INUSE))));
926     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
927     return ret;
928 }
929
930
931
932
933 /***********************************************************************
934  *           HEAP_IsRealArena  [Internal]
935  * Validates a block is a valid arena.
936  *
937  * RETURNS
938  *      TRUE: Success
939  *      FALSE: Failure
940  */
941 static BOOL HEAP_IsRealArena(
942               HANDLE heap,   /* [in] Handle to the heap */
943               DWORD flags,   /* [in] Bit flags that control access during operation */
944               LPCVOID block, /* [in] Optional pointer to memory block to validate */
945               BOOL quiet     /* [in] Flag - if true, HEAP_ValidateInUseArena
946                               *             does not complain    */
947 ) {
948     SUBHEAP *subheap;
949     HEAP *heapPtr = (HEAP *)(heap);
950     BOOL ret = TRUE;
951
952     if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
953     {
954         ERR("Invalid heap %08x!\n", heap );
955         return FALSE;
956     }
957
958     flags &= HEAP_NO_SERIALIZE;
959     flags |= heapPtr->flags;
960     /* calling HeapLock may result in infinite recursion, so do the critsect directly */
961     if (!(flags & HEAP_NO_SERIALIZE))
962         RtlEnterCriticalSection( &heapPtr->critSection );
963
964     if (block)
965     {
966         /* Only check this single memory block */
967
968         /* The following code is really HEAP_IsInsideHeap   *
969          * with serialization already done.                 */
970         if (!(subheap = HEAP_FindSubHeap( heapPtr, block )) ||
971             ((char *)block < (char *)subheap + subheap->headerSize
972                               + sizeof(ARENA_INUSE)))
973         {
974             if (quiet == NOISY) 
975             {
976                 ERR("Heap %08lx: block %08lx is not inside heap\n",
977                      (DWORD)heap, (DWORD)block );
978             }
979             else if (WARN_ON(heap)) 
980             {
981                 WARN("Heap %08lx: block %08lx is not inside heap\n",
982                      (DWORD)heap, (DWORD)block );
983             }
984             ret = FALSE;
985         } else
986             ret = HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)block - 1, quiet );
987
988         if (!(flags & HEAP_NO_SERIALIZE))
989             RtlLeaveCriticalSection( &heapPtr->critSection );
990         return ret;
991     }
992
993     subheap = &heapPtr->subheap;
994     while (subheap && ret)
995     {
996         char *ptr = (char *)subheap + subheap->headerSize;
997         while (ptr < (char *)subheap + subheap->size)
998         {
999             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1000             {
1001                 if (!HEAP_ValidateFreeArena( subheap, (ARENA_FREE *)ptr )) {
1002                     ret = FALSE;
1003                     break;
1004                 }
1005                 ptr += sizeof(ARENA_FREE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1006             }
1007             else
1008             {
1009                 if (!HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)ptr, NOISY )) {
1010                     ret = FALSE;
1011                     break;
1012                 }
1013                 ptr += sizeof(ARENA_INUSE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1014             }
1015         }
1016         subheap = subheap->next;
1017     }
1018
1019     if (!(flags & HEAP_NO_SERIALIZE))
1020         RtlLeaveCriticalSection( &heapPtr->critSection );
1021     return ret;
1022 }
1023
1024
1025 /***********************************************************************
1026  *           HeapCreate   (KERNEL32.336)
1027  * RETURNS
1028  *      Handle of heap: Success
1029  *      NULL: Failure
1030  *
1031  * @implemented
1032  */
1033 HANDLE STDCALL
1034 RtlCreateHeap(ULONG flags,
1035               PVOID BaseAddress,
1036               ULONG maxSize,
1037               ULONG initialSize,
1038               PVOID Unknown,
1039               PRTL_HEAP_DEFINITION Definition)
1040 {
1041     SUBHEAP *subheap;
1042     ULONG i;
1043    
1044     /* Allocate the heap block */
1045
1046     if (!maxSize)
1047     {
1048         maxSize = HEAP_DEF_SIZE;
1049         flags |= HEAP_GROWABLE;
1050     }
1051     if (!(subheap = HEAP_CreateSubHeap( BaseAddress, NULL, flags, initialSize, maxSize )))
1052     {
1053         return 0;
1054     }
1055
1056    RtlEnterCriticalSection (&RtlpProcessHeapsListLock);
1057    for (i = 0; i < NtCurrentPeb ()->NumberOfHeaps; i++)
1058      {
1059         if (NtCurrentPeb ()->ProcessHeaps[i] == NULL)
1060           {
1061              NtCurrentPeb()->ProcessHeaps[i] = (PVOID)subheap;
1062              break;
1063           }
1064      }
1065    RtlLeaveCriticalSection (&RtlpProcessHeapsListLock);
1066
1067     return (HANDLE)subheap;
1068 }
1069
1070 /***********************************************************************
1071  *           HeapDestroy   (KERNEL32.337)
1072  * RETURNS
1073  *      TRUE: Success
1074  *      FALSE: Failure
1075  *
1076  * @implemented
1077  */
1078 BOOL STDCALL
1079 RtlDestroyHeap(HANDLE heap) /* [in] Handle of heap */
1080 {
1081     HEAP *heapPtr = HEAP_GetPtr( heap );
1082     SUBHEAP *subheap;
1083     ULONG i, flags;
1084    
1085     TRACE("%08x\n", heap );
1086     if (!heapPtr) return FALSE;
1087
1088    RtlEnterCriticalSection (&RtlpProcessHeapsListLock);
1089    for (i = 0; i < NtCurrentPeb ()->NumberOfHeaps; i++)
1090      {
1091         if (NtCurrentPeb ()->ProcessHeaps[i] == heap)
1092           {
1093              NtCurrentPeb()->ProcessHeaps[i] = NULL;
1094              break;
1095           }
1096      }
1097    RtlLeaveCriticalSection (&RtlpProcessHeapsListLock);
1098    
1099     RtlDeleteCriticalSection( &heapPtr->critSection );
1100     subheap = &heapPtr->subheap;
1101     // We must save the flags. The first subheap is located after 
1102     // the heap structure. If we release the first subheap, 
1103     // we release also the heap structure.
1104     flags = heapPtr->flags;
1105     while (subheap)
1106     {
1107         SUBHEAP *next = subheap->next;
1108
1109         if (!(flags & HEAP_NO_VALLOC))
1110           {
1111             ULONG dummySize = 0;
1112             ZwFreeVirtualMemory(NtCurrentProcess(),
1113                                 (PVOID*)&subheap,
1114                                 &dummySize,
1115                                 MEM_RELEASE);
1116           }
1117         subheap = next;
1118     }
1119     return TRUE;
1120 }
1121
1122
1123 /***********************************************************************
1124  *           HeapAlloc   (KERNEL32.334)
1125  * RETURNS
1126  *      Pointer to allocated memory block
1127  *      NULL: Failure
1128  *
1129  * @implemented
1130  */
1131 PVOID STDCALL
1132 RtlAllocateHeap(HANDLE heap,   /* [in] Handle of private heap block */
1133                 ULONG flags,   /* [in] Heap allocation control flags */
1134                 ULONG size)    /* [in] Number of bytes to allocate */
1135 {
1136     ARENA_FREE *pArena;
1137     ARENA_INUSE *pInUse;
1138     SUBHEAP *subheap;
1139     HEAP *heapPtr = HEAP_GetPtr( heap );
1140
1141     /* Validate the parameters */
1142
1143     if (!heapPtr) 
1144     {
1145         if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1146         return NULL;
1147     }
1148     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY;
1149     flags |= heapPtr->flags;
1150     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1151     size = (size + 3) & ~3;
1152     if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
1153
1154     /* Locate a suitable free block */
1155
1156     if (!(pArena = HEAP_FindFreeBlock( heapPtr, size, &subheap )))
1157     {
1158         TRACE("(%08x,%08lx,%08lx): returning NULL\n",
1159                   heap, flags, size  );
1160         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1161         if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1162         return NULL;
1163     }
1164
1165     /* Remove the arena from the free list */
1166
1167     pArena->next->prev = pArena->prev;
1168     pArena->prev->next = pArena->next;
1169
1170     /* Build the in-use arena */
1171
1172     pInUse = (ARENA_INUSE *)pArena;
1173     pInUse->size      = (pInUse->size & ~ARENA_FLAG_FREE)
1174                         + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1175     pInUse->callerEIP = GET_EIP();
1176     pInUse->threadId  = (DWORD)NtCurrentTeb()->Cid.UniqueThread;
1177     pInUse->magic     = ARENA_INUSE_MAGIC;
1178
1179     /* Shrink the block */
1180
1181     HEAP_ShrinkBlock( subheap, pInUse, size );
1182
1183     if (flags & HEAP_ZERO_MEMORY)
1184         memset( pInUse + 1, 0, pInUse->size & ARENA_SIZE_MASK );
1185     else if (TRACE_ON(heap))
1186         memset( pInUse + 1, ARENA_INUSE_FILLER, pInUse->size & ARENA_SIZE_MASK );
1187
1188     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1189
1190     TRACE("(%08x,%08lx,%08lx): returning %08lx\n",
1191                   heap, flags, size, (DWORD)(pInUse + 1) );
1192     return (LPVOID)(pInUse + 1);
1193 }
1194
1195
1196 /***********************************************************************
1197  *           HeapFree   (KERNEL32.338)
1198  * RETURNS
1199  *      TRUE: Success
1200  *      FALSE: Failure
1201  *
1202  * @implemented
1203  */
1204 BOOLEAN STDCALL RtlFreeHeap(
1205               HANDLE heap, /* [in] Handle of heap */
1206               ULONG flags,   /* [in] Heap freeing flags */
1207               PVOID ptr     /* [in] Address of memory to free */
1208 ) {
1209     ARENA_INUSE *pInUse;
1210     SUBHEAP *subheap;
1211     HEAP *heapPtr = HEAP_GetPtr( heap );
1212
1213     /* Validate the parameters */
1214
1215     if (!heapPtr) return FALSE;
1216     if (!ptr)  /* Freeing a NULL ptr is doesn't indicate an error in Win2k */
1217     {
1218         WARN("(%08x,%08lx,%08lx): asked to free NULL\n",
1219                    heap, flags, (DWORD)ptr );
1220         return TRUE;
1221     }
1222
1223     flags &= HEAP_NO_SERIALIZE;
1224     flags |= heapPtr->flags;
1225     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1226     if (!HEAP_IsRealArena( heap, HEAP_NO_SERIALIZE, ptr, QUIET ))
1227     {
1228         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1229         TRACE("(%08x,%08lx,%08lx): returning FALSE\n",
1230                       heap, flags, (DWORD)ptr );
1231         return FALSE;
1232     }
1233
1234     /* Turn the block into a free block */
1235
1236     pInUse  = (ARENA_INUSE *)ptr - 1;
1237     subheap = HEAP_FindSubHeap( heapPtr, pInUse );
1238     HEAP_MakeInUseBlockFree( subheap, pInUse, heapPtr->flags );
1239
1240     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1241
1242     TRACE("(%08x,%08lx,%08lx): returning TRUE\n",
1243                   heap, flags, (DWORD)ptr );
1244     return TRUE;
1245 }
1246
1247
1248 /***********************************************************************
1249  *           HeapReAlloc   (KERNEL32.340)
1250  * RETURNS
1251  *      Pointer to reallocated memory block
1252  *      NULL: Failure
1253  *
1254  * @implemented
1255  */
1256 LPVOID STDCALL RtlReAllocateHeap(
1257               HANDLE heap, /* [in] Handle of heap block */
1258               DWORD flags,   /* [in] Heap reallocation flags */
1259               LPVOID ptr,    /* [in] Address of memory to reallocate */
1260               DWORD size     /* [in] Number of bytes to reallocate */
1261 ) {
1262     ARENA_INUSE *pArena;
1263     DWORD oldSize;
1264     HEAP *heapPtr;
1265     SUBHEAP *subheap;
1266
1267     if (!ptr) return RtlAllocateHeap( heap, flags, size );  /* FIXME: correct? */
1268     if (!(heapPtr = HEAP_GetPtr( heap ))) return FALSE;
1269
1270     /* Validate the parameters */
1271
1272     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY |
1273              HEAP_REALLOC_IN_PLACE_ONLY;
1274     flags |= heapPtr->flags;
1275     size = (size + 3) & ~3;
1276     if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
1277
1278     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1279     if (!HEAP_IsRealArena( heap, HEAP_NO_SERIALIZE, ptr, QUIET ))
1280     {
1281         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1282         TRACE("(%08x,%08lx,%08lx,%08lx): returning NULL\n",
1283                       heap, flags, (DWORD)ptr, size );
1284         if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1285         return NULL;
1286     }
1287
1288     /* Check if we need to grow the block */
1289
1290     pArena = (ARENA_INUSE *)ptr - 1;
1291     pArena->threadId = (DWORD)NtCurrentTeb()->Cid.UniqueThread;
1292
1293     subheap = HEAP_FindSubHeap( heapPtr, pArena );
1294     oldSize = (pArena->size & ARENA_SIZE_MASK);
1295     if (size > oldSize)
1296     {
1297         char *pNext = (char *)(pArena + 1) + oldSize;
1298         if ((pNext < (char *)subheap + subheap->size) &&
1299             (*(DWORD *)pNext & ARENA_FLAG_FREE) &&
1300             (oldSize + (*(DWORD *)pNext & ARENA_SIZE_MASK) + sizeof(ARENA_FREE) >= size))
1301         {
1302             /* The next block is free and large enough */
1303             ARENA_FREE *pFree = (ARENA_FREE *)pNext;
1304             pFree->next->prev = pFree->prev;
1305             pFree->prev->next = pFree->next;
1306             pArena->size += (pFree->size & ARENA_SIZE_MASK) + sizeof(*pFree);
1307             if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
1308                                                + size + HEAP_MIN_BLOCK_SIZE,
1309                               heapPtr->flags))
1310             {
1311                 if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1312                 if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1313                 return NULL;
1314             }
1315             HEAP_ShrinkBlock( subheap, pArena, size );
1316         }
1317         else  /* Do it the hard way */
1318         {
1319             ARENA_FREE *pNew;
1320             ARENA_INUSE *pInUse;
1321             SUBHEAP *newsubheap;
1322
1323             if ((flags & HEAP_REALLOC_IN_PLACE_ONLY) ||
1324                 !(pNew = HEAP_FindFreeBlock( heapPtr, size, &newsubheap )))
1325             {
1326                 if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1327                 if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1328                 return NULL;
1329             }
1330
1331             /* Build the in-use arena */
1332
1333             pNew->next->prev = pNew->prev;
1334             pNew->prev->next = pNew->next;
1335             pInUse = (ARENA_INUSE *)pNew;
1336             pInUse->size     = (pInUse->size & ~ARENA_FLAG_FREE)
1337                                + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1338             pInUse->threadId = (DWORD)NtCurrentTeb()->Cid.UniqueThread;
1339             pInUse->magic    = ARENA_INUSE_MAGIC;
1340             HEAP_ShrinkBlock( newsubheap, pInUse, size );
1341             memcpy( pInUse + 1, pArena + 1, oldSize );
1342
1343             /* Free the previous block */
1344
1345             HEAP_MakeInUseBlockFree( subheap, pArena, flags );
1346             subheap = newsubheap;
1347             pArena  = pInUse;
1348         }
1349     }
1350     else HEAP_ShrinkBlock( subheap, pArena, size );  /* Shrink the block */
1351
1352     /* Clear the extra bytes if needed */
1353
1354     if (size > oldSize)
1355     {
1356         if (flags & HEAP_ZERO_MEMORY)
1357             memset( (char *)(pArena + 1) + oldSize, 0,
1358                     (pArena->size & ARENA_SIZE_MASK) - oldSize );
1359         else if (TRACE_ON(heap))
1360             memset( (char *)(pArena + 1) + oldSize, ARENA_INUSE_FILLER,
1361                     (pArena->size & ARENA_SIZE_MASK) - oldSize );
1362     }
1363
1364     /* Return the new arena */
1365
1366     pArena->callerEIP = GET_EIP();
1367     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1368
1369     TRACE("(%08x,%08lx,%08lx,%08lx): returning %08lx\n",
1370                   heap, flags, (DWORD)ptr, size, (DWORD)(pArena + 1) );
1371     return (LPVOID)(pArena + 1);
1372 }
1373
1374
1375 /***********************************************************************
1376  *           HeapCompact   (KERNEL32.335)
1377  *
1378  * @unimplemented
1379  */
1380 DWORD STDCALL RtlCompactHeap( HANDLE heap, DWORD flags )
1381 {
1382     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1383     return 0;
1384 }
1385
1386
1387 /***********************************************************************
1388  *           HeapLock   (KERNEL32.339)
1389  * Attempts to acquire the critical section object for a specified heap.
1390  *
1391  * RETURNS
1392  *      TRUE: Success
1393  *      FALSE: Failure
1394  *
1395  * @implemented
1396  */
1397 BOOL STDCALL RtlLockHeap(
1398               HANDLE heap /* [in] Handle of heap to lock for exclusive access */
1399 ) {
1400     HEAP *heapPtr = HEAP_GetPtr( heap );
1401     if (!heapPtr) return FALSE;
1402     RtlEnterCriticalSection( &heapPtr->critSection );
1403     return TRUE;
1404 }
1405
1406
1407 /***********************************************************************
1408  *           HeapUnlock   (KERNEL32.342)
1409  * Releases ownership of the critical section object.
1410  *
1411  * RETURNS
1412  *      TRUE: Success
1413  *      FALSE: Failure
1414  *
1415  * @implemented
1416  */
1417 BOOL STDCALL RtlUnlockHeap(
1418               HANDLE heap /* [in] Handle to the heap to unlock */
1419 ) {
1420     HEAP *heapPtr = HEAP_GetPtr( heap );
1421     if (!heapPtr) return FALSE;
1422     RtlLeaveCriticalSection( &heapPtr->critSection );
1423     return TRUE;
1424 }
1425
1426
1427 /***********************************************************************
1428  *           HeapSize   (KERNEL32.341)
1429  * RETURNS
1430  *      Size in bytes of allocated memory
1431  *      0xffffffff: Failure
1432  *
1433  * @implemented
1434  */
1435 DWORD STDCALL RtlSizeHeap(
1436              HANDLE heap, /* [in] Handle of heap */
1437              DWORD flags,   /* [in] Heap size control flags */
1438              LPVOID ptr     /* [in] Address of memory to return size for */
1439 ) {
1440     DWORD ret;
1441     HEAP *heapPtr = HEAP_GetPtr( heap );
1442
1443     if (!heapPtr) return FALSE;
1444     flags &= HEAP_NO_SERIALIZE;
1445     flags |= heapPtr->flags;
1446     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1447     if (!HEAP_IsRealArena( heap, HEAP_NO_SERIALIZE, ptr, QUIET ))
1448     {
1449         SetLastError( ERROR_INVALID_PARAMETER );
1450         ret = 0xffffffff;
1451     }
1452     else
1453     {
1454         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr - 1;
1455         ret = pArena->size & ARENA_SIZE_MASK;
1456     }
1457     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1458
1459     TRACE("(%08x,%08lx,%08lx): returning %08lx\n",
1460                   heap, flags, (DWORD)ptr, ret );
1461     return ret;
1462 }
1463
1464
1465 /***********************************************************************
1466  *           HeapValidate   (KERNEL32.343)
1467  * Validates a specified heap.
1468  *
1469  * NOTES
1470  *      Flags is ignored.
1471  *
1472  * RETURNS
1473  *      TRUE: Success
1474  *      FALSE: Failure
1475  *
1476  * @implemented
1477  */
1478 BOOL STDCALL RtlValidateHeap(
1479               HANDLE heap, /* [in] Handle to the heap */
1480               DWORD flags,   /* [in] Bit flags that control access during operation */
1481               PVOID block  /* [in] Optional pointer to memory block to validate */
1482 ) {
1483
1484     HEAP *heapPtr = HEAP_GetPtr( heap );
1485     if (!heapPtr) return FALSE;
1486     return HEAP_IsRealArena( heapPtr, flags, block, QUIET );
1487 }
1488
1489
1490 /***********************************************************************
1491  *           HeapWalk   (KERNEL32.344)
1492  * Enumerates the memory blocks in a specified heap.
1493  * See HEAP_Dump() for info on heap structure.
1494  *
1495  * TODO
1496  *   - handling of PROCESS_HEAP_ENTRY_MOVEABLE and
1497  *     PROCESS_HEAP_ENTRY_DDESHARE (needs heap.c support)
1498  *
1499  * RETURNS
1500  *      TRUE: Success
1501  *      FALSE: Failure
1502  */
1503 #if 0
1504 BOOL STDCALL HeapWalk(
1505               HANDLE heap,               /* [in]  Handle to heap to enumerate */
1506               LPPROCESS_HEAP_ENTRY entry /* [out] Pointer to structure of enumeration info */
1507 ) {
1508     HEAP *heapPtr = HEAP_GetPtr(heap);
1509     SUBHEAP *sub, *currentheap = NULL;
1510     BOOL ret = FALSE;
1511     char *ptr;
1512     int region_index = 0;
1513
1514     if (!heapPtr || !entry)
1515     {
1516         SetLastError(ERROR_INVALID_PARAMETER);
1517         return FALSE;
1518     }
1519
1520     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1521
1522     /* set ptr to the next arena to be examined */
1523
1524     if (!entry->lpData) /* first call (init) ? */
1525     {
1526         TRACE("begin walking of heap 0x%08x.\n", heap);
1527         /*HEAP_Dump(heapPtr);*/
1528         currentheap = &heapPtr->subheap;
1529         ptr = (char*)currentheap + currentheap->headerSize;
1530     }
1531     else
1532     {
1533         ptr = entry->lpData;
1534         sub = &heapPtr->subheap;
1535         while (sub)
1536         {
1537             if (((char *)ptr >= (char *)sub) &&
1538                 ((char *)ptr < (char *)sub + sub->size))
1539             {
1540                 currentheap = sub;
1541                 break;
1542             }
1543             sub = sub->next;
1544             region_index++;
1545         }
1546         if (currentheap == NULL)
1547         {
1548             ERR("no matching subheap found, shouldn't happen !\n");
1549             SetLastError(ERROR_NO_MORE_ITEMS);
1550             goto HW_end;
1551         }
1552
1553         ptr += entry->cbData; /* point to next arena */
1554         if (ptr > (char *)currentheap + currentheap->size - 1)
1555         {   /* proceed with next subheap */
1556             if (!(currentheap = currentheap->next))
1557             {  /* successfully finished */
1558                 TRACE("end reached.\n");
1559                 SetLastError(ERROR_NO_MORE_ITEMS);
1560                 goto HW_end;
1561             }
1562             ptr = (char*)currentheap + currentheap->headerSize;
1563         }
1564     }
1565
1566     entry->wFlags = 0;
1567     if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1568     {
1569         ARENA_FREE *pArena = (ARENA_FREE *)ptr;
1570
1571         /*TRACE("free, magic: %04x\n", pArena->magic);*/
1572
1573         entry->lpData = pArena + 1;
1574         entry->cbData = pArena->size & ARENA_SIZE_MASK;
1575         entry->cbOverhead = sizeof(ARENA_FREE);
1576         entry->wFlags = PROCESS_HEAP_UNCOMMITTED_RANGE;
1577     }
1578     else
1579     {
1580         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
1581
1582         /*TRACE("busy, magic: %04x\n", pArena->magic);*/
1583         
1584         entry->lpData = pArena + 1;
1585         entry->cbData = pArena->size & ARENA_SIZE_MASK;
1586         entry->cbOverhead = sizeof(ARENA_INUSE);
1587         entry->wFlags = PROCESS_HEAP_ENTRY_BUSY;
1588         /* FIXME: can't handle PROCESS_HEAP_ENTRY_MOVEABLE
1589         and PROCESS_HEAP_ENTRY_DDESHARE yet */
1590     }
1591
1592     entry->iRegionIndex = region_index;
1593
1594     /* first element of heap ? */
1595     if (ptr == (char *)(currentheap + currentheap->headerSize))
1596     {
1597         entry->wFlags |= PROCESS_HEAP_REGION;
1598         entry->Foo.Region.dwCommittedSize = currentheap->commitSize;
1599         entry->Foo.Region.dwUnCommittedSize =
1600                 currentheap->size - currentheap->commitSize;
1601         entry->Foo.Region.lpFirstBlock = /* first valid block */
1602                 currentheap + currentheap->headerSize;
1603         entry->Foo.Region.lpLastBlock  = /* first invalid block */
1604                 currentheap + currentheap->size;
1605     }
1606     ret = TRUE;
1607
1608 HW_end:
1609     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1610
1611     return ret;
1612 }
1613 #endif
1614
1615 VOID
1616 RtlInitializeHeapManager(VOID)
1617 {
1618    PPEB Peb;
1619    
1620    Peb = NtCurrentPeb();
1621    
1622    Peb->NumberOfHeaps = 0;
1623    Peb->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof(PEB)) / sizeof(HANDLE);
1624    Peb->ProcessHeaps = (PVOID)Peb + sizeof(PEB);
1625    
1626    RtlInitializeCriticalSection(&RtlpProcessHeapsListLock);
1627 }
1628
1629
1630 /*
1631  * @implemented
1632  */
1633 NTSTATUS STDCALL
1634 RtlEnumProcessHeaps(DWORD STDCALL(*func)(void*,LONG),
1635                     LONG lParam)
1636 {
1637    NTSTATUS Status = STATUS_SUCCESS;
1638    ULONG i;
1639
1640    RtlEnterCriticalSection(&RtlpProcessHeapsListLock);
1641
1642    for (i = 0; i < NtCurrentPeb()->NumberOfHeaps; i++)
1643      {
1644         Status = func(NtCurrentPeb()->ProcessHeaps[i],lParam);
1645         if (!NT_SUCCESS(Status))
1646           break;
1647      }
1648
1649    RtlLeaveCriticalSection(&RtlpProcessHeapsListLock);
1650
1651    return Status;
1652 }
1653
1654
1655 /*
1656  * @implemented
1657  */
1658 ULONG STDCALL
1659 RtlGetProcessHeaps(ULONG HeapCount,
1660                    HANDLE *HeapArray)
1661 {
1662    ULONG Result = 0;
1663
1664    RtlEnterCriticalSection(&RtlpProcessHeapsListLock);
1665
1666    if (NtCurrentPeb()->NumberOfHeaps <= HeapCount)
1667      {
1668         Result = NtCurrentPeb()->NumberOfHeaps;
1669         memmove(HeapArray,
1670                 NtCurrentPeb()->ProcessHeaps,
1671                 Result * sizeof(HANDLE));
1672      }
1673
1674    RtlLeaveCriticalSection (&RtlpProcessHeapsListLock);
1675
1676    return Result;
1677 }
1678
1679
1680 /*
1681  * @implemented
1682  */
1683 BOOLEAN STDCALL
1684 RtlValidateProcessHeaps(VOID)
1685 {
1686    HANDLE Heaps[128];
1687    BOOLEAN Result = TRUE;
1688    ULONG HeapCount;
1689    ULONG i;
1690
1691    HeapCount = RtlGetProcessHeaps(128, Heaps);
1692    for (i = 0; i < HeapCount; i++)
1693      {
1694         if (!RtlValidateHeap(Heaps[i], 0, NULL))
1695           Result = FALSE;
1696      }
1697
1698    return Result;
1699 }
1700
1701 /* EOF */