Bugfix for EnumPrinters level 2.
[wine] / memory / heap.c
1 /*
2  * Win32 heap functions
3  *
4  * Copyright 1996 Alexandre Julliard
5  * Copyright 1998 Ulrich Weigand
6  */
7
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include "wine/winbase16.h"
12 #include "wine/winestring.h"
13 #include "selectors.h"
14 #include "global.h"
15 #include "winbase.h"
16 #include "winerror.h"
17 #include "winnt.h"
18 #include "heap.h"
19 #include "toolhelp.h"
20 #include "debug.h"
21
22 DEFAULT_DEBUG_CHANNEL(heap)
23
24 /* Note: the heap data structures are based on what Pietrek describes in his
25  * book 'Windows 95 System Programming Secrets'. The layout is not exactly
26  * the same, but could be easily adapted if it turns out some programs
27  * require it.
28  */
29
30 typedef struct tagARENA_INUSE
31 {
32     DWORD  size;                    /* Block size; must be the first field */
33     WORD   threadId;                /* Allocating thread id */
34     WORD   magic;                   /* Magic number */
35     void  *callerEIP;               /* EIP of caller upon allocation */
36 } ARENA_INUSE;
37
38 typedef struct tagARENA_FREE
39 {
40     DWORD                 size;     /* Block size; must be the first field */
41     WORD                  threadId; /* Freeing thread id */
42     WORD                  magic;    /* Magic number */
43     struct tagARENA_FREE *next;     /* Next free arena */
44     struct tagARENA_FREE *prev;     /* Prev free arena */
45 } ARENA_FREE;
46
47 #define ARENA_FLAG_FREE        0x00000001  /* flags OR'ed with arena size */
48 #define ARENA_FLAG_PREV_FREE   0x00000002
49 #define ARENA_SIZE_MASK        0xfffffffc
50 #define ARENA_INUSE_MAGIC      0x4842      /* Value for arena 'magic' field */
51 #define ARENA_FREE_MAGIC       0x4846      /* Value for arena 'magic' field */
52
53 #define ARENA_INUSE_FILLER     0x55
54 #define ARENA_FREE_FILLER      0xaa
55
56 #define HEAP_NB_FREE_LISTS   4   /* Number of free lists */
57
58 /* Max size of the blocks on the free lists */
59 static const DWORD HEAP_freeListSizes[HEAP_NB_FREE_LISTS] =
60 {
61     0x20, 0x80, 0x200, 0xffffffff
62 };
63
64 typedef struct
65 {
66     DWORD       size;
67     ARENA_FREE  arena;
68 } FREE_LIST_ENTRY;
69
70 struct tagHEAP;
71
72 typedef struct tagSUBHEAP
73 {
74     DWORD               size;       /* Size of the whole sub-heap */
75     DWORD               commitSize; /* Committed size of the sub-heap */
76     DWORD               headerSize; /* Size of the heap header */
77     struct tagSUBHEAP  *next;       /* Next sub-heap */
78     struct tagHEAP     *heap;       /* Main heap structure */
79     DWORD               magic;      /* Magic number */
80     WORD                selector;   /* Selector for HEAP_WINE_SEGPTR heaps */
81 } SUBHEAP;
82
83 #define SUBHEAP_MAGIC    ((DWORD)('S' | ('U'<<8) | ('B'<<16) | ('H'<<24)))
84
85 typedef struct tagHEAP
86 {
87     SUBHEAP          subheap;       /* First sub-heap */
88     struct tagHEAP  *next;          /* Next heap for this process */
89     FREE_LIST_ENTRY  freeList[HEAP_NB_FREE_LISTS];  /* Free lists */
90     CRITICAL_SECTION critSection;   /* Critical section for serialization */
91     DWORD            flags;         /* Heap flags */
92     DWORD            magic;         /* Magic number */
93 } HEAP;
94
95 #define HEAP_MAGIC       ((DWORD)('H' | ('E'<<8) | ('A'<<16) | ('P'<<24)))
96
97 #define HEAP_DEF_SIZE        0x110000   /* Default heap size = 1Mb + 64Kb */
98 #define HEAP_MIN_BLOCK_SIZE  (8+sizeof(ARENA_FREE))  /* Min. heap block size */
99
100 HANDLE SystemHeap = 0;
101 HANDLE SegptrHeap = 0;
102
103
104 #ifdef __GNUC__
105 #define GET_EIP()    (__builtin_return_address(0))
106 #define SET_EIP(ptr) ((ARENA_INUSE*)(ptr) - 1)->callerEIP = GET_EIP()
107 #else
108 #define GET_EIP()    0
109 #define SET_EIP(ptr) /* nothing */
110 #endif  /* __GNUC__ */
111
112 /***********************************************************************
113  *           HEAP_Dump
114  */
115 void HEAP_Dump( HEAP *heap )
116 {
117     int i;
118     SUBHEAP *subheap;
119     char *ptr;
120
121     DUMP( "Heap: %08lx\n", (DWORD)heap );
122     DUMP( "Next: %08lx  Sub-heaps: %08lx",
123           (DWORD)heap->next, (DWORD)&heap->subheap );
124     subheap = &heap->subheap;
125     while (subheap->next)
126     {
127         DUMP( " -> %08lx", (DWORD)subheap->next );
128         subheap = subheap->next;
129     }
130
131     DUMP( "\nFree lists:\n Block   Stat   Size    Id\n" );
132     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
133         DUMP( "%08lx free %08lx %04x prev=%08lx next=%08lx\n",
134               (DWORD)&heap->freeList[i].arena, heap->freeList[i].arena.size,
135               heap->freeList[i].arena.threadId,
136               (DWORD)heap->freeList[i].arena.prev,
137               (DWORD)heap->freeList[i].arena.next );
138
139     subheap = &heap->subheap;
140     while (subheap)
141     {
142         DWORD freeSize = 0, usedSize = 0, arenaSize = subheap->headerSize;
143         DUMP( "\n\nSub-heap %08lx: size=%08lx committed=%08lx\n",
144               (DWORD)subheap, subheap->size, subheap->commitSize );
145         
146         DUMP( "\n Block   Stat   Size    Id\n" );
147         ptr = (char*)subheap + subheap->headerSize;
148         while (ptr < (char *)subheap + subheap->size)
149         {
150             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
151             {
152                 ARENA_FREE *pArena = (ARENA_FREE *)ptr;
153                 DUMP( "%08lx free %08lx %04x prev=%08lx next=%08lx\n",
154                       (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
155                       pArena->threadId, (DWORD)pArena->prev,
156                       (DWORD)pArena->next);
157                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
158                 arenaSize += sizeof(ARENA_FREE);
159                 freeSize += pArena->size & ARENA_SIZE_MASK;
160             }
161             else if (*(DWORD *)ptr & ARENA_FLAG_PREV_FREE)
162             {
163                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
164                 DUMP( "%08lx Used %08lx %04x back=%08lx EIP=%p\n",
165                       (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
166                       pArena->threadId, *((DWORD *)pArena - 1),
167                       pArena->callerEIP );
168                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
169                 arenaSize += sizeof(ARENA_INUSE);
170                 usedSize += pArena->size & ARENA_SIZE_MASK;
171             }
172             else
173             {
174                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
175                 DUMP( "%08lx used %08lx %04x EIP=%p\n",
176                       (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
177                       pArena->threadId, pArena->callerEIP );
178                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
179                 arenaSize += sizeof(ARENA_INUSE);
180                 usedSize += pArena->size & ARENA_SIZE_MASK;
181             }
182         }
183         DUMP( "\nTotal: Size=%08lx Committed=%08lx Free=%08lx Used=%08lx Arenas=%08lx (%ld%%)\n\n",
184               subheap->size, subheap->commitSize, freeSize, usedSize,
185               arenaSize, (arenaSize * 100) / subheap->size );
186         subheap = subheap->next;
187     }
188 }
189
190
191 /***********************************************************************
192  *           HEAP_GetPtr
193  * RETURNS
194  *      Pointer to the heap
195  *      NULL: Failure
196  */
197 static HEAP *HEAP_GetPtr(
198              HANDLE heap /* [in] Handle to the heap */
199 ) {
200     HEAP *heapPtr = (HEAP *)heap;
201     if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
202     {
203         ERR(heap, "Invalid heap %08x!\n", heap );
204         SetLastError( ERROR_INVALID_HANDLE );
205         return NULL;
206     }
207     if (TRACE_ON(heap) && !HeapValidate( heap, 0, NULL ))
208     {
209         HEAP_Dump( heapPtr );
210         assert( FALSE );
211         SetLastError( ERROR_INVALID_HANDLE );
212         return NULL;
213     }
214     return heapPtr;
215 }
216
217
218 /***********************************************************************
219  *           HEAP_InsertFreeBlock
220  *
221  * Insert a free block into the free list.
222  */
223 static void HEAP_InsertFreeBlock( HEAP *heap, ARENA_FREE *pArena )
224 {
225     FREE_LIST_ENTRY *pEntry = heap->freeList;
226     while (pEntry->size < pArena->size) pEntry++;
227     pArena->size      |= ARENA_FLAG_FREE;
228     pArena->next       = pEntry->arena.next;
229     pArena->next->prev = pArena;
230     pArena->prev       = &pEntry->arena;
231     pEntry->arena.next = pArena;
232 }
233
234
235 /***********************************************************************
236  *           HEAP_FindSubHeap
237  * Find the sub-heap containing a given address.
238  *
239  * RETURNS
240  *      Pointer: Success
241  *      NULL: Failure
242  */
243 static SUBHEAP *HEAP_FindSubHeap(
244                 HEAP *heap, /* [in] Heap pointer */
245                 LPCVOID ptr /* [in] Address */
246 ) {
247     SUBHEAP *sub = &heap->subheap;
248     while (sub)
249     {
250         if (((char *)ptr >= (char *)sub) &&
251             ((char *)ptr < (char *)sub + sub->size)) return sub;
252         sub = sub->next;
253     }
254     return NULL;
255 }
256
257
258 /***********************************************************************
259  *           HEAP_Commit
260  *
261  * Make sure the heap storage is committed up to (not including) ptr.
262  */
263 static BOOL HEAP_Commit( SUBHEAP *subheap, void *ptr )
264 {
265     DWORD size = (DWORD)((char *)ptr - (char *)subheap);
266     size = (size + 0xfff) & 0xfffff000;  /* Align size on a page boundary */
267     if (size > subheap->size) size = subheap->size;
268     if (size <= subheap->commitSize) return TRUE;
269     if (!VirtualAlloc( (char *)subheap + subheap->commitSize,
270                        size - subheap->commitSize, MEM_COMMIT,
271                        PAGE_EXECUTE_READWRITE))
272     {
273         WARN(heap, "Could not commit %08lx bytes at %08lx for heap %08lx\n",
274                  size - subheap->commitSize,
275                  (DWORD)((char *)subheap + subheap->commitSize),
276                  (DWORD)subheap->heap );
277         return FALSE;
278     }
279     subheap->commitSize = size;
280     return TRUE;
281 }
282
283
284 /***********************************************************************
285  *           HEAP_Decommit
286  *
287  * If possible, decommit the heap storage from (including) 'ptr'.
288  */
289 static BOOL HEAP_Decommit( SUBHEAP *subheap, void *ptr )
290 {
291     DWORD size = (DWORD)((char *)ptr - (char *)subheap);
292     size = (size + 0xfff) & 0xfffff000;  /* Align size on a page boundary */
293     if (size >= subheap->commitSize) return TRUE;
294     if (!VirtualFree( (char *)subheap + size,
295                       subheap->commitSize - size, MEM_DECOMMIT ))
296     {
297         WARN(heap, "Could not decommit %08lx bytes at %08lx for heap %08lx\n",
298                  subheap->commitSize - size,
299                  (DWORD)((char *)subheap + size),
300                  (DWORD)subheap->heap );
301         return FALSE;
302     }
303     subheap->commitSize = size;
304     return TRUE;
305 }
306
307
308 /***********************************************************************
309  *           HEAP_CreateFreeBlock
310  *
311  * Create a free block at a specified address. 'size' is the size of the
312  * whole block, including the new arena.
313  */
314 static void HEAP_CreateFreeBlock( SUBHEAP *subheap, void *ptr, DWORD size )
315 {
316     ARENA_FREE *pFree;
317
318     /* Create a free arena */
319
320     pFree = (ARENA_FREE *)ptr;
321     pFree->threadId = GetCurrentTask();
322     pFree->magic = ARENA_FREE_MAGIC;
323
324     /* If debugging, erase the freed block content */
325
326     if (TRACE_ON(heap))
327     {
328         char *pEnd = (char *)ptr + size;
329         if (pEnd > (char *)subheap + subheap->commitSize)
330             pEnd = (char *)subheap + subheap->commitSize;
331         if (pEnd > (char *)(pFree + 1))
332             memset( pFree + 1, ARENA_FREE_FILLER, pEnd - (char *)(pFree + 1) );
333     }
334
335     /* Check if next block is free also */
336
337     if (((char *)ptr + size < (char *)subheap + subheap->size) &&
338         (*(DWORD *)((char *)ptr + size) & ARENA_FLAG_FREE))
339     {
340         /* Remove the next arena from the free list */
341         ARENA_FREE *pNext = (ARENA_FREE *)((char *)ptr + size);
342         pNext->next->prev = pNext->prev;
343         pNext->prev->next = pNext->next;
344         size += (pNext->size & ARENA_SIZE_MASK) + sizeof(*pNext);
345         if (TRACE_ON(heap))
346             memset( pNext, ARENA_FREE_FILLER, sizeof(ARENA_FREE) );
347     }
348
349     /* Set the next block PREV_FREE flag and pointer */
350
351     if ((char *)ptr + size < (char *)subheap + subheap->size)
352     {
353         DWORD *pNext = (DWORD *)((char *)ptr + size);
354         *pNext |= ARENA_FLAG_PREV_FREE;
355         *(ARENA_FREE **)(pNext - 1) = pFree;
356     }
357
358     /* Last, insert the new block into the free list */
359
360     pFree->size = size - sizeof(*pFree);
361     HEAP_InsertFreeBlock( subheap->heap, pFree );
362 }
363
364
365 /***********************************************************************
366  *           HEAP_MakeInUseBlockFree
367  *
368  * Turn an in-use block into a free block. Can also decommit the end of
369  * the heap, and possibly even free the sub-heap altogether.
370  */
371 static void HEAP_MakeInUseBlockFree( SUBHEAP *subheap, ARENA_INUSE *pArena )
372 {
373     ARENA_FREE *pFree;
374     DWORD size = (pArena->size & ARENA_SIZE_MASK) + sizeof(*pArena);
375
376     /* Check if we can merge with previous block */
377
378     if (pArena->size & ARENA_FLAG_PREV_FREE)
379     {
380         pFree = *((ARENA_FREE **)pArena - 1);
381         size += (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
382         /* Remove it from the free list */
383         pFree->next->prev = pFree->prev;
384         pFree->prev->next = pFree->next;
385     }
386     else pFree = (ARENA_FREE *)pArena;
387
388     /* Create a free block */
389
390     HEAP_CreateFreeBlock( subheap, pFree, size );
391     size = (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
392     if ((char *)pFree + size < (char *)subheap + subheap->size)
393         return;  /* Not the last block, so nothing more to do */
394
395     /* Free the whole sub-heap if it's empty and not the original one */
396
397     if (((char *)pFree == (char *)subheap + subheap->headerSize) &&
398         (subheap != &subheap->heap->subheap))
399     {
400         SUBHEAP *pPrev = &subheap->heap->subheap;
401         /* Remove the free block from the list */
402         pFree->next->prev = pFree->prev;
403         pFree->prev->next = pFree->next;
404         /* Remove the subheap from the list */
405         while (pPrev && (pPrev->next != subheap)) pPrev = pPrev->next;
406         if (pPrev) pPrev->next = subheap->next;
407         /* Free the memory */
408         subheap->magic = 0;
409         if (subheap->selector) FreeSelector16( subheap->selector );
410         VirtualFree( subheap, 0, MEM_RELEASE );
411         return;
412     }
413     
414     /* Decommit the end of the heap */
415
416     HEAP_Decommit( subheap, pFree + 1 );
417 }
418
419
420 /***********************************************************************
421  *           HEAP_ShrinkBlock
422  *
423  * Shrink an in-use block.
424  */
425 static void HEAP_ShrinkBlock(SUBHEAP *subheap, ARENA_INUSE *pArena, DWORD size)
426 {
427     if ((pArena->size & ARENA_SIZE_MASK) >= size + HEAP_MIN_BLOCK_SIZE)
428     {
429         HEAP_CreateFreeBlock( subheap, (char *)(pArena + 1) + size,
430                               (pArena->size & ARENA_SIZE_MASK) - size );
431         pArena->size = (pArena->size & ~ARENA_SIZE_MASK) | size;
432     }
433     else
434     {
435         /* Turn off PREV_FREE flag in next block */
436         char *pNext = (char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK);
437         if (pNext < (char *)subheap + subheap->size)
438             *(DWORD *)pNext &= ~ARENA_FLAG_PREV_FREE;
439     }
440 }
441
442 /***********************************************************************
443  *           HEAP_InitSubHeap
444  */
445 static BOOL HEAP_InitSubHeap( HEAP *heap, LPVOID address, DWORD flags,
446                                 DWORD commitSize, DWORD totalSize )
447 {
448     SUBHEAP *subheap = (SUBHEAP *)address;
449     WORD selector = 0;
450     FREE_LIST_ENTRY *pEntry;
451     int i;
452
453     /* Commit memory */
454
455     if (!VirtualAlloc(address, commitSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
456     {
457         WARN(heap, "Could not commit %08lx bytes for sub-heap %08lx\n",
458                    commitSize, (DWORD)address );
459         return FALSE;
460     }
461
462     /* Allocate a selector if needed */
463
464     if (flags & HEAP_WINE_SEGPTR)
465     {
466         selector = SELECTOR_AllocBlock( address, totalSize,
467                            (flags & (HEAP_WINE_CODESEG|HEAP_WINE_CODE16SEG))
468                             ? SEGMENT_CODE : SEGMENT_DATA,
469                            (flags & HEAP_WINE_CODESEG) != 0, FALSE );
470         if (!selector)
471         {
472             ERR(heap, "Could not allocate selector\n" );
473             return FALSE;
474         }
475     }
476
477     /* Fill the sub-heap structure */
478
479     subheap->heap       = heap;
480     subheap->selector   = selector;
481     subheap->size       = totalSize;
482     subheap->commitSize = commitSize;
483     subheap->magic      = SUBHEAP_MAGIC;
484
485     if ( subheap != (SUBHEAP *)heap )
486     {
487         /* If this is a secondary subheap, insert it into list */
488
489         subheap->headerSize = sizeof(SUBHEAP);
490         subheap->next       = heap->subheap.next;
491         heap->subheap.next  = subheap;
492     }
493     else
494     {
495         /* If this is a primary subheap, initialize main heap */
496
497         subheap->headerSize = sizeof(HEAP);
498         subheap->next       = NULL;
499         heap->next          = NULL;
500         heap->flags         = flags;
501         heap->magic         = HEAP_MAGIC;
502
503         /* Build the free lists */
504
505         for (i = 0, pEntry = heap->freeList; i < HEAP_NB_FREE_LISTS; i++, pEntry++)
506         {
507             pEntry->size           = HEAP_freeListSizes[i];
508             pEntry->arena.size     = 0 | ARENA_FLAG_FREE;
509             pEntry->arena.next     = i < HEAP_NB_FREE_LISTS-1 ?
510                          &heap->freeList[i+1].arena : &heap->freeList[0].arena;
511             pEntry->arena.prev     = i ? &heap->freeList[i-1].arena : 
512                                    &heap->freeList[HEAP_NB_FREE_LISTS-1].arena;
513             pEntry->arena.threadId = 0;
514             pEntry->arena.magic    = ARENA_FREE_MAGIC;
515         }
516
517         /* Initialize critical section */
518
519         InitializeCriticalSection( &heap->critSection );
520         if (!SystemHeap) /* System heap critical section has to be global */
521             MakeCriticalSectionGlobal( &heap->critSection );
522     }
523  
524     /* Create the first free block */
525
526     HEAP_CreateFreeBlock( subheap, (LPBYTE)subheap + subheap->headerSize, 
527                           subheap->size - subheap->headerSize );
528
529     return TRUE;
530 }
531
532 /***********************************************************************
533  *           HEAP_CreateSubHeap
534  *
535  * Create a sub-heap of the given size.
536  * If heap == NULL, creates a main heap.
537  */
538 static SUBHEAP *HEAP_CreateSubHeap( HEAP *heap, DWORD flags, 
539                                     DWORD commitSize, DWORD totalSize )
540 {
541     LPVOID address;
542
543     /* Round-up sizes on a 64K boundary */
544
545     if (flags & HEAP_WINE_SEGPTR)
546     {
547         totalSize = commitSize = 0x10000;  /* Only 64K at a time for SEGPTRs */
548     }
549     else
550     {
551         totalSize  = (totalSize + 0xffff) & 0xffff0000;
552         commitSize = (commitSize + 0xffff) & 0xffff0000;
553         if (!commitSize) commitSize = 0x10000;
554         if (totalSize < commitSize) totalSize = commitSize;
555     }
556
557     /* Allocate the memory block */
558
559     if (!(address = VirtualAlloc( NULL, totalSize,
560                                   MEM_RESERVE, PAGE_EXECUTE_READWRITE )))
561     {
562         WARN(heap, "Could not VirtualAlloc %08lx bytes\n",
563                  totalSize );
564         return NULL;
565     }
566
567     /* Initialize subheap */
568
569     if (!HEAP_InitSubHeap( heap? heap : (HEAP *)address, 
570                            address, flags, commitSize, totalSize ))
571     {
572         VirtualFree( address, 0, MEM_RELEASE );
573         return NULL;
574     }
575
576     return (SUBHEAP *)address;
577 }
578
579
580 /***********************************************************************
581  *           HEAP_FindFreeBlock
582  *
583  * Find a free block at least as large as the requested size, and make sure
584  * the requested size is committed.
585  */
586 static ARENA_FREE *HEAP_FindFreeBlock( HEAP *heap, DWORD size,
587                                        SUBHEAP **ppSubHeap )
588 {
589     SUBHEAP *subheap;
590     ARENA_FREE *pArena;
591     FREE_LIST_ENTRY *pEntry = heap->freeList;
592
593     /* Find a suitable free list, and in it find a block large enough */
594
595     while (pEntry->size < size) pEntry++;
596     pArena = pEntry->arena.next;
597     while (pArena != &heap->freeList[0].arena)
598     {
599         if (pArena->size > size)
600         {
601             subheap = HEAP_FindSubHeap( heap, pArena );
602             if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
603                                                + size + HEAP_MIN_BLOCK_SIZE))
604                 return NULL;
605             *ppSubHeap = subheap;
606             return pArena;
607         }
608
609         pArena = pArena->next;
610     }
611
612     /* If no block was found, attempt to grow the heap */
613
614     if (!(heap->flags & HEAP_GROWABLE))
615     {
616         WARN(heap, "Not enough space in heap %08lx for %08lx bytes\n",
617                  (DWORD)heap, size );
618         return NULL;
619     }
620     size += sizeof(SUBHEAP) + sizeof(ARENA_FREE);
621     if (!(subheap = HEAP_CreateSubHeap( heap, heap->flags, size,
622                                         MAX( HEAP_DEF_SIZE, size ) )))
623         return NULL;
624
625     TRACE(heap, "created new sub-heap %08lx of %08lx bytes for heap %08lx\n",
626                 (DWORD)subheap, size, (DWORD)heap );
627
628     *ppSubHeap = subheap;
629     return (ARENA_FREE *)(subheap + 1);
630 }
631
632
633 /***********************************************************************
634  *           HEAP_IsValidArenaPtr
635  *
636  * Check that the pointer is inside the range possible for arenas.
637  */
638 static BOOL HEAP_IsValidArenaPtr( HEAP *heap, void *ptr )
639 {
640     int i;
641     SUBHEAP *subheap = HEAP_FindSubHeap( heap, ptr );
642     if (!subheap) return FALSE;
643     if ((char *)ptr >= (char *)subheap + subheap->headerSize) return TRUE;
644     if (subheap != &heap->subheap) return FALSE;
645     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
646         if (ptr == (void *)&heap->freeList[i].arena) return TRUE;
647     return FALSE;
648 }
649
650
651 /***********************************************************************
652  *           HEAP_ValidateFreeArena
653  */
654 static BOOL HEAP_ValidateFreeArena( SUBHEAP *subheap, ARENA_FREE *pArena )
655 {
656     char *heapEnd = (char *)subheap + subheap->size;
657
658     /* Check magic number */
659     if (pArena->magic != ARENA_FREE_MAGIC)
660     {
661         ERR(heap, "Heap %08lx: invalid free arena magic for %08lx\n",
662                  (DWORD)subheap->heap, (DWORD)pArena );
663         return FALSE;
664     }
665     /* Check size flags */
666     if (!(pArena->size & ARENA_FLAG_FREE) ||
667         (pArena->size & ARENA_FLAG_PREV_FREE))
668     {
669         ERR(heap, "Heap %08lx: bad flags %lx for free arena %08lx\n",
670                  (DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
671     }
672     /* Check arena size */
673     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
674     {
675         ERR(heap, "Heap %08lx: bad size %08lx for free arena %08lx\n",
676                  (DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
677         return FALSE;
678     }
679     /* Check that next pointer is valid */
680     if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->next ))
681     {
682         ERR(heap, "Heap %08lx: bad next ptr %08lx for arena %08lx\n",
683                  (DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
684         return FALSE;
685     }
686     /* Check that next arena is free */
687     if (!(pArena->next->size & ARENA_FLAG_FREE) ||
688         (pArena->next->magic != ARENA_FREE_MAGIC))
689     { 
690         ERR(heap, "Heap %08lx: next arena %08lx invalid for %08lx\n", 
691                  (DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
692         return FALSE;
693     }
694     /* Check that prev pointer is valid */
695     if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->prev ))
696     {
697         ERR(heap, "Heap %08lx: bad prev ptr %08lx for arena %08lx\n",
698                  (DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
699         return FALSE;
700     }
701     /* Check that prev arena is free */
702     if (!(pArena->prev->size & ARENA_FLAG_FREE) ||
703         (pArena->prev->magic != ARENA_FREE_MAGIC))
704     { 
705         ERR(heap, "Heap %08lx: prev arena %08lx invalid for %08lx\n", 
706                  (DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
707         return FALSE;
708     }
709     /* Check that next block has PREV_FREE flag */
710     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd)
711     {
712         if (!(*(DWORD *)((char *)(pArena + 1) +
713             (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
714         {
715             ERR(heap, "Heap %08lx: free arena %08lx next block has no PREV_FREE flag\n",
716                      (DWORD)subheap->heap, (DWORD)pArena );
717             return FALSE;
718         }
719         /* Check next block back pointer */
720         if (*((ARENA_FREE **)((char *)(pArena + 1) +
721             (pArena->size & ARENA_SIZE_MASK)) - 1) != pArena)
722         {
723             ERR(heap, "Heap %08lx: arena %08lx has wrong back ptr %08lx\n",
724                      (DWORD)subheap->heap, (DWORD)pArena,
725                      *((DWORD *)((char *)(pArena+1)+ (pArena->size & ARENA_SIZE_MASK)) - 1));
726             return FALSE;
727         }
728     }
729     return TRUE;
730 }
731
732
733 /***********************************************************************
734  *           HEAP_ValidateInUseArena
735  */
736 static BOOL HEAP_ValidateInUseArena( SUBHEAP *subheap, ARENA_INUSE *pArena )
737 {
738     char *heapEnd = (char *)subheap + subheap->size;
739
740     /* Check magic number */
741     if (pArena->magic != ARENA_INUSE_MAGIC)
742     {
743         ERR(heap, "Heap %08lx: invalid in-use arena magic for %08lx\n",
744                  (DWORD)subheap->heap, (DWORD)pArena );
745         return FALSE;
746     }
747     /* Check size flags */
748     if (pArena->size & ARENA_FLAG_FREE) 
749     {
750         ERR(heap, "Heap %08lx: bad flags %lx for in-use arena %08lx\n",
751                  (DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
752     }
753     /* Check arena size */
754     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
755     {
756         ERR(heap, "Heap %08lx: bad size %08lx for in-use arena %08lx\n",
757                  (DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
758         return FALSE;
759     }
760     /* Check next arena PREV_FREE flag */
761     if (((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd) &&
762         (*(DWORD *)((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
763     {
764         ERR(heap, "Heap %08lx: in-use arena %08lx next block has PREV_FREE flag\n",
765                  (DWORD)subheap->heap, (DWORD)pArena );
766         return FALSE;
767     }
768     /* Check prev free arena */
769     if (pArena->size & ARENA_FLAG_PREV_FREE)
770     {
771         ARENA_FREE *pPrev = *((ARENA_FREE **)pArena - 1);
772         /* Check prev pointer */
773         if (!HEAP_IsValidArenaPtr( subheap->heap, pPrev ))
774         {
775             ERR(heap, "Heap %08lx: bad back ptr %08lx for arena %08lx\n",
776                     (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
777             return FALSE;
778         }
779         /* Check that prev arena is free */
780         if (!(pPrev->size & ARENA_FLAG_FREE) ||
781             (pPrev->magic != ARENA_FREE_MAGIC))
782         { 
783             ERR(heap, "Heap %08lx: prev arena %08lx invalid for in-use %08lx\n", 
784                      (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
785             return FALSE;
786         }
787         /* Check that prev arena is really the previous block */
788         if ((char *)(pPrev + 1) + (pPrev->size & ARENA_SIZE_MASK) != (char *)pArena)
789         {
790             ERR(heap, "Heap %08lx: prev arena %08lx is not prev for in-use %08lx\n",
791                      (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
792             return FALSE;
793         }
794     }
795     return TRUE;
796 }
797
798
799 /***********************************************************************
800  *           HEAP_IsInsideHeap
801  * Checks whether the pointer points to a block inside a given heap.
802  *
803  * NOTES
804  *      Should this return BOOL32?
805  *
806  * RETURNS
807  *      !0: Success
808  *      0: Failure
809  */
810 int HEAP_IsInsideHeap(
811     HANDLE heap, /* [in] Heap */
812     DWORD flags,   /* [in] Flags */
813     LPCVOID ptr    /* [in] Pointer */
814 ) {
815     HEAP *heapPtr = HEAP_GetPtr( heap );
816     SUBHEAP *subheap;
817     int ret;
818
819     /* Validate the parameters */
820
821     if (!heapPtr) return 0;
822     flags |= heapPtr->flags;
823     if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
824     ret = (((subheap = HEAP_FindSubHeap( heapPtr, ptr )) != NULL) &&
825            (((char *)ptr >= (char *)subheap + subheap->headerSize
826                               + sizeof(ARENA_INUSE))));
827     if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
828     return ret;
829 }
830
831
832 /***********************************************************************
833  *           HEAP_GetSegptr
834  *
835  * Transform a linear pointer into a SEGPTR. The pointer must have been
836  * allocated from a HEAP_WINE_SEGPTR heap.
837  */
838 SEGPTR HEAP_GetSegptr( HANDLE heap, DWORD flags, LPCVOID ptr )
839 {
840     HEAP *heapPtr = HEAP_GetPtr( heap );
841     SUBHEAP *subheap;
842     SEGPTR ret;
843
844     /* Validate the parameters */
845
846     if (!heapPtr) return 0;
847     flags |= heapPtr->flags;
848     if (!(flags & HEAP_WINE_SEGPTR))
849     {
850         ERR(heap, "Heap %08x is not a SEGPTR heap\n",
851                  heap );
852         return 0;
853     }
854     if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
855
856     /* Get the subheap */
857
858     if (!(subheap = HEAP_FindSubHeap( heapPtr, ptr )))
859     {
860         ERR(heap, "%p is not inside heap %08x\n",
861                  ptr, heap );
862         if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
863         return 0;
864     }
865
866     /* Build the SEGPTR */
867
868     ret = PTR_SEG_OFF_TO_SEGPTR(subheap->selector, (DWORD)ptr-(DWORD)subheap);
869     if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
870     return ret;
871 }
872
873
874 /***********************************************************************
875  *           HeapCreate   (KERNEL32.336)
876  * RETURNS
877  *      Handle of heap: Success
878  *      NULL: Failure
879  */
880 HANDLE WINAPI HeapCreate(
881                 DWORD flags,       /* [in] Heap allocation flag */
882                 DWORD initialSize, /* [in] Initial heap size */
883                 DWORD maxSize      /* [in] Maximum heap size */
884 ) {
885     SUBHEAP *subheap;
886
887     /* Allocate the heap block */
888
889     if (!maxSize)
890     {
891         maxSize = HEAP_DEF_SIZE;
892         flags |= HEAP_GROWABLE;
893     }
894     if (!(subheap = HEAP_CreateSubHeap( NULL, flags, initialSize, maxSize )))
895     {
896         SetLastError( ERROR_OUTOFMEMORY );
897         return 0;
898     }
899
900     return (HANDLE)subheap;
901 }
902
903 /***********************************************************************
904  *           HeapDestroy   (KERNEL32.337)
905  * RETURNS
906  *      TRUE: Success
907  *      FALSE: Failure
908  */
909 BOOL WINAPI HeapDestroy(
910               HANDLE heap /* [in] Handle of heap */
911 ) {
912     HEAP *heapPtr = HEAP_GetPtr( heap );
913     SUBHEAP *subheap;
914
915     TRACE(heap, "%08x\n", heap );
916     if (!heapPtr) return FALSE;
917
918     DeleteCriticalSection( &heapPtr->critSection );
919     subheap = &heapPtr->subheap;
920     while (subheap)
921     {
922         SUBHEAP *next = subheap->next;
923         if (subheap->selector) FreeSelector16( subheap->selector );
924         VirtualFree( subheap, 0, MEM_RELEASE );
925         subheap = next;
926     }
927     return TRUE;
928 }
929
930
931 /***********************************************************************
932  *           HeapAlloc   (KERNEL32.334)
933  * RETURNS
934  *      Pointer to allocated memory block
935  *      NULL: Failure
936  */
937 LPVOID WINAPI HeapAlloc(
938               HANDLE heap, /* [in] Handle of private heap block */
939               DWORD flags,   /* [in] Heap allocation control flags */
940               DWORD size     /* [in] Number of bytes to allocate */
941 ) {
942     ARENA_FREE *pArena;
943     ARENA_INUSE *pInUse;
944     SUBHEAP *subheap;
945     HEAP *heapPtr = HEAP_GetPtr( heap );
946
947     /* Validate the parameters */
948
949     if (!heapPtr) return NULL;
950     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY;
951     flags |= heapPtr->flags;
952     if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
953     size = (size + 3) & ~3;
954     if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
955
956     /* Locate a suitable free block */
957
958     if (!(pArena = HEAP_FindFreeBlock( heapPtr, size, &subheap )))
959     {
960         TRACE(heap, "(%08x,%08lx,%08lx): returning NULL\n",
961                   heap, flags, size  );
962         if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
963         SetLastError( ERROR_COMMITMENT_LIMIT );
964         return NULL;
965     }
966
967     /* Remove the arena from the free list */
968
969     pArena->next->prev = pArena->prev;
970     pArena->prev->next = pArena->next;
971
972     /* Build the in-use arena */
973
974     pInUse = (ARENA_INUSE *)pArena;
975     pInUse->size      = (pInUse->size & ~ARENA_FLAG_FREE)
976                         + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
977     pInUse->callerEIP = GET_EIP();
978     pInUse->threadId  = GetCurrentTask();
979     pInUse->magic     = ARENA_INUSE_MAGIC;
980
981     /* Shrink the block */
982
983     HEAP_ShrinkBlock( subheap, pInUse, size );
984
985     if (flags & HEAP_ZERO_MEMORY) memset( pInUse + 1, 0, size );
986     else if (TRACE_ON(heap)) memset( pInUse + 1, ARENA_INUSE_FILLER, size );
987
988     if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
989
990     TRACE(heap, "(%08x,%08lx,%08lx): returning %08lx\n",
991                   heap, flags, size, (DWORD)(pInUse + 1) );
992     return (LPVOID)(pInUse + 1);
993 }
994
995
996 /***********************************************************************
997  *           HeapFree   (KERNEL32.338)
998  * RETURNS
999  *      TRUE: Success
1000  *      FALSE: Failure
1001  */
1002 BOOL WINAPI HeapFree(
1003               HANDLE heap, /* [in] Handle of heap */
1004               DWORD flags,   /* [in] Heap freeing flags */
1005               LPVOID ptr     /* [in] Address of memory to free */
1006 ) {
1007     ARENA_INUSE *pInUse;
1008     SUBHEAP *subheap;
1009     HEAP *heapPtr = HEAP_GetPtr( heap );
1010
1011     /* Validate the parameters */
1012
1013     if (!heapPtr) return FALSE;
1014     flags &= HEAP_NO_SERIALIZE;
1015     flags |= heapPtr->flags;
1016     if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
1017     if (!ptr)
1018     {
1019         WARN(heap, "(%08x,%08lx,%08lx): asked to free NULL\n",
1020                    heap, flags, (DWORD)ptr );
1021     }
1022     if (!ptr || !HeapValidate( heap, HEAP_NO_SERIALIZE, ptr ))
1023     {
1024         if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1025         SetLastError( ERROR_INVALID_PARAMETER );
1026         TRACE(heap, "(%08x,%08lx,%08lx): returning FALSE\n",
1027                       heap, flags, (DWORD)ptr );
1028         return FALSE;
1029     }
1030
1031     /* Turn the block into a free block */
1032
1033     pInUse  = (ARENA_INUSE *)ptr - 1;
1034     subheap = HEAP_FindSubHeap( heapPtr, pInUse );
1035     HEAP_MakeInUseBlockFree( subheap, pInUse );
1036
1037     if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1038 /*    SetLastError( 0 ); */
1039
1040     TRACE(heap, "(%08x,%08lx,%08lx): returning TRUE\n",
1041                   heap, flags, (DWORD)ptr );
1042     return TRUE;
1043 }
1044
1045
1046 /***********************************************************************
1047  *           HeapReAlloc   (KERNEL32.340)
1048  * RETURNS
1049  *      Pointer to reallocated memory block
1050  *      NULL: Failure
1051  */
1052 LPVOID WINAPI HeapReAlloc(
1053               HANDLE heap, /* [in] Handle of heap block */
1054               DWORD flags,   /* [in] Heap reallocation flags */
1055               LPVOID ptr,    /* [in] Address of memory to reallocate */
1056               DWORD size     /* [in] Number of bytes to reallocate */
1057 ) {
1058     ARENA_INUSE *pArena;
1059     DWORD oldSize;
1060     HEAP *heapPtr;
1061     SUBHEAP *subheap;
1062
1063     if (!ptr) return HeapAlloc( heap, flags, size );  /* FIXME: correct? */
1064     if (!(heapPtr = HEAP_GetPtr( heap ))) return FALSE;
1065
1066     /* Validate the parameters */
1067
1068     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY |
1069              HEAP_REALLOC_IN_PLACE_ONLY;
1070     flags |= heapPtr->flags;
1071     size = (size + 3) & ~3;
1072     if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
1073
1074     if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
1075     if (!HeapValidate( heap, HEAP_NO_SERIALIZE, ptr ))
1076     {
1077         if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1078         SetLastError( ERROR_INVALID_PARAMETER );
1079         TRACE(heap, "(%08x,%08lx,%08lx,%08lx): returning NULL\n",
1080                       heap, flags, (DWORD)ptr, size );
1081         return NULL;
1082     }
1083
1084     /* Check if we need to grow the block */
1085
1086     pArena = (ARENA_INUSE *)ptr - 1;
1087     pArena->threadId = GetCurrentTask();
1088     subheap = HEAP_FindSubHeap( heapPtr, pArena );
1089     oldSize = (pArena->size & ARENA_SIZE_MASK);
1090     if (size > oldSize)
1091     {
1092         char *pNext = (char *)(pArena + 1) + oldSize;
1093         if ((pNext < (char *)subheap + subheap->size) &&
1094             (*(DWORD *)pNext & ARENA_FLAG_FREE) &&
1095             (oldSize + (*(DWORD *)pNext & ARENA_SIZE_MASK) + sizeof(ARENA_FREE) >= size))
1096         {
1097             /* The next block is free and large enough */
1098             ARENA_FREE *pFree = (ARENA_FREE *)pNext;
1099             pFree->next->prev = pFree->prev;
1100             pFree->prev->next = pFree->next;
1101             pArena->size += (pFree->size & ARENA_SIZE_MASK) + sizeof(*pFree);
1102             if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
1103                                                + size + HEAP_MIN_BLOCK_SIZE))
1104             {
1105                 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1106                 SetLastError( ERROR_OUTOFMEMORY );
1107                 return NULL;
1108             }
1109             HEAP_ShrinkBlock( subheap, pArena, size );
1110         }
1111         else  /* Do it the hard way */
1112         {
1113             ARENA_FREE *pNew;
1114             ARENA_INUSE *pInUse;
1115             SUBHEAP *newsubheap;
1116
1117             if ((flags & HEAP_REALLOC_IN_PLACE_ONLY) ||
1118                 !(pNew = HEAP_FindFreeBlock( heapPtr, size, &newsubheap )))
1119             {
1120                 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1121                 SetLastError( ERROR_OUTOFMEMORY );
1122                 return NULL;
1123             }
1124
1125             /* Build the in-use arena */
1126
1127             pNew->next->prev = pNew->prev;
1128             pNew->prev->next = pNew->next;
1129             pInUse = (ARENA_INUSE *)pNew;
1130             pInUse->size     = (pInUse->size & ~ARENA_FLAG_FREE)
1131                                + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1132             pInUse->threadId = GetCurrentTask();
1133             pInUse->magic    = ARENA_INUSE_MAGIC;
1134             HEAP_ShrinkBlock( newsubheap, pInUse, size );
1135             memcpy( pInUse + 1, pArena + 1, oldSize );
1136
1137             /* Free the previous block */
1138
1139             HEAP_MakeInUseBlockFree( subheap, pArena );
1140             subheap = newsubheap;
1141             pArena  = pInUse;
1142         }
1143     }
1144     else HEAP_ShrinkBlock( subheap, pArena, size );  /* Shrink the block */
1145
1146     /* Clear the extra bytes if needed */
1147
1148     if (size > oldSize)
1149     {
1150         if (flags & HEAP_ZERO_MEMORY)
1151             memset( (char *)(pArena + 1) + oldSize, 0,
1152                     (pArena->size & ARENA_SIZE_MASK) - oldSize );
1153         else if (TRACE_ON(heap))
1154             memset( (char *)(pArena + 1) + oldSize, ARENA_INUSE_FILLER,
1155                     (pArena->size & ARENA_SIZE_MASK) - oldSize );
1156     }
1157
1158     /* Return the new arena */
1159
1160     pArena->callerEIP = GET_EIP();
1161     if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1162
1163     TRACE(heap, "(%08x,%08lx,%08lx,%08lx): returning %08lx\n",
1164                   heap, flags, (DWORD)ptr, size, (DWORD)(pArena + 1) );
1165     return (LPVOID)(pArena + 1);
1166 }
1167
1168
1169 /***********************************************************************
1170  *           HeapCompact   (KERNEL32.335)
1171  */
1172 DWORD WINAPI HeapCompact( HANDLE heap, DWORD flags )
1173 {
1174     return 0;
1175 }
1176
1177
1178 /***********************************************************************
1179  *           HeapLock   (KERNEL32.339)
1180  * Attempts to acquire the critical section object for a specified heap.
1181  *
1182  * RETURNS
1183  *      TRUE: Success
1184  *      FALSE: Failure
1185  */
1186 BOOL WINAPI HeapLock(
1187               HANDLE heap /* [in] Handle of heap to lock for exclusive access */
1188 ) {
1189     HEAP *heapPtr = HEAP_GetPtr( heap );
1190     if (!heapPtr) return FALSE;
1191     EnterCriticalSection( &heapPtr->critSection );
1192     return TRUE;
1193 }
1194
1195
1196 /***********************************************************************
1197  *           HeapUnlock   (KERNEL32.342)
1198  * Releases ownership of the critical section object.
1199  *
1200  * RETURNS
1201  *      TRUE: Success
1202  *      FALSE: Failure
1203  */
1204 BOOL WINAPI HeapUnlock(
1205               HANDLE heap /* [in] Handle to the heap to unlock */
1206 ) {
1207     HEAP *heapPtr = HEAP_GetPtr( heap );
1208     if (!heapPtr) return FALSE;
1209     LeaveCriticalSection( &heapPtr->critSection );
1210     return TRUE;
1211 }
1212
1213
1214 /***********************************************************************
1215  *           HeapSize   (KERNEL32.341)
1216  * RETURNS
1217  *      Size in bytes of allocated memory
1218  *      0xffffffff: Failure
1219  */
1220 DWORD WINAPI HeapSize(
1221              HANDLE heap, /* [in] Handle of heap */
1222              DWORD flags,   /* [in] Heap size control flags */
1223              LPVOID ptr     /* [in] Address of memory to return size for */
1224 ) {
1225     DWORD ret;
1226     HEAP *heapPtr = HEAP_GetPtr( heap );
1227
1228     if (!heapPtr) return FALSE;
1229     flags &= HEAP_NO_SERIALIZE;
1230     flags |= heapPtr->flags;
1231     if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
1232     if (!HeapValidate( heap, HEAP_NO_SERIALIZE, ptr ))
1233     {
1234         SetLastError( ERROR_INVALID_PARAMETER );
1235         ret = 0xffffffff;
1236     }
1237     else
1238     {
1239         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr - 1;
1240         ret = pArena->size & ARENA_SIZE_MASK;
1241     }
1242     if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1243
1244     TRACE(heap, "(%08x,%08lx,%08lx): returning %08lx\n",
1245                   heap, flags, (DWORD)ptr, ret );
1246     return ret;
1247 }
1248
1249
1250 /***********************************************************************
1251  *           HeapValidate   (KERNEL32.343)
1252  * Validates a specified heap.
1253  *
1254  * NOTES
1255  *      Flags is ignored.
1256  *
1257  * RETURNS
1258  *      TRUE: Success
1259  *      FALSE: Failure
1260  */
1261 BOOL WINAPI HeapValidate(
1262               HANDLE heap, /* [in] Handle to the heap */
1263               DWORD flags,   /* [in] Bit flags that control access during operation */
1264               LPCVOID block  /* [in] Optional pointer to memory block to validate */
1265 ) {
1266     SUBHEAP *subheap;
1267     HEAP *heapPtr = (HEAP *)(heap);
1268     BOOL ret = TRUE;
1269
1270     if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
1271     {
1272         ERR(heap, "Invalid heap %08x!\n", heap );
1273         return FALSE;
1274     }
1275
1276     flags &= HEAP_NO_SERIALIZE;
1277     flags |= heapPtr->flags;
1278     /* calling HeapLock may result in infinite recursion, so do the critsect directly */
1279     if (!(flags & HEAP_NO_SERIALIZE))
1280         EnterCriticalSection( &heapPtr->critSection );
1281     if (block)
1282     {
1283         /* Only check this single memory block */
1284         if (!(subheap = HEAP_FindSubHeap( heapPtr, block )) ||
1285             ((char *)block < (char *)subheap + subheap->headerSize
1286                               + sizeof(ARENA_INUSE)))
1287         {
1288             ERR(heap, "Heap %08lx: block %08lx is not inside heap\n",
1289                      (DWORD)heap, (DWORD)block );
1290             ret = FALSE;
1291         } else
1292             ret = HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)block - 1 );
1293
1294         if (!(flags & HEAP_NO_SERIALIZE))
1295             LeaveCriticalSection( &heapPtr->critSection );
1296         return ret;
1297     }
1298
1299     subheap = &heapPtr->subheap;
1300     while (subheap && ret)
1301     {
1302         char *ptr = (char *)subheap + subheap->headerSize;
1303         while (ptr < (char *)subheap + subheap->size)
1304         {
1305             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1306             {
1307                 if (!HEAP_ValidateFreeArena( subheap, (ARENA_FREE *)ptr )) {
1308                     ret = FALSE;
1309                     break;
1310                 }
1311                 ptr += sizeof(ARENA_FREE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1312             }
1313             else
1314             {
1315                 if (!HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)ptr )) {
1316                     ret = FALSE;
1317                     break;
1318                 }
1319                 ptr += sizeof(ARENA_INUSE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1320             }
1321         }
1322         subheap = subheap->next;
1323     }
1324     if (!(flags & HEAP_NO_SERIALIZE))
1325         LeaveCriticalSection( &heapPtr->critSection );
1326     return ret;
1327 }
1328
1329
1330 /***********************************************************************
1331  *           HeapWalk   (KERNEL32.344)
1332  * Enumerates the memory blocks in a specified heap.
1333  *
1334  * RETURNS
1335  *      TRUE: Success
1336  *      FALSE: Failure
1337  */
1338 BOOL WINAPI HeapWalk(
1339               HANDLE heap,               /* [in]  Handle to heap to enumerate */
1340               LPPROCESS_HEAP_ENTRY *entry  /* [out] Pointer to structure of enumeration info */
1341 ) {
1342     FIXME(heap, "(%08x): stub.\n", heap );
1343     return FALSE;
1344 }
1345
1346
1347 /***********************************************************************
1348  *           HEAP_xalloc
1349  *
1350  * Same as HeapAlloc(), but die on failure.
1351  */
1352 LPVOID HEAP_xalloc( HANDLE heap, DWORD flags, DWORD size )
1353 {
1354     LPVOID p = HeapAlloc( heap, flags, size );
1355     if (!p)
1356     {
1357         MSG("Virtual memory exhausted.\n" );
1358         exit(1);
1359     }
1360     SET_EIP(p);
1361     return p;
1362 }
1363
1364
1365 /***********************************************************************
1366  *           HEAP_xrealloc
1367  *
1368  * Same as HeapReAlloc(), but die on failure.
1369  */
1370 LPVOID HEAP_xrealloc( HANDLE heap, DWORD flags, LPVOID lpMem, DWORD size )
1371 {
1372     LPVOID p = HeapReAlloc( heap, flags, lpMem, size );
1373     if (!p)
1374     {
1375         MSG("Virtual memory exhausted.\n" );
1376         exit(1);
1377     }
1378     SET_EIP(p);
1379     return p;
1380 }
1381
1382
1383 /***********************************************************************
1384  *           HEAP_strdupA
1385  */
1386 LPSTR HEAP_strdupA( HANDLE heap, DWORD flags, LPCSTR str )
1387 {
1388     LPSTR p = HEAP_xalloc( heap, flags, strlen(str) + 1 );
1389     SET_EIP(p);
1390     strcpy( p, str );
1391     return p;
1392 }
1393
1394
1395 /***********************************************************************
1396  *           HEAP_strdupW
1397  */
1398 LPWSTR HEAP_strdupW( HANDLE heap, DWORD flags, LPCWSTR str )
1399 {
1400     INT len = lstrlenW(str) + 1;
1401     LPWSTR p = HEAP_xalloc( heap, flags, len * sizeof(WCHAR) );
1402     SET_EIP(p);
1403     lstrcpyW( p, str );
1404     return p;
1405 }
1406
1407
1408 /***********************************************************************
1409  *           HEAP_strdupAtoW
1410  */
1411 LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str )
1412 {
1413     LPWSTR ret;
1414
1415     if (!str) return NULL;
1416     ret = HEAP_xalloc( heap, flags, (strlen(str)+1) * sizeof(WCHAR) );
1417     SET_EIP(ret);
1418     lstrcpyAtoW( ret, str );
1419     return ret;
1420 }
1421
1422
1423 /***********************************************************************
1424  *           HEAP_strdupWtoA
1425  */
1426 LPSTR HEAP_strdupWtoA( HANDLE heap, DWORD flags, LPCWSTR str )
1427 {
1428     LPSTR ret;
1429
1430     if (!str) return NULL;
1431     ret = HEAP_xalloc( heap, flags, lstrlenW(str) + 1 );
1432     SET_EIP(ret);
1433     lstrcpyWtoA( ret, str );
1434     return ret;
1435 }
1436
1437
1438
1439 /***********************************************************************
1440  * 32-bit local heap functions (Win95; undocumented)
1441  */
1442
1443 #define HTABLE_SIZE      0x10000
1444 #define HTABLE_PAGESIZE  0x1000
1445 #define HTABLE_NPAGES    (HTABLE_SIZE / HTABLE_PAGESIZE)
1446
1447 #include "pshpack1.h"
1448 typedef struct _LOCAL32HEADER
1449 {
1450     WORD     freeListFirst[HTABLE_NPAGES];
1451     WORD     freeListSize[HTABLE_NPAGES];
1452     WORD     freeListLast[HTABLE_NPAGES];
1453
1454     DWORD    selectorTableOffset;
1455     WORD     selectorTableSize;
1456     WORD     selectorDelta;
1457
1458     DWORD    segment;
1459     LPBYTE   base;
1460
1461     DWORD    limit;
1462     DWORD    flags;
1463
1464     DWORD    magic;
1465     HANDLE heap;
1466
1467 } LOCAL32HEADER;
1468 #include "poppack.h"
1469
1470 #define LOCAL32_MAGIC    ((DWORD)('L' | ('H'<<8) | ('3'<<16) | ('2'<<24)))
1471
1472 /***********************************************************************
1473  *           Local32Init   (KERNEL.208)
1474  */
1475 HANDLE WINAPI Local32Init16( WORD segment, DWORD tableSize,
1476                              DWORD heapSize, DWORD flags )
1477 {
1478     DWORD totSize, segSize = 0;
1479     LPBYTE base;
1480     LOCAL32HEADER *header;
1481     HEAP *heap;
1482     WORD *selectorTable;
1483     WORD selectorEven, selectorOdd;
1484     int i, nrBlocks;
1485
1486     /* Determine new heap size */
1487
1488     if ( segment )
1489     {
1490         if ( (segSize = GetSelectorLimit16( segment )) == 0 )
1491             return 0;
1492         else
1493             segSize++;
1494     }
1495
1496     if ( heapSize == -1L )
1497         heapSize = 1024L*1024L;   /* FIXME */
1498
1499     heapSize = (heapSize + 0xffff) & 0xffff0000;
1500     segSize  = (segSize  + 0x0fff) & 0xfffff000;
1501     totSize  = segSize + HTABLE_SIZE + heapSize;
1502
1503
1504     /* Allocate memory and initialize heap */
1505
1506     if ( !(base = VirtualAlloc( NULL, totSize, MEM_RESERVE, PAGE_READWRITE )) )
1507         return 0;
1508
1509     if ( !VirtualAlloc( base, segSize + HTABLE_PAGESIZE, 
1510                         MEM_COMMIT, PAGE_READWRITE ) )
1511     {
1512         VirtualFree( base, 0, MEM_RELEASE );
1513         return 0;
1514     }
1515
1516     heap = (HEAP *)(base + segSize + HTABLE_SIZE);
1517     if ( !HEAP_InitSubHeap( heap, (LPVOID)heap, 0, 0x10000, heapSize ) )
1518     {
1519         VirtualFree( base, 0, MEM_RELEASE );
1520         return 0;
1521     }
1522
1523
1524     /* Set up header and handle table */
1525     
1526     header = (LOCAL32HEADER *)(base + segSize);
1527     header->base    = base;
1528     header->limit   = HTABLE_PAGESIZE-1;
1529     header->flags   = 0;
1530     header->magic   = LOCAL32_MAGIC;
1531     header->heap    = (HANDLE)heap;
1532
1533     header->freeListFirst[0] = sizeof(LOCAL32HEADER);
1534     header->freeListLast[0]  = HTABLE_PAGESIZE - 4;
1535     header->freeListSize[0]  = (HTABLE_PAGESIZE - sizeof(LOCAL32HEADER)) / 4;
1536
1537     for (i = header->freeListFirst[0]; i < header->freeListLast[0]; i += 4)
1538         *(DWORD *)((LPBYTE)header + i) = i+4;
1539
1540     header->freeListFirst[1] = 0xffff;
1541
1542
1543     /* Set up selector table */
1544   
1545     nrBlocks      = (totSize + 0x7fff) >> 15; 
1546     selectorTable = (LPWORD) HeapAlloc( header->heap,  0, nrBlocks * 2 );
1547     selectorEven  = SELECTOR_AllocBlock( base, totSize, 
1548                                          SEGMENT_DATA, FALSE, FALSE );
1549     selectorOdd   = SELECTOR_AllocBlock( base + 0x8000, totSize - 0x8000, 
1550                                          SEGMENT_DATA, FALSE, FALSE );
1551     
1552     if ( !selectorTable || !selectorEven || !selectorOdd )
1553     {
1554         if ( selectorTable ) HeapFree( header->heap, 0, selectorTable );
1555         if ( selectorEven  ) SELECTOR_FreeBlock( selectorEven, totSize >> 16 );
1556         if ( selectorOdd   ) SELECTOR_FreeBlock( selectorOdd, (totSize-0x8000) >> 16 );
1557         HeapDestroy( header->heap );
1558         VirtualFree( base, 0, MEM_RELEASE );
1559         return 0;
1560     }
1561
1562     header->selectorTableOffset = (LPBYTE)selectorTable - header->base;
1563     header->selectorTableSize   = nrBlocks * 4;  /* ??? Win95 does it this way! */
1564     header->selectorDelta       = selectorEven - selectorOdd;
1565     header->segment             = segment? segment : selectorEven;
1566
1567     for (i = 0; i < nrBlocks; i++)
1568         selectorTable[i] = (i & 1)? selectorOdd  + ((i >> 1) << __AHSHIFT)
1569                                   : selectorEven + ((i >> 1) << __AHSHIFT);
1570
1571     /* Move old segment */
1572
1573     if ( segment )
1574     {
1575         /* FIXME: This is somewhat ugly and relies on implementation
1576                   details about 16-bit global memory handles ... */
1577
1578         LPBYTE oldBase = (LPBYTE)GetSelectorBase( segment );
1579         memcpy( base, oldBase, segSize );
1580         GLOBAL_MoveBlock( segment, base, totSize );
1581         HeapFree( SystemHeap, 0, oldBase );
1582     }
1583     
1584     return (HANDLE)header;
1585 }
1586
1587 /***********************************************************************
1588  *           Local32_SearchHandle
1589  */
1590 static LPDWORD Local32_SearchHandle( LOCAL32HEADER *header, DWORD addr )
1591 {
1592     LPDWORD handle;
1593
1594     for ( handle = (LPDWORD)((LPBYTE)header + sizeof(LOCAL32HEADER));
1595           handle < (LPDWORD)((LPBYTE)header + header->limit);
1596           handle++)
1597     {
1598         if (*handle == addr)
1599             return handle;
1600     }
1601
1602     return NULL;
1603 }
1604
1605 /***********************************************************************
1606  *           Local32_ToHandle
1607  */
1608 static VOID Local32_ToHandle( LOCAL32HEADER *header, INT16 type, 
1609                               DWORD addr, LPDWORD *handle, LPBYTE *ptr )
1610 {
1611     *handle = NULL;
1612     *ptr    = NULL;
1613
1614     switch (type)
1615     {
1616         case -2:    /* 16:16 pointer, no handles */
1617             *ptr    = PTR_SEG_TO_LIN( addr );
1618             *handle = (LPDWORD)*ptr;
1619             break;
1620
1621         case -1:    /* 32-bit offset, no handles */
1622             *ptr    = header->base + addr;
1623             *handle = (LPDWORD)*ptr;
1624             break;
1625
1626         case 0:     /* handle */
1627             if (    addr >= sizeof(LOCAL32HEADER) 
1628                  && addr <  header->limit && !(addr & 3) 
1629                  && *(LPDWORD)((LPBYTE)header + addr) >= HTABLE_SIZE )
1630             {
1631                 *handle = (LPDWORD)((LPBYTE)header + addr);
1632                 *ptr    = header->base + **handle;
1633             }
1634             break;
1635
1636         case 1:     /* 16:16 pointer */
1637             *ptr    = PTR_SEG_TO_LIN( addr );
1638             *handle = Local32_SearchHandle( header, *ptr - header->base );
1639             break;
1640
1641         case 2:     /* 32-bit offset */
1642             *ptr    = header->base + addr;
1643             *handle = Local32_SearchHandle( header, *ptr - header->base );
1644             break;
1645     }
1646 }
1647
1648 /***********************************************************************
1649  *           Local32_FromHandle
1650  */
1651 static VOID Local32_FromHandle( LOCAL32HEADER *header, INT16 type, 
1652                                 DWORD *addr, LPDWORD handle, LPBYTE ptr )
1653 {
1654     switch (type)
1655     {
1656         case -2:    /* 16:16 pointer */
1657         case  1:
1658         {
1659             WORD *selTable = (LPWORD)(header->base + header->selectorTableOffset);
1660             DWORD offset   = (LPBYTE)ptr - header->base;
1661             *addr = MAKELONG( offset & 0x7fff, selTable[offset >> 15] ); 
1662         }
1663         break;
1664
1665         case -1:    /* 32-bit offset */
1666         case  2:
1667             *addr = ptr - header->base;
1668             break;
1669
1670         case  0:    /* handle */
1671             *addr = (LPBYTE)handle - (LPBYTE)header;
1672             break;
1673     }
1674 }
1675
1676 /***********************************************************************
1677  *           Local32Alloc   (KERNEL.209)
1678  */
1679 DWORD WINAPI Local32Alloc16( HANDLE heap, DWORD size, INT16 type, DWORD flags )
1680 {
1681     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1682     LPDWORD handle;
1683     LPBYTE ptr;
1684     DWORD addr;
1685
1686     /* Allocate memory */
1687     ptr = HeapAlloc( header->heap, 
1688                      (flags & LMEM_MOVEABLE)? HEAP_ZERO_MEMORY : 0, size );
1689     if (!ptr) return 0;
1690
1691
1692     /* Allocate handle if requested */
1693     if (type >= 0)
1694     {
1695         int page, i;
1696
1697         /* Find first page of handle table with free slots */
1698         for (page = 0; page < HTABLE_NPAGES; page++)
1699             if (header->freeListFirst[page] != 0)
1700                 break;
1701         if (page == HTABLE_NPAGES)
1702         {
1703             WARN( heap, "Out of handles!\n" );
1704             HeapFree( header->heap, 0, ptr );
1705             return 0;
1706         }
1707
1708         /* If virgin page, initialize it */
1709         if (header->freeListFirst[page] == 0xffff)
1710         {
1711             if ( !VirtualAlloc( (LPBYTE)header + (page << 12), 
1712                                 0x1000, MEM_COMMIT, PAGE_READWRITE ) )
1713             {
1714                 WARN( heap, "Cannot grow handle table!\n" );
1715                 HeapFree( header->heap, 0, ptr );
1716                 return 0;
1717             }
1718             
1719             header->limit += HTABLE_PAGESIZE;
1720
1721             header->freeListFirst[page] = 0;
1722             header->freeListLast[page]  = HTABLE_PAGESIZE - 4;
1723             header->freeListSize[page]  = HTABLE_PAGESIZE / 4;
1724            
1725             for (i = 0; i < HTABLE_PAGESIZE; i += 4)
1726                 *(DWORD *)((LPBYTE)header + i) = i+4;
1727
1728             if (page < HTABLE_NPAGES-1) 
1729                 header->freeListFirst[page+1] = 0xffff;
1730         }
1731
1732         /* Allocate handle slot from page */
1733         handle = (LPDWORD)((LPBYTE)header + header->freeListFirst[page]);
1734         if (--header->freeListSize[page] == 0)
1735             header->freeListFirst[page] = header->freeListLast[page] = 0;
1736         else
1737             header->freeListFirst[page] = *handle;
1738
1739         /* Store 32-bit offset in handle slot */
1740         *handle = ptr - header->base;
1741     }
1742     else
1743     {
1744         handle = (LPDWORD)ptr;
1745         header->flags |= 1;
1746     }
1747
1748
1749     /* Convert handle to requested output type */
1750     Local32_FromHandle( header, type, &addr, handle, ptr );
1751     return addr;
1752 }
1753
1754 /***********************************************************************
1755  *           Local32ReAlloc   (KERNEL.210)
1756  */
1757 DWORD WINAPI Local32ReAlloc16( HANDLE heap, DWORD addr, INT16 type,
1758                              DWORD size, DWORD flags )
1759 {
1760     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1761     LPDWORD handle;
1762     LPBYTE ptr;
1763
1764     if (!addr)
1765         return Local32Alloc16( heap, size, type, flags );
1766
1767     /* Retrieve handle and pointer */
1768     Local32_ToHandle( header, type, addr, &handle, &ptr );
1769     if (!handle) return FALSE;
1770
1771     /* Reallocate memory block */
1772     ptr = HeapReAlloc( header->heap, 
1773                        (flags & LMEM_MOVEABLE)? HEAP_ZERO_MEMORY : 0, 
1774                        ptr, size );
1775     if (!ptr) return 0;
1776
1777     /* Modify handle */
1778     if (type >= 0)
1779         *handle = ptr - header->base;
1780     else
1781         handle = (LPDWORD)ptr;
1782
1783     /* Convert handle to requested output type */
1784     Local32_FromHandle( header, type, &addr, handle, ptr );
1785     return addr;
1786 }
1787
1788 /***********************************************************************
1789  *           Local32Free   (KERNEL.211)
1790  */
1791 BOOL WINAPI Local32Free16( HANDLE heap, DWORD addr, INT16 type )
1792 {
1793     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1794     LPDWORD handle;
1795     LPBYTE ptr;
1796
1797     /* Retrieve handle and pointer */
1798     Local32_ToHandle( header, type, addr, &handle, &ptr );
1799     if (!handle) return FALSE;
1800
1801     /* Free handle if necessary */
1802     if (type >= 0)
1803     {
1804         int offset = (LPBYTE)handle - (LPBYTE)header;
1805         int page   = offset >> 12;
1806
1807         /* Return handle slot to page free list */
1808         if (header->freeListSize[page]++ == 0)
1809             header->freeListFirst[page] = header->freeListLast[page]  = offset;
1810         else
1811             *(LPDWORD)((LPBYTE)header + header->freeListLast[page]) = offset,
1812             header->freeListLast[page] = offset;
1813
1814         *handle = 0;
1815
1816         /* Shrink handle table when possible */
1817         while (page > 0 && header->freeListSize[page] == HTABLE_PAGESIZE / 4)
1818         {
1819             if ( VirtualFree( (LPBYTE)header + 
1820                               (header->limit & ~(HTABLE_PAGESIZE-1)),
1821                               HTABLE_PAGESIZE, MEM_DECOMMIT ) )
1822                 break;
1823
1824             header->limit -= HTABLE_PAGESIZE;
1825             header->freeListFirst[page] = 0xffff;
1826             page--;
1827         }
1828     }
1829
1830     /* Free memory */
1831     return HeapFree( header->heap, 0, ptr );
1832 }
1833
1834 /***********************************************************************
1835  *           Local32Translate   (KERNEL.213)
1836  */
1837 DWORD WINAPI Local32Translate16( HANDLE heap, DWORD addr, INT16 type1, INT16 type2 )
1838 {
1839     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1840     LPDWORD handle;
1841     LPBYTE ptr;
1842
1843     Local32_ToHandle( header, type1, addr, &handle, &ptr );
1844     if (!handle) return 0;
1845
1846     Local32_FromHandle( header, type2, &addr, handle, ptr );
1847     return addr;
1848 }
1849
1850 /***********************************************************************
1851  *           Local32Size   (KERNEL.214)
1852  */
1853 DWORD WINAPI Local32Size16( HANDLE heap, DWORD addr, INT16 type )
1854 {
1855     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1856     LPDWORD handle;
1857     LPBYTE ptr;
1858
1859     Local32_ToHandle( header, type, addr, &handle, &ptr );
1860     if (!handle) return 0;
1861
1862     return HeapSize( header->heap, 0, ptr );
1863 }
1864
1865 /***********************************************************************
1866  *           Local32ValidHandle   (KERNEL.215)
1867  */
1868 BOOL WINAPI Local32ValidHandle16( HANDLE heap, WORD addr )
1869 {
1870     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1871     LPDWORD handle;
1872     LPBYTE ptr;
1873
1874     Local32_ToHandle( header, 0, addr, &handle, &ptr );
1875     return handle != NULL;
1876 }
1877
1878 /***********************************************************************
1879  *           Local32GetSegment   (KERNEL.229)
1880  */
1881 WORD WINAPI Local32GetSegment16( HANDLE heap )
1882 {
1883     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1884     return header->segment;
1885 }
1886
1887 /***********************************************************************
1888  *           Local32_GetHeap
1889  */
1890 static LOCAL32HEADER *Local32_GetHeap( HGLOBAL16 handle )
1891 {
1892     WORD selector = GlobalHandleToSel16( handle );
1893     DWORD base  = GetSelectorBase( selector ); 
1894     DWORD limit = GetSelectorLimit16( selector ); 
1895
1896     /* Hmmm. This is a somewhat stupid heuristic, but Windows 95 does
1897        it this way ... */
1898
1899     if ( limit > 0x10000 && ((LOCAL32HEADER *)base)->magic == LOCAL32_MAGIC )
1900         return (LOCAL32HEADER *)base;
1901
1902     base  += 0x10000;
1903     limit -= 0x10000;
1904
1905     if ( limit > 0x10000 && ((LOCAL32HEADER *)base)->magic == LOCAL32_MAGIC )
1906         return (LOCAL32HEADER *)base;
1907
1908     return NULL;
1909 }
1910
1911 /***********************************************************************
1912  *           Local32Info   (KERNEL.444)  (TOOLHELP.84)
1913  */
1914 BOOL16 WINAPI Local32Info16( LOCAL32INFO *pLocal32Info, HGLOBAL16 handle )
1915 {
1916     SUBHEAP *heapPtr;
1917     LPBYTE ptr;
1918     int i;
1919
1920     LOCAL32HEADER *header = Local32_GetHeap( handle );
1921     if ( !header ) return FALSE;
1922
1923     if ( !pLocal32Info || pLocal32Info->dwSize < sizeof(LOCAL32INFO) )
1924         return FALSE;
1925
1926     heapPtr = (SUBHEAP *)HEAP_GetPtr( header->heap );
1927     pLocal32Info->dwMemReserved = heapPtr->size;
1928     pLocal32Info->dwMemCommitted = heapPtr->commitSize;
1929     pLocal32Info->dwTotalFree = 0L;
1930     pLocal32Info->dwLargestFreeBlock = 0L;
1931
1932     /* Note: Local32 heaps always have only one subheap! */
1933     ptr = (LPBYTE)heapPtr + heapPtr->headerSize;
1934     while ( ptr < (LPBYTE)heapPtr + heapPtr->size )
1935     {
1936         if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1937         {
1938             ARENA_FREE *pArena = (ARENA_FREE *)ptr;
1939             DWORD size = (pArena->size & ARENA_SIZE_MASK);
1940             ptr += sizeof(*pArena) + size;
1941
1942             pLocal32Info->dwTotalFree += size;
1943             if ( size > pLocal32Info->dwLargestFreeBlock )
1944                 pLocal32Info->dwLargestFreeBlock = size;
1945         }
1946         else
1947         {
1948             ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
1949             DWORD size = (pArena->size & ARENA_SIZE_MASK);
1950             ptr += sizeof(*pArena) + size;
1951         }
1952     }
1953
1954     pLocal32Info->dwcFreeHandles = 0;
1955     for ( i = 0; i < HTABLE_NPAGES; i++ )
1956     {
1957         if ( header->freeListFirst[i] == 0xffff ) break;
1958         pLocal32Info->dwcFreeHandles += header->freeListSize[i];
1959     }
1960     pLocal32Info->dwcFreeHandles += (HTABLE_NPAGES - i) * HTABLE_PAGESIZE/4;
1961
1962     return TRUE;
1963 }
1964
1965 /***********************************************************************
1966  *           Local32First   (KERNEL.445)  (TOOLHELP.85)
1967  */
1968 BOOL16 WINAPI Local32First16( LOCAL32ENTRY *pLocal32Entry, HGLOBAL16 handle )
1969 {
1970     FIXME( heap, "(%p, %04X): stub!\n", pLocal32Entry, handle );
1971     return FALSE;
1972 }
1973
1974 /***********************************************************************
1975  *           Local32Next   (KERNEL.446)  (TOOLHELP.86)
1976  */
1977 BOOL16 WINAPI Local32Next16( LOCAL32ENTRY *pLocal32Entry )
1978 {
1979     FIXME( heap, "(%p): stub!\n", pLocal32Entry );
1980     return FALSE;
1981 }
1982