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