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