Release 951105
[wine.git] / memory / local.c
blobe20bd9c33541f133f7a5ff3aa4374f27753e48a2
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 "module.h"
21 #include "stackframe.h"
22 #include "toolhelp.h"
23 #include "stddebug.h"
24 #include "debug.h"
27 #ifndef WINELIB
28 #pragma pack(1)
29 #endif
31 typedef struct
33 /* Arena header */
34 WORD prev; /* Previous arena | arena type */
35 WORD next; /* Next arena */
36 /* Start of the memory block or free-list info */
37 WORD size; /* Size of the free block */
38 WORD free_prev; /* Previous free block */
39 WORD free_next; /* Next free block */
40 } LOCALARENA;
42 #define ARENA_HEADER_SIZE 4
43 #define ARENA_HEADER( handle) ( ((handle) & ~3) - ARENA_HEADER_SIZE)
45 /* Arena types (stored in 'prev' field of the arena) */
46 #define LOCAL_ARENA_FREE 0
47 #define LOCAL_ARENA_FIXED 1
48 #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)
96 /* determine whether the handle belongs to a fixed or a moveable block */
97 #define HANDLE_FIXED(handle) (((handle) & 3) == 0)
98 #define HANDLE_MOVEABLE(handle) (((handle) & 3) == 2)
100 /***********************************************************************
101 * LOCAL_GetHeap
103 * Return a pointer to the local heap, making sure it exists.
105 static LOCALHEAPINFO *LOCAL_GetHeap( WORD ds )
107 LOCALHEAPINFO *pInfo;
108 INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( ds, 0 );
109 dprintf_local( stddeb, "Heap at %p, %04x\n", ptr, ptr->heap );
110 if (!ptr->heap) return 0;
111 pInfo = (LOCALHEAPINFO*)((char*)ptr + ptr->heap);
112 if (pInfo->magic != LOCAL_HEAP_MAGIC) return NULL;
113 return pInfo;
117 /***********************************************************************
118 * LOCAL_MakeBlockFree
120 * Make a block free, inserting it in the free-list.
121 * 'block' is the handle of the block arena; 'baseptr' points to
122 * the beginning of the data segment containing the heap.
124 static void LOCAL_MakeBlockFree( char *baseptr, WORD block )
126 LOCALARENA *pArena, *pNext;
127 WORD next;
129 /* Mark the block as free */
131 pArena = ARENA_PTR( baseptr, block );
132 pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FREE;
133 pArena->size = pArena->next - block;
135 /* Find the next free block (last block is always free) */
137 next = pArena->next;
138 for (;;)
140 pNext = ARENA_PTR( baseptr, next );
141 if ((pNext->prev & 3) == LOCAL_ARENA_FREE) break;
142 next = pNext->next;
145 dprintf_local( stddeb, "Local_AddFreeBlock %04x, next %04x\n", block, next );
146 /* Insert the free block in the free-list */
148 pArena->free_prev = pNext->free_prev;
149 pArena->free_next = next;
150 ARENA_PTR(baseptr,pNext->free_prev)->free_next = block;
151 pNext->free_prev = block;
155 /***********************************************************************
156 * LOCAL_RemoveFreeBlock
158 * Remove a block from the free-list.
159 * 'block' is the handle of the block arena; 'baseptr' points to
160 * the beginning of the data segment containing the heap.
162 static void LOCAL_RemoveFreeBlock( char *baseptr, WORD block )
164 /* Mark the block as fixed */
166 LOCALARENA *pArena = ARENA_PTR( baseptr, block );
167 pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FIXED;
169 /* Remove it from the list */
171 ARENA_PTR(baseptr,pArena->free_prev)->free_next = pArena->free_next;
172 ARENA_PTR(baseptr,pArena->free_next)->free_prev = pArena->free_prev;
176 /***********************************************************************
177 * LOCAL_AddBlock
179 * Insert a new block in the heap.
180 * 'new' is the handle of the new block arena; 'baseptr' points to
181 * the beginning of the data segment containing the heap; 'prev' is
182 * the block before the new one.
184 static void LOCAL_AddBlock( char *baseptr, WORD prev, WORD new )
186 LOCALARENA *pPrev = ARENA_PTR( baseptr, prev );
187 LOCALARENA *pNew = ARENA_PTR( baseptr, new );
189 pNew->prev = prev | LOCAL_ARENA_FIXED;
190 pNew->next = pPrev->next;
191 ARENA_PTR(baseptr,pPrev->next)->prev &= 3;
192 ARENA_PTR(baseptr,pPrev->next)->prev |= new;
193 pPrev->next = new;
197 /***********************************************************************
198 * LOCAL_RemoveBlock
200 * Remove a block from the heap.
201 * 'block' is the handle of the block arena; 'baseptr' points to
202 * the beginning of the data segment containing the heap.
204 static void LOCAL_RemoveBlock( char *baseptr, WORD block )
206 LOCALARENA *pArena, *pTmp;
208 /* Remove the block from the free-list */
210 dprintf_local( stddeb, "Local_RemoveBlock\n");
211 pArena = ARENA_PTR( baseptr, block );
212 if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
213 LOCAL_RemoveFreeBlock( baseptr, block );
215 /* If the previous block is free, expand its size */
217 pTmp = ARENA_PTR( baseptr, pArena->prev & ~3 );
218 if ((pTmp->prev & 3) == LOCAL_ARENA_FREE)
219 pTmp->size += pArena->next - block;
221 /* Remove the block from the linked list */
223 pTmp->next = pArena->next;
224 pTmp = ARENA_PTR( baseptr, pArena->next );
225 pTmp->prev = (pTmp->prev & 3) | (pArena->prev & ~3);
229 /***********************************************************************
230 * LOCAL_PrintHeap
232 static void LOCAL_PrintHeap( WORD ds )
234 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
235 LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
236 WORD arena;
238 if (!debugging_local) return;
239 if (!pInfo)
241 printf( "Local Heap corrupted! ds=%04x\n", ds );
242 return;
244 printf( "Local Heap ds=%04x first=%04x last=%04x items=%d\n",
245 ds, pInfo->first, pInfo->last, pInfo->items );
247 arena = pInfo->first;
248 for (;;)
250 LOCALARENA *pArena = ARENA_PTR(ptr,arena);
251 printf( " %04x: prev=%04x next=%04x type=%d\n", arena,
252 pArena->prev & ~3, pArena->next, pArena->prev & 3 );
253 if (arena == pInfo->first)
255 printf( " size=%d free_prev=%04x free_next=%04x\n",
256 pArena->size, pArena->free_prev, pArena->free_next );
258 if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
260 printf( " size=%d free_prev=%04x free_next=%04x\n",
261 pArena->size, pArena->free_prev, pArena->free_next );
262 if (pArena->next == arena) break; /* last one */
263 if (ARENA_PTR(ptr,pArena->free_next)->free_prev != arena)
265 printf( "*** arena->free_next->free_prev != arena\n" );
266 break;
269 if (pArena->next == arena)
271 printf( "*** last block is not marked free\n" );
272 break;
274 if ((ARENA_PTR(ptr,pArena->next)->prev & ~3) != arena)
276 printf( "*** arena->next->prev != arena (%04x, %04x)\n",
277 pArena->next, ARENA_PTR(ptr,pArena->next)->prev);
278 break;
280 arena = pArena->next;
285 /***********************************************************************
286 * LocalInit (KERNEL.4)
288 BOOL LocalInit( HANDLE selector, WORD start, WORD end )
290 char *ptr;
291 WORD heapInfoArena, freeArena, lastArena;
292 LOCALHEAPINFO *pHeapInfo;
293 LOCALARENA *pArena, *pFirstArena, *pLastArena;
294 NE_MODULE *pModule;
296 /* The initial layout of the heap is: */
297 /* - first arena (FIXED) */
298 /* - heap info structure (FIXED) */
299 /* - large free block (FREE) */
300 /* - last arena (FREE) */
302 dprintf_local(stddeb, "LocalInit: %04x %04x-%04x\n", selector, start, end);
303 if (!selector) selector = CURRENT_DS;
304 pHeapInfo = LOCAL_GetHeap(selector);
306 if (pHeapInfo) {
307 fprintf( stderr, "LocalInit: Heap %04x initialized twice.\n", selector);
308 if (debugging_local) LOCAL_PrintHeap(selector);
311 if (start == 0) {
312 /* Check if the segment is the DGROUP of a module */
314 if ((pModule = (NE_MODULE *)GlobalLock( GetExePtr( selector ) )))
316 SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
317 if (pModule->dgroup && (pSeg->selector == selector)) {
318 /* We can't just use the simple method of using the value
319 * of minsize + stacksize, since there are programs that
320 * resize the data segment before calling InitTask(). So,
321 * we must put it at the end of the segment */
322 start = GlobalSize( GlobalHandle( selector ) );
323 start -= end;
324 end += start;
325 dprintf_local( stddeb," new start %04x, minstart: %04x\n", start, pSeg->minsize + pModule->stack_size);
329 ptr = PTR_SEG_OFF_TO_LIN( selector, 0 );
331 start = LALIGN( MAX( start, sizeof(INSTANCEDATA) ) );
332 heapInfoArena = LALIGN(start + sizeof(LOCALARENA) );
333 freeArena = LALIGN( heapInfoArena + ARENA_HEADER_SIZE
334 + sizeof(LOCALHEAPINFO) );
335 lastArena = (end - sizeof(LOCALARENA)) & ~3;
337 /* Make sure there's enough space. */
339 if (freeArena + sizeof(LOCALARENA) >= lastArena) return FALSE;
341 /* Initialise the first arena */
343 pFirstArena = ARENA_PTR( ptr, start );
344 pFirstArena->prev = start | LOCAL_ARENA_FIXED;
345 pFirstArena->next = heapInfoArena;
346 pFirstArena->size = LALIGN(sizeof(LOCALARENA));
347 pFirstArena->free_prev = start; /* this one */
348 pFirstArena->free_next = freeArena;
350 /* Initialise the arena of the heap info structure */
352 pArena = ARENA_PTR( ptr, heapInfoArena );
353 pArena->prev = start | LOCAL_ARENA_FIXED;
354 pArena->next = freeArena;
356 /* Initialise the heap info structure */
358 pHeapInfo = (LOCALHEAPINFO *) (ptr + heapInfoArena + ARENA_HEADER_SIZE );
359 memset( pHeapInfo, 0, sizeof(LOCALHEAPINFO) );
360 pHeapInfo->items = 4;
361 pHeapInfo->first = start;
362 pHeapInfo->last = lastArena;
363 pHeapInfo->htable = 0;
364 pHeapInfo->hdelta = 0x20;
365 pHeapInfo->extra = 0x200;
366 pHeapInfo->minsize = lastArena - freeArena;
367 pHeapInfo->magic = LOCAL_HEAP_MAGIC;
369 /* Initialise the large free block */
371 pArena = ARENA_PTR( ptr, freeArena );
372 pArena->prev = heapInfoArena | LOCAL_ARENA_FREE;
373 pArena->next = lastArena;
374 pArena->size = lastArena - freeArena;
375 pArena->free_prev = start;
376 pArena->free_next = lastArena;
378 /* Initialise the last block */
380 pLastArena = ARENA_PTR( ptr, lastArena );
381 pLastArena->prev = freeArena | LOCAL_ARENA_FREE;
382 pLastArena->next = lastArena; /* this one */
383 pLastArena->size = LALIGN(sizeof(LOCALARENA));
384 pLastArena->free_prev = freeArena;
385 pLastArena->free_next = lastArena; /* this one */
387 /* Store the local heap address in the instance data */
389 ((INSTANCEDATA *)ptr)->heap = heapInfoArena + ARENA_HEADER_SIZE;
390 LOCAL_PrintHeap( selector );
391 return TRUE;
394 /***********************************************************************
395 * LOCAL_GrowHeap
397 static void LOCAL_GrowHeap( WORD ds )
399 HANDLE hseg = GlobalHandle( ds );
400 LONG oldsize = GlobalSize( hseg );
401 LONG end;
402 LOCALHEAPINFO *pHeapInfo;
403 WORD freeArena, lastArena;
404 LOCALARENA *pArena, *pLastArena;
405 char *ptr;
407 /* if nothing can be gained, return */
408 if (oldsize > 0xfff0) return;
409 hseg = GlobalReAlloc( hseg, 0x10000, GMEM_FIXED );
410 ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
411 pHeapInfo = LOCAL_GetHeap( ds );
412 if (pHeapInfo == NULL) {
413 fprintf( stderr, "Local_GrowHeap: heap not found\n" );
414 return;
416 end = GlobalSize( hseg );
417 lastArena = (end - sizeof(LOCALARENA)) & ~3;
419 /* Update the HeapInfo */
420 pHeapInfo->items++;
421 freeArena = pHeapInfo->last;
422 pHeapInfo->last = lastArena;
423 pHeapInfo->minsize += end - oldsize;
425 /* grow the old last block */
426 /* FIXME: merge two adjacent free blocks */
427 pArena = ARENA_PTR( ptr, freeArena );
428 pArena->size = lastArena - freeArena;
429 pArena->next = lastArena;
430 pArena->free_next = lastArena;
432 /* Initialise the new last block */
434 pLastArena = ARENA_PTR( ptr, lastArena );
435 pLastArena->prev = freeArena | LOCAL_ARENA_FREE;
436 pLastArena->next = lastArena; /* this one */
437 pLastArena->size = LALIGN(sizeof(LOCALARENA));
438 pLastArena->free_prev = freeArena;
439 pLastArena->free_next = lastArena; /* this one */
441 dprintf_local( stddeb, "Heap expanded\n" );
442 LOCAL_PrintHeap( ds );
445 /***********************************************************************
446 * LOCAL_Compact
448 static WORD LOCAL_Compact( WORD ds, WORD minfree, WORD flags )
450 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
451 LOCALHEAPINFO *pInfo;
452 LOCALARENA *pArena;
453 WORD arena;
454 WORD freespace = 0;
456 if (!(pInfo = LOCAL_GetHeap( ds ))) {
457 dprintf_local( stddeb, "Local_FindFreeBlock: Local heap not found\n" );
458 LOCAL_PrintHeap(ds);
459 return 0;
462 arena = pInfo->first;
463 pArena = ARENA_PTR( ptr, arena );
464 while (arena != pArena->free_next) {
465 arena = pArena->free_next;
466 pArena = ARENA_PTR( ptr, arena );
467 if (pArena->size >= freespace) freespace = pArena->size;
470 if (flags & LMEM_NOCOMPACT) return freespace;
472 if (flags & LMEM_NODISCARD) return freespace;
473 return freespace;
476 /***********************************************************************
477 * LOCAL_FindFreeBlock
479 static HLOCAL LOCAL_FindFreeBlock( WORD ds, WORD size )
481 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
482 LOCALHEAPINFO *pInfo;
483 LOCALARENA *pArena;
484 WORD arena;
486 if (!(pInfo = LOCAL_GetHeap( ds ))) {
487 dprintf_local( stddeb, "Local_FindFreeBlock: Local heap not found\n" );
488 LOCAL_PrintHeap(ds);
489 return 0;
492 arena = pInfo->first;
493 pArena = ARENA_PTR( ptr, arena );
494 for (;;) {
495 arena = pArena->free_next;
496 pArena = ARENA_PTR( ptr, arena );
497 if (arena == pArena->free_next) break;
498 if (pArena->size >= size) return arena;
500 dprintf_local( stddeb, "Local_FindFreeBlock: not enough space\n" );
501 if (debugging_local) LOCAL_PrintHeap(ds);
502 return 0;
505 /***********************************************************************
506 * LOCAL_GetBlock
507 * The segment may get moved around in this function, so all callers
508 * should reset their pointer variables.
510 static HLOCAL LOCAL_GetBlock( WORD ds, WORD size, WORD flags )
512 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
513 LOCALHEAPINFO *pInfo;
514 LOCALARENA *pArena;
515 WORD arena;
517 if (!(pInfo = LOCAL_GetHeap( ds ))) {
518 dprintf_local( stddeb, "Local_GetBlock: Local heap not found\n");
519 LOCAL_PrintHeap(ds);
520 return 0;
523 size += ARENA_HEADER_SIZE;
524 size = LALIGN( MAX( size, sizeof(LOCALARENA) ) );
526 /* Find a suitable free block */
527 arena = LOCAL_FindFreeBlock( ds, size );
528 if (arena == 0) {
529 /* no space: try to make some */
530 LOCAL_Compact( ds, size, flags );
531 arena = LOCAL_FindFreeBlock( ds, size );
533 if (arena == 0) {
534 /* still no space: try to grow the segment */
535 LOCAL_GrowHeap( ds );
536 ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
537 pInfo = LOCAL_GetHeap( ds );
538 arena = LOCAL_FindFreeBlock( ds, size );
540 if (arena == 0) {
541 fprintf( stderr, "Local_GetBlock: not enough space!\n" );
542 return 0;
545 /* Make a block out of the free arena */
546 pArena = ARENA_PTR( ptr, arena );
547 dprintf_local( stddeb, "LOCAL_GetBlock size = %04x, arena at %04x size %04x\n", size,
548 arena, pArena->size );
549 if (pArena->size > size + LALIGN(sizeof(LOCALARENA)))
551 LOCAL_AddBlock( ptr, arena, arena+size );
552 LOCAL_MakeBlockFree( ptr, arena+size );
553 pInfo->items++;
555 LOCAL_RemoveFreeBlock( ptr, arena );
557 if (flags & LMEM_ZEROINIT) {
558 memset( (char *)pArena + ARENA_HEADER_SIZE, 0, size - ARENA_HEADER_SIZE );
561 dprintf_local( stddeb, "Local_GetBlock: arena at %04x\n", arena );
562 return arena + ARENA_HEADER_SIZE;
565 /***********************************************************************
566 * LOCAL_NewHTable
568 static BOOL LOCAL_NewHTable( WORD ds )
570 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
571 LOCALHEAPINFO *pInfo;
572 HLOCAL handle;
574 dprintf_local( stddeb, "Local_NewHTable\n" );
575 if (!(pInfo = LOCAL_GetHeap( ds ))) {
576 dprintf_local( stddeb, "Local heap not found\n");
577 LOCAL_PrintHeap(ds);
578 return FALSE;
581 handle = LOCAL_GetBlock( ds, pInfo->hdelta*4 + 2, LMEM_FIXED );
582 ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
583 pInfo = LOCAL_GetHeap( ds );
584 if (handle == 0) return FALSE;
585 *(WORD *)(ptr + handle) = 0; /* no handles in this block yet */
586 pInfo->htable = handle;
587 return TRUE;
590 /***********************************************************************
591 * LOCAL_GetNewHandle
593 static HLOCAL LOCAL_GetNewHandle( WORD ds )
595 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
596 LOCALHEAPINFO *pInfo;
597 WORD count;
599 if (!(pInfo = LOCAL_GetHeap( ds ))) {
600 dprintf_local( stddeb, "LOCAL_GetNewHandle: Local heap not found\n");
601 LOCAL_PrintHeap(ds);
602 return 0;
604 /* Check if we need a new handle table */
605 if (pInfo->htable == 0) {
606 if (!LOCAL_NewHTable( ds )) return 0;
607 ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
608 pInfo = LOCAL_GetHeap( ds );
610 if (*(WORD *)(ptr + pInfo->htable) == pInfo->hdelta) {
611 if (!LOCAL_NewHTable( ds )) return 0;
612 ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
613 pInfo = LOCAL_GetHeap( ds );
616 /* increase count */
617 count = (*(WORD *)(ptr + pInfo->htable))++;
618 dprintf_local( stddeb, "Local_GetNewHandle: %04x\n", pInfo->htable + 2 + 4*count );
619 return pInfo->htable + 2 + 4*count;
622 /***********************************************************************
623 * LOCAL_FreeArena
625 static HLOCAL LOCAL_FreeArena( WORD ds, WORD arena )
627 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
628 LOCALHEAPINFO *pInfo;
629 LOCALARENA *pArena, *pPrev, *pNext;
631 dprintf_local( stddeb, "LocalFreeArena: %04x ds=%04x\n", arena, ds );
632 if (!(pInfo = LOCAL_GetHeap( ds ))) return arena;
634 pArena = ARENA_PTR( ptr, arena );
635 if ((pArena->prev & 3) == LOCAL_ARENA_FREE) {
636 /* shouldn't happen */
637 fprintf( stderr, "LocalFreeArena: Trying to free a block twice!\n" );
638 LOCAL_PrintHeap( ds );
639 return arena;
642 /* Check if we can merge with the previous block */
644 pPrev = ARENA_PTR( ptr, pArena->prev & ~3 );
645 pNext = ARENA_PTR( ptr, pArena->next );
646 if ((pPrev->prev & 3) == LOCAL_ARENA_FREE)
648 arena = pArena->prev & ~3;
649 pArena = pPrev;
650 LOCAL_RemoveBlock( ptr, pPrev->next );
651 pInfo->items--;
653 else /* Make a new free block */
655 LOCAL_MakeBlockFree( ptr, arena );
658 /* Check if we can merge with the next block */
660 if ((pArena->next == pArena->free_next) &&
661 (pArena->next != pInfo->last))
663 LOCAL_RemoveBlock( ptr, pArena->next );
664 pInfo->items--;
666 return 0;
670 /***********************************************************************
671 * LOCAL_Free
673 * Implementation of LocalFree().
675 HLOCAL LOCAL_Free( HANDLE ds, HLOCAL handle )
677 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
678 WORD arena;
680 dprintf_local( stddeb, "LocalFree: %04x ds=%04x\n", handle, ds );
682 if (HANDLE_FIXED( handle )) {
683 arena = ARENA_HEADER( handle );
684 } else {
685 arena = ARENA_HEADER( *(WORD *)(ptr + handle) );
686 dprintf_local( stddeb, "LocalFree: real block at %04x\n", arena);
688 arena = LOCAL_FreeArena( ds, arena );
689 if (arena != 0) return handle; /* couldn't free it */
690 return 0;
694 /***********************************************************************
695 * LOCAL_Alloc
697 * Implementation of LocalAlloc().
699 HLOCAL LOCAL_Alloc( HANDLE ds, WORD flags, WORD size )
701 char *ptr;
702 HLOCAL handle;
704 dprintf_local( stddeb, "LocalAlloc: %04x %d ds=%04x\n", flags, size, ds );
706 if (flags & LMEM_MOVEABLE) {
707 LOCALHANDLEENTRY *plhe;
708 HLOCAL hmem;
710 hmem = LOCAL_GetBlock( ds, size + 2, flags );
711 if (hmem == 0) return 0;
712 handle = LOCAL_GetNewHandle( ds );
713 if (handle == 0) {
714 fprintf( stderr, "LocalAlloc: couldn't get handle\n");
715 LOCAL_FreeArena( ds, ARENA_HEADER(hmem) );
716 return 0;
718 ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
719 *(WORD *)(ptr + hmem) = handle;
720 plhe = (LOCALHANDLEENTRY *)(ptr + handle);
721 plhe->addr = hmem + 2;
722 plhe->lock = 0;
723 } else {
724 handle = LOCAL_GetBlock( ds, size, flags );
726 return handle;
730 /***********************************************************************
731 * LOCAL_ReAlloc
733 * Implementation of LocalReAlloc().
735 HLOCAL LOCAL_ReAlloc( HANDLE ds, HLOCAL handle, WORD size, WORD flags )
737 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
738 LOCALHEAPINFO *pInfo;
739 LOCALARENA *pArena, *pNext;
740 WORD arena, newhandle, blockhandle;
741 LONG nextarena;
743 if (!handle) return LOCAL_Alloc( ds, size, flags );
745 dprintf_local( stddeb, "LocalReAlloc: %04x %d %04x ds=%04x\n",
746 handle, size, flags, ds );
747 if (!(pInfo = LOCAL_GetHeap( ds ))) return 0;
749 if (HANDLE_FIXED( handle )) {
750 blockhandle = handle;
751 } else {
752 size += 2;
753 blockhandle = *(WORD *)(ptr + handle);
754 dprintf_local( stddeb, " blockhandle %04x (%04x)\n", blockhandle,
755 *(WORD *)(ptr + blockhandle - 2));
757 arena = ARENA_HEADER( blockhandle );
758 dprintf_local( stddeb, "LocalReAlloc: arena is %04x\n", arena );
759 pArena = ARENA_PTR( ptr, arena );
761 if (flags & LMEM_MODIFY) {
762 dprintf_local( stddeb, "LMEM_MODIFY set\n");
763 return handle;
765 if (!size) size = 1;
766 size = LALIGN( size );
767 nextarena = LALIGN(blockhandle + size);
769 /* Check for size reduction */
771 if (nextarena < pArena->next)
773 if (nextarena < pArena->next - LALIGN(sizeof(LOCALARENA)))
775 dprintf_local( stddeb, "size reduction, making new free block\n");
776 /* It is worth making a new free block */
777 LOCAL_AddBlock( ptr, arena, nextarena );
778 LOCAL_MakeBlockFree( ptr, nextarena );
779 pInfo->items++;
781 dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
782 return handle;
785 /* Check if the next block is free */
787 pNext = ARENA_PTR( ptr, pArena->next );
788 if (((pNext->prev & 3) == LOCAL_ARENA_FREE) &&
789 (nextarena <= pNext->next))
791 LOCAL_RemoveBlock( ptr, pArena->next );
792 if (nextarena < pArena->next - LALIGN(sizeof(LOCALARENA)))
794 dprintf_local( stddeb, "size increase, making new free block\n");
795 /* It is worth making a new free block */
796 LOCAL_AddBlock( ptr, arena, nextarena );
797 LOCAL_MakeBlockFree( ptr, nextarena );
798 pInfo->items++;
800 dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
801 return handle;
804 /* Now we have to allocate a new block */
806 newhandle = LOCAL_GetBlock( ds, size, flags );
807 if (newhandle == 0) return 0;
808 ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
809 memcpy( ptr + newhandle, ptr + (arena + ARENA_HEADER_SIZE), size );
810 LOCAL_FreeArena( ds, arena );
811 if (HANDLE_MOVEABLE( handle )) {
812 newhandle += 2;
813 dprintf_local( stddeb, "LocalReAlloc: fixing handle\n");
814 *(WORD *)(ptr + handle) = newhandle;
815 newhandle = handle;
817 dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", newhandle );
818 return newhandle;
822 /***********************************************************************
823 * LOCAL_Lock
825 HANDLE LOCAL_Lock( HANDLE ds, HLOCAL handle )
827 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
829 dprintf_local( stddeb, "LocalLock: %04x ", handle );
830 if (HANDLE_MOVEABLE(handle))
832 LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
833 if (pEntry->lock < 255) pEntry->lock++;
834 handle = pEntry->addr;
836 dprintf_local( stddeb, "returning %04x\n", handle );
837 return handle;
841 /***********************************************************************
842 * LOCAL_Unlock
844 BOOL LOCAL_Unlock( WORD ds, HLOCAL handle )
846 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
848 dprintf_local( stddeb, "LocalUnlock: %04x\n", handle );
849 if (HANDLE_MOVEABLE(handle))
851 LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
852 if (!pEntry->lock || (pEntry->lock == 255)) return FALSE;
853 /* For moveable block, return the new lock count */
854 /* (see _Windows_Internals_ p. 197) */
855 return --pEntry->lock;
857 else return FALSE;
861 /***********************************************************************
862 * LOCAL_Size
864 * Implementation of LocalSize().
866 WORD LOCAL_Size( WORD ds, HLOCAL handle )
868 char *ptr = PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
869 LOCALARENA *pArena;
870 WORD arena;
872 dprintf_local( stddeb, "LocalSize: %04x ds=%04x\n", handle, ds );
874 if (HANDLE_FIXED( handle )) {
875 arena = ARENA_HEADER( handle );
876 } else {
877 arena = ARENA_HEADER( handle = *(WORD *)(ptr + handle) );
879 pArena = ARENA_PTR( ptr, arena );
880 return pArena->next - handle;
884 /***********************************************************************
885 * LOCAL_HeapSize
887 * Implementation of LocalHeapSize().
889 WORD LOCAL_HeapSize( WORD ds )
891 LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
892 if (!pInfo) return 0;
893 return pInfo->last - pInfo->first;
897 /***********************************************************************
898 * LocalAlloc (KERNEL.5)
900 HLOCAL LocalAlloc( WORD flags, WORD size )
902 return LOCAL_Alloc( CURRENT_DS, flags, size );
906 /***********************************************************************
907 * LocalReAlloc (KERNEL.6)
909 HLOCAL LocalReAlloc( HLOCAL handle, WORD size, WORD flags )
911 return LOCAL_ReAlloc( CURRENT_DS, handle, size, flags );
915 /***********************************************************************
916 * LocalFree (KERNEL.7)
918 HLOCAL LocalFree( HLOCAL handle )
920 return LOCAL_Free( CURRENT_DS, handle );
924 /***********************************************************************
925 * LocalLock (KERNEL.8)
927 NPVOID LocalLock( HLOCAL handle )
929 return LOCAL_Lock( CURRENT_DS, handle );
933 /***********************************************************************
934 * LocalUnlock (KERNEL.9)
936 BOOL LocalUnlock( HLOCAL handle )
938 return LOCAL_Unlock( CURRENT_DS, handle );
942 /***********************************************************************
943 * LocalSize (KERNEL.10)
945 WORD LocalSize( HLOCAL handle )
947 return LOCAL_Size( CURRENT_DS, handle );
951 /***********************************************************************
952 * LocalHandle (KERNEL.11)
954 HLOCAL LocalHandle( WORD addr )
956 char *ptr = PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
958 dprintf_local( stddeb, "LocalHandle: %04x\n", addr );
959 if (HANDLE_MOVEABLE( addr )) {
960 addr = *(WORD *)(ptr + addr - 2);
962 return addr;
966 /***********************************************************************
967 * LocalFlags (KERNEL.12)
969 WORD LocalFlags( HLOCAL handle )
971 dprintf_local( stddeb, "LocalFlags: %04x\n", handle );
972 return 0;
976 /***********************************************************************
977 * LocalCompact (KERNEL.13)
979 WORD LocalCompact( WORD minfree )
981 dprintf_local( stddeb, "LocalCompact: %04x\n", minfree );
982 return LOCAL_Compact( CURRENT_DS, minfree, 0 );
986 /***********************************************************************
987 * LocalNotify (KERNEL.14)
989 FARPROC LocalNotify( FARPROC func )
991 dprintf_local( stddeb, "LocalNotify: %08lx\n", func );
992 return 0;
996 /***********************************************************************
997 * LocalShrink (KERNEL.121)
999 WORD LocalShrink( HLOCAL handle, WORD newsize )
1001 dprintf_local( stddeb, "LocalShrink: %04x %04x\n", handle, newsize );
1002 return 0;
1006 /***********************************************************************
1007 * GetHeapSpaces (KERNEL.138)
1009 DWORD GetHeapSpaces( HMODULE module )
1011 return MAKELONG( 0x7fff, 0xffff );
1015 /***********************************************************************
1016 * LocalCountFree (KERNEL.161)
1018 void LocalCountFree()
1020 dprintf_local( stddeb, "LocalCountFree:\n" );
1024 /***********************************************************************
1025 * LocalHeapSize (KERNEL.162)
1027 WORD LocalHeapSize()
1029 dprintf_local( stddeb, "LocalHeapSize:\n" );
1030 return LOCAL_HeapSize( CURRENT_DS );
1034 /***********************************************************************
1035 * LocalHandleDelta (KERNEL.310)
1037 WORD LocalHandleDelta( WORD delta )
1039 dprintf_local( stddeb, "LocalHandleDelta: %04x\n", delta );
1040 return 0;
1044 /***********************************************************************
1045 * LocalInfo (TOOLHELP.56)
1047 BOOL LocalInfo( LOCALINFO *pLocalInfo, HGLOBAL handle )
1049 LOCALHEAPINFO *pInfo = LOCAL_GetHeap(SELECTOROF(WIN16_GlobalLock(handle)));
1050 if (!pInfo) return FALSE;
1051 pLocalInfo->wcItems = pInfo->items;
1052 return TRUE;
1056 /***********************************************************************
1057 * LocalFirst (TOOLHELP.57)
1059 BOOL LocalFirst( LOCALENTRY *pLocalEntry, HGLOBAL handle )
1061 WORD ds = SELECTOROF( WIN16_GlobalLock( handle ) );
1062 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1063 LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
1064 if (!pInfo) return FALSE;
1066 pLocalEntry->hHandle = pInfo->first + ARENA_HEADER_SIZE;
1067 pLocalEntry->wAddress = pLocalEntry->hHandle;
1068 pLocalEntry->wFlags = LF_FIXED;
1069 pLocalEntry->wcLock = 0;
1070 pLocalEntry->wType = LT_NORMAL;
1071 pLocalEntry->hHeap = handle;
1072 pLocalEntry->wHeapType = NORMAL_HEAP;
1073 pLocalEntry->wNext = ARENA_PTR(ptr,pInfo->first)->next;
1074 pLocalEntry->wSize = pLocalEntry->wNext - pLocalEntry->hHandle;
1075 return TRUE;
1079 /***********************************************************************
1080 * LocalNext (TOOLHELP.58)
1082 BOOL LocalNext( LOCALENTRY *pLocalEntry )
1084 WORD ds = SELECTOROF( pLocalEntry->hHeap );
1085 char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1086 LOCALARENA *pArena;
1088 if (!LOCAL_GetHeap( ds )) return FALSE;
1089 if (!pLocalEntry->wNext) return FALSE;
1090 pArena = ARENA_PTR( ptr, pLocalEntry->wNext );
1092 pLocalEntry->hHandle = pLocalEntry->wNext + ARENA_HEADER_SIZE;
1093 pLocalEntry->wAddress = pLocalEntry->hHandle;
1094 pLocalEntry->wFlags = (pArena->prev & 3) + 1;
1095 pLocalEntry->wcLock = 0;
1096 pLocalEntry->wType = LT_NORMAL;
1097 if (pArena->next != pLocalEntry->wNext) /* last one? */
1098 pLocalEntry->wNext = pArena->next;
1099 else
1100 pLocalEntry->wNext = 0;
1101 pLocalEntry->wSize = pLocalEntry->wNext - pLocalEntry->hHandle;
1102 return TRUE;