4 * Copyright 1995 Alexandre Julliard
9 * All local heap functions need the current DS as first parameter
10 * when called from the emulation library, so they take one more
11 * parameter than usual.
20 #include "stackframe.h"
33 WORD prev; /* Previous arena | arena type */
34 WORD next; /* Next arena */
35 /* Start of the memory block or free-list info */
36 WORD size; /* Size of the free block */
37 WORD free_prev; /* Previous free block */
38 WORD free_next; /* Next free block */
41 #define ARENA_HEADER_SIZE 4
43 /* Arena types (stored in 'prev' field of the arena) */
44 #define LOCAL_ARENA_FREE 0
45 #define LOCAL_ARENA_FIXED 1
46 #define LOCAL_ARENA_MOVEABLE 3
52 WORD addr; /* Address of the MOVEABLE block */
53 BYTE flags; /* Flags for this block */
54 BYTE lock; /* Lock count */
59 WORD check; /* Heap checking flag */
60 WORD freeze; /* Heap frozen flag */
61 WORD items; /* Count of items on the heap */
62 WORD first; /* First item of the heap */
63 WORD pad1; /* Always 0 */
64 WORD last; /* Last item of the heap */
65 WORD pad2; /* Always 0 */
66 BYTE ncompact; /* Compactions counter */
67 BYTE dislevel; /* Discard level */
68 DWORD distotal; /* Total bytes discarded */
69 WORD htable; /* Pointer to handle table */
70 WORD hfree; /* Pointer to free handle table */
71 WORD hdelta; /* Delta to expand the handle table */
72 WORD expand; /* Pointer to expand function (unused) */
73 WORD pstat; /* Pointer to status structure (unused) */
74 DWORD notify WINE_PACKED; /* Pointer to LocalNotify() function */
75 WORD lock; /* Lock count for the heap */
76 WORD extra; /* Extra bytes to allocate when expanding */
77 WORD minsize; /* Minimum size of the heap */
78 WORD magic; /* Magic number */
85 #define LOCAL_HEAP_MAGIC 0x484c /* 'LH' */
88 /* All local heap allocations are aligned on 4-byte boundaries */
89 #define LALIGN(word) (((word) + 3) & ~3)
91 #define ARENA_PTR(ptr,arena) ((LOCALARENA *)((char*)(ptr)+(arena)))
92 #define ARENA_PREV(ptr,arena) (ARENA_PTR(ptr,arena)->prev & ~3)
93 #define ARENA_NEXT(ptr,arena) (ARENA_PTR(ptr,arena)->next)
94 #define ARENA_FLAGS(ptr,arena) (ARENA_PTR(ptr,arena)->prev & 3)
97 /***********************************************************************
100 * Return a pointer to the local heap, making sure it exists.
102 static LOCALHEAPINFO *LOCAL_GetHeap( WORD ds )
104 LOCALHEAPINFO *pInfo;
105 INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( ds, 0 );
106 if (!ptr->heap) return 0;
107 pInfo = (LOCALHEAPINFO*)((char*)ptr + ptr->heap);
108 if (pInfo->magic != LOCAL_HEAP_MAGIC) return NULL;
113 /***********************************************************************
116 * Make a block free, inserting it in the free-list.
117 * 'block' is the handle of the block arena; 'baseptr' points to
118 * the beginning of the data segment containing the heap.
120 static void LOCAL_AddFreeBlock( char *baseptr, WORD block )
122 LOCALARENA *pArena, *pNext;
125 /* Mark the block as free */
127 pArena = ARENA_PTR( baseptr, block );
128 pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FREE;
129 pArena->size = pArena->next - block;
131 /* Find the next free block (last block is always free) */
136 pNext = ARENA_PTR( baseptr, next );
137 if ((pNext->prev & 3) == LOCAL_ARENA_FREE) break;
141 /* Insert the free block in the free-list */
143 pArena->free_prev = pNext->free_prev;
144 pArena->free_next = next;
145 ARENA_PTR(baseptr,pNext->free_prev)->free_next = block;
146 pNext->free_prev = block;
150 /***********************************************************************
151 * LOCAL_RemoveFreeBlock
153 * Remove a block from the free-list.
154 * 'block' is the handle of the block arena; 'baseptr' points to
155 * the beginning of the data segment containing the heap.
157 static void LOCAL_RemoveFreeBlock( char *baseptr, WORD block )
159 /* Mark the block as fixed */
161 LOCALARENA *pArena = ARENA_PTR( baseptr, block );
162 pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FIXED;
164 /* Remove it from the list */
166 ARENA_PTR(baseptr,pArena->free_prev)->free_next = pArena->free_next;
167 ARENA_PTR(baseptr,pArena->free_next)->free_prev = pArena->free_prev;
171 /***********************************************************************
174 * Insert a new block in the heap.
175 * 'new' is the handle of the new block arena; 'baseptr' points to
176 * the beginning of the data segment containing the heap; 'prev' is
177 * the block before the new one.
179 static void LOCAL_AddBlock( char *baseptr, WORD prev, WORD new )
181 LOCALARENA *pPrev = ARENA_PTR( baseptr, prev );
182 LOCALARENA *pNew = ARENA_PTR( baseptr, new );
184 pNew->prev = prev | LOCAL_ARENA_FIXED;
185 pNew->next = pPrev->next;
186 ARENA_PTR(baseptr,pPrev->next)->prev &= 3;
187 ARENA_PTR(baseptr,pPrev->next)->prev |= new;
192 /***********************************************************************
195 * Remove a block from the heap.
196 * 'block' is the handle of the block arena; 'baseptr' points to
197 * the beginning of the data segment containing the heap.
199 static void LOCAL_RemoveBlock( char *baseptr, WORD block )
201 LOCALARENA *pArena, *pTmp;
203 /* Remove the block from the free-list */
205 pArena = ARENA_PTR( baseptr, block );
206 if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
207 LOCAL_RemoveFreeBlock( baseptr, block );
209 /* If the previous block is free, expand its size */
211 pTmp = ARENA_PTR( baseptr, pArena->prev & ~3 );
212 if ((pTmp->prev & 3) == LOCAL_ARENA_FREE)
213 pTmp->size += pArena->next - block;
215 /* Remove the block from the linked list */
217 pTmp->next = pArena->next;
218 pTmp = ARENA_PTR( baseptr, pArena->next );
219 pTmp->prev = (pTmp->prev & 3) | (pArena->prev & ~3);
223 /***********************************************************************
226 static void LOCAL_PrintHeap( WORD ds )
228 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
229 LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
234 printf( "Local Heap corrupted! ds=%04x\n", ds );
237 printf( "Local Heap ds=%04x first=%04x last=%04x items=%d\n",
238 ds, pInfo->first, pInfo->last, pInfo->items );
240 arena = pInfo->first;
243 LOCALARENA *pArena = ARENA_PTR(ptr,arena);
244 printf( " %04x: prev=%04x next=%04x type=%d\n", arena,
245 pArena->prev & ~3, pArena->next, pArena->prev & 3 );
246 if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
248 printf( " size=%d free_prev=%04x free_next=%04x\n",
249 pArena->size, pArena->free_prev, pArena->free_next );
250 if (pArena->next == arena) break; /* last one */
251 if (ARENA_PTR(ptr,pArena->free_next)->free_prev != arena)
253 printf( "*** arena->free_next->free_prev != arena\n" );
257 if (pArena->next == arena)
259 printf( "*** last block is not marked free\n" );
262 if ((ARENA_PTR(ptr,pArena->next)->prev & ~3) != arena)
264 printf( "*** arena->next->prev != arena\n" );
267 arena = pArena->next;
272 /***********************************************************************
273 * LocalInit (KERNEL.4)
275 HLOCAL LocalInit( WORD selector, WORD start, WORD end )
278 WORD heapInfoArena, freeArena, lastArena;
279 LOCALHEAPINFO *pHeapInfo;
280 LOCALARENA *pArena, *pFirstArena, *pLastArena;
282 /* The initial layout of the heap is: */
283 /* - first arena (FIXED) */
284 /* - heap info structure (FIXED) */
285 /* - large free block (FREE) */
286 /* - last arena (FREE) */
288 dprintf_local(stddeb, "LocalInit: %04x %04x-%04x\n", selector, start, end);
289 if (!selector) selector = CURRENT_DS;
290 ptr = PTR_SEG_OFF_TO_LIN( selector, 0 );
291 pHeapInfo = LOCAL_GetHeap(selector);
292 /* If there's already a local heap in this segment, */
293 /* we simply return TRUE. Helps some programs, but */
294 /* does not seem to be 100% correct yet (there are */
295 /* still some "heap corrupted" messages in LocalAlloc */
297 dprintf_local(stddeb,"LocalInit: Heap %04x initialized twice.\n",selector);
298 if (debugging_local) LOCAL_PrintHeap(selector);
301 start = LALIGN( max( start, sizeof(INSTANCEDATA) ) );
302 heapInfoArena = LALIGN(start + sizeof(LOCALARENA) );
303 freeArena = LALIGN( heapInfoArena + ARENA_HEADER_SIZE
304 + sizeof(LOCALHEAPINFO) );
305 lastArena = (end - sizeof(LOCALARENA)) & ~3;
307 /* Make sure there's enough space. */
309 if (freeArena + sizeof(LOCALARENA) >= lastArena) return FALSE;
311 /* Initialise the first arena */
313 pFirstArena = ARENA_PTR( ptr, start );
314 pFirstArena->prev = start | LOCAL_ARENA_FIXED;
315 pFirstArena->next = heapInfoArena;
316 pFirstArena->size = LALIGN(sizeof(LOCALARENA));
317 pFirstArena->free_prev = start; /* this one */
318 pFirstArena->free_next = freeArena;
320 /* Initialise the arena of the heap info structure */
322 pArena = ARENA_PTR( ptr, heapInfoArena );
323 pArena->prev = start | LOCAL_ARENA_FIXED;
324 pArena->next = freeArena;
326 /* Initialise the heap info structure */
328 pHeapInfo = (LOCALHEAPINFO *) (ptr + heapInfoArena + ARENA_HEADER_SIZE );
329 memset( pHeapInfo, 0, sizeof(LOCALHEAPINFO) );
330 pHeapInfo->items = 4;
331 pHeapInfo->first = start;
332 pHeapInfo->last = lastArena;
333 pHeapInfo->hdelta = 0x20;
334 pHeapInfo->extra = 0x200;
335 pHeapInfo->minsize = lastArena - freeArena;
336 pHeapInfo->magic = LOCAL_HEAP_MAGIC;
338 /* Initialise the large free block */
340 pArena = ARENA_PTR( ptr, freeArena );
341 pArena->prev = heapInfoArena | LOCAL_ARENA_FREE;
342 pArena->next = lastArena;
343 pArena->size = lastArena - freeArena;
344 pArena->free_prev = start;
345 pArena->free_next = lastArena;
347 /* Initialise the last block */
349 pLastArena = ARENA_PTR( ptr, lastArena );
350 pLastArena->prev = heapInfoArena | LOCAL_ARENA_FREE;
351 pLastArena->next = lastArena; /* this one */
352 pLastArena->size = LALIGN(sizeof(LOCALARENA));
353 pLastArena->free_prev = freeArena;
354 pLastArena->free_next = lastArena; /* this one */
356 /* Store the local heap address in the instance data */
358 ((INSTANCEDATA *)ptr)->heap = heapInfoArena + ARENA_HEADER_SIZE;
363 /***********************************************************************
366 * Implementation of LocalAlloc().
368 HLOCAL LOCAL_Alloc( WORD ds, WORD flags, WORD size )
370 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
371 LOCALHEAPINFO *pInfo;
375 dprintf_local( stddeb, "LocalAlloc: %04x %d ds=%04x\n", flags, size, ds );
377 /* Find a suitable free block */
379 if (!(pInfo = LOCAL_GetHeap( ds ))) {
383 size += ARENA_HEADER_SIZE;
384 size = LALIGN( max( size, sizeof(LOCALARENA) ) );
385 arena = pInfo->first;
386 pArena = ARENA_PTR( ptr, arena );
389 if (arena == pArena->free_next) {
391 return 0; /* not found */
393 arena = pArena->free_next;
394 pArena = ARENA_PTR( ptr, arena );
395 if (pArena->size >= size) break;
398 /* Make a block out of the free arena */
400 if (pArena->size > size + LALIGN(sizeof(LOCALARENA)))
402 LOCAL_AddBlock( ptr, arena, arena+size );
403 LOCAL_AddFreeBlock( ptr, arena+size );
406 LOCAL_RemoveFreeBlock( ptr, arena );
408 dprintf_local( stddeb, "LocalAlloc: returning %04x\n",
409 arena + ARENA_HEADER_SIZE );
410 return arena + ARENA_HEADER_SIZE;
414 /***********************************************************************
417 * Implementation of LocalReAlloc().
419 HLOCAL LOCAL_ReAlloc( WORD ds, HLOCAL handle, WORD size, WORD flags )
421 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
422 LOCALHEAPINFO *pInfo;
423 LOCALARENA *pArena, *pNext;
424 WORD arena, newhandle;
426 dprintf_local( stddeb, "LocalReAlloc: %04x %d %04x ds=%04x\n",
427 handle, size, flags, ds );
428 if (!(pInfo = LOCAL_GetHeap( ds ))) return 0;
429 arena = handle - ARENA_HEADER_SIZE;
430 pArena = ARENA_PTR( ptr, arena );
432 size = LALIGN( size );
434 /* Check for size reduction */
436 if (size < pArena->next - handle)
438 if (handle + size < pArena->next - LALIGN(sizeof(LOCALARENA)))
440 /* It is worth making a new free block */
441 LOCAL_AddBlock( ptr, arena, handle + size );
442 LOCAL_AddFreeBlock( ptr, handle + size );
445 dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
449 /* Check if the next block is free */
451 pNext = ARENA_PTR( ptr, pArena->next );
452 if (((pNext->prev & 3) == LOCAL_ARENA_FREE) &&
453 (size <= pNext->next - handle))
455 LOCAL_RemoveBlock( ptr, pArena->next );
456 if (handle + size < pArena->next - LALIGN(sizeof(LOCALARENA)))
458 /* It is worth making a new free block */
459 LOCAL_AddBlock( ptr, arena, handle + size );
460 LOCAL_AddFreeBlock( ptr, handle + size );
463 dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
467 /* Now we have to allocate a new block */
469 newhandle = LOCAL_Alloc( ds, flags, size );
470 if (!newhandle) return 0;
471 memcpy( ptr + newhandle, ptr + handle, pArena->next - handle );
472 LOCAL_Free( ds, handle );
473 dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", newhandle );
478 /***********************************************************************
481 * Implementation of LocalFree().
483 HLOCAL LOCAL_Free( WORD ds, HLOCAL handle )
485 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
486 LOCALHEAPINFO *pInfo;
487 LOCALARENA *pArena, *pPrev, *pNext;
490 dprintf_local( stddeb, "LocalFree: %04x ds=%04x\n", handle, ds );
491 if (!(pInfo = LOCAL_GetHeap( ds ))) return handle;
492 arena = handle - ARENA_HEADER_SIZE;
493 pArena = ARENA_PTR( ptr, arena );
494 if ((pArena->prev & 3) == LOCAL_ARENA_FREE) return handle;
496 /* Check if we can merge with the previous block */
498 pPrev = ARENA_PTR( ptr, pArena->prev & ~3 );
499 pNext = ARENA_PTR( ptr, pArena->next );
500 if ((pPrev->prev & 3) == LOCAL_ARENA_FREE)
502 arena = pArena->prev & ~3;
504 LOCAL_RemoveBlock( ptr, pPrev->next );
507 else /* Make a new free block */
509 LOCAL_AddFreeBlock( ptr, arena );
512 /* Check if we can merge with the next block */
514 if ((pArena->next == pArena->free_next) &&
515 (pArena->next != pInfo->last))
517 LOCAL_RemoveBlock( ptr, pArena->next );
524 /***********************************************************************
527 * Implementation of LocalSize().
529 WORD LOCAL_Size( WORD ds, HLOCAL handle )
531 LOCALARENA *pArena = PTR_SEG_OFF_TO_LIN( ds, handle - ARENA_HEADER_SIZE );
532 return pArena->next - handle;
536 /***********************************************************************
539 * Implementation of LocalHeapSize().
541 WORD LOCAL_HeapSize( WORD ds )
543 LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
544 if (!pInfo) return 0;
545 return pInfo->last - pInfo->first;
549 /***********************************************************************
550 * LocalAlloc (KERNEL.5)
552 HLOCAL LocalAlloc( WORD flags, WORD size )
554 return LOCAL_Alloc( CURRENT_DS, flags, size );
558 /***********************************************************************
559 * LocalReAlloc (KERNEL.6)
561 HLOCAL LocalReAlloc( HLOCAL handle, WORD flags, WORD size )
563 return LOCAL_ReAlloc( CURRENT_DS, handle, flags, size );
567 /***********************************************************************
568 * LocalFree (KERNEL.7)
570 HLOCAL LocalFree( HLOCAL handle )
572 return LOCAL_Free( CURRENT_DS, handle );
576 /***********************************************************************
577 * LocalLock (KERNEL.8)
579 WORD LocalLock( HLOCAL handle )
585 /***********************************************************************
586 * LocalUnlock (KERNEL.9)
588 BOOL LocalUnlock( HLOCAL handle )
594 /***********************************************************************
595 * LocalSize (KERNEL.10)
597 WORD LocalSize( HLOCAL handle )
599 return LOCAL_Size( CURRENT_DS, handle );
603 /***********************************************************************
604 * LocalHandle (KERNEL.11)
606 HLOCAL LocalHandle( WORD addr )
608 dprintf_local( stddeb, "LocalHandle: %04x\n", addr );
613 /***********************************************************************
614 * LocalFlags (KERNEL.12)
616 WORD LocalFlags( HLOCAL handle )
618 dprintf_local( stddeb, "LocalFlags: %04x\n", handle );
623 /***********************************************************************
624 * LocalCompact (KERNEL.13)
626 WORD LocalCompact( WORD minfree )
631 /***********************************************************************
632 * LocalNotify (KERNEL.14)
634 FARPROC LocalNotify( FARPROC func )
639 /***********************************************************************
640 * LocalShrink (KERNEL.121)
642 WORD LocalShrink( HLOCAL handle, WORD newsize )
647 /***********************************************************************
648 * GetHeapSpaces (KERNEL.138)
650 DWORD GetHeapSpaces( HMODULE module )
652 return MAKELONG( 0x7fff, 0xffff );
656 /***********************************************************************
657 * LocalCountFree (KERNEL.161)
659 void LocalCountFree()
664 /***********************************************************************
665 * LocalHeapSize (KERNEL.162)
669 return LOCAL_HeapSize( CURRENT_DS );
673 /***********************************************************************
674 * LocalHandleDelta (KERNEL.310)
676 WORD LocalHandleDelta( WORD delta )
681 /***********************************************************************
682 * LocalInfo (TOOLHELP.56)
684 BOOL LocalInfo( LOCALINFO *pLocalInfo, HGLOBAL handle )
686 LOCALHEAPINFO *pInfo = LOCAL_GetHeap(SELECTOROF(WIN16_GlobalLock(handle)));
687 if (!pInfo) return FALSE;
688 pLocalInfo->wcItems = pInfo->items;
693 /***********************************************************************
694 * LocalFirst (TOOLHELP.57)
696 BOOL LocalFirst( LOCALENTRY *pLocalEntry, HGLOBAL handle )
698 WORD ds = SELECTOROF( WIN16_GlobalLock( handle ) );
699 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
700 LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
701 if (!pInfo) return FALSE;
703 pLocalEntry->hHandle = pInfo->first + ARENA_HEADER_SIZE;
704 pLocalEntry->wAddress = pLocalEntry->hHandle;
705 pLocalEntry->wFlags = LF_FIXED;
706 pLocalEntry->wcLock = 0;
707 pLocalEntry->wType = LT_NORMAL;
708 pLocalEntry->hHeap = handle;
709 pLocalEntry->wHeapType = NORMAL_HEAP;
710 pLocalEntry->wNext = ARENA_PTR(ptr,pInfo->first)->next;
711 pLocalEntry->wSize = pLocalEntry->wNext - pLocalEntry->hHandle;
716 /***********************************************************************
717 * LocalNext (TOOLHELP.58)
719 BOOL LocalNext( LOCALENTRY *pLocalEntry )
721 WORD ds = SELECTOROF( pLocalEntry->hHeap );
722 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
725 if (!LOCAL_GetHeap( ds )) return FALSE;
726 if (!pLocalEntry->wNext) return FALSE;
727 pArena = ARENA_PTR( ptr, pLocalEntry->wNext );
729 pLocalEntry->hHandle = pLocalEntry->wNext + ARENA_HEADER_SIZE;
730 pLocalEntry->wAddress = pLocalEntry->hHandle;
731 pLocalEntry->wFlags = (pArena->prev & 3) + 1;
732 pLocalEntry->wcLock = 0;
733 pLocalEntry->wType = LT_NORMAL;
734 if (pArena->next != pLocalEntry->wNext) /* last one? */
735 pLocalEntry->wNext = pArena->next;
737 pLocalEntry->wNext = 0;
738 pLocalEntry->wSize = pLocalEntry->wNext - pLocalEntry->hHandle;