/* * Local heap functions * * Copyright 1995 Alexandre Julliard */ /* * Note: * All local heap functions need the current DS as first parameter * when called from the emulation library, so they take one more * parameter than usual. */ #include #include #include "windows.h" #include "ldt.h" #include "instance.h" #include "local.h" #include "module.h" #include "stackframe.h" #include "toolhelp.h" #include "stddebug.h" #include "debug.h" #ifndef WINELIB #pragma pack(1) #endif typedef struct { /* Arena header */ WORD prev; /* Previous arena | arena type */ WORD next; /* Next arena */ /* Start of the memory block or free-list info */ WORD size; /* Size of the free block */ WORD free_prev; /* Previous free block */ WORD free_next; /* Next free block */ } LOCALARENA; #define ARENA_HEADER_SIZE 4 #define ARENA_HEADER( handle) ( ((handle) & ~3) - ARENA_HEADER_SIZE) /* Arena types (stored in 'prev' field of the arena) */ #define LOCAL_ARENA_FREE 0 #define LOCAL_ARENA_FIXED 1 #define LOCAL_ARENA_MOVEABLE 3 typedef struct { WORD addr; /* Address of the MOVEABLE block */ BYTE flags; /* Flags for this block */ BYTE lock; /* Lock count */ } LOCALHANDLEENTRY; typedef struct { WORD check; /* Heap checking flag */ WORD freeze; /* Heap frozen flag */ WORD items; /* Count of items on the heap */ WORD first; /* First item of the heap */ WORD pad1; /* Always 0 */ WORD last; /* Last item of the heap */ WORD pad2; /* Always 0 */ BYTE ncompact; /* Compactions counter */ BYTE dislevel; /* Discard level */ DWORD distotal; /* Total bytes discarded */ WORD htable; /* Pointer to handle table */ WORD hfree; /* Pointer to free handle table */ WORD hdelta; /* Delta to expand the handle table */ WORD expand; /* Pointer to expand function (unused) */ WORD pstat; /* Pointer to status structure (unused) */ DWORD notify WINE_PACKED; /* Pointer to LocalNotify() function */ WORD lock; /* Lock count for the heap */ WORD extra; /* Extra bytes to allocate when expanding */ WORD minsize; /* Minimum size of the heap */ WORD magic; /* Magic number */ } LOCALHEAPINFO; #ifndef WINELIB #pragma pack(4) #endif #define LOCAL_HEAP_MAGIC 0x484c /* 'LH' */ /* All local heap allocations are aligned on 4-byte boundaries */ #define LALIGN(word) (((word) + 3) & ~3) #define ARENA_PTR(ptr,arena) ((LOCALARENA *)((char*)(ptr)+(arena))) #define ARENA_PREV(ptr,arena) (ARENA_PTR(ptr,arena)->prev & ~3) #define ARENA_NEXT(ptr,arena) (ARENA_PTR(ptr,arena)->next) #define ARENA_FLAGS(ptr,arena) (ARENA_PTR(ptr,arena)->prev & 3) /* determine whether the handle belongs to a fixed or a moveable block */ #define HANDLE_FIXED(handle) (((handle) & 3) == 0) #define HANDLE_MOVEABLE(handle) (((handle) & 3) == 2) /*********************************************************************** * LOCAL_GetHeap * * Return a pointer to the local heap, making sure it exists. */ static LOCALHEAPINFO *LOCAL_GetHeap( WORD ds ) { LOCALHEAPINFO *pInfo; INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( ds, 0 ); dprintf_local( stddeb, "Heap at %p, %04x\n", ptr, ptr->heap ); if (!ptr->heap) return 0; pInfo = (LOCALHEAPINFO*)((char*)ptr + ptr->heap); if (pInfo->magic != LOCAL_HEAP_MAGIC) return NULL; return pInfo; } /*********************************************************************** * LOCAL_MakeBlockFree * * Make a block free, inserting it in the free-list. * 'block' is the handle of the block arena; 'baseptr' points to * the beginning of the data segment containing the heap. */ static void LOCAL_MakeBlockFree( char *baseptr, WORD block ) { LOCALARENA *pArena, *pNext; WORD next; /* Mark the block as free */ pArena = ARENA_PTR( baseptr, block ); pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FREE; pArena->size = pArena->next - block; /* Find the next free block (last block is always free) */ next = pArena->next; for (;;) { pNext = ARENA_PTR( baseptr, next ); if ((pNext->prev & 3) == LOCAL_ARENA_FREE) break; next = pNext->next; } dprintf_local( stddeb, "Local_AddFreeBlock %04x, next %04x\n", block, next ); /* Insert the free block in the free-list */ pArena->free_prev = pNext->free_prev; pArena->free_next = next; ARENA_PTR(baseptr,pNext->free_prev)->free_next = block; pNext->free_prev = block; } /*********************************************************************** * LOCAL_RemoveFreeBlock * * Remove a block from the free-list. * 'block' is the handle of the block arena; 'baseptr' points to * the beginning of the data segment containing the heap. */ static void LOCAL_RemoveFreeBlock( char *baseptr, WORD block ) { /* Mark the block as fixed */ LOCALARENA *pArena = ARENA_PTR( baseptr, block ); pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FIXED; /* Remove it from the list */ ARENA_PTR(baseptr,pArena->free_prev)->free_next = pArena->free_next; ARENA_PTR(baseptr,pArena->free_next)->free_prev = pArena->free_prev; } /*********************************************************************** * LOCAL_AddBlock * * Insert a new block in the heap. * 'new' is the handle of the new block arena; 'baseptr' points to * the beginning of the data segment containing the heap; 'prev' is * the block before the new one. */ static void LOCAL_AddBlock( char *baseptr, WORD prev, WORD new ) { LOCALARENA *pPrev = ARENA_PTR( baseptr, prev ); LOCALARENA *pNew = ARENA_PTR( baseptr, new ); pNew->prev = prev | LOCAL_ARENA_FIXED; pNew->next = pPrev->next; ARENA_PTR(baseptr,pPrev->next)->prev &= 3; ARENA_PTR(baseptr,pPrev->next)->prev |= new; pPrev->next = new; } /*********************************************************************** * LOCAL_RemoveBlock * * Remove a block from the heap. * 'block' is the handle of the block arena; 'baseptr' points to * the beginning of the data segment containing the heap. */ static void LOCAL_RemoveBlock( char *baseptr, WORD block ) { LOCALARENA *pArena, *pTmp; /* Remove the block from the free-list */ dprintf_local( stddeb, "Local_RemoveBlock\n"); pArena = ARENA_PTR( baseptr, block ); if ((pArena->prev & 3) == LOCAL_ARENA_FREE) LOCAL_RemoveFreeBlock( baseptr, block ); /* If the previous block is free, expand its size */ pTmp = ARENA_PTR( baseptr, pArena->prev & ~3 ); if ((pTmp->prev & 3) == LOCAL_ARENA_FREE) pTmp->size += pArena->next - block; /* Remove the block from the linked list */ pTmp->next = pArena->next; pTmp = ARENA_PTR( baseptr, pArena->next ); pTmp->prev = (pTmp->prev & 3) | (pArena->prev & ~3); } /*********************************************************************** * LOCAL_PrintHeap */ static void LOCAL_PrintHeap( WORD ds ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds ); WORD arena; if (!debugging_local) return; if (!pInfo) { printf( "Local Heap corrupted! ds=%04x\n", ds ); return; } printf( "Local Heap ds=%04x first=%04x last=%04x items=%d\n", ds, pInfo->first, pInfo->last, pInfo->items ); arena = pInfo->first; for (;;) { LOCALARENA *pArena = ARENA_PTR(ptr,arena); printf( " %04x: prev=%04x next=%04x type=%d\n", arena, pArena->prev & ~3, pArena->next, pArena->prev & 3 ); if (arena == pInfo->first) { printf( " size=%d free_prev=%04x free_next=%04x\n", pArena->size, pArena->free_prev, pArena->free_next ); } if ((pArena->prev & 3) == LOCAL_ARENA_FREE) { printf( " size=%d free_prev=%04x free_next=%04x\n", pArena->size, pArena->free_prev, pArena->free_next ); if (pArena->next == arena) break; /* last one */ if (ARENA_PTR(ptr,pArena->free_next)->free_prev != arena) { printf( "*** arena->free_next->free_prev != arena\n" ); break; } } if (pArena->next == arena) { printf( "*** last block is not marked free\n" ); break; } if ((ARENA_PTR(ptr,pArena->next)->prev & ~3) != arena) { printf( "*** arena->next->prev != arena (%04x, %04x)\n", pArena->next, ARENA_PTR(ptr,pArena->next)->prev); break; } arena = pArena->next; } } /*********************************************************************** * LocalInit (KERNEL.4) */ BOOL LocalInit( HANDLE selector, WORD start, WORD end ) { char *ptr; WORD heapInfoArena, freeArena, lastArena; LOCALHEAPINFO *pHeapInfo; LOCALARENA *pArena, *pFirstArena, *pLastArena; NE_MODULE *pModule; /* The initial layout of the heap is: */ /* - first arena (FIXED) */ /* - heap info structure (FIXED) */ /* - large free block (FREE) */ /* - last arena (FREE) */ dprintf_local(stddeb, "LocalInit: %04x %04x-%04x\n", selector, start, end); if (!selector) selector = CURRENT_DS; pHeapInfo = LOCAL_GetHeap(selector); if (pHeapInfo) { fprintf( stderr, "LocalInit: Heap %04x initialized twice.\n", selector); if (debugging_local) LOCAL_PrintHeap(selector); } if (start == 0) { /* Check if the segment is the DGROUP of a module */ if ((pModule = (NE_MODULE *)GlobalLock( GetExePtr( selector ) ))) { SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1; if (pModule->dgroup && (pSeg->selector == selector)) { /* We can't just use the simple method of using the value * of minsize + stacksize, since there are programs that * resize the data segment before calling InitTask(). So, * we must put it at the end of the segment */ start = GlobalSize( GlobalHandle( selector ) ); start -= end; end += start; dprintf_local( stddeb," new start %04x, minstart: %04x\n", start, pSeg->minsize + pModule->stack_size); } } } ptr = PTR_SEG_OFF_TO_LIN( selector, 0 ); start = LALIGN( max( start, sizeof(INSTANCEDATA) ) ); heapInfoArena = LALIGN(start + sizeof(LOCALARENA) ); freeArena = LALIGN( heapInfoArena + ARENA_HEADER_SIZE + sizeof(LOCALHEAPINFO) ); lastArena = (end - sizeof(LOCALARENA)) & ~3; /* Make sure there's enough space. */ if (freeArena + sizeof(LOCALARENA) >= lastArena) return FALSE; /* Initialise the first arena */ pFirstArena = ARENA_PTR( ptr, start ); pFirstArena->prev = start | LOCAL_ARENA_FIXED; pFirstArena->next = heapInfoArena; pFirstArena->size = LALIGN(sizeof(LOCALARENA)); pFirstArena->free_prev = start; /* this one */ pFirstArena->free_next = freeArena; /* Initialise the arena of the heap info structure */ pArena = ARENA_PTR( ptr, heapInfoArena ); pArena->prev = start | LOCAL_ARENA_FIXED; pArena->next = freeArena; /* Initialise the heap info structure */ pHeapInfo = (LOCALHEAPINFO *) (ptr + heapInfoArena + ARENA_HEADER_SIZE ); memset( pHeapInfo, 0, sizeof(LOCALHEAPINFO) ); pHeapInfo->items = 4; pHeapInfo->first = start; pHeapInfo->last = lastArena; pHeapInfo->htable = 0; pHeapInfo->hdelta = 0x20; pHeapInfo->extra = 0x200; pHeapInfo->minsize = lastArena - freeArena; pHeapInfo->magic = LOCAL_HEAP_MAGIC; /* Initialise the large free block */ pArena = ARENA_PTR( ptr, freeArena ); pArena->prev = heapInfoArena | LOCAL_ARENA_FREE; pArena->next = lastArena; pArena->size = lastArena - freeArena; pArena->free_prev = start; pArena->free_next = lastArena; /* Initialise the last block */ pLastArena = ARENA_PTR( ptr, lastArena ); pLastArena->prev = freeArena | LOCAL_ARENA_FREE; pLastArena->next = lastArena; /* this one */ pLastArena->size = LALIGN(sizeof(LOCALARENA)); pLastArena->free_prev = freeArena; pLastArena->free_next = lastArena; /* this one */ /* Store the local heap address in the instance data */ ((INSTANCEDATA *)ptr)->heap = heapInfoArena + ARENA_HEADER_SIZE; LOCAL_PrintHeap( selector ); return TRUE; } /*********************************************************************** * LOCAL_GrowHeap */ static void LOCAL_GrowHeap( WORD ds ) { HANDLE hseg = GlobalHandle( ds ); LONG oldsize = GlobalSize( hseg ); LONG end; LOCALHEAPINFO *pHeapInfo; WORD freeArena, lastArena; LOCALARENA *pArena, *pLastArena; char *ptr; /* if nothing can be gained, return */ if (oldsize > 0xfff0) return; hseg = GlobalReAlloc( hseg, 0x10000, GMEM_FIXED ); ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); pHeapInfo = LOCAL_GetHeap( ds ); if (pHeapInfo == NULL) { fprintf( stderr, "Local_GrowHeap: heap not found\n" ); return; } end = GlobalSize( hseg ); lastArena = (end - sizeof(LOCALARENA)) & ~3; /* Update the HeapInfo */ pHeapInfo->items++; freeArena = pHeapInfo->last; pHeapInfo->last = lastArena; pHeapInfo->minsize += end - oldsize; /* grow the old last block */ /* FIXME: merge two adjacent free blocks */ pArena = ARENA_PTR( ptr, freeArena ); pArena->size = lastArena - freeArena; pArena->next = lastArena; pArena->free_next = lastArena; /* Initialise the new last block */ pLastArena = ARENA_PTR( ptr, lastArena ); pLastArena->prev = freeArena | LOCAL_ARENA_FREE; pLastArena->next = lastArena; /* this one */ pLastArena->size = LALIGN(sizeof(LOCALARENA)); pLastArena->free_prev = freeArena; pLastArena->free_next = lastArena; /* this one */ dprintf_local( stddeb, "Heap expanded\n" ); LOCAL_PrintHeap( ds ); } /*********************************************************************** * LOCAL_Compact */ static WORD LOCAL_Compact( WORD ds, WORD minfree, WORD flags ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALHEAPINFO *pInfo; LOCALARENA *pArena; WORD arena; WORD freespace = 0; if (!(pInfo = LOCAL_GetHeap( ds ))) { dprintf_local( stddeb, "Local_FindFreeBlock: Local heap not found\n" ); LOCAL_PrintHeap(ds); return 0; } arena = pInfo->first; pArena = ARENA_PTR( ptr, arena ); while (arena != pArena->free_next) { arena = pArena->free_next; pArena = ARENA_PTR( ptr, arena ); if (pArena->size >= freespace) freespace = pArena->size; } if (flags & LMEM_NOCOMPACT) return freespace; if (flags & LMEM_NODISCARD) return freespace; return freespace; } /*********************************************************************** * LOCAL_FindFreeBlock */ static HLOCAL LOCAL_FindFreeBlock( WORD ds, WORD size ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALHEAPINFO *pInfo; LOCALARENA *pArena; WORD arena; if (!(pInfo = LOCAL_GetHeap( ds ))) { dprintf_local( stddeb, "Local_FindFreeBlock: Local heap not found\n" ); LOCAL_PrintHeap(ds); return 0; } arena = pInfo->first; pArena = ARENA_PTR( ptr, arena ); for (;;) { arena = pArena->free_next; pArena = ARENA_PTR( ptr, arena ); if (arena == pArena->free_next) break; if (pArena->size >= size) return arena; } dprintf_local( stddeb, "Local_FindFreeBlock: not enough space\n" ); if (debugging_local) LOCAL_PrintHeap(ds); return 0; } /*********************************************************************** * LOCAL_GetBlock * The segment may get moved around in this function, so all callers * should reset their pointer variables. */ static HLOCAL LOCAL_GetBlock( WORD ds, WORD size, WORD flags ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALHEAPINFO *pInfo; LOCALARENA *pArena; WORD arena; if (!(pInfo = LOCAL_GetHeap( ds ))) { dprintf_local( stddeb, "Local_GetBlock: Local heap not found\n"); LOCAL_PrintHeap(ds); return 0; } size += ARENA_HEADER_SIZE; size = LALIGN( max( size, sizeof(LOCALARENA) ) ); /* Find a suitable free block */ arena = LOCAL_FindFreeBlock( ds, size ); if (arena == 0) { /* no space: try to make some */ LOCAL_Compact( ds, size, flags ); arena = LOCAL_FindFreeBlock( ds, size ); } if (arena == 0) { /* still no space: try to grow the segment */ LOCAL_GrowHeap( ds ); ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); pInfo = LOCAL_GetHeap( ds ); arena = LOCAL_FindFreeBlock( ds, size ); } if (arena == 0) { fprintf( stderr, "Local_GetBlock: not enough space!\n" ); return 0; } /* Make a block out of the free arena */ pArena = ARENA_PTR( ptr, arena ); dprintf_local( stddeb, "LOCAL_GetBlock size = %04x, arena at %04x size %04x\n", size, arena, pArena->size ); if (pArena->size > size + LALIGN(sizeof(LOCALARENA))) { LOCAL_AddBlock( ptr, arena, arena+size ); LOCAL_MakeBlockFree( ptr, arena+size ); pInfo->items++; } LOCAL_RemoveFreeBlock( ptr, arena ); if (flags & LMEM_ZEROINIT) { memset( (char *)pArena + ARENA_HEADER_SIZE, 0, size - ARENA_HEADER_SIZE ); } dprintf_local( stddeb, "Local_GetBlock: arena at %04x\n", arena ); return arena + ARENA_HEADER_SIZE; } /*********************************************************************** * LOCAL_NewHTable */ static BOOL LOCAL_NewHTable( WORD ds ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALHEAPINFO *pInfo; HLOCAL handle; dprintf_local( stddeb, "Local_NewHTable\n" ); if (!(pInfo = LOCAL_GetHeap( ds ))) { dprintf_local( stddeb, "Local heap not found\n"); LOCAL_PrintHeap(ds); return FALSE; } handle = LOCAL_GetBlock( ds, pInfo->hdelta*4 + 2, LMEM_FIXED ); ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); pInfo = LOCAL_GetHeap( ds ); if (handle == 0) return FALSE; *(WORD *)(ptr + handle) = 0; /* no handles in this block yet */ pInfo->htable = handle; return TRUE; } /*********************************************************************** * LOCAL_GetNewHandle */ static HLOCAL LOCAL_GetNewHandle( WORD ds ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALHEAPINFO *pInfo; WORD count; if (!(pInfo = LOCAL_GetHeap( ds ))) { dprintf_local( stddeb, "LOCAL_GetNewHandle: Local heap not found\n"); LOCAL_PrintHeap(ds); return 0; } /* Check if we need a new handle table */ if (pInfo->htable == 0) { if (!LOCAL_NewHTable( ds )) return 0; ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); pInfo = LOCAL_GetHeap( ds ); } if (*(WORD *)(ptr + pInfo->htable) == pInfo->hdelta) { if (!LOCAL_NewHTable( ds )) return 0; ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); pInfo = LOCAL_GetHeap( ds ); } /* increase count */ count = (*(WORD *)(ptr + pInfo->htable))++; dprintf_local( stddeb, "Local_GetNewHandle: %04x\n", pInfo->htable + 2 + 4*count ); return pInfo->htable + 2 + 4*count; } /*********************************************************************** * LOCAL_FreeArena */ static HLOCAL LOCAL_FreeArena( WORD ds, WORD arena ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALHEAPINFO *pInfo; LOCALARENA *pArena, *pPrev, *pNext; dprintf_local( stddeb, "LocalFreeArena: %04x ds=%04x\n", arena, ds ); if (!(pInfo = LOCAL_GetHeap( ds ))) return arena; pArena = ARENA_PTR( ptr, arena ); if ((pArena->prev & 3) == LOCAL_ARENA_FREE) { /* shouldn't happen */ fprintf( stderr, "LocalFreeArena: Trying to free a block twice!\n" ); LOCAL_PrintHeap( ds ); return arena; } /* Check if we can merge with the previous block */ pPrev = ARENA_PTR( ptr, pArena->prev & ~3 ); pNext = ARENA_PTR( ptr, pArena->next ); if ((pPrev->prev & 3) == LOCAL_ARENA_FREE) { arena = pArena->prev & ~3; pArena = pPrev; LOCAL_RemoveBlock( ptr, pPrev->next ); pInfo->items--; } else /* Make a new free block */ { LOCAL_MakeBlockFree( ptr, arena ); } /* Check if we can merge with the next block */ if ((pArena->next == pArena->free_next) && (pArena->next != pInfo->last)) { LOCAL_RemoveBlock( ptr, pArena->next ); pInfo->items--; } return 0; } /*********************************************************************** * LOCAL_Free * * Implementation of LocalFree(). */ HLOCAL LOCAL_Free( HANDLE ds, HLOCAL handle ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); WORD arena; dprintf_local( stddeb, "LocalFree: %04x ds=%04x\n", handle, ds ); if (HANDLE_FIXED( handle )) { arena = ARENA_HEADER( handle ); } else { arena = ARENA_HEADER( *(WORD *)(ptr + handle) ); dprintf_local( stddeb, "LocalFree: real block at %04x\n", arena); } arena = LOCAL_FreeArena( ds, arena ); if (arena != 0) return handle; /* couldn't free it */ return 0; } /*********************************************************************** * LOCAL_Alloc * * Implementation of LocalAlloc(). */ HLOCAL LOCAL_Alloc( HANDLE ds, WORD flags, WORD size ) { char *ptr; HLOCAL handle; dprintf_local( stddeb, "LocalAlloc: %04x %d ds=%04x\n", flags, size, ds ); if (flags & LMEM_MOVEABLE) { LOCALHANDLEENTRY *plhe; HLOCAL hmem; hmem = LOCAL_GetBlock( ds, size + 2, flags ); if (hmem == 0) return 0; handle = LOCAL_GetNewHandle( ds ); if (handle == 0) { fprintf( stderr, "LocalAlloc: couldn't get handle\n"); LOCAL_FreeArena( ds, ARENA_HEADER(hmem) ); return 0; } ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); *(WORD *)(ptr + hmem) = handle; plhe = (LOCALHANDLEENTRY *)(ptr + handle); plhe->addr = hmem + 2; plhe->lock = 0; } else { handle = LOCAL_GetBlock( ds, size, flags ); } return handle; } /*********************************************************************** * LOCAL_ReAlloc * * Implementation of LocalReAlloc(). */ HLOCAL LOCAL_ReAlloc( HANDLE ds, HLOCAL handle, WORD size, WORD flags ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALHEAPINFO *pInfo; LOCALARENA *pArena, *pNext; WORD arena, newhandle, blockhandle; LONG nextarena; dprintf_local( stddeb, "LocalReAlloc: %04x %d %04x ds=%04x\n", handle, size, flags, ds ); if (!(pInfo = LOCAL_GetHeap( ds ))) return 0; if (HANDLE_FIXED( handle )) { blockhandle = handle; } else { size += 2; blockhandle = *(WORD *)(ptr + handle); dprintf_local( stddeb, " blockhandle %04x (%04x)\n", blockhandle, *(WORD *)(ptr + blockhandle - 2)); } arena = ARENA_HEADER( blockhandle ); dprintf_local( stddeb, "LocalReAlloc: arena is %04x\n", arena ); pArena = ARENA_PTR( ptr, arena ); if (flags & LMEM_MODIFY) { dprintf_local( stddeb, "LMEM_MODIFY set\n"); return handle; } if (!size) size = 1; size = LALIGN( size ); nextarena = LALIGN(blockhandle + size); /* Check for size reduction */ if (nextarena < pArena->next) { if (nextarena < pArena->next - LALIGN(sizeof(LOCALARENA))) { dprintf_local( stddeb, "size reduction, making new free block\n"); /* It is worth making a new free block */ LOCAL_AddBlock( ptr, arena, nextarena ); LOCAL_MakeBlockFree( ptr, nextarena ); pInfo->items++; } dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle ); return handle; } /* Check if the next block is free */ pNext = ARENA_PTR( ptr, pArena->next ); if (((pNext->prev & 3) == LOCAL_ARENA_FREE) && (nextarena <= pNext->next)) { LOCAL_RemoveBlock( ptr, pArena->next ); if (nextarena < pArena->next - LALIGN(sizeof(LOCALARENA))) { dprintf_local( stddeb, "size increase, making new free block\n"); /* It is worth making a new free block */ LOCAL_AddBlock( ptr, arena, nextarena ); LOCAL_MakeBlockFree( ptr, nextarena ); pInfo->items++; } dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle ); return handle; } /* Now we have to allocate a new block */ newhandle = LOCAL_GetBlock( ds, size, flags ); if (newhandle == 0) return 0; ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); memcpy( ptr + newhandle, ptr + (arena + ARENA_HEADER_SIZE), size ); LOCAL_FreeArena( ds, arena ); if (HANDLE_MOVEABLE( handle )) { newhandle += 2; dprintf_local( stddeb, "LocalReAlloc: fixing handle\n"); *(WORD *)(ptr + handle) = newhandle; newhandle = handle; } dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", newhandle ); return newhandle; } /*********************************************************************** * LOCAL_Lock */ HANDLE LOCAL_Lock( HANDLE ds, HLOCAL handle ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); dprintf_local( stddeb, "LocalLock: %04x ", handle ); if (HANDLE_MOVEABLE(handle)) { LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle); if (pEntry->lock < 255) pEntry->lock++; handle = pEntry->addr; } dprintf_local( stddeb, "returning %04x\n", handle ); return handle; } /*********************************************************************** * LOCAL_Unlock */ BOOL LOCAL_Unlock( WORD ds, HLOCAL handle ) { char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); dprintf_local( stddeb, "LocalUnlock: %04x\n", handle ); if (HANDLE_MOVEABLE(handle)) { LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle); if (!pEntry->lock || (pEntry->lock == 255)) return FALSE; /* For moveable block, return the new lock count */ /* (see _Windows_Internals_ p. 197) */ return --pEntry->lock; } else return FALSE; } /*********************************************************************** * LOCAL_Size * * Implementation of LocalSize(). */ WORD LOCAL_Size( WORD ds, HLOCAL handle ) { char *ptr = PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 ); LOCALARENA *pArena; WORD arena; dprintf_local( stddeb, "LocalSize: %04x ds=%04x\n", handle, ds ); if (HANDLE_FIXED( handle )) { arena = ARENA_HEADER( handle ); } else { arena = ARENA_HEADER( handle = *(WORD *)(ptr + handle) ); } pArena = ARENA_PTR( ptr, arena ); return pArena->next - handle; } /*********************************************************************** * LOCAL_HeapSize * * Implementation of LocalHeapSize(). */ WORD LOCAL_HeapSize( WORD ds ) { LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds ); if (!pInfo) return 0; return pInfo->last - pInfo->first; } /*********************************************************************** * LocalAlloc (KERNEL.5) */ HLOCAL LocalAlloc( WORD flags, WORD size ) { return LOCAL_Alloc( CURRENT_DS, flags, size ); } /*********************************************************************** * LocalReAlloc (KERNEL.6) */ HLOCAL LocalReAlloc( HLOCAL handle, WORD flags, WORD size ) { return LOCAL_ReAlloc( CURRENT_DS, handle, flags, size ); } /*********************************************************************** * LocalFree (KERNEL.7) */ HLOCAL LocalFree( HLOCAL handle ) { return LOCAL_Free( CURRENT_DS, handle ); } /*********************************************************************** * LocalLock (KERNEL.8) */ NPVOID LocalLock( HLOCAL handle ) { return LOCAL_Lock( CURRENT_DS, handle ); } /*********************************************************************** * LocalUnlock (KERNEL.9) */ BOOL LocalUnlock( HLOCAL handle ) { return LOCAL_Unlock( CURRENT_DS, handle ); } /*********************************************************************** * LocalSize (KERNEL.10) */ WORD LocalSize( HLOCAL handle ) { return LOCAL_Size( CURRENT_DS, handle ); } /*********************************************************************** * LocalHandle (KERNEL.11) */ HLOCAL LocalHandle( WORD addr ) { char *ptr = PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 ); dprintf_local( stddeb, "LocalHandle: %04x\n", addr ); if (HANDLE_MOVEABLE( addr )) { addr = *(WORD *)(ptr + addr - 2); } return addr; } /*********************************************************************** * LocalFlags (KERNEL.12) */ WORD LocalFlags( HLOCAL handle ) { dprintf_local( stddeb, "LocalFlags: %04x\n", handle ); return 0; } /*********************************************************************** * LocalCompact (KERNEL.13) */ WORD LocalCompact( WORD minfree ) { dprintf_local( stddeb, "LocalCompact: %04x\n", minfree ); return LOCAL_Compact( CURRENT_DS, minfree, 0 ); } /*********************************************************************** * LocalNotify (KERNEL.14) */ FARPROC LocalNotify( FARPROC func ) { dprintf_local( stddeb, "LocalNotify: %08lx\n", func ); return 0; } /*********************************************************************** * LocalShrink (KERNEL.121) */ WORD LocalShrink( HLOCAL handle, WORD newsize ) { dprintf_local( stddeb, "LocalShrink: %04x %04x\n", handle, newsize ); return 0; } /*********************************************************************** * GetHeapSpaces (KERNEL.138) */ DWORD GetHeapSpaces( HMODULE module ) { return MAKELONG( 0x7fff, 0xffff ); } /*********************************************************************** * LocalCountFree (KERNEL.161) */ void LocalCountFree() { dprintf_local( stddeb, "LocalCountFree:\n" ); } /*********************************************************************** * LocalHeapSize (KERNEL.162) */ WORD LocalHeapSize() { dprintf_local( stddeb, "LocalHeapSize:\n" ); return LOCAL_HeapSize( CURRENT_DS ); } /*********************************************************************** * LocalHandleDelta (KERNEL.310) */ WORD LocalHandleDelta( WORD delta ) { dprintf_local( stddeb, "LocalHandleDelta: %04x\n", delta ); return 0; } /*********************************************************************** * LocalInfo (TOOLHELP.56) */ BOOL LocalInfo( LOCALINFO *pLocalInfo, HGLOBAL handle ) { LOCALHEAPINFO *pInfo = LOCAL_GetHeap(SELECTOROF(WIN16_GlobalLock(handle))); if (!pInfo) return FALSE; pLocalInfo->wcItems = pInfo->items; return TRUE; } /*********************************************************************** * LocalFirst (TOOLHELP.57) */ BOOL LocalFirst( LOCALENTRY *pLocalEntry, HGLOBAL handle ) { WORD ds = SELECTOROF( WIN16_GlobalLock( handle ) ); char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds ); if (!pInfo) return FALSE; pLocalEntry->hHandle = pInfo->first + ARENA_HEADER_SIZE; pLocalEntry->wAddress = pLocalEntry->hHandle; pLocalEntry->wFlags = LF_FIXED; pLocalEntry->wcLock = 0; pLocalEntry->wType = LT_NORMAL; pLocalEntry->hHeap = handle; pLocalEntry->wHeapType = NORMAL_HEAP; pLocalEntry->wNext = ARENA_PTR(ptr,pInfo->first)->next; pLocalEntry->wSize = pLocalEntry->wNext - pLocalEntry->hHandle; return TRUE; } /*********************************************************************** * LocalNext (TOOLHELP.58) */ BOOL LocalNext( LOCALENTRY *pLocalEntry ) { WORD ds = SELECTOROF( pLocalEntry->hHeap ); char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 ); LOCALARENA *pArena; if (!LOCAL_GetHeap( ds )) return FALSE; if (!pLocalEntry->wNext) return FALSE; pArena = ARENA_PTR( ptr, pLocalEntry->wNext ); pLocalEntry->hHandle = pLocalEntry->wNext + ARENA_HEADER_SIZE; pLocalEntry->wAddress = pLocalEntry->hHandle; pLocalEntry->wFlags = (pArena->prev & 3) + 1; pLocalEntry->wcLock = 0; pLocalEntry->wType = LT_NORMAL; if (pArena->next != pLocalEntry->wNext) /* last one? */ pLocalEntry->wNext = pArena->next; else pLocalEntry->wNext = 0; pLocalEntry->wSize = pLocalEntry->wNext - pLocalEntry->hHandle; return TRUE; }