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 /* FIXME: What should be done if there's already */
289 /* a local heap in this segment? */
290 dprintf_local(stddeb
, "LocalInit: %04x %04x-%04x\n", selector
, start
, end
);
291 if (!selector
) selector
= CURRENT_DS
;
292 ptr
= PTR_SEG_OFF_TO_LIN( selector
, 0 );
293 start
= LALIGN( max( start
, sizeof(INSTANCEDATA
) ) );
294 heapInfoArena
= LALIGN(start
+ sizeof(LOCALARENA
) );
295 freeArena
= LALIGN( heapInfoArena
+ ARENA_HEADER_SIZE
296 + sizeof(LOCALHEAPINFO
) );
297 lastArena
= (end
- sizeof(LOCALARENA
)) & ~3;
299 /* Make sure there's enough space. */
301 if (freeArena
+ sizeof(LOCALARENA
) >= lastArena
) return FALSE
;
303 /* Initialise the first arena */
305 pFirstArena
= ARENA_PTR( ptr
, start
);
306 pFirstArena
->prev
= start
| LOCAL_ARENA_FIXED
;
307 pFirstArena
->next
= heapInfoArena
;
308 pFirstArena
->size
= LALIGN(sizeof(LOCALARENA
));
309 pFirstArena
->free_prev
= start
; /* this one */
310 pFirstArena
->free_next
= freeArena
;
312 /* Initialise the arena of the heap info structure */
314 pArena
= ARENA_PTR( ptr
, heapInfoArena
);
315 pArena
->prev
= start
| LOCAL_ARENA_FIXED
;
316 pArena
->next
= freeArena
;
318 /* Initialise the heap info structure */
320 pHeapInfo
= (LOCALHEAPINFO
*) (ptr
+ heapInfoArena
+ ARENA_HEADER_SIZE
);
321 memset( pHeapInfo
, 0, sizeof(LOCALHEAPINFO
) );
322 pHeapInfo
->items
= 4;
323 pHeapInfo
->first
= start
;
324 pHeapInfo
->last
= lastArena
;
325 pHeapInfo
->hdelta
= 0x20;
326 pHeapInfo
->extra
= 0x200;
327 pHeapInfo
->minsize
= lastArena
- freeArena
;
328 pHeapInfo
->magic
= LOCAL_HEAP_MAGIC
;
330 /* Initialise the large free block */
332 pArena
= ARENA_PTR( ptr
, freeArena
);
333 pArena
->prev
= heapInfoArena
| LOCAL_ARENA_FREE
;
334 pArena
->next
= lastArena
;
335 pArena
->size
= lastArena
- freeArena
;
336 pArena
->free_prev
= start
;
337 pArena
->free_next
= lastArena
;
339 /* Initialise the last block */
341 pLastArena
= ARENA_PTR( ptr
, lastArena
);
342 pLastArena
->prev
= heapInfoArena
| LOCAL_ARENA_FREE
;
343 pLastArena
->next
= lastArena
; /* this one */
344 pLastArena
->size
= LALIGN(sizeof(LOCALARENA
));
345 pLastArena
->free_prev
= freeArena
;
346 pLastArena
->free_next
= lastArena
; /* this one */
348 /* Store the local heap address in the instance data */
350 ((INSTANCEDATA
*)ptr
)->heap
= heapInfoArena
+ ARENA_HEADER_SIZE
;
355 /***********************************************************************
358 * Implementation of LocalAlloc().
360 HLOCAL
LOCAL_Alloc( WORD ds
, WORD flags
, WORD size
)
362 char *ptr
= PTR_SEG_OFF_TO_LIN( ds
, 0 );
363 LOCALHEAPINFO
*pInfo
;
367 dprintf_local( stddeb
, "LocalAlloc: %04x %d ds=%04x\n", flags
, size
, ds
);
369 /* Find a suitable free block */
371 if (!(pInfo
= LOCAL_GetHeap( ds
))) return 0;
372 size
+= ARENA_HEADER_SIZE
;
373 size
= LALIGN( max( size
, sizeof(LOCALARENA
) ) );
374 arena
= pInfo
->first
;
375 pArena
= ARENA_PTR( ptr
, arena
);
378 if (arena
== pArena
->free_next
) return 0; /* not found */
379 arena
= pArena
->free_next
;
380 pArena
= ARENA_PTR( ptr
, arena
);
381 if (pArena
->size
>= size
) break;
384 /* Make a block out of the free arena */
386 if (pArena
->size
> size
+ LALIGN(sizeof(LOCALARENA
)))
388 LOCAL_AddBlock( ptr
, arena
, arena
+size
);
389 LOCAL_AddFreeBlock( ptr
, arena
+size
);
392 LOCAL_RemoveFreeBlock( ptr
, arena
);
394 dprintf_local( stddeb
, "LocalAlloc: returning %04x\n",
395 arena
+ ARENA_HEADER_SIZE
);
396 return arena
+ ARENA_HEADER_SIZE
;
400 /***********************************************************************
403 * Implementation of LocalReAlloc().
405 HLOCAL
LOCAL_ReAlloc( WORD ds
, HLOCAL handle
, WORD size
, WORD flags
)
407 char *ptr
= PTR_SEG_OFF_TO_LIN( ds
, 0 );
408 LOCALHEAPINFO
*pInfo
;
409 LOCALARENA
*pArena
, *pNext
;
410 WORD arena
, newhandle
;
412 dprintf_local( stddeb
, "LocalReAlloc: %04x %d %04x ds=%04x\n",
413 handle
, size
, flags
, ds
);
414 if (!(pInfo
= LOCAL_GetHeap( ds
))) return 0;
415 arena
= handle
- ARENA_HEADER_SIZE
;
416 pArena
= ARENA_PTR( ptr
, arena
);
418 size
= LALIGN( size
);
420 /* Check for size reduction */
422 if (size
< pArena
->next
- handle
)
424 if (handle
+ size
< pArena
->next
- LALIGN(sizeof(LOCALARENA
)))
426 /* It is worth making a new free block */
427 LOCAL_AddBlock( ptr
, arena
, handle
+ size
);
428 LOCAL_AddFreeBlock( ptr
, handle
+ size
);
431 dprintf_local( stddeb
, "LocalReAlloc: returning %04x\n", handle
);
435 /* Check if the next block is free */
437 pNext
= ARENA_PTR( ptr
, pArena
->next
);
438 if (((pNext
->prev
& 3) == LOCAL_ARENA_FREE
) &&
439 (size
<= pNext
->next
- handle
))
441 LOCAL_RemoveBlock( ptr
, pArena
->next
);
442 if (handle
+ size
< pArena
->next
- LALIGN(sizeof(LOCALARENA
)))
444 /* It is worth making a new free block */
445 LOCAL_AddBlock( ptr
, arena
, handle
+ size
);
446 LOCAL_AddFreeBlock( ptr
, handle
+ size
);
449 dprintf_local( stddeb
, "LocalReAlloc: returning %04x\n", handle
);
453 /* Now we have to allocate a new block */
455 newhandle
= LOCAL_Alloc( ds
, flags
, size
);
456 if (!newhandle
) return 0;
457 memcpy( ptr
+ newhandle
, ptr
+ handle
, pArena
->next
- handle
);
458 LOCAL_Free( ds
, handle
);
459 dprintf_local( stddeb
, "LocalReAlloc: returning %04x\n", newhandle
);
464 /***********************************************************************
467 * Implementation of LocalFree().
469 HLOCAL
LOCAL_Free( WORD ds
, HLOCAL handle
)
471 char *ptr
= PTR_SEG_OFF_TO_LIN( ds
, 0 );
472 LOCALHEAPINFO
*pInfo
;
473 LOCALARENA
*pArena
, *pPrev
, *pNext
;
476 dprintf_local( stddeb
, "LocalFree: %04x ds=%04x\n", handle
, ds
);
477 if (!(pInfo
= LOCAL_GetHeap( ds
))) return handle
;
478 arena
= handle
- ARENA_HEADER_SIZE
;
479 pArena
= ARENA_PTR( ptr
, arena
);
480 if ((pArena
->prev
& 3) == LOCAL_ARENA_FREE
) return handle
;
482 /* Check if we can merge with the previous block */
484 pPrev
= ARENA_PTR( ptr
, pArena
->prev
& ~3 );
485 pNext
= ARENA_PTR( ptr
, pArena
->next
);
486 if ((pPrev
->prev
& 3) == LOCAL_ARENA_FREE
)
488 arena
= pArena
->prev
& ~3;
490 LOCAL_RemoveBlock( ptr
, pPrev
->next
);
493 else /* Make a new free block */
495 LOCAL_AddFreeBlock( ptr
, arena
);
498 /* Check if we can merge with the next block */
500 if ((pArena
->next
== pArena
->free_next
) &&
501 (pArena
->next
!= pInfo
->last
))
503 LOCAL_RemoveBlock( ptr
, pArena
->next
);
510 /***********************************************************************
513 * Implementation of LocalSize().
515 WORD
LOCAL_Size( WORD ds
, HLOCAL handle
)
517 LOCALARENA
*pArena
= PTR_SEG_OFF_TO_LIN( ds
, handle
- ARENA_HEADER_SIZE
);
518 return pArena
->next
- handle
;
522 /***********************************************************************
525 * Implementation of LocalHeapSize().
527 WORD
LOCAL_HeapSize( WORD ds
)
529 LOCALHEAPINFO
*pInfo
= LOCAL_GetHeap( ds
);
530 if (!pInfo
) return 0;
531 return pInfo
->last
- pInfo
->first
;
535 /***********************************************************************
536 * LocalAlloc (KERNEL.5)
538 HLOCAL
LocalAlloc( WORD flags
, WORD size
)
540 return LOCAL_Alloc( CURRENT_DS
, flags
, size
);
544 /***********************************************************************
545 * LocalReAlloc (KERNEL.6)
547 HLOCAL
LocalReAlloc( HLOCAL handle
, WORD flags
, WORD size
)
549 return LOCAL_ReAlloc( CURRENT_DS
, handle
, flags
, size
);
553 /***********************************************************************
554 * LocalFree (KERNEL.7)
556 HLOCAL
LocalFree( HLOCAL handle
)
558 return LOCAL_Free( CURRENT_DS
, handle
);
562 /***********************************************************************
563 * LocalLock (KERNEL.8)
565 WORD
LocalLock( HLOCAL handle
)
571 /***********************************************************************
572 * LocalUnlock (KERNEL.9)
574 BOOL
LocalUnlock( HLOCAL handle
)
580 /***********************************************************************
581 * LocalSize (KERNEL.10)
583 WORD
LocalSize( HLOCAL handle
)
585 return LOCAL_Size( CURRENT_DS
, handle
);
589 /***********************************************************************
590 * LocalHandle (KERNEL.11)
592 HLOCAL
LocalHandle( WORD addr
)
594 dprintf_local( stddeb
, "LocalHandle: %04x\n", addr
);
599 /***********************************************************************
600 * LocalFlags (KERNEL.12)
602 WORD
LocalFlags( HLOCAL handle
)
604 dprintf_local( stddeb
, "LocalFlags: %04x\n", handle
);
609 /***********************************************************************
610 * LocalCompact (KERNEL.13)
612 WORD
LocalCompact( WORD minfree
)
617 /***********************************************************************
618 * LocalNotify (KERNEL.14)
620 FARPROC
LocalNotify( FARPROC func
)
625 /***********************************************************************
626 * LocalShrink (KERNEL.121)
628 WORD
LocalShrink( HLOCAL handle
, WORD newsize
)
633 /***********************************************************************
634 * GetHeapSpaces (KERNEL.138)
636 DWORD
GetHeapSpaces( HMODULE module
)
641 /***********************************************************************
642 * LocalCountFree (KERNEL.161)
644 void LocalCountFree()
649 /***********************************************************************
650 * LocalHeapSize (KERNEL.162)
654 return LOCAL_HeapSize( CURRENT_DS
);
658 /***********************************************************************
659 * LocalHandleDelta (KERNEL.310)
661 WORD
LocalHandleDelta( WORD delta
)
666 /***********************************************************************
667 * LocalInfo (TOOLHELP.56)
669 BOOL
LocalInfo( LOCALINFO
*pLocalInfo
, HGLOBAL handle
)
671 LOCALHEAPINFO
*pInfo
= LOCAL_GetHeap(SELECTOROF(WIN16_GlobalLock(handle
)));
672 if (!pInfo
) return FALSE
;
673 pLocalInfo
->wcItems
= pInfo
->items
;
678 /***********************************************************************
679 * LocalFirst (TOOLHELP.57)
681 BOOL
LocalFirst( LOCALENTRY
*pLocalEntry
, HGLOBAL handle
)
683 WORD ds
= SELECTOROF( WIN16_GlobalLock( handle
) );
684 char *ptr
= PTR_SEG_OFF_TO_LIN( ds
, 0 );
685 LOCALHEAPINFO
*pInfo
= LOCAL_GetHeap( ds
);
686 if (!pInfo
) return FALSE
;
688 pLocalEntry
->hHandle
= pInfo
->first
+ ARENA_HEADER_SIZE
;
689 pLocalEntry
->wAddress
= pLocalEntry
->hHandle
;
690 pLocalEntry
->wFlags
= LF_FIXED
;
691 pLocalEntry
->wcLock
= 0;
692 pLocalEntry
->wType
= LT_NORMAL
;
693 pLocalEntry
->hHeap
= handle
;
694 pLocalEntry
->wHeapType
= NORMAL_HEAP
;
695 pLocalEntry
->wNext
= ARENA_PTR(ptr
,pInfo
->first
)->next
;
696 pLocalEntry
->wSize
= pLocalEntry
->wNext
- pLocalEntry
->hHandle
;
701 /***********************************************************************
702 * LocalNext (TOOLHELP.58)
704 BOOL
LocalNext( LOCALENTRY
*pLocalEntry
)
706 WORD ds
= SELECTOROF( pLocalEntry
->hHeap
);
707 char *ptr
= PTR_SEG_OFF_TO_LIN( ds
, 0 );
710 if (!LOCAL_GetHeap( ds
)) return FALSE
;
711 if (!pLocalEntry
->wNext
) return FALSE
;
712 pArena
= ARENA_PTR( ptr
, pLocalEntry
->wNext
);
714 pLocalEntry
->hHandle
= pLocalEntry
->wNext
+ ARENA_HEADER_SIZE
;
715 pLocalEntry
->wAddress
= pLocalEntry
->hHandle
;
716 pLocalEntry
->wFlags
= (pArena
->prev
& 3) + 1;
717 pLocalEntry
->wcLock
= 0;
718 pLocalEntry
->wType
= LT_NORMAL
;
719 if (pArena
->next
!= pLocalEntry
->wNext
) /* last one? */
720 pLocalEntry
->wNext
= pArena
->next
;
722 pLocalEntry
->wNext
= 0;
723 pLocalEntry
->wSize
= pLocalEntry
->wNext
- pLocalEntry
->hHandle
;