Changed many WARN()s related to internal Wine memory failures to
[wine] / memory / local.c
1 /*
2  * Local heap functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  * Copyright 1996 Huw Davies
6  */
7
8 /*
9  * Note:
10  * All local heap functions need the current DS as first parameter
11  * when called from the emulation library, so they take one more
12  * parameter than usual.
13  */
14
15 #include <stdlib.h>
16 #include <string.h>
17 #include "windows.h"
18 #include "ldt.h"
19 #include "task.h"
20 #include "global.h"
21 #include "heap.h"
22 #include "instance.h"
23 #include "local.h"
24 #include "module.h"
25 #include "stackframe.h"
26 #include "toolhelp.h"
27 #include "debug.h"
28
29 typedef struct
30 {
31 /* Arena header */
32     WORD prev;          /* Previous arena | arena type */
33     WORD next;          /* Next arena */
34 /* Start of the memory block or free-list info */
35     WORD size;          /* Size of the free block */
36     WORD free_prev;     /* Previous free block */
37     WORD free_next;     /* Next free block */
38 } LOCALARENA;
39
40 #define ARENA_HEADER_SIZE      4
41 #define ARENA_HEADER( handle) ( ((handle) & ~3) - ARENA_HEADER_SIZE)
42
43   /* Arena types (stored in 'prev' field of the arena) */
44 #define LOCAL_ARENA_FREE       0
45 #define LOCAL_ARENA_FIXED      1
46
47 /* Layout of a handle entry table
48  *
49  * WORD                     count of entries
50  * LOCALHANDLEENTRY[count]  entries
51  * WORD                     near ptr to next table
52  */
53 typedef struct
54 {
55     WORD addr;                /* Address of the MOVEABLE block */
56     BYTE flags;               /* Flags for this block */
57     BYTE lock;                /* Lock count */
58 } LOCALHANDLEENTRY;
59
60 /*
61  * We make addr = 4n + 2 and set *((WORD *)addr - 1) = &addr like Windows does
62  * in case something actually relies on this.
63  * Note the ARENA_HEADER(addr) still produces the desired result ie. 4n - 4
64  *
65  * An unused handle has lock = flags = 0xff. In windows addr is that of next
66  * free handle, at the moment in wine we set it to 0.
67  *
68  * A discarded block's handle has lock = addr = 0 and flags = 0x40
69  * (LMEM_DISCARDED >> 8)
70  */
71
72 #pragma pack(1)
73
74 typedef struct
75 {
76     WORD check;                 /* 00 Heap checking flag */
77     WORD freeze;                /* 02 Heap frozen flag */
78     WORD items;                 /* 04 Count of items on the heap */
79     WORD first;                 /* 06 First item of the heap */
80     WORD pad1;                  /* 08 Always 0 */
81     WORD last;                  /* 0a Last item of the heap */
82     WORD pad2;                  /* 0c Always 0 */
83     BYTE ncompact;              /* 0e Compactions counter */
84     BYTE dislevel;              /* 0f Discard level */
85     DWORD distotal;             /* 10 Total bytes discarded */
86     WORD htable;                /* 14 Pointer to handle table */
87     WORD hfree;                 /* 16 Pointer to free handle table */
88     WORD hdelta;                /* 18 Delta to expand the handle table */
89     WORD expand;                /* 1a Pointer to expand function (unused) */
90     WORD pstat;                 /* 1c Pointer to status structure (unused) */
91     FARPROC16 notify WINE_PACKED; /* 1e Pointer to LocalNotify() function */
92     WORD lock;                  /* 22 Lock count for the heap */
93     WORD extra;                 /* 24 Extra bytes to allocate when expanding */
94     WORD minsize;               /* 26 Minimum size of the heap */
95     WORD magic;                 /* 28 Magic number */
96 } LOCALHEAPINFO;
97
98 #pragma pack(4)
99
100 #define LOCAL_HEAP_MAGIC  0x484c  /* 'LH' */
101
102 WORD USER_HeapSel = 0;  /* USER heap selector */
103 WORD GDI_HeapSel = 0;   /* GDI heap selector */
104
105   /* All local heap allocations are aligned on 4-byte boundaries */
106 #define LALIGN(word)          (((word) + 3) & ~3)
107
108 #define ARENA_PTR(ptr,arena)       ((LOCALARENA *)((char*)(ptr)+(arena)))
109 #define ARENA_PREV(ptr,arena)      (ARENA_PTR((ptr),(arena))->prev & ~3)
110 #define ARENA_NEXT(ptr,arena)      (ARENA_PTR((ptr),(arena))->next)
111 #define ARENA_FLAGS(ptr,arena)     (ARENA_PTR((ptr),(arena))->prev & 3)
112
113   /* determine whether the handle belongs to a fixed or a moveable block */
114 #define HANDLE_FIXED(handle) (((handle) & 3) == 0)
115 #define HANDLE_MOVEABLE(handle) (((handle) & 3) == 2)
116
117 /***********************************************************************
118  *           LOCAL_GetHeap
119  *
120  * Return a pointer to the local heap, making sure it exists.
121  */
122 static LOCALHEAPINFO *LOCAL_GetHeap( HANDLE16 ds )
123 {
124     LOCALHEAPINFO *pInfo;
125     INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( ds, 0 );
126     TRACE(local, "Heap at %p, %04x\n", ptr, ptr->heap );
127     if (!ptr || !ptr->heap) return NULL;
128     if (IsBadReadPtr16( (SEGPTR)MAKELONG(ptr->heap,ds), sizeof(LOCALHEAPINFO)))
129         return NULL;
130     pInfo = (LOCALHEAPINFO*)((char*)ptr + ptr->heap);
131     if (pInfo->magic != LOCAL_HEAP_MAGIC) return NULL;
132     return pInfo;
133 }
134
135
136 /***********************************************************************
137  *           LOCAL_MakeBlockFree
138  *
139  * Make a block free, inserting it in the free-list.
140  * 'block' is the handle of the block arena; 'baseptr' points to
141  * the beginning of the data segment containing the heap.
142  */
143 static void LOCAL_MakeBlockFree( char *baseptr, WORD block )
144 {
145     LOCALARENA *pArena, *pNext;
146     WORD next;
147
148       /* Mark the block as free */
149
150     pArena = ARENA_PTR( baseptr, block );
151     pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FREE;
152     pArena->size = pArena->next - block;
153     
154       /* Find the next free block (last block is always free) */
155
156     next = pArena->next;
157     for (;;)
158     {
159         pNext = ARENA_PTR( baseptr, next );
160         if ((pNext->prev & 3) == LOCAL_ARENA_FREE) break;
161         next = pNext->next;
162     }
163
164     TRACE(local, "Local_MakeBlockFree %04x, next %04x\n", block, next );
165       /* Insert the free block in the free-list */
166
167     pArena->free_prev = pNext->free_prev;
168     pArena->free_next = next;
169     ARENA_PTR(baseptr,pNext->free_prev)->free_next = block;
170     pNext->free_prev  = block;
171 }
172
173
174 /***********************************************************************
175  *           LOCAL_RemoveFreeBlock
176  *
177  * Remove a block from the free-list.
178  * 'block' is the handle of the block arena; 'baseptr' points to
179  * the beginning of the data segment containing the heap.
180  */
181 static void LOCAL_RemoveFreeBlock( char *baseptr, WORD block )
182 {
183       /* Mark the block as fixed */
184
185     LOCALARENA *pArena = ARENA_PTR( baseptr, block );
186     pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FIXED;
187
188       /* Remove it from the list */
189
190     ARENA_PTR(baseptr,pArena->free_prev)->free_next = pArena->free_next;
191     ARENA_PTR(baseptr,pArena->free_next)->free_prev = pArena->free_prev;
192 }
193
194
195 /***********************************************************************
196  *           LOCAL_AddBlock
197  *
198  * Insert a new block in the heap.
199  * 'new' is the handle of the new block arena; 'baseptr' points to
200  * the beginning of the data segment containing the heap; 'prev' is
201  * the block before the new one.
202  */
203 static void LOCAL_AddBlock( char *baseptr, WORD prev, WORD new )
204 {
205     LOCALARENA *pPrev = ARENA_PTR( baseptr, prev );
206     LOCALARENA *pNew  = ARENA_PTR( baseptr, new );
207
208     pNew->prev = (prev & ~3) | LOCAL_ARENA_FIXED;
209     pNew->next = pPrev->next;
210     ARENA_PTR(baseptr,pPrev->next)->prev &= 3;
211     ARENA_PTR(baseptr,pPrev->next)->prev |= new;
212     pPrev->next = new;
213 }
214
215
216 /***********************************************************************
217  *           LOCAL_RemoveBlock
218  *
219  * Remove a block from the heap.
220  * 'block' is the handle of the block arena; 'baseptr' points to
221  * the beginning of the data segment containing the heap.
222  */
223 static void LOCAL_RemoveBlock( char *baseptr, WORD block )
224 {
225     LOCALARENA *pArena, *pTmp;
226
227       /* Remove the block from the free-list */
228
229     TRACE(local, "Local_RemoveBlock\n");
230     pArena = ARENA_PTR( baseptr, block );
231     if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
232         LOCAL_RemoveFreeBlock( baseptr, block );
233
234       /* If the previous block is free, expand its size */
235
236     pTmp = ARENA_PTR( baseptr, pArena->prev & ~3 );
237     if ((pTmp->prev & 3) == LOCAL_ARENA_FREE)
238         pTmp->size += pArena->next - block;
239
240       /* Remove the block from the linked list */
241
242     pTmp->next = pArena->next;
243     pTmp = ARENA_PTR( baseptr, pArena->next );
244     pTmp->prev = (pTmp->prev & 3) | (pArena->prev & ~3);
245 }
246
247
248 /***********************************************************************
249  *           LOCAL_PrintHeap
250  */
251 static void LOCAL_PrintHeap( HANDLE16 ds )
252 {
253     char *ptr;
254     LOCALHEAPINFO *pInfo;
255     WORD arena;
256
257     /* FIXME - the test should be done when calling the function! 
258                plus is not clear that we should print this info
259                only when TRACE_ON is on! */
260     if(!TRACE_ON(local)) return;
261
262     ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
263     pInfo = LOCAL_GetHeap( ds );
264
265     if (!pInfo)
266     {
267         DUMP( "Local Heap corrupted!  ds=%04x\n", ds );
268         return;
269     }
270     DUMP( "Local Heap  ds=%04x first=%04x last=%04x items=%d\n",
271           ds, pInfo->first, pInfo->last, pInfo->items );
272
273     arena = pInfo->first;
274     for (;;)
275     {
276         LOCALARENA *pArena = ARENA_PTR(ptr,arena);
277         DUMP( "  %04x: prev=%04x next=%04x type=%d\n", arena,
278               pArena->prev & ~3, pArena->next, pArena->prev & 3 );
279         if (arena == pInfo->first)
280         {
281             DUMP( "        size=%d free_prev=%04x free_next=%04x\n",
282                   pArena->size, pArena->free_prev, pArena->free_next );
283         }
284         if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
285         {
286             DUMP( "        size=%d free_prev=%04x free_next=%04x\n",
287                   pArena->size, pArena->free_prev, pArena->free_next );
288             if (pArena->next == arena) break;  /* last one */
289             if (ARENA_PTR(ptr,pArena->free_next)->free_prev != arena)
290             {
291                 DUMP( "*** arena->free_next->free_prev != arena\n" );
292                 break;
293             }
294         }
295         if (pArena->next == arena)
296         {
297             DUMP( "*** last block is not marked free\n" );
298             break;
299         }
300         if ((ARENA_PTR(ptr,pArena->next)->prev & ~3) != arena)
301         {
302             DUMP( "*** arena->next->prev != arena (%04x, %04x)\n",
303                   pArena->next, ARENA_PTR(ptr,pArena->next)->prev);
304             break;
305         }
306         arena = pArena->next;
307     }
308 }
309
310
311 /***********************************************************************
312  *           LocalInit   (KERNEL.4)
313  */
314 BOOL16 WINAPI LocalInit( HANDLE16 selector, WORD start, WORD end )
315 {
316     char *ptr;
317     WORD heapInfoArena, freeArena, lastArena;
318     LOCALHEAPINFO *pHeapInfo;
319     LOCALARENA *pArena, *pFirstArena, *pLastArena;
320     NE_MODULE *pModule;
321     
322       /* The initial layout of the heap is: */
323       /* - first arena         (FIXED)      */
324       /* - heap info structure (FIXED)      */
325       /* - large free block    (FREE)       */
326       /* - last arena          (FREE)       */
327
328     TRACE(local, "%04x %04x-%04x\n", selector, start, end);
329     if (!selector) selector = CURRENT_DS;
330
331     if (TRACE_ON(heap))
332     {
333         /* If TRACE_ON(heap) is set, the global heap blocks are */
334         /* cleared before use, so we can test for double initialization. */
335         if (LOCAL_GetHeap(selector))
336         {
337             ERR(local, "Heap %04x initialized twice.\n", selector);
338             LOCAL_PrintHeap(selector);
339         }
340     }
341
342     if (start == 0) {
343       /* Check if the segment is the DGROUP of a module */
344
345         if ((pModule = NE_GetPtr( selector )))
346         {
347             SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
348             if (pModule->dgroup && (GlobalHandleToSel(pSeg->hSeg) == selector))
349             {
350                 /* We can't just use the simple method of using the value
351                  * of minsize + stacksize, since there are programs that
352                  * resize the data segment before calling InitTask(). So,
353                  * we must put it at the end of the segment */
354                 start = GlobalSize16( GlobalHandle16( selector ) );
355                 start -= end;
356                 end += start;
357                 TRACE(local," new start %04x, minstart: %04x\n", start, pSeg->minsize + pModule->stack_size);
358             }
359         }
360     }
361     ptr = PTR_SEG_OFF_TO_LIN( selector, 0 );
362
363     start = LALIGN( MAX( start, sizeof(INSTANCEDATA) ) );
364     heapInfoArena = LALIGN(start + sizeof(LOCALARENA) );
365     freeArena = LALIGN( heapInfoArena + ARENA_HEADER_SIZE
366                         + sizeof(LOCALHEAPINFO) );
367     lastArena = (end - sizeof(LOCALARENA)) & ~3;
368
369       /* Make sure there's enough space.       */
370
371     if (freeArena + sizeof(LOCALARENA) >= lastArena) return FALSE;
372
373       /* Initialise the first arena */
374
375     pFirstArena = ARENA_PTR( ptr, start );
376     pFirstArena->prev      = start | LOCAL_ARENA_FIXED;
377     pFirstArena->next      = heapInfoArena;
378     pFirstArena->size      = LALIGN(sizeof(LOCALARENA));
379     pFirstArena->free_prev = start;  /* this one */
380     pFirstArena->free_next = freeArena;
381
382       /* Initialise the arena of the heap info structure */
383
384     pArena = ARENA_PTR( ptr, heapInfoArena );
385     pArena->prev = start | LOCAL_ARENA_FIXED;
386     pArena->next = freeArena;
387
388       /* Initialise the heap info structure */
389
390     pHeapInfo = (LOCALHEAPINFO *) (ptr + heapInfoArena + ARENA_HEADER_SIZE );
391     memset( pHeapInfo, 0, sizeof(LOCALHEAPINFO) );
392     pHeapInfo->items   = 4;
393     pHeapInfo->first   = start;
394     pHeapInfo->last    = lastArena;
395     pHeapInfo->htable  = 0;
396     pHeapInfo->hdelta  = 0x20;
397     pHeapInfo->extra   = 0x200;
398     pHeapInfo->minsize = lastArena - freeArena;
399     pHeapInfo->magic   = LOCAL_HEAP_MAGIC;
400
401       /* Initialise the large free block */
402
403     pArena = ARENA_PTR( ptr, freeArena );
404     pArena->prev      = heapInfoArena | LOCAL_ARENA_FREE;
405     pArena->next      = lastArena;
406     pArena->size      = lastArena - freeArena;
407     pArena->free_prev = start;
408     pArena->free_next = lastArena;
409
410       /* Initialise the last block */
411
412     pLastArena = ARENA_PTR( ptr, lastArena );
413     pLastArena->prev      = freeArena | LOCAL_ARENA_FREE;
414     pLastArena->next      = lastArena;  /* this one */
415     pLastArena->size      = LALIGN(sizeof(LOCALARENA));
416     pLastArena->free_prev = freeArena;
417     pLastArena->free_next = lastArena;  /* this one */
418
419       /* Store the local heap address in the instance data */
420
421     ((INSTANCEDATA *)ptr)->heap = heapInfoArena + ARENA_HEADER_SIZE;
422     LOCAL_PrintHeap( selector );
423     return TRUE;
424 }
425
426 /***********************************************************************
427  *           LOCAL_GrowHeap
428  */
429 static void LOCAL_GrowHeap( HANDLE16 ds )
430 {
431     HANDLE16 hseg = GlobalHandle16( ds );
432     LONG oldsize = GlobalSize16( hseg );
433     LONG end;
434     LOCALHEAPINFO *pHeapInfo;
435     WORD freeArena, lastArena;
436     LOCALARENA *pArena, *pLastArena;
437     char *ptr;
438     
439     /* if nothing can be gained, return */
440     if (oldsize > 0xfff0) return;
441     hseg = GlobalReAlloc16( hseg, 0x10000, GMEM_FIXED );
442     ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
443     pHeapInfo = LOCAL_GetHeap( ds );
444     if (pHeapInfo == NULL) {
445         ERR(local, "Heap not found\n" );
446         return;
447     }
448     end = GlobalSize16( hseg );
449     lastArena = (end - sizeof(LOCALARENA)) & ~3;
450
451       /* Update the HeapInfo */
452     pHeapInfo->items++;
453     freeArena = pHeapInfo->last;
454     pHeapInfo->last = lastArena;
455     pHeapInfo->minsize += end - oldsize;
456     
457       /* grow the old last block */
458     pArena = ARENA_PTR( ptr, freeArena );
459     pArena->size      = lastArena - freeArena;
460     pArena->next      = lastArena;
461     pArena->free_next = lastArena;
462
463       /* Initialise the new last block */
464
465     pLastArena = ARENA_PTR( ptr, lastArena );
466     pLastArena->prev      = freeArena | LOCAL_ARENA_FREE;
467     pLastArena->next      = lastArena;  /* this one */
468     pLastArena->size      = LALIGN(sizeof(LOCALARENA));
469     pLastArena->free_prev = freeArena;
470     pLastArena->free_next = lastArena;  /* this one */
471     
472     /* If block before freeArena is also free then merge them */
473     if((ARENA_PTR(ptr, (pArena->prev & ~3))->prev & 3) == LOCAL_ARENA_FREE)
474     {
475         LOCAL_RemoveBlock(ptr, freeArena);
476         pHeapInfo->items--;
477     }
478
479     TRACE(local, "Heap expanded\n" );
480     LOCAL_PrintHeap( ds );
481 }
482
483
484 /***********************************************************************
485  *           LOCAL_FreeArena
486  */
487 static HLOCAL16 LOCAL_FreeArena( WORD ds, WORD arena )
488 {
489     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
490     LOCALHEAPINFO *pInfo;
491     LOCALARENA *pArena, *pPrev, *pNext;
492
493     TRACE(local, "%04x ds=%04x\n", arena, ds );
494     if (!(pInfo = LOCAL_GetHeap( ds ))) return arena;
495
496     pArena = ARENA_PTR( ptr, arena );
497     if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
498     {
499         /* shouldn't happen */
500         ERR(local, "Trying to free block %04x twice!\n",
501                  arena );
502         LOCAL_PrintHeap( ds );
503         return arena;
504     }
505
506       /* Check if we can merge with the previous block */
507
508     pPrev = ARENA_PTR( ptr, pArena->prev & ~3 );
509     pNext = ARENA_PTR( ptr, pArena->next );
510     if ((pPrev->prev & 3) == LOCAL_ARENA_FREE)
511     {
512         arena  = pArena->prev & ~3;
513         pArena = pPrev;
514         LOCAL_RemoveBlock( ptr, pPrev->next );
515         pInfo->items--;
516     }
517     else  /* Make a new free block */
518     {
519         LOCAL_MakeBlockFree( ptr, arena );
520     }
521
522       /* Check if we can merge with the next block */
523
524     if ((pArena->next == pArena->free_next) &&
525         (pArena->next != pInfo->last))
526     {
527         LOCAL_RemoveBlock( ptr, pArena->next );
528         pInfo->items--;
529     }
530     return 0;
531 }
532
533
534 /***********************************************************************
535  *           LOCAL_ShrinkArena
536  *
537  * Shrink an arena by creating a free block at its end if possible.
538  * 'size' includes the arena header, and must be aligned.
539  */
540 static void LOCAL_ShrinkArena( WORD ds, WORD arena, WORD size )
541 {
542     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
543     LOCALARENA *pArena = ARENA_PTR( ptr, arena );
544
545     if (arena + size + LALIGN(sizeof(LOCALARENA)) < pArena->next)
546     {
547         LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
548         if (!pInfo) return;
549         LOCAL_AddBlock( ptr, arena, arena + size );
550         pInfo->items++;
551         LOCAL_FreeArena( ds, arena + size );
552     }
553 }
554
555
556 /***********************************************************************
557  *           LOCAL_GrowArenaDownward
558  *
559  * Grow an arena downward by using the previous arena (must be free).
560  */
561 static void LOCAL_GrowArenaDownward( WORD ds, WORD arena, WORD newsize )
562 {
563     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
564     LOCALHEAPINFO *pInfo;
565     LOCALARENA *pArena = ARENA_PTR( ptr, arena );
566     WORD prevArena = pArena->prev & ~3;
567     LOCALARENA *pPrevArena = ARENA_PTR( ptr, prevArena );
568     WORD offset, size;
569     char *p;
570
571     if (!(pInfo = LOCAL_GetHeap( ds ))) return;
572     offset = pPrevArena->size;
573     size = pArena->next - arena - ARENA_HEADER_SIZE;
574     LOCAL_RemoveFreeBlock( ptr, prevArena );
575     LOCAL_RemoveBlock( ptr, arena );
576     pInfo->items--;
577     p = (char *)pPrevArena + ARENA_HEADER_SIZE;
578     while (offset < size)
579     {
580         memcpy( p, p + offset, offset );
581         p += offset;
582         size -= offset;
583     }
584     if (size) memcpy( p, p + offset, size );
585     LOCAL_ShrinkArena( ds, prevArena, newsize );
586 }
587
588
589
590 /***********************************************************************
591  *           LOCAL_GrowArenaUpward
592  *
593  * Grow an arena upward by using the next arena (must be free and big
594  * enough). Newsize includes the arena header and must be aligned.
595  */
596 static void LOCAL_GrowArenaUpward( WORD ds, WORD arena, WORD newsize )
597 {
598     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
599     LOCALHEAPINFO *pInfo;
600     LOCALARENA *pArena = ARENA_PTR( ptr, arena );
601     WORD nextArena = pArena->next;
602
603     if (!(pInfo = LOCAL_GetHeap( ds ))) return;
604     LOCAL_RemoveBlock( ptr, nextArena );
605     pInfo->items--;
606     LOCAL_ShrinkArena( ds, arena, newsize );
607 }
608
609
610 /***********************************************************************
611  *           LOCAL_GetFreeSpace
612  */
613 static WORD LOCAL_GetFreeSpace(WORD ds, WORD countdiscard)
614 {
615     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
616     LOCALHEAPINFO *pInfo;
617     LOCALARENA *pArena;
618     WORD arena;
619     WORD freespace = 0;
620     
621     if (!(pInfo = LOCAL_GetHeap( ds )))
622     {
623         ERR(local, "Local heap not found\n" );
624         LOCAL_PrintHeap(ds);
625         return 0;
626     }
627     arena = pInfo->first;
628     pArena = ARENA_PTR( ptr, arena );
629     while (arena != pArena->free_next)
630     {
631         arena = pArena->free_next;
632         pArena = ARENA_PTR( ptr, arena );
633         if (pArena->size >= freespace) freespace = pArena->size;
634     }
635     /* FIXME doesn't yet calculate space that would become free if everything
636        were discarded when countdiscard == 1 */
637     if (freespace < ARENA_HEADER_SIZE) freespace = 0;
638     else freespace -= ARENA_HEADER_SIZE;
639     return freespace;
640 }
641
642
643 /***********************************************************************
644  *           LOCAL_Compact
645  */
646 WORD LOCAL_Compact( HANDLE16 ds, UINT16 minfree, UINT16 flags )
647 {
648     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
649     LOCALHEAPINFO *pInfo;
650     LOCALARENA *pArena, *pMoveArena, *pFinalArena;
651     WORD arena, movearena, finalarena, table;
652     WORD count, movesize, size;
653     WORD freespace;
654     LOCALHANDLEENTRY *pEntry;
655
656     if (!(pInfo = LOCAL_GetHeap( ds )))
657     {
658         ERR(local, "Local heap not found\n" );
659         LOCAL_PrintHeap(ds);
660         return 0;
661     }
662     TRACE(local, "ds = %04x, minfree = %04x, flags = %04x\n",
663                  ds, minfree, flags);
664     freespace = LOCAL_GetFreeSpace(ds, minfree ? 0 : 1);
665     if(freespace >= minfree || (flags & LMEM_NOCOMPACT))
666     {
667         TRACE(local, "Returning %04x.\n", freespace);
668         return freespace;
669     }
670     TRACE(local, "Compacting heap %04x.\n", ds);
671     table = pInfo->htable;
672     while(table)
673     {
674         pEntry = (LOCALHANDLEENTRY *)(ptr + table + sizeof(WORD));
675         for(count = *(WORD *)(ptr + table); count > 0; count--, pEntry++)
676         {
677             if((pEntry->lock == 0) && (pEntry->flags != (LMEM_DISCARDED >> 8)))
678             {
679                 /* OK we can move this one if we want */
680                 TRACE(local, "handle %04x (block %04x) can be moved.\n",
681                              (WORD)((char *)pEntry - ptr), pEntry->addr);
682                 movearena = ARENA_HEADER(pEntry->addr);
683                 pMoveArena = ARENA_PTR(ptr, movearena);
684                 movesize = pMoveArena->next - movearena;
685                 arena = pInfo->first;
686                 pArena = ARENA_PTR(ptr, arena);
687                 size = 0xffff;
688                 finalarena = 0;
689                 /* Try to find the smallest arena that will do, */
690                 /* which is below us in memory */
691                 for(;;)
692                 {
693                     arena = pArena->free_next;
694                     pArena = ARENA_PTR(ptr, arena);
695                     if(arena >= movearena)
696                         break;
697                     if(arena == pArena->free_next)
698                         break;
699                     if((pArena->size >= movesize) && (pArena->size < size))
700                     {
701                         size = pArena->size;
702                         finalarena = arena;
703                     }
704                 }
705                 if (finalarena) /* Actually got somewhere to move */
706                 {
707                     TRACE(local, "Moving it to %04x.\n", finalarena);
708                     pFinalArena = ARENA_PTR(ptr, finalarena);
709                     size = pFinalArena->size;
710                     LOCAL_RemoveFreeBlock(ptr, finalarena);
711                     LOCAL_ShrinkArena( ds, finalarena, movesize );
712                     /* Copy the arena to it's new location */
713                     memcpy((char *)pFinalArena + ARENA_HEADER_SIZE,
714                            (char *)pMoveArena + ARENA_HEADER_SIZE,
715                            movesize - ARENA_HEADER_SIZE );
716                     /* Free the old location */  
717                     LOCAL_FreeArena(ds, movearena);
718                     /* Update handle table entry */
719                     pEntry->addr = finalarena + ARENA_HEADER_SIZE + sizeof(HLOCAL16) ;
720                 }
721                 else if((ARENA_PTR(ptr, pMoveArena->prev & ~3)->prev & 3)
722                                == LOCAL_ARENA_FREE)
723                 {
724                     /* Previous arena is free (but < movesize)  */
725                     /* so we can 'slide' movearena down into it */
726                     finalarena = pMoveArena->prev & ~3;
727                     LOCAL_GrowArenaDownward( ds, movearena, movesize );
728                     /* Update handle table entry */
729                     pEntry->addr = finalarena + ARENA_HEADER_SIZE + sizeof(HLOCAL16) ;
730                 }
731             }
732         }
733         table = *(WORD *)pEntry;
734     }
735     freespace = LOCAL_GetFreeSpace(ds, minfree ? 0 : 1);
736     if(freespace >= minfree || (flags & LMEM_NODISCARD))
737     {
738         TRACE(local, "Returning %04x.\n", freespace);
739         return freespace;
740     }
741
742     table = pInfo->htable;
743     while(table)
744     {
745         pEntry = (LOCALHANDLEENTRY *)(ptr + table + sizeof(WORD));
746         for(count = *(WORD *)(ptr + table); count > 0; count--, pEntry++)
747         {
748             if(pEntry->addr && pEntry->lock == 0 &&
749              (pEntry->flags & (LMEM_DISCARDABLE >> 8)))
750             {
751                 TRACE(local, "Discarding handle %04x (block %04x).\n",
752                               (char *)pEntry - ptr, pEntry->addr);
753                 LOCAL_FreeArena(ds, ARENA_HEADER(pEntry->addr));
754                 pEntry->addr = 0;
755                 pEntry->flags = (LMEM_DISCARDED >> 8);
756                 /* Call localnotify proc */
757             }
758         }
759         table = *(WORD *)pEntry;
760     }
761     return LOCAL_Compact(ds, 0xffff, LMEM_NODISCARD);
762 }
763
764
765 /***********************************************************************
766  *           LOCAL_FindFreeBlock
767  */
768 static HLOCAL16 LOCAL_FindFreeBlock( HANDLE16 ds, WORD size )
769 {
770     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
771     LOCALHEAPINFO *pInfo;
772     LOCALARENA *pArena;
773     WORD arena;
774
775     if (!(pInfo = LOCAL_GetHeap( ds )))
776     {
777         ERR(local, "Local heap not found\n" );
778         LOCAL_PrintHeap(ds);
779         return 0;
780     }
781
782     arena = pInfo->first;
783     pArena = ARENA_PTR( ptr, arena );
784     for (;;) {
785         arena = pArena->free_next;
786         pArena = ARENA_PTR( ptr, arena );
787         if (arena == pArena->free_next) break;
788         if (pArena->size >= size) return arena;
789     }
790     ERR(local, "not enough space\n" );
791     LOCAL_PrintHeap(ds);
792     return 0;
793 }
794
795 /***********************************************************************
796  *           LOCAL_GetBlock
797  * The segment may get moved around in this function, so all callers
798  * should reset their pointer variables.
799  */
800 static HLOCAL16 LOCAL_GetBlock( HANDLE16 ds, WORD size, WORD flags )
801 {
802     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
803     LOCALHEAPINFO *pInfo;
804     LOCALARENA *pArena;
805     WORD arena;
806
807     if (!(pInfo = LOCAL_GetHeap( ds )))
808     {
809         ERR(local, "Local heap not found\n");
810         LOCAL_PrintHeap(ds);
811         return 0;
812     }
813
814     size += ARENA_HEADER_SIZE;
815     size = LALIGN( MAX( size, sizeof(LOCALARENA) ) );
816
817       /* Find a suitable free block */
818     arena = LOCAL_FindFreeBlock( ds, size );
819     if (arena == 0) {
820         /* no space: try to make some */
821         LOCAL_Compact( ds, size, flags );
822         arena = LOCAL_FindFreeBlock( ds, size );
823     }
824     if (arena == 0) {
825         /* still no space: try to grow the segment */
826         LOCAL_GrowHeap( ds );
827         ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
828         pInfo = LOCAL_GetHeap( ds );
829         arena = LOCAL_FindFreeBlock( ds, size );
830     }
831     if (arena == 0) {
832         if (ds == GDI_HeapSel) { 
833             WARN(local, "not enough space in GDI local heap "
834                          "(%04x) for %d bytes\n", ds, size );
835         } else if (ds == USER_HeapSel) {
836             WARN(local, "not enough space in USER local heap "
837                          "(%04x) for %d bytes\n", ds, size );
838         } else {
839             WARN(local, "not enough space in local heap "
840                          "%04x for %d bytes\n", ds, size );
841         }
842         return 0;
843     }
844
845       /* Make a block out of the free arena */
846     pArena = ARENA_PTR( ptr, arena );
847     TRACE(local, "LOCAL_GetBlock size = %04x, arena %04x size %04x\n",
848                   size, arena, pArena->size );
849     LOCAL_RemoveFreeBlock( ptr, arena );
850     LOCAL_ShrinkArena( ds, arena, size );
851
852     if (flags & LMEM_ZEROINIT)
853         memset((char *)pArena + ARENA_HEADER_SIZE, 0, size-ARENA_HEADER_SIZE);
854     return arena + ARENA_HEADER_SIZE;
855 }
856
857
858 /***********************************************************************
859  *           LOCAL_NewHTable
860  */
861 static BOOL16 LOCAL_NewHTable( HANDLE16 ds )
862 {
863     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
864     LOCALHEAPINFO *pInfo;
865     LOCALHANDLEENTRY *pEntry;
866     HLOCAL16 handle;
867     int i;
868
869     TRACE(local, "Local_NewHTable\n" );
870     if (!(pInfo = LOCAL_GetHeap( ds )))
871     {
872         ERR(local, "Local heap not found\n");
873         LOCAL_PrintHeap(ds);
874         return FALSE;
875     }
876
877     if (!(handle = LOCAL_GetBlock( ds, pInfo->hdelta * sizeof(LOCALHANDLEENTRY)
878                                    + 2 * sizeof(WORD), LMEM_FIXED )))
879         return FALSE;
880     if (!(ptr = PTR_SEG_OFF_TO_LIN( ds, 0 )))
881         ERR(local, "ptr == NULL after GetBlock.\n");
882     if (!(pInfo = LOCAL_GetHeap( ds )))
883         ERR(local,"pInfo == NULL after GetBlock.\n");
884
885     /* Fill the entry table */
886
887     *(WORD *)(ptr + handle) = pInfo->hdelta;
888     pEntry = (LOCALHANDLEENTRY *)(ptr + handle + sizeof(WORD));
889     for (i = pInfo->hdelta; i > 0; i--, pEntry++) {
890         pEntry->lock = pEntry->flags = 0xff;
891         pEntry->addr = 0;
892     }
893     *(WORD *)pEntry = pInfo->htable;
894     pInfo->htable = handle;
895     return TRUE;
896 }
897
898
899 /***********************************************************************
900  *           LOCAL_GetNewHandleEntry
901  */
902 static HLOCAL16 LOCAL_GetNewHandleEntry( HANDLE16 ds )
903 {
904     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
905     LOCALHEAPINFO *pInfo;
906     LOCALHANDLEENTRY *pEntry = NULL;
907     WORD table;
908
909     if (!(pInfo = LOCAL_GetHeap( ds )))
910     {
911         ERR(local, "Local heap not found\n");
912         LOCAL_PrintHeap(ds);
913         return 0;
914     }
915
916     /* Find a free slot in existing tables */
917
918     table = pInfo->htable;
919     while (table)
920     {
921         WORD count = *(WORD *)(ptr + table);
922         pEntry = (LOCALHANDLEENTRY *)(ptr + table + sizeof(WORD));
923         for (; count > 0; count--, pEntry++)
924             if (pEntry->lock == 0xff) break;
925         if (count) break;
926         table = *(WORD *)pEntry;
927     }
928
929     if (!table)  /* We need to create a new table */
930     {
931         if (!LOCAL_NewHTable( ds )) return 0;
932         ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
933         pInfo = LOCAL_GetHeap( ds );
934         pEntry = (LOCALHANDLEENTRY *)(ptr + pInfo->htable + sizeof(WORD));
935     }
936
937     /* Now allocate this entry */
938
939     pEntry->lock = 0;
940     pEntry->flags = 0;
941     TRACE(local, "(%04x): %04x\n",
942                    ds, ((char *)pEntry - ptr) );
943     return (HLOCAL16)((char *)pEntry - ptr);
944 }
945
946
947 /***********************************************************************
948  *           LOCAL_FreeHandleEntry
949  *
950  * Free a handle table entry.
951  */
952 static void LOCAL_FreeHandleEntry( HANDLE16 ds, HLOCAL16 handle )
953 {
954     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
955     LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
956     LOCALHEAPINFO *pInfo;
957     WORD *pTable;
958     WORD table, count, i;
959
960     if (!(pInfo = LOCAL_GetHeap( ds ))) return;
961
962     /* Find the table where this handle comes from */
963
964     pTable = &pInfo->htable;
965     while (*pTable)
966     {
967         WORD size = (*(WORD *)(ptr + *pTable)) * sizeof(LOCALHANDLEENTRY);
968         if ((handle >= *pTable + sizeof(WORD)) &&
969             (handle < *pTable + sizeof(WORD) + size)) break;  /* Found it */
970         pTable = (WORD *)(ptr + *pTable + sizeof(WORD) + size);
971     }
972     if (!*pTable)
973     {
974         ERR(local, "Invalid entry %04x\n", handle);
975         LOCAL_PrintHeap( ds );
976         return;
977     }
978
979     /* Make the entry free */
980
981     pEntry->addr = 0;  /* just in case */
982     pEntry->lock = 0xff;
983     pEntry->flags = 0xff; 
984     /* Now check if all entries in this table are free */
985
986     table = *pTable;
987     pEntry = (LOCALHANDLEENTRY *)(ptr + table + sizeof(WORD));
988     count = *(WORD *)(ptr + table);
989     for (i = count; i > 0; i--, pEntry++) if (pEntry->lock != 0xff) return;
990     
991     /* Remove the table from the linked list and free it */
992
993     TRACE(local, "(%04x): freeing table %04x\n",
994                    ds, table);
995     *pTable = *(WORD *)pEntry;
996     LOCAL_FreeArena( ds, ARENA_HEADER( table ) );
997 }
998
999
1000 /***********************************************************************
1001  *           LOCAL_Free
1002  *
1003  * Implementation of LocalFree().
1004  */
1005 HLOCAL16 LOCAL_Free( HANDLE16 ds, HLOCAL16 handle )
1006 {
1007     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1008
1009     TRACE(local, "%04x ds=%04x\n", handle, ds );
1010     
1011     if (!handle) { WARN(local, "Handle is 0.\n" ); return 0; }
1012     if (HANDLE_FIXED( handle ))
1013     {
1014         if (!LOCAL_FreeArena( ds, ARENA_HEADER( handle ) )) return 0;  /* OK */
1015         else return handle;  /* couldn't free it */
1016     }
1017     else
1018     {
1019         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1020         if (pEntry->flags != (LMEM_DISCARDED >> 8))
1021         {
1022             TRACE(local, "real block at %04x\n",
1023                            pEntry->addr );
1024             if (LOCAL_FreeArena( ds, ARENA_HEADER(pEntry->addr) ))
1025                 return handle; /* couldn't free it */
1026         }
1027         LOCAL_FreeHandleEntry( ds, handle );
1028         return 0;  /* OK */
1029     }
1030 }
1031
1032
1033 /***********************************************************************
1034  *           LOCAL_Alloc
1035  *
1036  * Implementation of LocalAlloc().
1037  *
1038  */
1039 HLOCAL16 LOCAL_Alloc( HANDLE16 ds, WORD flags, WORD size )
1040 {
1041     char *ptr;
1042     HLOCAL16 handle;
1043     
1044     TRACE(local, "%04x %d ds=%04x\n", flags, size, ds );
1045
1046     if(size > 0 && size <= 4) size = 5;
1047     if (flags & LMEM_MOVEABLE)
1048     {
1049         LOCALHANDLEENTRY *plhe;
1050         HLOCAL16 hmem;
1051
1052         if(size)
1053         {
1054             if (!(hmem = LOCAL_GetBlock( ds, size + sizeof(HLOCAL16), flags )))
1055                 return 0;
1056         }
1057         else /* We just need to allocate a discarded handle */
1058             hmem = 0;
1059         if (!(handle = LOCAL_GetNewHandleEntry( ds )))
1060         {
1061             WARN(local, "Couldn't get handle.\n");
1062             if(hmem)
1063                 LOCAL_FreeArena( ds, ARENA_HEADER(hmem) );
1064             return 0;
1065         }
1066         ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1067         plhe = (LOCALHANDLEENTRY *)(ptr + handle);
1068         plhe->lock = 0;
1069         if(hmem)
1070         {
1071             plhe->addr = hmem + sizeof(HLOCAL16);
1072             plhe->flags = (BYTE)((flags & 0x0f00) >> 8);
1073             *(HLOCAL16 *)(ptr + hmem) = handle;
1074         }
1075         else
1076         {
1077             plhe->addr = 0;
1078             plhe->flags = LMEM_DISCARDED >> 8;
1079         }
1080     }
1081     else /* FIXED */
1082     {
1083         if(!size)
1084             return 0;
1085         handle = LOCAL_GetBlock( ds, size, flags );
1086     }
1087     return handle;
1088 }
1089
1090
1091 /***********************************************************************
1092  *           LOCAL_ReAlloc
1093  *
1094  * Implementation of LocalReAlloc().
1095  */
1096 HLOCAL16 LOCAL_ReAlloc( HANDLE16 ds, HLOCAL16 handle, WORD size, WORD flags )
1097 {
1098     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1099     LOCALHEAPINFO *pInfo;
1100     LOCALARENA *pArena, *pNext;
1101     LOCALHANDLEENTRY *pEntry = NULL;
1102     WORD arena, oldsize;
1103     HLOCAL16 hmem, blockhandle;
1104     LONG nextarena;
1105
1106     if (!handle) return 0;
1107     if(HANDLE_MOVEABLE(handle) &&
1108      ((LOCALHANDLEENTRY *)(ptr + handle))->lock == 0xff) /* An unused handle */
1109         return 0;
1110
1111     TRACE(local, "%04x %d %04x ds=%04x\n",
1112                    handle, size, flags, ds );
1113     if (!(pInfo = LOCAL_GetHeap( ds ))) return 0;
1114     
1115     if (HANDLE_FIXED( handle ))
1116         blockhandle = handle;
1117     else
1118     {
1119         pEntry = (LOCALHANDLEENTRY *) (ptr + handle);
1120         if(pEntry->flags == (LMEM_DISCARDED >> 8))
1121         {
1122             HLOCAL16 hl;
1123             if(pEntry->addr)
1124                 WARN(local,"Dicarded block has non-zero addr.\n");
1125             TRACE(local, "ReAllocating discarded block\n");
1126             if(size <= 4) size = 5;
1127             if (!(hl = LOCAL_GetBlock( ds, size + sizeof(HLOCAL16), flags)))
1128                 return 0;
1129             ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );  /* Reload ptr */
1130             pEntry = (LOCALHANDLEENTRY *) (ptr + handle);
1131             pEntry->addr = hl + sizeof(HLOCAL16);
1132             pEntry->flags = 0;
1133             pEntry->lock = 0;
1134             *(HLOCAL16 *)(ptr + hl) = handle;
1135             return handle;
1136         }
1137         if (((blockhandle = pEntry->addr) & 3) != 2)
1138         {
1139             ERR(local, "(%04x,%04x): invalid handle\n",
1140                      ds, handle );
1141             return 0;
1142         }
1143         if(*((HLOCAL16 *)(ptr + blockhandle) - 1) != handle) {
1144             ERR(local, "Back ptr to handle is invalid\n");
1145             return 0;
1146         }
1147     }
1148
1149     if (flags & LMEM_MODIFY)
1150     {
1151         if (HANDLE_MOVEABLE(handle))
1152         {
1153             pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1154             pEntry->flags = (flags & 0x0f00) >> 8;
1155             TRACE(local, "Changing flags to %x.\n", pEntry->flags);
1156         }
1157         return handle;
1158     }
1159
1160     if (!size)
1161     {
1162         if (flags & LMEM_MOVEABLE)
1163         {
1164             if (HANDLE_FIXED(handle))
1165             {
1166                 TRACE(local, "Freeing fixed block.\n");
1167                 return LOCAL_Free( ds, handle );
1168             }
1169             else /* Moveable block */
1170             {
1171                 pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1172                 if (pEntry->lock == 0)
1173                 {
1174                     /* discards moveable blocks */
1175                     TRACE(local,"Discarding block\n");
1176                     LOCAL_FreeArena(ds, ARENA_HEADER(pEntry->addr));
1177                     pEntry->addr = 0;
1178                     pEntry->flags = (LMEM_DISCARDED >> 8);
1179                     return handle;
1180                 }
1181             }
1182             return 0;
1183         }
1184         else if(flags == 0)
1185         {
1186             pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1187             if (pEntry->lock == 0)
1188             {
1189                 /* Frees block */
1190                 return LOCAL_Free( ds, handle );
1191             }
1192         }
1193         return 0;
1194     }
1195
1196     arena = ARENA_HEADER( blockhandle );
1197     TRACE(local, "arena is %04x\n", arena );
1198     pArena = ARENA_PTR( ptr, arena );
1199
1200     if(size <= 4) size = 5;
1201     oldsize = pArena->next - arena - ARENA_HEADER_SIZE;
1202     nextarena = LALIGN(blockhandle + size);
1203
1204       /* Check for size reduction */
1205
1206     if (nextarena <= pArena->next)
1207     {
1208         TRACE(local, "size reduction, making new free block\n");
1209         LOCAL_ShrinkArena(ds, arena, nextarena - arena);
1210         TRACE(local, "returning %04x\n", handle );
1211         return handle;
1212     }
1213
1214       /* Check if the next block is free and large enough */
1215
1216     pNext = ARENA_PTR( ptr, pArena->next );
1217     if (((pNext->prev & 3) == LOCAL_ARENA_FREE) &&
1218         (nextarena <= pNext->next))
1219     {
1220         TRACE(local, "size increase, making new free block\n");
1221         LOCAL_GrowArenaUpward(ds, arena, nextarena - arena);
1222         TRACE(local, "returning %04x\n", handle );
1223         return handle;
1224     }
1225
1226     /* Now we have to allocate a new block, but not if (fixed block or locked
1227        block) and no LMEM_MOVEABLE */
1228
1229     if (!(flags & LMEM_MOVEABLE))
1230     {
1231         if (HANDLE_FIXED(handle))
1232         {
1233             ERR(local, "Needed to move fixed block, but LMEM_MOVEABLE not specified.\n");
1234             return 0;
1235         }
1236         else
1237         {
1238             if(((LOCALHANDLEENTRY *)(ptr + handle))->lock != 0)
1239             {
1240                 ERR(local, "Needed to move locked block, but LMEM_MOVEABLE not specified.\n");
1241                 return 0;
1242             }
1243         }
1244     }
1245     if(HANDLE_MOVEABLE(handle)) size += sizeof(HLOCAL16);
1246     hmem = LOCAL_GetBlock( ds, size, flags );
1247     ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );  /* Reload ptr                             */
1248     if(HANDLE_MOVEABLE(handle))         /* LOCAL_GetBlock might have triggered    */
1249     {                                   /* a compaction, which might in turn have */
1250       blockhandle = pEntry->addr ;      /* moved the very block we are resizing   */
1251       arena = ARENA_HEADER( blockhandle );   /* thus, we reload arena, too        */
1252     }
1253     if (!hmem)
1254     {
1255         /* Remove the block from the heap and try again */
1256         LPSTR buffer = HeapAlloc( GetProcessHeap(), 0, oldsize );
1257         if (!buffer) return 0;
1258         memcpy( buffer, ptr + arena + ARENA_HEADER_SIZE, oldsize );
1259         LOCAL_FreeArena( ds, arena );
1260         if (!(hmem = LOCAL_GetBlock( ds, size, flags )))
1261         {
1262             if (!(hmem = LOCAL_GetBlock( ds, oldsize, flags )))
1263             {
1264                 ERR(local, "Can't restore saved block\n" );
1265                 HeapFree( GetProcessHeap(), 0, buffer );
1266                 return 0;
1267             }
1268             size = oldsize;
1269         }
1270         ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );  /* Reload ptr */
1271         memcpy( ptr + hmem, buffer, oldsize );
1272         HeapFree( GetProcessHeap(), 0, buffer );
1273     }
1274     else
1275     {
1276         memcpy( ptr + hmem, ptr + (arena + ARENA_HEADER_SIZE), oldsize );
1277         LOCAL_FreeArena( ds, arena );
1278     }
1279     if (HANDLE_MOVEABLE( handle ))
1280     {
1281         TRACE(local, "fixing handle\n");
1282         pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1283         pEntry->addr = hmem + sizeof(HLOCAL16);
1284         /* Back ptr should still be correct */
1285         if(*(HLOCAL16 *)(ptr + hmem) != handle)
1286             ERR(local, "back ptr is invalid.\n");
1287         hmem = handle;
1288     }
1289     if (size == oldsize) hmem = 0;  /* Realloc failed */
1290     TRACE(local, "returning %04x\n", hmem );
1291     return hmem;
1292 }
1293
1294
1295 /***********************************************************************
1296  *           LOCAL_InternalLock
1297  */
1298 static HLOCAL16 LOCAL_InternalLock( LPSTR heap, HLOCAL16 handle )
1299 {
1300     HLOCAL16 old_handle = handle;
1301
1302     if (HANDLE_MOVEABLE(handle))
1303     {
1304         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(heap + handle);
1305         if (pEntry->flags == LMEM_DISCARDED) return 0;
1306         if (pEntry->lock < 0xfe) pEntry->lock++;
1307         handle = pEntry->addr;
1308     }
1309     TRACE(local, "%04x returning %04x\n", 
1310                    old_handle, handle );
1311     return handle;
1312 }
1313
1314
1315 /***********************************************************************
1316  *           LOCAL_Lock
1317  */
1318 LPSTR LOCAL_Lock( HANDLE16 ds, HLOCAL16 handle )
1319 {
1320     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1321     return handle ? ptr + LOCAL_InternalLock( ptr, handle ) : NULL;
1322 }
1323
1324
1325 /***********************************************************************
1326  *           LOCAL_LockSegptr
1327  */
1328 SEGPTR LOCAL_LockSegptr( HANDLE16 ds, HLOCAL16 handle )
1329 {
1330     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1331     return PTR_SEG_OFF_TO_SEGPTR( ds, LOCAL_InternalLock( ptr, handle ) );
1332 }
1333
1334
1335 /***********************************************************************
1336  *           LOCAL_Unlock
1337  */
1338 BOOL16 LOCAL_Unlock( HANDLE16 ds, HLOCAL16 handle )
1339 {
1340     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1341
1342     TRACE(local, "%04x\n", handle );
1343     if (HANDLE_MOVEABLE(handle))
1344     {
1345         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1346         if (!pEntry->lock || (pEntry->lock == 0xff)) return FALSE;
1347         /* For moveable block, return the new lock count */
1348         /* (see _Windows_Internals_ p. 197) */
1349         return --pEntry->lock;
1350     }
1351     else return FALSE;
1352 }
1353
1354
1355 /***********************************************************************
1356  *           LOCAL_Size
1357  *
1358  * Implementation of LocalSize().
1359  */
1360 WORD LOCAL_Size( HANDLE16 ds, HLOCAL16 handle )
1361 {
1362     char *ptr = PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
1363     LOCALARENA *pArena;
1364
1365     TRACE(local, "%04x ds=%04x\n", handle, ds );
1366
1367     if (HANDLE_MOVEABLE( handle )) handle = *(WORD *)(ptr + handle);
1368     if (!handle) return 0;
1369     pArena = ARENA_PTR( ptr, ARENA_HEADER(handle) );
1370     return pArena->next - handle;
1371 }
1372
1373
1374 /***********************************************************************
1375  *           LOCAL_Flags
1376  *
1377  * Implementation of LocalFlags().
1378  */
1379 WORD LOCAL_Flags( HANDLE16 ds, HLOCAL16 handle )
1380 {
1381     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1382
1383     if (HANDLE_MOVEABLE(handle))
1384     {
1385         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1386         TRACE(local, "(%04x,%04x): returning %04x\n",
1387                        ds, handle, pEntry->lock | (pEntry->flags << 8) );
1388         return pEntry->lock | (pEntry->flags << 8);
1389     }
1390     else
1391     {
1392         TRACE(local, "(%04x,%04x): returning 0\n",
1393                        ds, handle );
1394         return 0;
1395     }
1396 }
1397
1398
1399 /***********************************************************************
1400  *           LOCAL_HeapSize
1401  *
1402  * Implementation of LocalHeapSize().
1403  */
1404 WORD LOCAL_HeapSize( HANDLE16 ds )
1405 {
1406     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
1407     if (!pInfo) return 0;
1408     return pInfo->last - pInfo->first;
1409 }
1410
1411
1412 /***********************************************************************
1413  *           LOCAL_CountFree
1414  *
1415  * Implementation of LocalCountFree().
1416  */
1417 WORD LOCAL_CountFree( HANDLE16 ds )
1418 {
1419     WORD arena, total;
1420     LOCALARENA *pArena;
1421     LOCALHEAPINFO *pInfo;
1422     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1423
1424     if (!(pInfo = LOCAL_GetHeap( ds )))
1425     {
1426         ERR(local, "(%04x): Local heap not found\n", ds );
1427         LOCAL_PrintHeap( ds );
1428         return 0;
1429     }
1430
1431     total = 0;
1432     arena = pInfo->first;
1433     pArena = ARENA_PTR( ptr, arena );
1434     for (;;)
1435     {
1436         arena = pArena->free_next;
1437         pArena = ARENA_PTR( ptr, arena );
1438         if (arena == pArena->free_next) break;
1439         total += pArena->size;
1440     }
1441     TRACE(local, "(%04x): returning %d\n", ds, total);
1442     return total;
1443 }
1444
1445
1446 /***********************************************************************
1447  *           LOCAL_Handle
1448  *
1449  * Implementation of LocalHandle().
1450  */
1451 HLOCAL16 LOCAL_Handle( HANDLE16 ds, WORD addr )
1452
1453     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1454     LOCALHEAPINFO *pInfo;
1455     WORD table;
1456
1457     if (!(pInfo = LOCAL_GetHeap( ds )))
1458     {
1459         ERR(local, "(%04x): Local heap not found\n", ds );
1460         LOCAL_PrintHeap( ds );
1461         return 0;
1462     }
1463
1464     /* Find the address in the entry tables */
1465
1466     table = pInfo->htable;
1467     while (table)
1468     {
1469         WORD count = *(WORD *)(ptr + table);
1470         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY*)(ptr+table+sizeof(WORD));
1471         for (; count > 0; count--, pEntry++)
1472             if (pEntry->addr == addr) return (HLOCAL16)((char *)pEntry - ptr);
1473         table = *(WORD *)pEntry;
1474     }
1475
1476     return (HLOCAL16)addr;  /* Fixed block handle is addr */
1477 }
1478
1479
1480 /***********************************************************************
1481  *           LocalAlloc16   (KERNEL.5)
1482  */
1483 HLOCAL16 WINAPI LocalAlloc16( UINT16 flags, WORD size )
1484 {
1485     return LOCAL_Alloc( CURRENT_DS, flags, size );
1486 }
1487
1488
1489 /***********************************************************************
1490  *           LocalReAlloc16   (KERNEL.6)
1491  */
1492 HLOCAL16 WINAPI LocalReAlloc16( HLOCAL16 handle, WORD size, UINT16 flags )
1493 {
1494     return LOCAL_ReAlloc( CURRENT_DS, handle, size, flags );
1495 }
1496
1497
1498 /***********************************************************************
1499  *           LocalFree16   (KERNEL.7)
1500  */
1501 HLOCAL16 WINAPI LocalFree16( HLOCAL16 handle )
1502 {
1503     return LOCAL_Free( CURRENT_DS, handle );
1504 }
1505
1506
1507 /***********************************************************************
1508  *           LocalLock16   (KERNEL.8)
1509  *
1510  * Note: only the offset part of the pointer is returned by the relay code.
1511  */
1512 SEGPTR WINAPI LocalLock16( HLOCAL16 handle )
1513 {
1514     return LOCAL_LockSegptr( CURRENT_DS, handle );
1515 }
1516
1517
1518 /***********************************************************************
1519  *           LocalUnlock16   (KERNEL.9)
1520  */
1521 BOOL16 WINAPI LocalUnlock16( HLOCAL16 handle )
1522 {
1523     return LOCAL_Unlock( CURRENT_DS, handle );
1524 }
1525
1526
1527 /***********************************************************************
1528  *           LocalSize16   (KERNEL.10)
1529  */
1530 UINT16 WINAPI LocalSize16( HLOCAL16 handle )
1531 {
1532     return LOCAL_Size( CURRENT_DS, handle );
1533 }
1534
1535
1536 /***********************************************************************
1537  *           LocalHandle16   (KERNEL.11)
1538  */
1539 HLOCAL16 WINAPI LocalHandle16( WORD addr )
1540
1541     return LOCAL_Handle( CURRENT_DS, addr );
1542 }
1543
1544
1545 /***********************************************************************
1546  *           LocalFlags16   (KERNEL.12)
1547  */
1548 UINT16 WINAPI LocalFlags16( HLOCAL16 handle )
1549 {
1550     return LOCAL_Flags( CURRENT_DS, handle );
1551 }
1552
1553
1554 /***********************************************************************
1555  *           LocalCompact16   (KERNEL.13)
1556  */
1557 UINT16 WINAPI LocalCompact16( UINT16 minfree )
1558 {
1559     TRACE(local, "%04x\n", minfree );
1560     return LOCAL_Compact( CURRENT_DS, minfree, 0 );
1561 }
1562
1563
1564 /***********************************************************************
1565  *           LocalNotify   (KERNEL.14)
1566  */
1567 FARPROC16 WINAPI LocalNotify( FARPROC16 func )
1568 {
1569     LOCALHEAPINFO *pInfo;
1570     FARPROC16 oldNotify;
1571     HANDLE16 ds = CURRENT_DS;
1572
1573     if (!(pInfo = LOCAL_GetHeap( ds )))
1574     {
1575         ERR(local, "(%04x): Local heap not found\n", ds );
1576         LOCAL_PrintHeap( ds );
1577         return 0;
1578     }
1579     TRACE(local, "(%04x): %08lx\n", ds, (DWORD)func );
1580     FIXME(local, "Half implemented\n");
1581     oldNotify = pInfo->notify;
1582     pInfo->notify = func;
1583     return oldNotify;
1584 }
1585
1586
1587 /***********************************************************************
1588  *           LocalShrink16   (KERNEL.121)
1589  */
1590 UINT16 WINAPI LocalShrink16( HGLOBAL16 handle, UINT16 newsize )
1591 {
1592     TRACE(local, "%04x %04x\n", handle, newsize );
1593     return 0;
1594 }
1595
1596
1597 /***********************************************************************
1598  *           GetHeapSpaces   (KERNEL.138)
1599  */
1600 DWORD WINAPI GetHeapSpaces( HMODULE16 module )
1601 {
1602     NE_MODULE *pModule;
1603     WORD ds;
1604
1605     if (!(pModule = NE_GetPtr( module ))) return 0;
1606     ds =
1607     GlobalHandleToSel((NE_SEG_TABLE( pModule ) + pModule->dgroup - 1)->hSeg);
1608     return MAKELONG( LOCAL_CountFree( ds ), LOCAL_HeapSize( ds ) );
1609 }
1610
1611
1612 /***********************************************************************
1613  *           LocalCountFree   (KERNEL.161)
1614  */
1615 WORD WINAPI LocalCountFree(void)
1616 {
1617     return LOCAL_CountFree( CURRENT_DS );
1618 }
1619
1620
1621 /***********************************************************************
1622  *           LocalHeapSize   (KERNEL.162)
1623  */
1624 WORD WINAPI LocalHeapSize(void)
1625 {
1626     TRACE(local, "(void)\n" );
1627     return LOCAL_HeapSize( CURRENT_DS );
1628 }
1629
1630
1631 /***********************************************************************
1632  *           LocalHandleDelta   (KERNEL.310)
1633  */
1634 WORD WINAPI LocalHandleDelta( WORD delta )
1635 {
1636     LOCALHEAPINFO *pInfo;
1637
1638     if (!(pInfo = LOCAL_GetHeap( CURRENT_DS )))
1639     {
1640         ERR(local, "Local heap not found\n");
1641         LOCAL_PrintHeap( CURRENT_DS );
1642         return 0;
1643     }
1644     if (delta) pInfo->hdelta = delta;
1645     TRACE(local, "returning %04x\n", pInfo->hdelta);
1646     return pInfo->hdelta;
1647 }
1648
1649
1650 /***********************************************************************
1651  *           LocalInfo   (TOOLHELP.56)
1652  */
1653 BOOL16 WINAPI LocalInfo( LOCALINFO *pLocalInfo, HGLOBAL16 handle )
1654 {
1655     LOCALHEAPINFO *pInfo = LOCAL_GetHeap(SELECTOROF(WIN16_GlobalLock16(handle)));
1656     if (!pInfo) return FALSE;
1657     pLocalInfo->wcItems = pInfo->items;
1658     return TRUE;
1659 }
1660
1661
1662 /***********************************************************************
1663  *           LocalFirst   (TOOLHELP.57)
1664  */
1665 BOOL16 WINAPI LocalFirst( LOCALENTRY *pLocalEntry, HGLOBAL16 handle )
1666 {
1667     WORD ds = GlobalHandleToSel( handle );
1668     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1669     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
1670     if (!pInfo) return FALSE;
1671
1672     pLocalEntry->hHandle   = pInfo->first + ARENA_HEADER_SIZE;
1673     pLocalEntry->wAddress  = pLocalEntry->hHandle;
1674     pLocalEntry->wFlags    = LF_FIXED;
1675     pLocalEntry->wcLock    = 0;
1676     pLocalEntry->wType     = LT_NORMAL;
1677     pLocalEntry->hHeap     = handle;
1678     pLocalEntry->wHeapType = NORMAL_HEAP;
1679     pLocalEntry->wNext     = ARENA_PTR(ptr,pInfo->first)->next;
1680     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
1681     return TRUE;
1682 }
1683
1684
1685 /***********************************************************************
1686  *           LocalNext   (TOOLHELP.58)
1687  */
1688 BOOL16 WINAPI LocalNext( LOCALENTRY *pLocalEntry )
1689 {
1690     WORD ds = GlobalHandleToSel( pLocalEntry->hHeap );
1691     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
1692     LOCALARENA *pArena;
1693
1694     if (!LOCAL_GetHeap( ds )) return FALSE;
1695     if (!pLocalEntry->wNext) return FALSE;
1696     pArena = ARENA_PTR( ptr, pLocalEntry->wNext );
1697
1698     pLocalEntry->hHandle   = pLocalEntry->wNext + ARENA_HEADER_SIZE;
1699     pLocalEntry->wAddress  = pLocalEntry->hHandle;
1700     pLocalEntry->wFlags    = (pArena->prev & 3) + 1;
1701     pLocalEntry->wcLock    = 0;
1702     pLocalEntry->wType     = LT_NORMAL;
1703     if (pArena->next != pLocalEntry->wNext)  /* last one? */
1704         pLocalEntry->wNext = pArena->next;
1705     else
1706         pLocalEntry->wNext = 0;
1707     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
1708     return TRUE;
1709 }
1710
1711
1712 /***********************************************************************
1713  *           LocalAlloc32   (KERNEL32.371)
1714  * RETURNS
1715  *      Handle: Success
1716  *      NULL: Failure
1717  */
1718 HLOCAL32 WINAPI LocalAlloc32(
1719                 UINT32 flags, /* [in] Allocation attributes */
1720                 DWORD size    /* [in] Number of bytes to allocate */
1721 ) {
1722     return (HLOCAL32)GlobalAlloc32( flags, size );
1723 }
1724
1725
1726 /***********************************************************************
1727  *           LocalCompact32   (KERNEL32.372)
1728  */
1729 UINT32 WINAPI LocalCompact32( UINT32 minfree )
1730 {
1731     return 0;  /* LocalCompact does nothing in Win32 */
1732 }
1733
1734
1735 /***********************************************************************
1736  *           LocalFlags32   (KERNEL32.374)
1737  * RETURNS
1738  *      Value specifying allocation flags and lock count.
1739  *      LMEM_INVALID_HANDLE: Failure
1740  */
1741 UINT32 WINAPI LocalFlags32(
1742               HLOCAL32 handle /* [in] Handle of memory object */
1743 ) {
1744     return GlobalFlags32( (HGLOBAL32)handle );
1745 }
1746
1747
1748 /***********************************************************************
1749  *           LocalFree32   (KERNEL32.375)
1750  * RETURNS
1751  *      NULL: Success
1752  *      Handle: Failure
1753  */
1754 HLOCAL32 WINAPI LocalFree32(
1755                 HLOCAL32 handle /* [in] Handle of memory object */
1756 ) {
1757     return (HLOCAL32)GlobalFree32( (HGLOBAL32)handle );
1758 }
1759
1760
1761 /***********************************************************************
1762  *           LocalHandle32   (KERNEL32.376)
1763  * RETURNS
1764  *      Handle: Success
1765  *      NULL: Failure
1766  */
1767 HLOCAL32 WINAPI LocalHandle32(
1768                 LPCVOID ptr /* [in] Address of local memory object */
1769 ) {
1770     return (HLOCAL32)GlobalHandle32( ptr );
1771 }
1772
1773
1774 /***********************************************************************
1775  *           LocalLock32   (KERNEL32.377)
1776  * Locks a local memory object and returns pointer to the first byte
1777  * of the memory block.
1778  *
1779  * RETURNS
1780  *      Pointer: Success
1781  *      NULL: Failure
1782  */
1783 LPVOID WINAPI LocalLock32(
1784               HLOCAL32 handle /* [in] Address of local memory object */
1785 ) {
1786     return GlobalLock32( (HGLOBAL32)handle );
1787 }
1788
1789
1790 /***********************************************************************
1791  *           LocalReAlloc32   (KERNEL32.378)
1792  * RETURNS
1793  *      Handle: Success
1794  *      NULL: Failure
1795  */
1796 HLOCAL32 WINAPI LocalReAlloc32(
1797                 HLOCAL32 handle, /* [in] Handle of memory object */
1798                 DWORD size,      /* [in] New size of block */
1799                 UINT32 flags     /* [in] How to reallocate object */
1800 ) {
1801     return (HLOCAL32)GlobalReAlloc32( (HGLOBAL32)handle, size, flags );
1802 }
1803
1804
1805 /***********************************************************************
1806  *           LocalShrink32   (KERNEL32.379)
1807  */
1808 UINT32 WINAPI LocalShrink32( HGLOBAL32 handle, UINT32 newsize )
1809 {
1810     return 0;  /* LocalShrink does nothing in Win32 */
1811 }
1812
1813
1814 /***********************************************************************
1815  *           LocalSize32   (KERNEL32.380)
1816  * RETURNS
1817  *      Size: Success
1818  *      0: Failure
1819  */
1820 UINT32 WINAPI LocalSize32(
1821               HLOCAL32 handle /* [in] Handle of memory object */
1822 ) {
1823     return GlobalSize32( (HGLOBAL32)handle );
1824 }
1825
1826
1827 /***********************************************************************
1828  *           LocalUnlock32   (KERNEL32.381)
1829  * RETURNS
1830  *      TRUE: Object is still locked
1831  *      FALSE: Object is unlocked
1832  */
1833 BOOL32 WINAPI LocalUnlock32(
1834               HLOCAL32 handle /* [in] Handle of memory object */
1835 ) {
1836     return GlobalUnlock32( (HGLOBAL32)handle );
1837 }