Release 950319
[wine/hacks.git] / memory / local.c
blob5adf693b6ad6285f59f4b764846095c3a6f261f4
1 /*
2 * Local heap functions
4 * Copyright 1995 Alexandre Julliard
5 */
7 /*
8 * Note:
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.
14 #include <stdlib.h>
15 #include <string.h>
16 #include "windows.h"
17 #include "ldt.h"
18 #include "instance.h"
19 #include "local.h"
20 #include "stackframe.h"
21 #include "toolhelp.h"
22 #include "stddebug.h"
23 #include "debug.h"
26 #ifndef WINELIB
27 #pragma pack(1)
28 #endif
30 typedef struct
32 /* Arena header */
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 */
39 } LOCALARENA;
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
50 typedef struct
52 WORD addr; /* Address of the MOVEABLE block */
53 BYTE flags; /* Flags for this block */
54 BYTE lock; /* Lock count */
55 } LOCALHANDLEENTRY;
57 typedef struct
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 */
79 } LOCALHEAPINFO;
81 #ifndef WINELIB
82 #pragma pack(4)
83 #endif
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 /***********************************************************************
98 * LOCAL_GetHeap
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;
109 return pInfo;
113 /***********************************************************************
114 * LOCAL_AddFreeBlock
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;
123 WORD next;
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) */
133 next = pArena->next;
134 for (;;)
136 pNext = ARENA_PTR( baseptr, next );
137 if ((pNext->prev & 3) == LOCAL_ARENA_FREE) break;
138 next = pNext->next;
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 /***********************************************************************
172 * LOCAL_AddBlock
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;
188 pPrev->next = new;
192 /***********************************************************************
193 * LOCAL_RemoveBlock
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 /***********************************************************************
224 * LOCAL_PrintHeap
226 static void LOCAL_PrintHeap( WORD ds )
228 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
229 LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
230 WORD arena;
232 if (!pInfo)
234 printf( "Local Heap corrupted! ds=%04x\n", ds );
235 return;
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;
241 for (;;)
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" );
254 break;
257 if (pArena->next == arena)
259 printf( "*** last block is not marked free\n" );
260 break;
262 if ((ARENA_PTR(ptr,pArena->next)->prev & ~3) != arena)
264 printf( "*** arena->next->prev != arena\n" );
265 break;
267 arena = pArena->next;
272 /***********************************************************************
273 * LocalInit (KERNEL.4)
275 HLOCAL LocalInit( WORD selector, WORD start, WORD end )
277 char *ptr;
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;
351 return TRUE;
355 /***********************************************************************
356 * LOCAL_Alloc
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;
364 LOCALARENA *pArena;
365 WORD arena;
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 );
376 for (;;)
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 );
390 pInfo->items++;
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 /***********************************************************************
401 * LOCAL_ReAlloc
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 );
417 if (!size) size = 1;
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 );
429 pInfo->items++;
431 dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
432 return 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 );
447 pInfo->items++;
449 dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
450 return 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 );
460 return newhandle;
464 /***********************************************************************
465 * LOCAL_Free
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;
474 WORD arena;
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;
489 pArena = pPrev;
490 LOCAL_RemoveBlock( ptr, pPrev->next );
491 pInfo->items--;
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 );
504 pInfo->items--;
506 return 0;
510 /***********************************************************************
511 * LOCAL_Size
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 /***********************************************************************
523 * LOCAL_HeapSize
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 )
567 return handle;
571 /***********************************************************************
572 * LocalUnlock (KERNEL.9)
574 BOOL LocalUnlock( HLOCAL handle )
576 return TRUE;
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 );
595 return addr;
599 /***********************************************************************
600 * LocalFlags (KERNEL.12)
602 WORD LocalFlags( HLOCAL handle )
604 dprintf_local( stddeb, "LocalFlags: %04x\n", handle );
605 return 0;
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)
652 WORD LocalHeapSize()
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;
674 return TRUE;
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;
697 return TRUE;
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 );
708 LOCALARENA *pArena;
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;
721 else
722 pLocalEntry->wNext = 0;
723 pLocalEntry->wSize = pLocalEntry->wNext - pLocalEntry->hHandle;
724 return TRUE;