Fix for SetAbortProc16, stub for SetAbortProc32 and tidy up some other
[wine/multimedia.git] / memory / heap.c
blob2e3f5800ea4a34bcc54ef95ba48ae1b8de6a9509
1 /*
2 * Win32 heap functions
4 * Copyright 1996 Alexandre Julliard
5 * Copyright 1998 Ulrich Weigand
6 */
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include "windows.h"
12 #include "selectors.h"
13 #include "global.h"
14 #include "winbase.h"
15 #include "winerror.h"
16 #include "winnt.h"
17 #include "heap.h"
18 #include "toolhelp.h"
19 #include "debug.h"
21 /* Note: the heap data structures are based on what Pietrek describes in his
22 * book 'Windows 95 System Programming Secrets'. The layout is not exactly
23 * the same, but could be easily adapted if it turns out some programs
24 * require it.
27 typedef struct tagARENA_INUSE
29 DWORD size; /* Block size; must be the first field */
30 WORD threadId; /* Allocating thread id */
31 WORD magic; /* Magic number */
32 DWORD callerEIP; /* EIP of caller upon allocation */
33 } ARENA_INUSE;
35 typedef struct tagARENA_FREE
37 DWORD size; /* Block size; must be the first field */
38 WORD threadId; /* Freeing thread id */
39 WORD magic; /* Magic number */
40 struct tagARENA_FREE *next; /* Next free arena */
41 struct tagARENA_FREE *prev; /* Prev free arena */
42 } ARENA_FREE;
44 #define ARENA_FLAG_FREE 0x00000001 /* flags OR'ed with arena size */
45 #define ARENA_FLAG_PREV_FREE 0x00000002
46 #define ARENA_SIZE_MASK 0xfffffffc
47 #define ARENA_INUSE_MAGIC 0x4842 /* Value for arena 'magic' field */
48 #define ARENA_FREE_MAGIC 0x4846 /* Value for arena 'magic' field */
50 #define ARENA_INUSE_FILLER 0x55
51 #define ARENA_FREE_FILLER 0xaa
53 #define HEAP_NB_FREE_LISTS 4 /* Number of free lists */
55 /* Max size of the blocks on the free lists */
56 static const DWORD HEAP_freeListSizes[HEAP_NB_FREE_LISTS] =
58 0x20, 0x80, 0x200, 0xffffffff
61 typedef struct
63 DWORD size;
64 ARENA_FREE arena;
65 } FREE_LIST_ENTRY;
67 struct tagHEAP;
69 typedef struct tagSUBHEAP
71 DWORD size; /* Size of the whole sub-heap */
72 DWORD commitSize; /* Committed size of the sub-heap */
73 DWORD headerSize; /* Size of the heap header */
74 struct tagSUBHEAP *next; /* Next sub-heap */
75 struct tagHEAP *heap; /* Main heap structure */
76 DWORD magic; /* Magic number */
77 WORD selector; /* Selector for HEAP_WINE_SEGPTR heaps */
78 } SUBHEAP;
80 #define SUBHEAP_MAGIC ((DWORD)('S' | ('U'<<8) | ('B'<<16) | ('H'<<24)))
82 typedef struct tagHEAP
84 SUBHEAP subheap; /* First sub-heap */
85 struct tagHEAP *next; /* Next heap for this process */
86 FREE_LIST_ENTRY freeList[HEAP_NB_FREE_LISTS]; /* Free lists */
87 CRITICAL_SECTION critSection; /* Critical section for serialization */
88 DWORD flags; /* Heap flags */
89 DWORD magic; /* Magic number */
90 } HEAP;
92 #define HEAP_MAGIC ((DWORD)('H' | ('E'<<8) | ('A'<<16) | ('P'<<24)))
94 #define HEAP_DEF_SIZE 0x110000 /* Default heap size = 1Mb + 64Kb */
95 #define HEAP_MIN_BLOCK_SIZE (8+sizeof(ARENA_FREE)) /* Min. heap block size */
97 HANDLE32 SystemHeap = 0;
98 HANDLE32 SegptrHeap = 0;
99 CRITICAL_SECTION *HEAP_SystemLock = NULL;
102 /***********************************************************************
103 * HEAP_Dump
105 void HEAP_Dump( HEAP *heap )
107 int i;
108 SUBHEAP *subheap;
109 char *ptr;
111 DUMP( "Heap: %08lx\n", (DWORD)heap );
112 DUMP( "Next: %08lx Sub-heaps: %08lx",
113 (DWORD)heap->next, (DWORD)&heap->subheap );
114 subheap = &heap->subheap;
115 while (subheap->next)
117 DUMP( " -> %08lx", (DWORD)subheap->next );
118 subheap = subheap->next;
121 DUMP( "\nFree lists:\n Block Stat Size Id\n" );
122 for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
123 DUMP( "%08lx free %08lx %04x prev=%08lx next=%08lx\n",
124 (DWORD)&heap->freeList[i].arena, heap->freeList[i].arena.size,
125 heap->freeList[i].arena.threadId,
126 (DWORD)heap->freeList[i].arena.prev,
127 (DWORD)heap->freeList[i].arena.next );
129 subheap = &heap->subheap;
130 while (subheap)
132 DWORD freeSize = 0, usedSize = 0, arenaSize = subheap->headerSize;
133 DUMP( "\n\nSub-heap %08lx: size=%08lx committed=%08lx\n",
134 (DWORD)subheap, subheap->size, subheap->commitSize );
136 DUMP( "\n Block Stat Size Id\n" );
137 ptr = (char*)subheap + subheap->headerSize;
138 while (ptr < (char *)subheap + subheap->size)
140 if (*(DWORD *)ptr & ARENA_FLAG_FREE)
142 ARENA_FREE *pArena = (ARENA_FREE *)ptr;
143 DUMP( "%08lx free %08lx %04x prev=%08lx next=%08lx\n",
144 (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
145 pArena->threadId, (DWORD)pArena->prev,
146 (DWORD)pArena->next);
147 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
148 arenaSize += sizeof(ARENA_FREE);
149 freeSize += pArena->size & ARENA_SIZE_MASK;
151 else if (*(DWORD *)ptr & ARENA_FLAG_PREV_FREE)
153 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
154 DUMP( "%08lx Used %08lx %04x back=%08lx EIP=%08lx\n",
155 (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
156 pArena->threadId, *((DWORD *)pArena - 1),
157 pArena->callerEIP );
158 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
159 arenaSize += sizeof(ARENA_INUSE);
160 usedSize += pArena->size & ARENA_SIZE_MASK;
162 else
164 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
165 DUMP( "%08lx used %08lx %04x EIP=%08lx\n",
166 (DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
167 pArena->threadId, pArena->callerEIP );
168 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
169 arenaSize += sizeof(ARENA_INUSE);
170 usedSize += pArena->size & ARENA_SIZE_MASK;
173 DUMP( "\nTotal: Size=%08lx Committed=%08lx Free=%08lx Used=%08lx Arenas=%08lx (%ld%%)\n\n",
174 subheap->size, subheap->commitSize, freeSize, usedSize,
175 arenaSize, (arenaSize * 100) / subheap->size );
176 subheap = subheap->next;
181 /***********************************************************************
182 * HEAP_GetPtr
183 * RETURNS
184 * Pointer to the heap
185 * NULL: Failure
187 static HEAP *HEAP_GetPtr(
188 HANDLE32 heap /* [in] Handle to the heap */
190 HEAP *heapPtr = (HEAP *)heap;
191 if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
193 ERR(heap, "Invalid heap %08x!\n", heap );
194 SetLastError( ERROR_INVALID_HANDLE );
195 return NULL;
197 if (TRACE_ON(heap) && !HeapValidate( heap, 0, NULL ))
199 HEAP_Dump( heapPtr );
200 assert( FALSE );
201 SetLastError( ERROR_INVALID_HANDLE );
202 return NULL;
204 return heapPtr;
208 /***********************************************************************
209 * HEAP_InsertFreeBlock
211 * Insert a free block into the free list.
213 static void HEAP_InsertFreeBlock( HEAP *heap, ARENA_FREE *pArena )
215 FREE_LIST_ENTRY *pEntry = heap->freeList;
216 while (pEntry->size < pArena->size) pEntry++;
217 pArena->size |= ARENA_FLAG_FREE;
218 pArena->next = pEntry->arena.next;
219 pArena->next->prev = pArena;
220 pArena->prev = &pEntry->arena;
221 pEntry->arena.next = pArena;
225 /***********************************************************************
226 * HEAP_FindSubHeap
227 * Find the sub-heap containing a given address.
229 * RETURNS
230 * Pointer: Success
231 * NULL: Failure
233 static SUBHEAP *HEAP_FindSubHeap(
234 HEAP *heap, /* [in] Heap pointer */
235 LPCVOID ptr /* [in] Address */
237 SUBHEAP *sub = &heap->subheap;
238 while (sub)
240 if (((char *)ptr >= (char *)sub) &&
241 ((char *)ptr < (char *)sub + sub->size)) return sub;
242 sub = sub->next;
244 return NULL;
248 /***********************************************************************
249 * HEAP_Commit
251 * Make sure the heap storage is committed up to (not including) ptr.
253 static BOOL32 HEAP_Commit( SUBHEAP *subheap, void *ptr )
255 DWORD size = (DWORD)((char *)ptr - (char *)subheap);
256 size = (size + 0xfff) & 0xfffff000; /* Align size on a page boundary */
257 if (size > subheap->size) size = subheap->size;
258 if (size <= subheap->commitSize) return TRUE;
259 if (!VirtualAlloc( (char *)subheap + subheap->commitSize,
260 size - subheap->commitSize, MEM_COMMIT,
261 PAGE_EXECUTE_READWRITE))
263 WARN(heap, "Could not commit %08lx bytes at %08lx for heap %08lx\n",
264 size - subheap->commitSize,
265 (DWORD)((char *)subheap + subheap->commitSize),
266 (DWORD)subheap->heap );
267 return FALSE;
269 subheap->commitSize = size;
270 return TRUE;
274 /***********************************************************************
275 * HEAP_Decommit
277 * If possible, decommit the heap storage from (including) 'ptr'.
279 static BOOL32 HEAP_Decommit( SUBHEAP *subheap, void *ptr )
281 DWORD size = (DWORD)((char *)ptr - (char *)subheap);
282 size = (size + 0xfff) & 0xfffff000; /* Align size on a page boundary */
283 if (size >= subheap->commitSize) return TRUE;
284 if (!VirtualFree( (char *)subheap + size,
285 subheap->commitSize - size, MEM_DECOMMIT ))
287 WARN(heap, "Could not decommit %08lx bytes at %08lx for heap %08lx\n",
288 subheap->commitSize - size,
289 (DWORD)((char *)subheap + size),
290 (DWORD)subheap->heap );
291 return FALSE;
293 subheap->commitSize = size;
294 return TRUE;
298 /***********************************************************************
299 * HEAP_CreateFreeBlock
301 * Create a free block at a specified address. 'size' is the size of the
302 * whole block, including the new arena.
304 static void HEAP_CreateFreeBlock( SUBHEAP *subheap, void *ptr, DWORD size )
306 ARENA_FREE *pFree;
308 /* Create a free arena */
310 pFree = (ARENA_FREE *)ptr;
311 pFree->threadId = GetCurrentTask();
312 pFree->magic = ARENA_FREE_MAGIC;
314 /* If debugging, erase the freed block content */
316 if (TRACE_ON(heap))
318 char *pEnd = (char *)ptr + size;
319 if (pEnd > (char *)subheap + subheap->commitSize)
320 pEnd = (char *)subheap + subheap->commitSize;
321 if (pEnd > (char *)(pFree + 1))
322 memset( pFree + 1, ARENA_FREE_FILLER, pEnd - (char *)(pFree + 1) );
325 /* Check if next block is free also */
327 if (((char *)ptr + size < (char *)subheap + subheap->size) &&
328 (*(DWORD *)((char *)ptr + size) & ARENA_FLAG_FREE))
330 /* Remove the next arena from the free list */
331 ARENA_FREE *pNext = (ARENA_FREE *)((char *)ptr + size);
332 pNext->next->prev = pNext->prev;
333 pNext->prev->next = pNext->next;
334 size += (pNext->size & ARENA_SIZE_MASK) + sizeof(*pNext);
335 if (TRACE_ON(heap))
336 memset( pNext, ARENA_FREE_FILLER, sizeof(ARENA_FREE) );
339 /* Set the next block PREV_FREE flag and pointer */
341 if ((char *)ptr + size < (char *)subheap + subheap->size)
343 DWORD *pNext = (DWORD *)((char *)ptr + size);
344 *pNext |= ARENA_FLAG_PREV_FREE;
345 *(ARENA_FREE **)(pNext - 1) = pFree;
348 /* Last, insert the new block into the free list */
350 pFree->size = size - sizeof(*pFree);
351 HEAP_InsertFreeBlock( subheap->heap, pFree );
355 /***********************************************************************
356 * HEAP_MakeInUseBlockFree
358 * Turn an in-use block into a free block. Can also decommit the end of
359 * the heap, and possibly even free the sub-heap altogether.
361 static void HEAP_MakeInUseBlockFree( SUBHEAP *subheap, ARENA_INUSE *pArena )
363 ARENA_FREE *pFree;
364 DWORD size = (pArena->size & ARENA_SIZE_MASK) + sizeof(*pArena);
366 /* Check if we can merge with previous block */
368 if (pArena->size & ARENA_FLAG_PREV_FREE)
370 pFree = *((ARENA_FREE **)pArena - 1);
371 size += (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
372 /* Remove it from the free list */
373 pFree->next->prev = pFree->prev;
374 pFree->prev->next = pFree->next;
376 else pFree = (ARENA_FREE *)pArena;
378 /* Create a free block */
380 HEAP_CreateFreeBlock( subheap, pFree, size );
381 size = (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
382 if ((char *)pFree + size < (char *)subheap + subheap->size)
383 return; /* Not the last block, so nothing more to do */
385 /* Free the whole sub-heap if it's empty and not the original one */
387 if (((char *)pFree == (char *)subheap + subheap->headerSize) &&
388 (subheap != &subheap->heap->subheap))
390 SUBHEAP *pPrev = &subheap->heap->subheap;
391 /* Remove the free block from the list */
392 pFree->next->prev = pFree->prev;
393 pFree->prev->next = pFree->next;
394 /* Remove the subheap from the list */
395 while (pPrev && (pPrev->next != subheap)) pPrev = pPrev->next;
396 if (pPrev) pPrev->next = subheap->next;
397 /* Free the memory */
398 subheap->magic = 0;
399 if (subheap->selector) FreeSelector( subheap->selector );
400 VirtualFree( subheap, 0, MEM_RELEASE );
401 return;
404 /* Decommit the end of the heap */
406 HEAP_Decommit( subheap, pFree + 1 );
410 /***********************************************************************
411 * HEAP_ShrinkBlock
413 * Shrink an in-use block.
415 static void HEAP_ShrinkBlock(SUBHEAP *subheap, ARENA_INUSE *pArena, DWORD size)
417 if ((pArena->size & ARENA_SIZE_MASK) >= size + HEAP_MIN_BLOCK_SIZE)
419 HEAP_CreateFreeBlock( subheap, (char *)(pArena + 1) + size,
420 (pArena->size & ARENA_SIZE_MASK) - size );
421 pArena->size = (pArena->size & ~ARENA_SIZE_MASK) | size;
423 else
425 /* Turn off PREV_FREE flag in next block */
426 char *pNext = (char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK);
427 if (pNext < (char *)subheap + subheap->size)
428 *(DWORD *)pNext &= ~ARENA_FLAG_PREV_FREE;
432 /***********************************************************************
433 * HEAP_InitSubHeap
435 static BOOL32 HEAP_InitSubHeap( HEAP *heap, LPVOID address, DWORD flags,
436 DWORD commitSize, DWORD totalSize )
438 SUBHEAP *subheap = (SUBHEAP *)address;
439 WORD selector = 0;
440 FREE_LIST_ENTRY *pEntry;
441 int i;
443 /* Commit memory */
445 if (!VirtualAlloc(address, commitSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
447 WARN(heap, "Could not commit %08lx bytes for sub-heap %08lx\n",
448 commitSize, (DWORD)address );
449 return FALSE;
452 /* Allocate a selector if needed */
454 if (flags & HEAP_WINE_SEGPTR)
456 selector = SELECTOR_AllocBlock( address, totalSize,
457 (flags & (HEAP_WINE_CODESEG|HEAP_WINE_CODE16SEG))
458 ? SEGMENT_CODE : SEGMENT_DATA,
459 (flags & HEAP_WINE_CODESEG) != 0, FALSE );
460 if (!selector)
462 ERR(heap, "Could not allocate selector\n" );
463 return FALSE;
467 /* Fill the sub-heap structure */
469 subheap->heap = heap;
470 subheap->selector = selector;
471 subheap->size = totalSize;
472 subheap->commitSize = commitSize;
473 subheap->magic = SUBHEAP_MAGIC;
475 if ( subheap != (SUBHEAP *)heap )
477 /* If this is a secondary subheap, insert it into list */
479 subheap->headerSize = sizeof(SUBHEAP);
480 subheap->next = heap->subheap.next;
481 heap->subheap.next = subheap;
483 else
485 /* If this is a primary subheap, initialize main heap */
487 subheap->headerSize = sizeof(HEAP);
488 subheap->next = NULL;
489 heap->next = NULL;
490 heap->flags = flags;
491 heap->magic = HEAP_MAGIC;
493 /* Build the free lists */
495 for (i = 0, pEntry = heap->freeList; i < HEAP_NB_FREE_LISTS; i++, pEntry++)
497 pEntry->size = HEAP_freeListSizes[i];
498 pEntry->arena.size = 0 | ARENA_FLAG_FREE;
499 pEntry->arena.next = i < HEAP_NB_FREE_LISTS-1 ?
500 &heap->freeList[i+1].arena : &heap->freeList[0].arena;
501 pEntry->arena.prev = i ? &heap->freeList[i-1].arena :
502 &heap->freeList[HEAP_NB_FREE_LISTS-1].arena;
503 pEntry->arena.threadId = 0;
504 pEntry->arena.magic = ARENA_FREE_MAGIC;
507 /* Initialize critical section */
509 InitializeCriticalSection( &heap->critSection );
510 if (!SystemHeap) HEAP_SystemLock = &heap->critSection;
513 /* Create the first free block */
515 HEAP_CreateFreeBlock( subheap, (LPBYTE)subheap + subheap->headerSize,
516 subheap->size - subheap->headerSize );
518 return TRUE;
521 /***********************************************************************
522 * HEAP_CreateSubHeap
524 * Create a sub-heap of the given size.
525 * If heap == NULL, creates a main heap.
527 static SUBHEAP *HEAP_CreateSubHeap( HEAP *heap, DWORD flags,
528 DWORD commitSize, DWORD totalSize )
530 LPVOID address;
532 /* Round-up sizes on a 64K boundary */
534 if (flags & HEAP_WINE_SEGPTR)
536 totalSize = commitSize = 0x10000; /* Only 64K at a time for SEGPTRs */
538 else
540 totalSize = (totalSize + 0xffff) & 0xffff0000;
541 commitSize = (commitSize + 0xffff) & 0xffff0000;
542 if (!commitSize) commitSize = 0x10000;
543 if (totalSize < commitSize) totalSize = commitSize;
546 /* Allocate the memory block */
548 if (!(address = VirtualAlloc( NULL, totalSize,
549 MEM_RESERVE, PAGE_EXECUTE_READWRITE )))
551 WARN(heap, "Could not VirtualAlloc %08lx bytes\n",
552 totalSize );
553 return NULL;
556 /* Initialize subheap */
558 if (!HEAP_InitSubHeap( heap? heap : (HEAP *)address,
559 address, flags, commitSize, totalSize ))
561 VirtualFree( address, 0, MEM_RELEASE );
562 return NULL;
565 return (SUBHEAP *)address;
569 /***********************************************************************
570 * HEAP_FindFreeBlock
572 * Find a free block at least as large as the requested size, and make sure
573 * the requested size is committed.
575 static ARENA_FREE *HEAP_FindFreeBlock( HEAP *heap, DWORD size,
576 SUBHEAP **ppSubHeap )
578 SUBHEAP *subheap;
579 ARENA_FREE *pArena;
580 FREE_LIST_ENTRY *pEntry = heap->freeList;
582 /* Find a suitable free list, and in it find a block large enough */
584 while (pEntry->size < size) pEntry++;
585 pArena = pEntry->arena.next;
586 while (pArena != &heap->freeList[0].arena)
588 if (pArena->size > size)
590 subheap = HEAP_FindSubHeap( heap, pArena );
591 if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
592 + size + HEAP_MIN_BLOCK_SIZE))
593 return NULL;
594 *ppSubHeap = subheap;
595 return pArena;
598 pArena = pArena->next;
601 /* If no block was found, attempt to grow the heap */
603 if (!(heap->flags & HEAP_GROWABLE))
605 WARN(heap, "Not enough space in heap %08lx for %08lx bytes\n",
606 (DWORD)heap, size );
607 return NULL;
609 size += sizeof(SUBHEAP) + sizeof(ARENA_FREE);
610 if (!(subheap = HEAP_CreateSubHeap( heap, heap->flags, size,
611 MAX( HEAP_DEF_SIZE, size ) )))
612 return NULL;
614 TRACE(heap, "created new sub-heap %08lx of %08lx bytes for heap %08lx\n",
615 (DWORD)subheap, size, (DWORD)heap );
617 *ppSubHeap = subheap;
618 return (ARENA_FREE *)(subheap + 1);
622 /***********************************************************************
623 * HEAP_IsValidArenaPtr
625 * Check that the pointer is inside the range possible for arenas.
627 static BOOL32 HEAP_IsValidArenaPtr( HEAP *heap, void *ptr )
629 int i;
630 SUBHEAP *subheap = HEAP_FindSubHeap( heap, ptr );
631 if (!subheap) return FALSE;
632 if ((char *)ptr >= (char *)subheap + subheap->headerSize) return TRUE;
633 if (subheap != &heap->subheap) return FALSE;
634 for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
635 if (ptr == (void *)&heap->freeList[i].arena) return TRUE;
636 return FALSE;
640 /***********************************************************************
641 * HEAP_ValidateFreeArena
643 static BOOL32 HEAP_ValidateFreeArena( SUBHEAP *subheap, ARENA_FREE *pArena )
645 char *heapEnd = (char *)subheap + subheap->size;
647 /* Check magic number */
648 if (pArena->magic != ARENA_FREE_MAGIC)
650 ERR(heap, "Heap %08lx: invalid free arena magic for %08lx\n",
651 (DWORD)subheap->heap, (DWORD)pArena );
652 return FALSE;
654 /* Check size flags */
655 if (!(pArena->size & ARENA_FLAG_FREE) ||
656 (pArena->size & ARENA_FLAG_PREV_FREE))
658 ERR(heap, "Heap %08lx: bad flags %lx for free arena %08lx\n",
659 (DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
661 /* Check arena size */
662 if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
664 ERR(heap, "Heap %08lx: bad size %08lx for free arena %08lx\n",
665 (DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
666 return FALSE;
668 /* Check that next pointer is valid */
669 if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->next ))
671 ERR(heap, "Heap %08lx: bad next ptr %08lx for arena %08lx\n",
672 (DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
673 return FALSE;
675 /* Check that next arena is free */
676 if (!(pArena->next->size & ARENA_FLAG_FREE) ||
677 (pArena->next->magic != ARENA_FREE_MAGIC))
679 ERR(heap, "Heap %08lx: next arena %08lx invalid for %08lx\n",
680 (DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
681 return FALSE;
683 /* Check that prev pointer is valid */
684 if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->prev ))
686 ERR(heap, "Heap %08lx: bad prev ptr %08lx for arena %08lx\n",
687 (DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
688 return FALSE;
690 /* Check that prev arena is free */
691 if (!(pArena->prev->size & ARENA_FLAG_FREE) ||
692 (pArena->prev->magic != ARENA_FREE_MAGIC))
694 ERR(heap, "Heap %08lx: prev arena %08lx invalid for %08lx\n",
695 (DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
696 return FALSE;
698 /* Check that next block has PREV_FREE flag */
699 if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd)
701 if (!(*(DWORD *)((char *)(pArena + 1) +
702 (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
704 ERR(heap, "Heap %08lx: free arena %08lx next block has no PREV_FREE flag\n",
705 (DWORD)subheap->heap, (DWORD)pArena );
706 return FALSE;
708 /* Check next block back pointer */
709 if (*((ARENA_FREE **)((char *)(pArena + 1) +
710 (pArena->size & ARENA_SIZE_MASK)) - 1) != pArena)
712 ERR(heap, "Heap %08lx: arena %08lx has wrong back ptr %08lx\n",
713 (DWORD)subheap->heap, (DWORD)pArena,
714 *((DWORD *)((char *)(pArena+1)+ (pArena->size & ARENA_SIZE_MASK)) - 1));
715 return FALSE;
718 return TRUE;
722 /***********************************************************************
723 * HEAP_ValidateInUseArena
725 static BOOL32 HEAP_ValidateInUseArena( SUBHEAP *subheap, ARENA_INUSE *pArena )
727 char *heapEnd = (char *)subheap + subheap->size;
729 /* Check magic number */
730 if (pArena->magic != ARENA_INUSE_MAGIC)
732 ERR(heap, "Heap %08lx: invalid in-use arena magic for %08lx\n",
733 (DWORD)subheap->heap, (DWORD)pArena );
734 return FALSE;
736 /* Check size flags */
737 if (pArena->size & ARENA_FLAG_FREE)
739 ERR(heap, "Heap %08lx: bad flags %lx for in-use arena %08lx\n",
740 (DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
742 /* Check arena size */
743 if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
745 ERR(heap, "Heap %08lx: bad size %08lx for in-use arena %08lx\n",
746 (DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
747 return FALSE;
749 /* Check next arena PREV_FREE flag */
750 if (((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd) &&
751 (*(DWORD *)((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
753 ERR(heap, "Heap %08lx: in-use arena %08lx next block has PREV_FREE flag\n",
754 (DWORD)subheap->heap, (DWORD)pArena );
755 return FALSE;
757 /* Check prev free arena */
758 if (pArena->size & ARENA_FLAG_PREV_FREE)
760 ARENA_FREE *pPrev = *((ARENA_FREE **)pArena - 1);
761 /* Check prev pointer */
762 if (!HEAP_IsValidArenaPtr( subheap->heap, pPrev ))
764 ERR(heap, "Heap %08lx: bad back ptr %08lx for arena %08lx\n",
765 (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
766 return FALSE;
768 /* Check that prev arena is free */
769 if (!(pPrev->size & ARENA_FLAG_FREE) ||
770 (pPrev->magic != ARENA_FREE_MAGIC))
772 ERR(heap, "Heap %08lx: prev arena %08lx invalid for in-use %08lx\n",
773 (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
774 return FALSE;
776 /* Check that prev arena is really the previous block */
777 if ((char *)(pPrev + 1) + (pPrev->size & ARENA_SIZE_MASK) != (char *)pArena)
779 ERR(heap, "Heap %08lx: prev arena %08lx is not prev for in-use %08lx\n",
780 (DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
781 return FALSE;
784 return TRUE;
788 /***********************************************************************
789 * HEAP_IsInsideHeap
790 * Checks whether the pointer points to a block inside a given heap.
792 * NOTES
793 * Should this return BOOL32?
795 * RETURNS
796 * !0: Success
797 * 0: Failure
799 int HEAP_IsInsideHeap(
800 HANDLE32 heap, /* [in] Heap */
801 DWORD flags, /* [in] Flags */
802 LPCVOID ptr /* [in] Pointer */
804 HEAP *heapPtr = HEAP_GetPtr( heap );
805 SUBHEAP *subheap;
806 int ret;
808 /* Validate the parameters */
810 if (!heapPtr) return 0;
811 flags |= heapPtr->flags;
812 if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
813 ret = (((subheap = HEAP_FindSubHeap( heapPtr, ptr )) != NULL) &&
814 (((char *)ptr >= (char *)subheap + subheap->headerSize
815 + sizeof(ARENA_INUSE))));
816 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
817 return ret;
821 /***********************************************************************
822 * HEAP_GetSegptr
824 * Transform a linear pointer into a SEGPTR. The pointer must have been
825 * allocated from a HEAP_WINE_SEGPTR heap.
827 SEGPTR HEAP_GetSegptr( HANDLE32 heap, DWORD flags, LPCVOID ptr )
829 HEAP *heapPtr = HEAP_GetPtr( heap );
830 SUBHEAP *subheap;
831 SEGPTR ret;
833 /* Validate the parameters */
835 if (!heapPtr) return 0;
836 flags |= heapPtr->flags;
837 if (!(flags & HEAP_WINE_SEGPTR))
839 ERR(heap, "Heap %08x is not a SEGPTR heap\n",
840 heap );
841 return 0;
843 if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
845 /* Get the subheap */
847 if (!(subheap = HEAP_FindSubHeap( heapPtr, ptr )))
849 ERR(heap, "%p is not inside heap %08x\n",
850 ptr, heap );
851 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
852 return 0;
855 /* Build the SEGPTR */
857 ret = PTR_SEG_OFF_TO_SEGPTR(subheap->selector, (DWORD)ptr-(DWORD)subheap);
858 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
859 return ret;
863 /***********************************************************************
864 * HeapCreate (KERNEL32.336)
865 * RETURNS
866 * Handle of heap: Success
867 * NULL: Failure
869 HANDLE32 WINAPI HeapCreate(
870 DWORD flags, /* [in] Heap allocation flag */
871 DWORD initialSize, /* [in] Initial heap size */
872 DWORD maxSize /* [in] Maximum heap size */
874 SUBHEAP *subheap;
876 /* Allocate the heap block */
878 if (!maxSize)
880 maxSize = HEAP_DEF_SIZE;
881 flags |= HEAP_GROWABLE;
883 if (!(subheap = HEAP_CreateSubHeap( NULL, flags, initialSize, maxSize )))
885 SetLastError( ERROR_OUTOFMEMORY );
886 return 0;
889 return (HANDLE32)subheap;
892 /***********************************************************************
893 * HeapDestroy (KERNEL32.337)
894 * RETURNS
895 * TRUE: Success
896 * FALSE: Failure
898 BOOL32 WINAPI HeapDestroy(
899 HANDLE32 heap /* [in] Handle of heap */
901 HEAP *heapPtr = HEAP_GetPtr( heap );
902 SUBHEAP *subheap;
904 TRACE(heap, "%08x\n", heap );
905 if (!heapPtr) return FALSE;
907 DeleteCriticalSection( &heapPtr->critSection );
908 subheap = &heapPtr->subheap;
909 while (subheap)
911 SUBHEAP *next = subheap->next;
912 if (subheap->selector) FreeSelector( subheap->selector );
913 VirtualFree( subheap, 0, MEM_RELEASE );
914 subheap = next;
916 return TRUE;
920 /***********************************************************************
921 * HeapAlloc (KERNEL32.334)
922 * RETURNS
923 * Pointer to allocated memory block
924 * NULL: Failure
926 LPVOID WINAPI HeapAlloc(
927 HANDLE32 heap, /* [in] Handle of private heap block */
928 DWORD flags, /* [in] Heap allocation control flags */
929 DWORD size /* [in] Number of bytes to allocate */
931 ARENA_FREE *pArena;
932 ARENA_INUSE *pInUse;
933 SUBHEAP *subheap;
934 HEAP *heapPtr = HEAP_GetPtr( heap );
936 /* Validate the parameters */
938 if (!heapPtr) return NULL;
939 flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY;
940 flags |= heapPtr->flags;
941 if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
942 size = (size + 3) & ~3;
943 if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
945 /* Locate a suitable free block */
947 if (!(pArena = HEAP_FindFreeBlock( heapPtr, size, &subheap )))
949 TRACE(heap, "(%08x,%08lx,%08lx): returning NULL\n",
950 heap, flags, size );
951 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
952 SetLastError( ERROR_COMMITMENT_LIMIT );
953 return NULL;
956 /* Remove the arena from the free list */
958 pArena->next->prev = pArena->prev;
959 pArena->prev->next = pArena->next;
961 /* Build the in-use arena */
963 pInUse = (ARENA_INUSE *)pArena;
964 pInUse->size = (pInUse->size & ~ARENA_FLAG_FREE)
965 + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
966 pInUse->callerEIP = *((DWORD *)&heap - 1); /* hack hack */
967 pInUse->threadId = GetCurrentTask();
968 pInUse->magic = ARENA_INUSE_MAGIC;
970 /* Shrink the block */
972 HEAP_ShrinkBlock( subheap, pInUse, size );
974 if (flags & HEAP_ZERO_MEMORY) memset( pInUse + 1, 0, size );
975 else if (TRACE_ON(heap)) memset( pInUse + 1, ARENA_INUSE_FILLER, size );
977 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
979 TRACE(heap, "(%08x,%08lx,%08lx): returning %08lx\n",
980 heap, flags, size, (DWORD)(pInUse + 1) );
981 return (LPVOID)(pInUse + 1);
985 /***********************************************************************
986 * HeapFree (KERNEL32.338)
987 * RETURNS
988 * TRUE: Success
989 * FALSE: Failure
991 BOOL32 WINAPI HeapFree(
992 HANDLE32 heap, /* [in] Handle of heap */
993 DWORD flags, /* [in] Heap freeing flags */
994 LPVOID ptr /* [in] Address of memory to free */
996 ARENA_INUSE *pInUse;
997 SUBHEAP *subheap;
998 HEAP *heapPtr = HEAP_GetPtr( heap );
1000 /* Validate the parameters */
1002 if (!heapPtr) return FALSE;
1003 flags &= HEAP_NO_SERIALIZE;
1004 flags |= heapPtr->flags;
1005 if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
1006 if (!ptr)
1008 WARN(heap, "(%08x,%08lx,%08lx): asked to free NULL\n",
1009 heap, flags, (DWORD)ptr );
1011 if (!ptr || !HeapValidate( heap, HEAP_NO_SERIALIZE, ptr ))
1013 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1014 SetLastError( ERROR_INVALID_PARAMETER );
1015 TRACE(heap, "(%08x,%08lx,%08lx): returning FALSE\n",
1016 heap, flags, (DWORD)ptr );
1017 return FALSE;
1020 /* Turn the block into a free block */
1022 pInUse = (ARENA_INUSE *)ptr - 1;
1023 subheap = HEAP_FindSubHeap( heapPtr, pInUse );
1024 HEAP_MakeInUseBlockFree( subheap, pInUse );
1026 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1027 /* SetLastError( 0 ); */
1029 TRACE(heap, "(%08x,%08lx,%08lx): returning TRUE\n",
1030 heap, flags, (DWORD)ptr );
1031 return TRUE;
1035 /***********************************************************************
1036 * HeapReAlloc (KERNEL32.340)
1037 * RETURNS
1038 * Pointer to reallocated memory block
1039 * NULL: Failure
1041 LPVOID WINAPI HeapReAlloc(
1042 HANDLE32 heap, /* [in] Handle of heap block */
1043 DWORD flags, /* [in] Heap reallocation flags */
1044 LPVOID ptr, /* [in] Address of memory to reallocate */
1045 DWORD size /* [in] Number of bytes to reallocate */
1047 ARENA_INUSE *pArena;
1048 DWORD oldSize;
1049 HEAP *heapPtr;
1050 SUBHEAP *subheap;
1052 if (!ptr) return HeapAlloc( heap, flags, size ); /* FIXME: correct? */
1053 if (!(heapPtr = HEAP_GetPtr( heap ))) return FALSE;
1055 /* Validate the parameters */
1057 flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY |
1058 HEAP_REALLOC_IN_PLACE_ONLY;
1059 flags |= heapPtr->flags;
1060 size = (size + 3) & ~3;
1061 if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
1063 if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
1064 if (!HeapValidate( heap, HEAP_NO_SERIALIZE, ptr ))
1066 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1067 SetLastError( ERROR_INVALID_PARAMETER );
1068 TRACE(heap, "(%08x,%08lx,%08lx,%08lx): returning NULL\n",
1069 heap, flags, (DWORD)ptr, size );
1070 return NULL;
1073 /* Check if we need to grow the block */
1075 pArena = (ARENA_INUSE *)ptr - 1;
1076 pArena->threadId = GetCurrentTask();
1077 subheap = HEAP_FindSubHeap( heapPtr, pArena );
1078 oldSize = (pArena->size & ARENA_SIZE_MASK);
1079 if (size > oldSize)
1081 char *pNext = (char *)(pArena + 1) + oldSize;
1082 if ((pNext < (char *)subheap + subheap->size) &&
1083 (*(DWORD *)pNext & ARENA_FLAG_FREE) &&
1084 (oldSize + (*(DWORD *)pNext & ARENA_SIZE_MASK) + sizeof(ARENA_FREE) >= size))
1086 /* The next block is free and large enough */
1087 ARENA_FREE *pFree = (ARENA_FREE *)pNext;
1088 pFree->next->prev = pFree->prev;
1089 pFree->prev->next = pFree->next;
1090 pArena->size += (pFree->size & ARENA_SIZE_MASK) + sizeof(*pFree);
1091 if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
1092 + size + HEAP_MIN_BLOCK_SIZE))
1094 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1095 SetLastError( ERROR_OUTOFMEMORY );
1096 return NULL;
1098 HEAP_ShrinkBlock( subheap, pArena, size );
1100 else /* Do it the hard way */
1102 ARENA_FREE *pNew;
1103 ARENA_INUSE *pInUse;
1104 SUBHEAP *newsubheap;
1106 if ((flags & HEAP_REALLOC_IN_PLACE_ONLY) ||
1107 !(pNew = HEAP_FindFreeBlock( heapPtr, size, &newsubheap )))
1109 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1110 SetLastError( ERROR_OUTOFMEMORY );
1111 return NULL;
1114 /* Build the in-use arena */
1116 pNew->next->prev = pNew->prev;
1117 pNew->prev->next = pNew->next;
1118 pInUse = (ARENA_INUSE *)pNew;
1119 pInUse->size = (pInUse->size & ~ARENA_FLAG_FREE)
1120 + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1121 pInUse->threadId = GetCurrentTask();
1122 pInUse->magic = ARENA_INUSE_MAGIC;
1123 HEAP_ShrinkBlock( newsubheap, pInUse, size );
1124 memcpy( pInUse + 1, pArena + 1, oldSize );
1126 /* Free the previous block */
1128 HEAP_MakeInUseBlockFree( subheap, pArena );
1129 subheap = newsubheap;
1130 pArena = pInUse;
1133 else HEAP_ShrinkBlock( subheap, pArena, size ); /* Shrink the block */
1135 /* Clear the extra bytes if needed */
1137 if (size > oldSize)
1139 if (flags & HEAP_ZERO_MEMORY)
1140 memset( (char *)(pArena + 1) + oldSize, 0,
1141 (pArena->size & ARENA_SIZE_MASK) - oldSize );
1142 else if (TRACE_ON(heap))
1143 memset( (char *)(pArena + 1) + oldSize, ARENA_INUSE_FILLER,
1144 (pArena->size & ARENA_SIZE_MASK) - oldSize );
1147 /* Return the new arena */
1149 pArena->callerEIP = *((DWORD *)&heap - 1); /* hack hack */
1150 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1152 TRACE(heap, "(%08x,%08lx,%08lx,%08lx): returning %08lx\n",
1153 heap, flags, (DWORD)ptr, size, (DWORD)(pArena + 1) );
1154 return (LPVOID)(pArena + 1);
1158 /***********************************************************************
1159 * HeapCompact (KERNEL32.335)
1161 DWORD WINAPI HeapCompact( HANDLE32 heap, DWORD flags )
1163 return 0;
1167 /***********************************************************************
1168 * HeapLock (KERNEL32.339)
1169 * Attempts to acquire the critical section object for a specified heap.
1171 * RETURNS
1172 * TRUE: Success
1173 * FALSE: Failure
1175 BOOL32 WINAPI HeapLock(
1176 HANDLE32 heap /* [in] Handle of heap to lock for exclusive access */
1178 HEAP *heapPtr = HEAP_GetPtr( heap );
1179 if (!heapPtr) return FALSE;
1180 EnterCriticalSection( &heapPtr->critSection );
1181 return TRUE;
1185 /***********************************************************************
1186 * HeapUnlock (KERNEL32.342)
1187 * Releases ownership of the critical section object.
1189 * RETURNS
1190 * TRUE: Success
1191 * FALSE: Failure
1193 BOOL32 WINAPI HeapUnlock(
1194 HANDLE32 heap /* [in] Handle to the heap to unlock */
1196 HEAP *heapPtr = HEAP_GetPtr( heap );
1197 if (!heapPtr) return FALSE;
1198 LeaveCriticalSection( &heapPtr->critSection );
1199 return TRUE;
1203 /***********************************************************************
1204 * HeapSize (KERNEL32.341)
1205 * RETURNS
1206 * Size in bytes of allocated memory
1207 * 0: Failure
1209 DWORD WINAPI HeapSize(
1210 HANDLE32 heap, /* [in] Handle of heap */
1211 DWORD flags, /* [in] Heap size control flags */
1212 LPVOID ptr /* [in] Address of memory to return size for */
1214 DWORD ret;
1215 HEAP *heapPtr = HEAP_GetPtr( heap );
1217 if (!heapPtr) return FALSE;
1218 flags &= HEAP_NO_SERIALIZE;
1219 flags |= heapPtr->flags;
1220 if (!(flags & HEAP_NO_SERIALIZE)) HeapLock( heap );
1221 if (!HeapValidate( heap, HEAP_NO_SERIALIZE, ptr ))
1223 SetLastError( ERROR_INVALID_PARAMETER );
1224 ret = 0xffffffff;
1226 else
1228 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr - 1;
1229 ret = pArena->size & ARENA_SIZE_MASK;
1231 if (!(flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
1233 TRACE(heap, "(%08x,%08lx,%08lx): returning %08lx\n",
1234 heap, flags, (DWORD)ptr, ret );
1235 return ret;
1239 /***********************************************************************
1240 * HeapValidate (KERNEL32.343)
1241 * Validates a specified heap.
1243 * NOTES
1244 * Flags is ignored.
1246 * RETURNS
1247 * TRUE: Success
1248 * FALSE: Failure
1250 BOOL32 WINAPI HeapValidate(
1251 HANDLE32 heap, /* [in] Handle to the heap */
1252 DWORD flags, /* [in] Bit flags that control access during operation */
1253 LPCVOID block /* [in] Optional pointer to memory block to validate */
1255 SUBHEAP *subheap;
1256 HEAP *heapPtr = (HEAP *)(heap);
1258 if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
1260 ERR(heap, "Invalid heap %08x!\n", heap );
1261 return FALSE;
1264 if (block)
1266 /* Only check this single memory block */
1267 if (!(subheap = HEAP_FindSubHeap( heapPtr, block )) ||
1268 ((char *)block < (char *)subheap + subheap->headerSize
1269 + sizeof(ARENA_INUSE)))
1271 ERR(heap, "Heap %08lx: block %08lx is not inside heap\n",
1272 (DWORD)heap, (DWORD)block );
1273 return FALSE;
1275 return HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)block - 1 );
1278 subheap = &heapPtr->subheap;
1279 while (subheap)
1281 char *ptr = (char *)subheap + subheap->headerSize;
1282 while (ptr < (char *)subheap + subheap->size)
1284 if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1286 if (!HEAP_ValidateFreeArena( subheap, (ARENA_FREE *)ptr ))
1287 return FALSE;
1288 ptr += sizeof(ARENA_FREE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1290 else
1292 if (!HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)ptr ))
1293 return FALSE;
1294 ptr += sizeof(ARENA_INUSE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1297 subheap = subheap->next;
1299 return TRUE;
1303 /***********************************************************************
1304 * HeapWalk (KERNEL32.344)
1305 * Enumerates the memory blocks in a specified heap.
1307 * RETURNS
1308 * TRUE: Success
1309 * FALSE: Failure
1311 BOOL32 WINAPI HeapWalk(
1312 HANDLE32 heap, /* [in] Handle to heap to enumerate */
1313 LPPROCESS_HEAP_ENTRY *entry /* [out] Pointer to structure of enumeration info */
1315 FIXME(heap, "(%08x): stub.\n", heap );
1316 return FALSE;
1320 /***********************************************************************
1321 * HEAP_xalloc
1323 * Same as HeapAlloc(), but die on failure.
1325 LPVOID HEAP_xalloc( HANDLE32 heap, DWORD flags, DWORD size )
1327 LPVOID p = HeapAlloc( heap, flags, size );
1328 if (!p)
1330 MSG("Virtual memory exhausted.\n" );
1331 exit(1);
1333 return p;
1337 /***********************************************************************
1338 * HEAP_strdupA
1340 LPSTR HEAP_strdupA( HANDLE32 heap, DWORD flags, LPCSTR str )
1342 LPSTR p = HEAP_xalloc( heap, flags, strlen(str) + 1 );
1343 strcpy( p, str );
1344 return p;
1348 /***********************************************************************
1349 * HEAP_strdupW
1351 LPWSTR HEAP_strdupW( HANDLE32 heap, DWORD flags, LPCWSTR str )
1353 INT32 len = lstrlen32W(str) + 1;
1354 LPWSTR p = HEAP_xalloc( heap, flags, len * sizeof(WCHAR) );
1355 lstrcpy32W( p, str );
1356 return p;
1360 /***********************************************************************
1361 * HEAP_strdupAtoW
1363 LPWSTR HEAP_strdupAtoW( HANDLE32 heap, DWORD flags, LPCSTR str )
1365 LPWSTR ret;
1367 if (!str) return NULL;
1368 ret = HEAP_xalloc( heap, flags, (strlen(str)+1) * sizeof(WCHAR) );
1369 lstrcpyAtoW( ret, str );
1370 return ret;
1374 /***********************************************************************
1375 * HEAP_strdupWtoA
1377 LPSTR HEAP_strdupWtoA( HANDLE32 heap, DWORD flags, LPCWSTR str )
1379 LPSTR ret;
1381 if (!str) return NULL;
1382 ret = HEAP_xalloc( heap, flags, lstrlen32W(str) + 1 );
1383 lstrcpyWtoA( ret, str );
1384 return ret;
1389 /***********************************************************************
1390 * 32-bit local heap functions (Win95; undocumented)
1393 #define HTABLE_SIZE 0x10000
1394 #define HTABLE_PAGESIZE 0x1000
1395 #define HTABLE_NPAGES (HTABLE_SIZE / HTABLE_PAGESIZE)
1397 #pragma pack(1)
1398 typedef struct _LOCAL32HEADER
1400 WORD freeListFirst[HTABLE_NPAGES];
1401 WORD freeListSize[HTABLE_NPAGES];
1402 WORD freeListLast[HTABLE_NPAGES];
1404 DWORD selectorTableOffset;
1405 WORD selectorTableSize;
1406 WORD selectorDelta;
1408 DWORD segment;
1409 LPBYTE base;
1411 DWORD limit;
1412 DWORD flags;
1414 DWORD magic;
1415 HANDLE32 heap;
1417 } LOCAL32HEADER;
1418 #pragma pack(4)
1420 #define LOCAL32_MAGIC ((DWORD)('L' | ('H'<<8) | ('3'<<16) | ('2'<<24)))
1422 /***********************************************************************
1423 * Local32Init (KERNEL.208)
1425 HANDLE32 WINAPI Local32Init( WORD segment, DWORD tableSize,
1426 DWORD heapSize, DWORD flags )
1428 DWORD totSize, segSize = 0;
1429 LPBYTE base;
1430 LOCAL32HEADER *header;
1431 HEAP *heap;
1432 WORD *selectorTable;
1433 WORD selectorEven, selectorOdd;
1434 int i, nrBlocks;
1436 /* Determine new heap size */
1438 if ( segment )
1440 if ( (segSize = GetSelectorLimit( segment )) == 0 )
1441 return 0;
1442 else
1443 segSize++;
1446 if ( heapSize == -1L )
1447 heapSize = 1024L*1024L; /* FIXME */
1449 heapSize = (heapSize + 0xffff) & 0xffff0000;
1450 segSize = (segSize + 0x0fff) & 0xfffff000;
1451 totSize = segSize + HTABLE_SIZE + heapSize;
1454 /* Allocate memory and initialize heap */
1456 if ( !(base = VirtualAlloc( NULL, totSize, MEM_RESERVE, PAGE_READWRITE )) )
1457 return 0;
1459 if ( !VirtualAlloc( base, segSize + HTABLE_PAGESIZE,
1460 MEM_COMMIT, PAGE_READWRITE ) )
1462 VirtualFree( base, 0, MEM_RELEASE );
1463 return 0;
1466 heap = (HEAP *)(base + segSize + HTABLE_SIZE);
1467 if ( !HEAP_InitSubHeap( heap, (LPVOID)heap, 0, 0x10000, heapSize ) )
1469 VirtualFree( base, 0, MEM_RELEASE );
1470 return 0;
1474 /* Set up header and handle table */
1476 header = (LOCAL32HEADER *)(base + segSize);
1477 header->base = base;
1478 header->limit = HTABLE_PAGESIZE-1;
1479 header->flags = 0;
1480 header->magic = LOCAL32_MAGIC;
1481 header->heap = (HANDLE32)heap;
1483 header->freeListFirst[0] = sizeof(LOCAL32HEADER);
1484 header->freeListLast[0] = HTABLE_PAGESIZE - 4;
1485 header->freeListSize[0] = (HTABLE_PAGESIZE - sizeof(LOCAL32HEADER)) / 4;
1487 for (i = header->freeListFirst[0]; i < header->freeListLast[0]; i += 4)
1488 *(DWORD *)((LPBYTE)header + i) = i+4;
1490 header->freeListFirst[1] = 0xffff;
1493 /* Set up selector table */
1495 nrBlocks = (totSize + 0x7fff) >> 15;
1496 selectorTable = (LPWORD) HeapAlloc( header->heap, 0, nrBlocks * 2 );
1497 selectorEven = SELECTOR_AllocBlock( base, totSize,
1498 SEGMENT_DATA, FALSE, FALSE );
1499 selectorOdd = SELECTOR_AllocBlock( base + 0x8000, totSize - 0x8000,
1500 SEGMENT_DATA, FALSE, FALSE );
1502 if ( !selectorTable || !selectorEven || !selectorOdd )
1504 if ( selectorTable ) HeapFree( header->heap, 0, selectorTable );
1505 if ( selectorEven ) SELECTOR_FreeBlock( selectorEven, totSize >> 16 );
1506 if ( selectorOdd ) SELECTOR_FreeBlock( selectorOdd, (totSize-0x8000) >> 16 );
1507 HeapDestroy( header->heap );
1508 VirtualFree( base, 0, MEM_RELEASE );
1509 return 0;
1512 header->selectorTableOffset = (LPBYTE)selectorTable - header->base;
1513 header->selectorTableSize = nrBlocks * 4; /* ??? Win95 does it this way! */
1514 header->selectorDelta = selectorEven - selectorOdd;
1515 header->segment = segment? segment : selectorEven;
1517 for (i = 0; i < nrBlocks; i++)
1518 selectorTable[i] = (i & 1)? selectorOdd + ((i >> 1) << __AHSHIFT)
1519 : selectorEven + ((i >> 1) << __AHSHIFT);
1521 /* Move old segment */
1523 if ( segment )
1525 /* FIXME: This is somewhat ugly and relies on implementation
1526 details about 16-bit global memory handles ... */
1528 LPBYTE oldBase = (LPBYTE)GetSelectorBase( segment );
1529 memcpy( base, oldBase, segSize );
1530 GLOBAL_MoveBlock( segment, base, totSize );
1531 HeapFree( SystemHeap, 0, oldBase );
1534 return (HANDLE32)header;
1537 /***********************************************************************
1538 * Local32_SearchHandle
1540 static LPDWORD Local32_SearchHandle( LOCAL32HEADER *header, DWORD addr )
1542 LPDWORD handle;
1544 for ( handle = (LPDWORD)((LPBYTE)header + sizeof(LOCAL32HEADER));
1545 handle < (LPDWORD)((LPBYTE)header + header->limit);
1546 handle++)
1548 if (*handle == addr)
1549 return handle;
1552 return NULL;
1555 /***********************************************************************
1556 * Local32_ToHandle
1558 static VOID Local32_ToHandle( LOCAL32HEADER *header, INT16 type,
1559 DWORD addr, LPDWORD *handle, LPBYTE *ptr )
1561 *handle = NULL;
1562 *ptr = NULL;
1564 switch (type)
1566 case -2: /* 16:16 pointer, no handles */
1567 *ptr = PTR_SEG_TO_LIN( addr );
1568 *handle = (LPDWORD)*ptr;
1569 break;
1571 case -1: /* 32-bit offset, no handles */
1572 *ptr = header->base + addr;
1573 *handle = (LPDWORD)*ptr;
1574 break;
1576 case 0: /* handle */
1577 if ( addr >= sizeof(LOCAL32HEADER)
1578 && addr < header->limit && !(addr & 3)
1579 && *(LPDWORD)((LPBYTE)header + addr) >= HTABLE_SIZE )
1581 *handle = (LPDWORD)((LPBYTE)header + addr);
1582 *ptr = header->base + **handle;
1584 break;
1586 case 1: /* 16:16 pointer */
1587 *ptr = PTR_SEG_TO_LIN( addr );
1588 *handle = Local32_SearchHandle( header, *ptr - header->base );
1589 break;
1591 case 2: /* 32-bit offset */
1592 *ptr = header->base + addr;
1593 *handle = Local32_SearchHandle( header, *ptr - header->base );
1594 break;
1598 /***********************************************************************
1599 * Local32_FromHandle
1601 static VOID Local32_FromHandle( LOCAL32HEADER *header, INT16 type,
1602 DWORD *addr, LPDWORD handle, LPBYTE ptr )
1604 switch (type)
1606 case -2: /* 16:16 pointer */
1607 case 1:
1609 WORD *selTable = (LPWORD)(header->base + header->selectorTableOffset);
1610 DWORD offset = (LPBYTE)ptr - header->base;
1611 *addr = MAKELONG( offset & 0x7fff, selTable[offset >> 15] );
1613 break;
1615 case -1: /* 32-bit offset */
1616 case 2:
1617 *addr = ptr - header->base;
1618 break;
1620 case 0: /* handle */
1621 *addr = (LPBYTE)handle - (LPBYTE)header;
1622 break;
1626 /***********************************************************************
1627 * Local32Alloc (KERNEL.209)
1629 DWORD WINAPI Local32Alloc( HANDLE32 heap, DWORD size, INT16 type, DWORD flags )
1631 LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1632 LPDWORD handle;
1633 LPBYTE ptr;
1634 DWORD addr;
1636 /* Allocate memory */
1637 ptr = HeapAlloc( header->heap,
1638 (flags & LMEM_MOVEABLE)? HEAP_ZERO_MEMORY : 0, size );
1639 if (!ptr) return 0;
1642 /* Allocate handle if requested */
1643 if (type >= 0)
1645 int page, i;
1647 /* Find first page of handle table with free slots */
1648 for (page = 0; page < HTABLE_NPAGES; page++)
1649 if (header->freeListFirst[page] != 0)
1650 break;
1651 if (page == HTABLE_NPAGES)
1653 WARN( heap, "Out of handles!\n" );
1654 HeapFree( header->heap, 0, ptr );
1655 return 0;
1658 /* If virgin page, initialize it */
1659 if (header->freeListFirst[page] == 0xffff)
1661 if ( !VirtualAlloc( (LPBYTE)header + (page << 12),
1662 0x1000, MEM_COMMIT, PAGE_READWRITE ) )
1664 WARN( heap, "Cannot grow handle table!\n" );
1665 HeapFree( header->heap, 0, ptr );
1666 return 0;
1669 header->limit += HTABLE_PAGESIZE;
1671 header->freeListFirst[page] = 0;
1672 header->freeListLast[page] = HTABLE_PAGESIZE - 4;
1673 header->freeListSize[page] = HTABLE_PAGESIZE / 4;
1675 for (i = 0; i < HTABLE_PAGESIZE; i += 4)
1676 *(DWORD *)((LPBYTE)header + i) = i+4;
1678 if (page < HTABLE_NPAGES-1)
1679 header->freeListFirst[page+1] = 0xffff;
1682 /* Allocate handle slot from page */
1683 handle = (LPDWORD)((LPBYTE)header + header->freeListFirst[page]);
1684 if (--header->freeListSize[page] == 0)
1685 header->freeListFirst[page] = header->freeListLast[page] = 0;
1686 else
1687 header->freeListFirst[page] = *handle;
1689 /* Store 32-bit offset in handle slot */
1690 *handle = ptr - header->base;
1692 else
1694 handle = (LPDWORD)ptr;
1695 header->flags |= 1;
1699 /* Convert handle to requested output type */
1700 Local32_FromHandle( header, type, &addr, handle, ptr );
1701 return addr;
1704 /***********************************************************************
1705 * Local32ReAlloc (KERNEL.210)
1707 DWORD WINAPI Local32ReAlloc( HANDLE32 heap, DWORD addr, INT16 type,
1708 DWORD size, DWORD flags )
1710 LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1711 LPDWORD handle;
1712 LPBYTE ptr;
1714 if (!addr)
1715 return Local32Alloc( heap, size, type, flags );
1717 /* Retrieve handle and pointer */
1718 Local32_ToHandle( header, type, addr, &handle, &ptr );
1719 if (!handle) return FALSE;
1721 /* Reallocate memory block */
1722 ptr = HeapReAlloc( header->heap,
1723 (flags & LMEM_MOVEABLE)? HEAP_ZERO_MEMORY : 0,
1724 ptr, size );
1725 if (!ptr) return 0;
1727 /* Modify handle */
1728 if (type >= 0)
1729 *handle = ptr - header->base;
1730 else
1731 handle = (LPDWORD)ptr;
1733 /* Convert handle to requested output type */
1734 Local32_FromHandle( header, type, &addr, handle, ptr );
1735 return addr;
1738 /***********************************************************************
1739 * Local32Free (KERNEL.211)
1741 BOOL32 WINAPI Local32Free( HANDLE32 heap, DWORD addr, INT16 type )
1743 LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1744 LPDWORD handle;
1745 LPBYTE ptr;
1747 /* Retrieve handle and pointer */
1748 Local32_ToHandle( header, type, addr, &handle, &ptr );
1749 if (!handle) return FALSE;
1751 /* Free handle if necessary */
1752 if (type >= 0)
1754 int offset = (LPBYTE)handle - (LPBYTE)header;
1755 int page = offset >> 12;
1757 /* Return handle slot to page free list */
1758 if (header->freeListSize[page]++ == 0)
1759 header->freeListFirst[page] = header->freeListLast[page] = offset;
1760 else
1761 *(LPDWORD)((LPBYTE)header + header->freeListLast[page]) = offset,
1762 header->freeListLast[page] = offset;
1764 *handle = 0;
1766 /* Shrink handle table when possible */
1767 while (page > 0 && header->freeListSize[page] == HTABLE_PAGESIZE / 4)
1769 if ( VirtualFree( (LPBYTE)header +
1770 (header->limit & ~(HTABLE_PAGESIZE-1)),
1771 HTABLE_PAGESIZE, MEM_DECOMMIT ) )
1772 break;
1774 header->limit -= HTABLE_PAGESIZE;
1775 header->freeListFirst[page] = 0xffff;
1776 page--;
1780 /* Free memory */
1781 return HeapFree( header->heap, 0, ptr );
1784 /***********************************************************************
1785 * Local32Translate (KERNEL.213)
1787 DWORD WINAPI Local32Translate( HANDLE32 heap, DWORD addr, INT16 type1, INT16 type2 )
1789 LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1790 LPDWORD handle;
1791 LPBYTE ptr;
1793 Local32_ToHandle( header, type1, addr, &handle, &ptr );
1794 if (!handle) return 0;
1796 Local32_FromHandle( header, type2, &addr, handle, ptr );
1797 return addr;
1800 /***********************************************************************
1801 * Local32Size (KERNEL.214)
1803 DWORD WINAPI Local32Size( HANDLE32 heap, DWORD addr, INT16 type )
1805 LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1806 LPDWORD handle;
1807 LPBYTE ptr;
1809 Local32_ToHandle( header, type, addr, &handle, &ptr );
1810 if (!handle) return 0;
1812 return HeapSize( header->heap, 0, ptr );
1815 /***********************************************************************
1816 * Local32ValidHandle (KERNEL.215)
1818 BOOL32 WINAPI Local32ValidHandle( HANDLE32 heap, WORD addr )
1820 LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1821 LPDWORD handle;
1822 LPBYTE ptr;
1824 Local32_ToHandle( header, 0, addr, &handle, &ptr );
1825 return handle != NULL;
1828 /***********************************************************************
1829 * Local32GetSegment (KERNEL.229)
1831 WORD WINAPI Local32GetSegment( HANDLE32 heap )
1833 LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
1834 return header->segment;
1837 /***********************************************************************
1838 * Local32_GetHeap
1840 static LOCAL32HEADER *Local32_GetHeap( HGLOBAL16 handle )
1842 WORD selector = GlobalHandleToSel( handle );
1843 DWORD base = GetSelectorBase( selector );
1844 DWORD limit = GetSelectorLimit( selector );
1846 /* Hmmm. This is a somewhat stupid heuristic, but Windows 95 does
1847 it this way ... */
1849 if ( limit > 0x10000 && ((LOCAL32HEADER *)base)->magic == LOCAL32_MAGIC )
1850 return (LOCAL32HEADER *)base;
1852 base += 0x10000;
1853 limit -= 0x10000;
1855 if ( limit > 0x10000 && ((LOCAL32HEADER *)base)->magic == LOCAL32_MAGIC )
1856 return (LOCAL32HEADER *)base;
1858 return NULL;
1861 /***********************************************************************
1862 * Local32Info (KERNEL.444) (TOOLHELP.84)
1864 BOOL16 WINAPI Local32Info( LOCAL32INFO *pLocal32Info, HGLOBAL16 handle )
1866 SUBHEAP *heapPtr;
1867 LPBYTE ptr;
1868 int i;
1870 LOCAL32HEADER *header = Local32_GetHeap( handle );
1871 if ( !header ) return FALSE;
1873 if ( !pLocal32Info || pLocal32Info->dwSize < sizeof(LOCAL32INFO) )
1874 return FALSE;
1876 heapPtr = (SUBHEAP *)HEAP_GetPtr( header->heap );
1877 pLocal32Info->dwMemReserved = heapPtr->size;
1878 pLocal32Info->dwMemCommitted = heapPtr->commitSize;
1879 pLocal32Info->dwTotalFree = 0L;
1880 pLocal32Info->dwLargestFreeBlock = 0L;
1882 /* Note: Local32 heaps always have only one subheap! */
1883 ptr = (LPBYTE)heapPtr + heapPtr->headerSize;
1884 while ( ptr < (LPBYTE)heapPtr + heapPtr->size )
1886 if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1888 ARENA_FREE *pArena = (ARENA_FREE *)ptr;
1889 DWORD size = (pArena->size & ARENA_SIZE_MASK);
1890 ptr += sizeof(*pArena) + size;
1892 pLocal32Info->dwTotalFree += size;
1893 if ( size > pLocal32Info->dwLargestFreeBlock )
1894 pLocal32Info->dwLargestFreeBlock = size;
1896 else
1898 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
1899 DWORD size = (pArena->size & ARENA_SIZE_MASK);
1900 ptr += sizeof(*pArena) + size;
1904 pLocal32Info->dwcFreeHandles = 0;
1905 for ( i = 0; i < HTABLE_NPAGES; i++ )
1907 if ( header->freeListFirst[i] == 0xffff ) break;
1908 pLocal32Info->dwcFreeHandles += header->freeListSize[i];
1910 pLocal32Info->dwcFreeHandles += (HTABLE_NPAGES - i) * HTABLE_PAGESIZE/4;
1912 return TRUE;
1915 /***********************************************************************
1916 * Local32First (KERNEL.445) (TOOLHELP.85)
1918 BOOL16 WINAPI Local32First( LOCAL32ENTRY *pLocal32Entry, HGLOBAL16 handle )
1920 FIXME( heap, "(%p, %04X): stub!\n", pLocal32Entry, handle );
1921 return FALSE;
1924 /***********************************************************************
1925 * Local32Next (KERNEL.446) (TOOLHELP.86)
1927 BOOL16 WINAPI Local32Next( LOCAL32ENTRY *pLocal32Entry )
1929 FIXME( heap, "(%p): stub!\n", pLocal32Entry );
1930 return FALSE;