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;