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