Moved memory/environ.c, memory/virtual.c and misc/cpu.c to
[wine] / dlls / kernel / local16.c
1 /*
2  * 16-bit local heap functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  * Copyright 1996 Huw Davies
6  * Copyright 1998 Ulrich Weigand
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /*
24  * Note:
25  * All local heap functions need the current DS as first parameter
26  * when called from the emulation library, so they take one more
27  * parameter than usual.
28  */
29
30 #include "config.h"
31
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include <stdlib.h>
35 #include <string.h>
36 #include "wine/winbase16.h"
37 #include "wownt32.h"
38 #include "instance.h"
39 #include "local.h"
40 #include "global.h"
41 #include "module.h"
42 #include "stackframe.h"
43 #include "selectors.h"
44 #include "toolhelp.h"
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(local);
48
49 typedef struct
50 {
51 /* Arena header */
52     WORD prev;          /* Previous arena | arena type */
53     WORD next;          /* Next arena */
54 /* Start of the memory block or free-list info */
55     WORD size;          /* Size of the free block */
56     WORD free_prev;     /* Previous free block */
57     WORD free_next;     /* Next free block */
58 } LOCALARENA;
59
60 #define ARENA_HEADER_SIZE      4
61 #define ARENA_HEADER( handle) ((handle) - ARENA_HEADER_SIZE)
62
63   /* Arena types (stored in 'prev' field of the arena) */
64 #define LOCAL_ARENA_FREE       0
65 #define LOCAL_ARENA_FIXED      1
66
67 /* LocalNotify() msgs */
68
69 #define LN_OUTOFMEM     0
70 #define LN_MOVE         1
71 #define LN_DISCARD      2
72
73 /* Layout of a handle entry table
74  *
75  * WORD                     count of entries
76  * LOCALHANDLEENTRY[count]  entries
77  * WORD                     near ptr to next table
78  */
79 typedef struct
80 {
81     WORD addr;                /* Address of the MOVEABLE block */
82     BYTE flags;               /* Flags for this block */
83     BYTE lock;                /* Lock count */
84 } LOCALHANDLEENTRY;
85
86 /*
87  * We make addr = 4n + 2 and set *((WORD *)addr - 1) = &addr like Windows does
88  * in case something actually relies on this.
89  * Note that if the architecture does not allow unaligned accesses, we make
90  * addr = 4n + 4 to avoid returning unaligned pointers from LocalAlloc etc.
91  *
92  * An unused handle has lock = flags = 0xff. In windows addr is that of next
93  * free handle, at the moment in wine we set it to 0.
94  *
95  * A discarded block's handle has lock = addr = 0 and flags = 0x40
96  * (LMEM_DISCARDED >> 8)
97  */
98
99 #ifdef ALLOW_UNALIGNED_ACCESS
100   #define MOVEABLE_PREFIX sizeof(HLOCAL16)
101 #else
102   #define MOVEABLE_PREFIX sizeof(int)
103 #endif
104
105
106 #include "pshpack1.h"
107
108 typedef struct
109 {
110     WORD check;                 /* 00 Heap checking flag */
111     WORD freeze;                /* 02 Heap frozen flag */
112     WORD items;                 /* 04 Count of items on the heap */
113     WORD first;                 /* 06 First item of the heap */
114     WORD pad1;                  /* 08 Always 0 */
115     WORD last;                  /* 0a Last item of the heap */
116     WORD pad2;                  /* 0c Always 0 */
117     BYTE ncompact;              /* 0e Compactions counter */
118     BYTE dislevel;              /* 0f Discard level */
119     DWORD distotal;             /* 10 Total bytes discarded */
120     WORD htable;                /* 14 Pointer to handle table */
121     WORD hfree;                 /* 16 Pointer to free handle table */
122     WORD hdelta;                /* 18 Delta to expand the handle table */
123     WORD expand;                /* 1a Pointer to expand function (unused) */
124     WORD pstat;                 /* 1c Pointer to status structure (unused) */
125     FARPROC16 notify WINE_PACKED; /* 1e Pointer to LocalNotify() function */
126     WORD lock;                  /* 22 Lock count for the heap */
127     WORD extra;                 /* 24 Extra bytes to allocate when expanding */
128     WORD minsize;               /* 26 Minimum size of the heap */
129     WORD magic;                 /* 28 Magic number */
130 } LOCALHEAPINFO;
131
132 #include "poppack.h"
133
134 #define LOCAL_HEAP_MAGIC  0x484c  /* 'LH' */
135
136   /* All local heap allocations are aligned on 4-byte boundaries */
137 #define LALIGN(word)          (((word) + 3) & ~3)
138
139 #define ARENA_PTR(ptr,arena)       ((LOCALARENA *)((char*)(ptr)+(arena)))
140 #define ARENA_PREV(ptr,arena)      (ARENA_PTR((ptr),(arena))->prev & ~3)
141 #define ARENA_NEXT(ptr,arena)      (ARENA_PTR((ptr),(arena))->next)
142 #define ARENA_FLAGS(ptr,arena)     (ARENA_PTR((ptr),(arena))->prev & 3)
143
144   /* determine whether the handle belongs to a fixed or a moveable block */
145 #define HANDLE_FIXED(handle) (((handle) & 3) == 0)
146 #define HANDLE_MOVEABLE(handle) (((handle) & 3) == 2)
147
148
149 /* 32-bit heap definitions */
150
151 #define HTABLE_SIZE      0x10000
152 #define HTABLE_PAGESIZE  0x1000
153 #define HTABLE_NPAGES    (HTABLE_SIZE / HTABLE_PAGESIZE)
154
155 #include "pshpack1.h"
156 typedef struct _LOCAL32HEADER
157 {
158     WORD     freeListFirst[HTABLE_NPAGES];
159     WORD     freeListSize[HTABLE_NPAGES];
160     WORD     freeListLast[HTABLE_NPAGES];
161
162     DWORD    selectorTableOffset;
163     WORD     selectorTableSize;
164     WORD     selectorDelta;
165
166     DWORD    segment;
167     LPBYTE   base;
168
169     DWORD    limit;
170     DWORD    flags;
171
172     DWORD    magic;
173     HANDLE heap;
174
175 } LOCAL32HEADER;
176 #include "poppack.h"
177
178 #define LOCAL32_MAGIC    ((DWORD)('L' | ('H'<<8) | ('3'<<16) | ('2'<<24)))
179
180
181 static inline BOOL16 call_notify_func( FARPROC16 proc, WORD msg, HLOCAL16 handle, WORD arg )
182 {
183     DWORD ret;
184     WORD args[3];
185
186     if (!proc) return FALSE;
187     args[2] = msg;
188     args[1] = handle;
189     args[0] = arg;
190     WOWCallback16Ex( (DWORD)proc, WCB16_PASCAL, sizeof(args), args, &ret );
191     return LOWORD(ret);
192 }
193
194
195 /***********************************************************************
196  *           LOCAL_GetHeap
197  *
198  * Return a pointer to the local heap, making sure it exists.
199  */
200 static LOCALHEAPINFO *LOCAL_GetHeap( HANDLE16 ds )
201 {
202     LOCALHEAPINFO *pInfo;
203     INSTANCEDATA *ptr = MapSL( MAKESEGPTR( ds, 0 ));
204     TRACE("Heap at %p, %04x\n", ptr, (ptr != NULL ? ptr->heap : 0xFFFF));
205     if (!ptr || !ptr->heap) return NULL;
206     if (IsBadReadPtr16( (SEGPTR)MAKELONG(ptr->heap,ds), sizeof(LOCALHEAPINFO)))
207     {
208         WARN("Bad pointer\n");
209         return NULL;
210     }
211     pInfo = (LOCALHEAPINFO*)((char*)ptr + ptr->heap);
212     if (pInfo->magic != LOCAL_HEAP_MAGIC)
213     {
214         WARN("Bad magic\n");
215         return NULL;
216     }
217     return pInfo;
218 }
219
220
221 /***********************************************************************
222  *           LOCAL_MakeBlockFree
223  *
224  * Make a block free, inserting it in the free-list.
225  * 'block' is the handle of the block arena; 'baseptr' points to
226  * the beginning of the data segment containing the heap.
227  */
228 static void LOCAL_MakeBlockFree( char *baseptr, WORD block )
229 {
230     LOCALARENA *pArena, *pNext;
231     WORD next;
232
233       /* Mark the block as free */
234
235     pArena = ARENA_PTR( baseptr, block );
236     pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FREE;
237     pArena->size = pArena->next - block;
238
239       /* Find the next free block (last block is always free) */
240
241     next = pArena->next;
242     for (;;)
243     {
244         pNext = ARENA_PTR( baseptr, next );
245         if ((pNext->prev & 3) == LOCAL_ARENA_FREE) break;
246         next = pNext->next;
247     }
248
249     TRACE("%04x, next %04x\n", block, next );
250       /* Insert the free block in the free-list */
251
252     pArena->free_prev = pNext->free_prev;
253     pArena->free_next = next;
254     ARENA_PTR(baseptr,pNext->free_prev)->free_next = block;
255     pNext->free_prev  = block;
256 }
257
258
259 /***********************************************************************
260  *           LOCAL_RemoveFreeBlock
261  *
262  * Remove a block from the free-list.
263  * 'block' is the handle of the block arena; 'baseptr' points to
264  * the beginning of the data segment containing the heap.
265  */
266 static void LOCAL_RemoveFreeBlock( char *baseptr, WORD block )
267 {
268       /* Mark the block as fixed */
269
270     LOCALARENA *pArena = ARENA_PTR( baseptr, block );
271     pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FIXED;
272
273       /* Remove it from the list */
274
275     ARENA_PTR(baseptr,pArena->free_prev)->free_next = pArena->free_next;
276     ARENA_PTR(baseptr,pArena->free_next)->free_prev = pArena->free_prev;
277 }
278
279
280 /***********************************************************************
281  *           LOCAL_AddBlock
282  *
283  * Insert a new block in the heap.
284  * 'new' is the handle of the new block arena; 'baseptr' points to
285  * the beginning of the data segment containing the heap; 'prev' is
286  * the block before the new one.
287  */
288 static void LOCAL_AddBlock( char *baseptr, WORD prev, WORD new )
289 {
290     LOCALARENA *pPrev = ARENA_PTR( baseptr, prev );
291     LOCALARENA *pNew  = ARENA_PTR( baseptr, new );
292
293     pNew->prev = (prev & ~3) | LOCAL_ARENA_FIXED;
294     pNew->next = pPrev->next;
295     ARENA_PTR(baseptr,pPrev->next)->prev &= 3;
296     ARENA_PTR(baseptr,pPrev->next)->prev |= new;
297     pPrev->next = new;
298 }
299
300
301 /***********************************************************************
302  *           LOCAL_RemoveBlock
303  *
304  * Remove a block from the heap.
305  * 'block' is the handle of the block arena; 'baseptr' points to
306  * the beginning of the data segment containing the heap.
307  */
308 static void LOCAL_RemoveBlock( char *baseptr, WORD block )
309 {
310     LOCALARENA *pArena, *pTmp;
311
312       /* Remove the block from the free-list */
313
314     TRACE("\n");
315     pArena = ARENA_PTR( baseptr, block );
316     if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
317         LOCAL_RemoveFreeBlock( baseptr, block );
318
319       /* If the previous block is free, expand its size */
320
321     pTmp = ARENA_PTR( baseptr, pArena->prev & ~3 );
322     if ((pTmp->prev & 3) == LOCAL_ARENA_FREE)
323         pTmp->size += pArena->next - block;
324
325       /* Remove the block from the linked list */
326
327     pTmp->next = pArena->next;
328     pTmp = ARENA_PTR( baseptr, pArena->next );
329     pTmp->prev = (pTmp->prev & 3) | (pArena->prev & ~3);
330 }
331
332
333 /***********************************************************************
334  *           LOCAL_PrintHeap
335  */
336 static void LOCAL_PrintHeap( HANDLE16 ds )
337 {
338     char *ptr;
339     LOCALHEAPINFO *pInfo;
340     WORD arena;
341
342     /* FIXME - the test should be done when calling the function!
343                plus is not clear that we should print this info
344                only when TRACE_ON is on! */
345     if(!TRACE_ON(local)) return;
346
347     ptr = MapSL( MAKESEGPTR( ds, 0 ));
348     pInfo = LOCAL_GetHeap( ds );
349
350     if (!pInfo)
351     {
352         DPRINTF( "Local Heap corrupted!  ds=%04x\n", ds );
353         return;
354     }
355     DPRINTF( "Local Heap  ds=%04x first=%04x last=%04x items=%d\n",
356              ds, pInfo->first, pInfo->last, pInfo->items );
357
358     arena = pInfo->first;
359     for (;;)
360     {
361         LOCALARENA *pArena = ARENA_PTR(ptr,arena);
362         DPRINTF( "  %04x: prev=%04x next=%04x type=%d\n", arena,
363               pArena->prev & ~3, pArena->next, pArena->prev & 3 );
364         if (arena == pInfo->first)
365         {
366             DPRINTF( "        size=%d free_prev=%04x free_next=%04x\n",
367                      pArena->size, pArena->free_prev, pArena->free_next );
368         }
369         if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
370         {
371             DPRINTF( "        size=%d free_prev=%04x free_next=%04x\n",
372                      pArena->size, pArena->free_prev, pArena->free_next );
373             if (pArena->next == arena) break;  /* last one */
374             if (ARENA_PTR(ptr,pArena->free_next)->free_prev != arena)
375             {
376                 DPRINTF( "*** arena->free_next->free_prev != arena\n" );
377                 break;
378             }
379         }
380         if (pArena->next == arena)
381         {
382             DPRINTF( "*** last block is not marked free\n" );
383             break;
384         }
385         if ((ARENA_PTR(ptr,pArena->next)->prev & ~3) != arena)
386         {
387             DPRINTF( "*** arena->next->prev != arena (%04x, %04x)\n",
388                      pArena->next, ARENA_PTR(ptr,pArena->next)->prev);
389             break;
390         }
391         arena = pArena->next;
392     }
393 }
394
395
396 /***********************************************************************
397  *           LocalInit   (KERNEL.4)
398  */
399 BOOL16 WINAPI LocalInit16( HANDLE16 selector, WORD start, WORD end )
400 {
401     char *ptr;
402     WORD heapInfoArena, freeArena, lastArena;
403     LOCALHEAPINFO *pHeapInfo;
404     LOCALARENA *pArena, *pFirstArena, *pLastArena;
405     BOOL16 ret = FALSE;
406
407       /* The initial layout of the heap is: */
408       /* - first arena         (FIXED)      */
409       /* - heap info structure (FIXED)      */
410       /* - large free block    (FREE)       */
411       /* - last arena          (FREE)       */
412
413     TRACE("%04x %04x-%04x\n", selector, start, end);
414     if (!selector) selector = CURRENT_DS;
415
416     if (TRACE_ON(local))
417     {
418         /* If TRACE_ON(heap) is set, the global heap blocks are */
419         /* cleared before use, so we can test for double initialization. */
420         if (LOCAL_GetHeap(selector))
421         {
422             ERR("Heap %04x initialized twice.\n", selector);
423             LOCAL_PrintHeap(selector);
424         }
425     }
426
427     if (start == 0)
428     {
429         /* start == 0 means: put the local heap at the end of the segment */
430
431         DWORD size = GlobalSize16( GlobalHandle16( selector ) );
432         start = (WORD)(size > 0xffff ? 0xffff : size) - 1;
433         if ( end > 0xfffe ) end = 0xfffe;
434         start -= end;
435         end += start;
436     }
437     ptr = MapSL( MAKESEGPTR( selector, 0 ) );
438
439     start = LALIGN( max( start, sizeof(INSTANCEDATA) ) );
440     heapInfoArena = LALIGN(start + sizeof(LOCALARENA) );
441     freeArena = LALIGN( heapInfoArena + ARENA_HEADER_SIZE
442                         + sizeof(LOCALHEAPINFO) );
443     lastArena = (end - sizeof(LOCALARENA)) & ~3;
444
445       /* Make sure there's enough space.       */
446
447     if (freeArena + sizeof(LOCALARENA) >= lastArena) goto done;
448
449       /* Initialise the first arena */
450
451     pFirstArena = ARENA_PTR( ptr, start );
452     pFirstArena->prev      = start | LOCAL_ARENA_FIXED;
453     pFirstArena->next      = heapInfoArena;
454     pFirstArena->size      = LALIGN(sizeof(LOCALARENA));
455     pFirstArena->free_prev = start;  /* this one */
456     pFirstArena->free_next = freeArena;
457
458       /* Initialise the arena of the heap info structure */
459
460     pArena = ARENA_PTR( ptr, heapInfoArena );
461     pArena->prev = start | LOCAL_ARENA_FIXED;
462     pArena->next = freeArena;
463
464       /* Initialise the heap info structure */
465
466     pHeapInfo = (LOCALHEAPINFO *) (ptr + heapInfoArena + ARENA_HEADER_SIZE );
467     memset( pHeapInfo, 0, sizeof(LOCALHEAPINFO) );
468     pHeapInfo->items   = 4;
469     pHeapInfo->first   = start;
470     pHeapInfo->last    = lastArena;
471     pHeapInfo->htable  = 0;
472     pHeapInfo->hdelta  = 0x20;
473     pHeapInfo->extra   = 0x200;
474     pHeapInfo->minsize = lastArena - freeArena;
475     pHeapInfo->magic   = LOCAL_HEAP_MAGIC;
476
477       /* Initialise the large free block */
478
479     pArena = ARENA_PTR( ptr, freeArena );
480     pArena->prev      = heapInfoArena | LOCAL_ARENA_FREE;
481     pArena->next      = lastArena;
482     pArena->size      = lastArena - freeArena;
483     pArena->free_prev = start;
484     pArena->free_next = lastArena;
485
486       /* Initialise the last block */
487
488     pLastArena = ARENA_PTR( ptr, lastArena );
489     pLastArena->prev      = freeArena | LOCAL_ARENA_FREE;
490     pLastArena->next      = lastArena;  /* this one */
491     pLastArena->size      = LALIGN(sizeof(LOCALARENA));
492     pLastArena->free_prev = freeArena;
493     pLastArena->free_next = lastArena;  /* this one */
494
495       /* Store the local heap address in the instance data */
496
497     ((INSTANCEDATA *)ptr)->heap = heapInfoArena + ARENA_HEADER_SIZE;
498     LOCAL_PrintHeap( selector );
499     ret = TRUE;
500
501  done:
502     CURRENT_STACK16->ecx = ret;  /* must be returned in cx too */
503     return ret;
504 }
505
506
507 /***********************************************************************
508  *           LOCAL_GrowHeap
509  */
510 static BOOL16 LOCAL_GrowHeap( HANDLE16 ds )
511 {
512     HANDLE16 hseg;
513     LONG oldsize;
514     LONG end;
515     LOCALHEAPINFO *pHeapInfo;
516     WORD freeArena, lastArena;
517     LOCALARENA *pArena, *pLastArena;
518     char *ptr;
519
520     hseg = GlobalHandle16( ds );
521     /* maybe mem allocated by Virtual*() ? */
522     if (!hseg) return FALSE;
523
524     oldsize = GlobalSize16( hseg );
525     /* if nothing can be gained, return */
526     if (oldsize > 0xfff0) return FALSE;
527     hseg = GlobalReAlloc16( hseg, 0x10000, GMEM_FIXED );
528     ptr = MapSL( MAKESEGPTR( ds, 0 ) );
529     pHeapInfo = LOCAL_GetHeap( ds );
530     if (pHeapInfo == NULL) {
531         ERR("Heap not found\n" );
532         return FALSE;
533     }
534     end = GlobalSize16( hseg );
535     lastArena = (end - sizeof(LOCALARENA)) & ~3;
536
537       /* Update the HeapInfo */
538     pHeapInfo->items++;
539     freeArena = pHeapInfo->last;
540     pHeapInfo->last = lastArena;
541     pHeapInfo->minsize += end - oldsize;
542
543       /* grow the old last block */
544     pArena = ARENA_PTR( ptr, freeArena );
545     pArena->size      = lastArena - freeArena;
546     pArena->next      = lastArena;
547     pArena->free_next = lastArena;
548
549       /* Initialise the new last block */
550
551     pLastArena = ARENA_PTR( ptr, lastArena );
552     pLastArena->prev      = freeArena | LOCAL_ARENA_FREE;
553     pLastArena->next      = lastArena;  /* this one */
554     pLastArena->size      = LALIGN(sizeof(LOCALARENA));
555     pLastArena->free_prev = freeArena;
556     pLastArena->free_next = lastArena;  /* this one */
557
558     /* If block before freeArena is also free then merge them */
559     if((ARENA_PTR(ptr, (pArena->prev & ~3))->prev & 3) == LOCAL_ARENA_FREE)
560     {
561         LOCAL_RemoveBlock(ptr, freeArena);
562         pHeapInfo->items--;
563     }
564
565     TRACE("Heap expanded\n" );
566     LOCAL_PrintHeap( ds );
567     return TRUE;
568 }
569
570
571 /***********************************************************************
572  *           LOCAL_FreeArena
573  */
574 static HLOCAL16 LOCAL_FreeArena( WORD ds, WORD arena )
575 {
576     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
577     LOCALHEAPINFO *pInfo;
578     LOCALARENA *pArena, *pPrev, *pNext;
579
580     TRACE("%04x ds=%04x\n", arena, ds );
581     if (!(pInfo = LOCAL_GetHeap( ds ))) return arena;
582
583     pArena = ARENA_PTR( ptr, arena );
584     if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
585     {
586         /* shouldn't happen */
587         ERR("Trying to free block %04x twice!\n",
588                  arena );
589         LOCAL_PrintHeap( ds );
590         return arena;
591     }
592
593       /* Check if we can merge with the previous block */
594
595     pPrev = ARENA_PTR( ptr, pArena->prev & ~3 );
596     pNext = ARENA_PTR( ptr, pArena->next );
597     if ((pPrev->prev & 3) == LOCAL_ARENA_FREE)
598     {
599         arena  = pArena->prev & ~3;
600         pArena = pPrev;
601         LOCAL_RemoveBlock( ptr, pPrev->next );
602         pInfo->items--;
603     }
604     else  /* Make a new free block */
605     {
606         LOCAL_MakeBlockFree( ptr, arena );
607     }
608
609       /* Check if we can merge with the next block */
610
611     if ((pArena->next == pArena->free_next) &&
612         (pArena->next != pInfo->last))
613     {
614         LOCAL_RemoveBlock( ptr, pArena->next );
615         pInfo->items--;
616     }
617     return 0;
618 }
619
620
621 /***********************************************************************
622  *           LOCAL_ShrinkArena
623  *
624  * Shrink an arena by creating a free block at its end if possible.
625  * 'size' includes the arena header, and must be aligned.
626  */
627 static void LOCAL_ShrinkArena( WORD ds, WORD arena, WORD size )
628 {
629     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
630     LOCALARENA *pArena = ARENA_PTR( ptr, arena );
631
632     if (arena + size + LALIGN(sizeof(LOCALARENA)) < pArena->next)
633     {
634         LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
635         if (!pInfo) return;
636         LOCAL_AddBlock( ptr, arena, arena + size );
637         pInfo->items++;
638         LOCAL_FreeArena( ds, arena + size );
639     }
640 }
641
642
643 /***********************************************************************
644  *           LOCAL_GrowArenaDownward
645  *
646  * Grow an arena downward by using the previous arena (must be free).
647  */
648 static void LOCAL_GrowArenaDownward( WORD ds, WORD arena, WORD newsize )
649 {
650     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
651     LOCALHEAPINFO *pInfo;
652     LOCALARENA *pArena = ARENA_PTR( ptr, arena );
653     WORD prevArena = pArena->prev & ~3;
654     LOCALARENA *pPrevArena = ARENA_PTR( ptr, prevArena );
655     WORD offset, size;
656     char *p;
657
658     if (!(pInfo = LOCAL_GetHeap( ds ))) return;
659     offset = pPrevArena->size;
660     size = pArena->next - arena - ARENA_HEADER_SIZE;
661     LOCAL_RemoveFreeBlock( ptr, prevArena );
662     LOCAL_RemoveBlock( ptr, arena );
663     pInfo->items--;
664     p = (char *)pPrevArena + ARENA_HEADER_SIZE;
665     while (offset < size)
666     {
667         memcpy( p, p + offset, offset );
668         p += offset;
669         size -= offset;
670     }
671     if (size) memcpy( p, p + offset, size );
672     LOCAL_ShrinkArena( ds, prevArena, newsize );
673 }
674
675
676
677 /***********************************************************************
678  *           LOCAL_GrowArenaUpward
679  *
680  * Grow an arena upward by using the next arena (must be free and big
681  * enough). Newsize includes the arena header and must be aligned.
682  */
683 static void LOCAL_GrowArenaUpward( WORD ds, WORD arena, WORD newsize )
684 {
685     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
686     LOCALHEAPINFO *pInfo;
687     LOCALARENA *pArena = ARENA_PTR( ptr, arena );
688     WORD nextArena = pArena->next;
689
690     if (!(pInfo = LOCAL_GetHeap( ds ))) return;
691     LOCAL_RemoveBlock( ptr, nextArena );
692     pInfo->items--;
693     LOCAL_ShrinkArena( ds, arena, newsize );
694 }
695
696
697 /***********************************************************************
698  *           LOCAL_GetFreeSpace
699  */
700 static WORD LOCAL_GetFreeSpace(WORD ds, WORD countdiscard)
701 {
702     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
703     LOCALHEAPINFO *pInfo;
704     LOCALARENA *pArena;
705     WORD arena;
706     WORD freespace = 0;
707
708     if (!(pInfo = LOCAL_GetHeap( ds )))
709     {
710         ERR("Local heap not found\n" );
711         LOCAL_PrintHeap(ds);
712         return 0;
713     }
714     arena = pInfo->first;
715     pArena = ARENA_PTR( ptr, arena );
716     while (arena != pArena->free_next)
717     {
718         arena = pArena->free_next;
719         pArena = ARENA_PTR( ptr, arena );
720         if (pArena->size >= freespace) freespace = pArena->size;
721     }
722     /* FIXME doesn't yet calculate space that would become free if everything
723        were discarded when countdiscard == 1 */
724     if (freespace < ARENA_HEADER_SIZE) freespace = 0;
725     else freespace -= ARENA_HEADER_SIZE;
726     return freespace;
727 }
728
729
730 /***********************************************************************
731  *           LOCAL_Compact
732  */
733 WORD LOCAL_Compact( HANDLE16 ds, UINT16 minfree, UINT16 flags )
734 {
735     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
736     LOCALHEAPINFO *pInfo;
737     LOCALARENA *pArena, *pMoveArena, *pFinalArena;
738     WORD arena, movearena, finalarena, table;
739     WORD count, movesize, size;
740     WORD freespace;
741     LOCALHANDLEENTRY *pEntry;
742
743     if (!(pInfo = LOCAL_GetHeap( ds )))
744     {
745         ERR("Local heap not found\n" );
746         LOCAL_PrintHeap(ds);
747         return 0;
748     }
749     TRACE("ds = %04x, minfree = %04x, flags = %04x\n",
750                  ds, minfree, flags);
751     freespace = LOCAL_GetFreeSpace(ds, minfree ? 0 : 1);
752     if(freespace >= minfree || (flags & LMEM_NOCOMPACT))
753     {
754         TRACE("Returning %04x.\n", freespace);
755         return freespace;
756     }
757     TRACE("Compacting heap %04x.\n", ds);
758     table = pInfo->htable;
759     while(table)
760     {
761         pEntry = (LOCALHANDLEENTRY *)(ptr + table + sizeof(WORD));
762         for(count = *(WORD *)(ptr + table); count > 0; count--, pEntry++)
763         {
764             if((pEntry->lock == 0) && (pEntry->flags != (LMEM_DISCARDED >> 8)))
765             {
766                 /* OK we can move this one if we want */
767                 TRACE("handle %04x (block %04x) can be moved.\n",
768                              (WORD)((char *)pEntry - ptr), pEntry->addr);
769                 movearena = ARENA_HEADER(pEntry->addr - MOVEABLE_PREFIX);
770                 pMoveArena = ARENA_PTR(ptr, movearena);
771                 movesize = pMoveArena->next - movearena;
772                 arena = pInfo->first;
773                 pArena = ARENA_PTR(ptr, arena);
774                 size = 0xffff;
775                 finalarena = 0;
776                 /* Try to find the smallest arena that will do, */
777                 /* which is below us in memory */
778                 for(;;)
779                 {
780                     arena = pArena->free_next;
781                     pArena = ARENA_PTR(ptr, arena);
782                     if(arena >= movearena)
783                         break;
784                     if(arena == pArena->free_next)
785                         break;
786                     if((pArena->size >= movesize) && (pArena->size < size))
787                     {
788                         size = pArena->size;
789                         finalarena = arena;
790                     }
791                 }
792                 if (finalarena) /* Actually got somewhere to move */
793                 {
794                     TRACE("Moving it to %04x.\n", finalarena);
795                     pFinalArena = ARENA_PTR(ptr, finalarena);
796                     size = pFinalArena->size;
797                     LOCAL_RemoveFreeBlock(ptr, finalarena);
798                     LOCAL_ShrinkArena( ds, finalarena, movesize );
799                     /* Copy the arena to it's new location */
800                     memcpy((char *)pFinalArena + ARENA_HEADER_SIZE,
801                            (char *)pMoveArena + ARENA_HEADER_SIZE,
802                            movesize - ARENA_HEADER_SIZE );
803                     /* Free the old location */
804                     LOCAL_FreeArena(ds, movearena);
805                     call_notify_func(pInfo->notify, LN_MOVE,
806                                      (WORD)((char *)pEntry - ptr), pEntry->addr);
807                     /* Update handle table entry */
808                     pEntry->addr = finalarena + ARENA_HEADER_SIZE + MOVEABLE_PREFIX;
809                 }
810                 else if((ARENA_PTR(ptr, pMoveArena->prev & ~3)->prev & 3)
811                                == LOCAL_ARENA_FREE)
812                 {
813                     /* Previous arena is free (but < movesize)  */
814                     /* so we can 'slide' movearena down into it */
815                     finalarena = pMoveArena->prev & ~3;
816                     LOCAL_GrowArenaDownward( ds, movearena, movesize );
817                     /* Update handle table entry */
818                     pEntry->addr = finalarena + ARENA_HEADER_SIZE + MOVEABLE_PREFIX;
819                 }
820             }
821         }
822         table = *(WORD *)pEntry;
823     }
824     freespace = LOCAL_GetFreeSpace(ds, minfree ? 0 : 1);
825     if(freespace >= minfree || (flags & LMEM_NODISCARD))
826     {
827         TRACE("Returning %04x.\n", freespace);
828         return freespace;
829     }
830
831     table = pInfo->htable;
832     while(table)
833     {
834         pEntry = (LOCALHANDLEENTRY *)(ptr + table + sizeof(WORD));
835         for(count = *(WORD *)(ptr + table); count > 0; count--, pEntry++)
836         {
837             if(pEntry->addr && pEntry->lock == 0 &&
838              (pEntry->flags & (LMEM_DISCARDABLE >> 8)))
839             {
840                 TRACE("Discarding handle %04x (block %04x).\n",
841                               (char *)pEntry - ptr, pEntry->addr);
842                 LOCAL_FreeArena(ds, ARENA_HEADER(pEntry->addr - MOVEABLE_PREFIX));
843                 call_notify_func(pInfo->notify, LN_DISCARD, (char *)pEntry - ptr, pEntry->flags);
844                 pEntry->addr = 0;
845                 pEntry->flags = (LMEM_DISCARDED >> 8);
846             }
847         }
848         table = *(WORD *)pEntry;
849     }
850     return LOCAL_Compact(ds, 0xffff, LMEM_NODISCARD);
851 }
852
853
854 /***********************************************************************
855  *           LOCAL_FindFreeBlock
856  */
857 static HLOCAL16 LOCAL_FindFreeBlock( HANDLE16 ds, WORD size )
858 {
859     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
860     LOCALHEAPINFO *pInfo;
861     LOCALARENA *pArena;
862     WORD arena;
863
864     if (!(pInfo = LOCAL_GetHeap( ds )))
865     {
866         ERR("Local heap not found\n" );
867         LOCAL_PrintHeap(ds);
868         return 0;
869     }
870
871     arena = pInfo->first;
872     pArena = ARENA_PTR( ptr, arena );
873     for (;;) {
874         arena = pArena->free_next;
875         pArena = ARENA_PTR( ptr, arena );
876         if (arena == pArena->free_next) break;
877         if (pArena->size >= size) return arena;
878     }
879     TRACE("not enough space\n" );
880     LOCAL_PrintHeap(ds);
881     return 0;
882 }
883
884
885 /***********************************************************************
886  *           get_heap_name
887  */
888 static const char *get_heap_name( WORD ds )
889 {
890     HINSTANCE16 inst = LoadLibrary16( "GDI" );
891     if (ds == GlobalHandleToSel16( inst ))
892     {
893         FreeLibrary16( inst );
894         return "GDI";
895     }
896     FreeLibrary16( inst );
897     inst = LoadLibrary16( "USER" );
898     if (ds == GlobalHandleToSel16( inst ))
899     {
900         FreeLibrary16( inst );
901         return "USER";
902     }
903     FreeLibrary16( inst );
904     return "local";
905 }
906
907 /***********************************************************************
908  *           LOCAL_GetBlock
909  * The segment may get moved around in this function, so all callers
910  * should reset their pointer variables.
911  */
912 static HLOCAL16 LOCAL_GetBlock( HANDLE16 ds, WORD size, WORD flags )
913 {
914     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
915     LOCALHEAPINFO *pInfo;
916     LOCALARENA *pArena;
917     WORD arena;
918
919     if (!(pInfo = LOCAL_GetHeap( ds )))
920     {
921         ERR("Local heap not found\n");
922         LOCAL_PrintHeap(ds);
923         return 0;
924     }
925
926     size += ARENA_HEADER_SIZE;
927     size = LALIGN( max( size, sizeof(LOCALARENA) ) );
928
929 #if 0
930 notify_done:
931 #endif
932       /* Find a suitable free block */
933     arena = LOCAL_FindFreeBlock( ds, size );
934     if (arena == 0) {
935         /* no space: try to make some */
936         LOCAL_Compact( ds, size, flags );
937         arena = LOCAL_FindFreeBlock( ds, size );
938     }
939     if (arena == 0) {
940         /* still no space: try to grow the segment */
941         if (!(LOCAL_GrowHeap( ds )))
942         {
943 #if 0
944             /* FIXME: doesn't work correctly yet */
945             if (call_notify_func(pInfo->notify, LN_OUTOFMEM, ds - 20, size)) /* FIXME: "size" correct ? (should indicate bytes needed) */
946                 goto notify_done;
947 #endif
948             ERR( "not enough space in %s heap %04x for %d bytes\n",
949                  get_heap_name(ds), ds, size );
950             return 0;
951         }
952         ptr = MapSL( MAKESEGPTR( ds, 0 ) );
953         pInfo = LOCAL_GetHeap( ds );
954         arena = LOCAL_FindFreeBlock( ds, size );
955     }
956     if (arena == 0) {
957         ERR( "not enough space in %s heap %04x for %d bytes\n",
958              get_heap_name(ds), ds, size );
959 #if 0
960         /* FIXME: "size" correct ? (should indicate bytes needed) */
961         if (call_notify_func(pInfo->notify, LN_OUTOFMEM, ds, size)) goto notify_done;
962 #endif
963         return 0;
964     }
965
966       /* Make a block out of the free arena */
967     pArena = ARENA_PTR( ptr, arena );
968     TRACE("size = %04x, arena %04x size %04x\n",
969                   size, arena, pArena->size );
970     LOCAL_RemoveFreeBlock( ptr, arena );
971     LOCAL_ShrinkArena( ds, arena, size );
972
973     if (flags & LMEM_ZEROINIT)
974         memset((char *)pArena + ARENA_HEADER_SIZE, 0, size-ARENA_HEADER_SIZE);
975     return arena + ARENA_HEADER_SIZE;
976 }
977
978
979 /***********************************************************************
980  *           LOCAL_NewHTable
981  */
982 static BOOL16 LOCAL_NewHTable( HANDLE16 ds )
983 {
984     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
985     LOCALHEAPINFO *pInfo;
986     LOCALHANDLEENTRY *pEntry;
987     HLOCAL16 handle;
988     int i;
989
990     TRACE("\n" );
991     if (!(pInfo = LOCAL_GetHeap( ds )))
992     {
993         ERR("Local heap not found\n");
994         LOCAL_PrintHeap(ds);
995         return FALSE;
996     }
997
998     if (!(handle = LOCAL_GetBlock( ds, pInfo->hdelta * sizeof(LOCALHANDLEENTRY)
999                                    + 2 * sizeof(WORD), LMEM_FIXED )))
1000         return FALSE;
1001     if (!(ptr = MapSL( MAKESEGPTR( ds, 0 ) )))
1002         ERR("ptr == NULL after GetBlock.\n");
1003     if (!(pInfo = LOCAL_GetHeap( ds )))
1004         ERR("pInfo == NULL after GetBlock.\n");
1005
1006     /* Fill the entry table */
1007
1008     *(WORD *)(ptr + handle) = pInfo->hdelta;
1009     pEntry = (LOCALHANDLEENTRY *)(ptr + handle + sizeof(WORD));
1010     for (i = pInfo->hdelta; i > 0; i--, pEntry++) {
1011         pEntry->lock = pEntry->flags = 0xff;
1012         pEntry->addr = 0;
1013     }
1014     *(WORD *)pEntry = pInfo->htable;
1015     pInfo->htable = handle;
1016     return TRUE;
1017 }
1018
1019
1020 /***********************************************************************
1021  *           LOCAL_GetNewHandleEntry
1022  */
1023 static HLOCAL16 LOCAL_GetNewHandleEntry( HANDLE16 ds )
1024 {
1025     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1026     LOCALHEAPINFO *pInfo;
1027     LOCALHANDLEENTRY *pEntry = NULL;
1028     WORD table;
1029
1030     if (!(pInfo = LOCAL_GetHeap( ds )))
1031     {
1032         ERR("Local heap not found\n");
1033         LOCAL_PrintHeap(ds);
1034         return 0;
1035     }
1036
1037     /* Find a free slot in existing tables */
1038
1039     table = pInfo->htable;
1040     while (table)
1041     {
1042         WORD count = *(WORD *)(ptr + table);
1043         pEntry = (LOCALHANDLEENTRY *)(ptr + table + sizeof(WORD));
1044         for (; count > 0; count--, pEntry++)
1045             if (pEntry->lock == 0xff) break;
1046         if (count) break;
1047         table = *(WORD *)pEntry;
1048     }
1049
1050     if (!table)  /* We need to create a new table */
1051     {
1052         if (!LOCAL_NewHTable( ds )) return 0;
1053         ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1054         pInfo = LOCAL_GetHeap( ds );
1055         pEntry = (LOCALHANDLEENTRY *)(ptr + pInfo->htable + sizeof(WORD));
1056     }
1057
1058     /* Now allocate this entry */
1059
1060     pEntry->lock = 0;
1061     pEntry->flags = 0;
1062     TRACE("(%04x): %04x\n",
1063                    ds, ((char *)pEntry - ptr) );
1064     return (HLOCAL16)((char *)pEntry - ptr);
1065 }
1066
1067
1068 /***********************************************************************
1069  *           LOCAL_FreeHandleEntry
1070  *
1071  * Free a handle table entry.
1072  */
1073 static void LOCAL_FreeHandleEntry( HANDLE16 ds, HLOCAL16 handle )
1074 {
1075     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1076     LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1077     LOCALHEAPINFO *pInfo;
1078     WORD *pTable;
1079     WORD table, count, i;
1080
1081     if (!(pInfo = LOCAL_GetHeap( ds ))) return;
1082
1083     /* Find the table where this handle comes from */
1084
1085     pTable = &pInfo->htable;
1086     while (*pTable)
1087     {
1088         WORD size = (*(WORD *)(ptr + *pTable)) * sizeof(LOCALHANDLEENTRY);
1089         if ((handle >= *pTable + sizeof(WORD)) &&
1090             (handle < *pTable + sizeof(WORD) + size)) break;  /* Found it */
1091         pTable = (WORD *)(ptr + *pTable + sizeof(WORD) + size);
1092     }
1093     if (!*pTable)
1094     {
1095         ERR("Invalid entry %04x\n", handle);
1096         LOCAL_PrintHeap( ds );
1097         return;
1098     }
1099
1100     /* Make the entry free */
1101
1102     pEntry->addr = 0;  /* just in case */
1103     pEntry->lock = 0xff;
1104     pEntry->flags = 0xff;
1105     /* Now check if all entries in this table are free */
1106
1107     table = *pTable;
1108     pEntry = (LOCALHANDLEENTRY *)(ptr + table + sizeof(WORD));
1109     count = *(WORD *)(ptr + table);
1110     for (i = count; i > 0; i--, pEntry++) if (pEntry->lock != 0xff) return;
1111
1112     /* Remove the table from the linked list and free it */
1113
1114     TRACE("(%04x): freeing table %04x\n",
1115                    ds, table);
1116     *pTable = *(WORD *)pEntry;
1117     LOCAL_FreeArena( ds, ARENA_HEADER( table ) );
1118 }
1119
1120
1121 /***********************************************************************
1122  *           LOCAL_Free
1123  *
1124  * Implementation of LocalFree().
1125  */
1126 HLOCAL16 LOCAL_Free( HANDLE16 ds, HLOCAL16 handle )
1127 {
1128     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1129
1130     TRACE("%04x ds=%04x\n", handle, ds );
1131
1132     if (!handle) { WARN("Handle is 0.\n" ); return 0; }
1133     if (HANDLE_FIXED( handle ))
1134     {
1135         if (!LOCAL_FreeArena( ds, ARENA_HEADER( handle ) )) return 0;  /* OK */
1136         else return handle;  /* couldn't free it */
1137     }
1138     else
1139     {
1140         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1141         if (pEntry->flags != (LMEM_DISCARDED >> 8))
1142         {
1143             TRACE("real block at %04x\n",
1144                            pEntry->addr );
1145             if (LOCAL_FreeArena( ds, ARENA_HEADER(pEntry->addr - MOVEABLE_PREFIX) ))
1146                 return handle; /* couldn't free it */
1147         }
1148         LOCAL_FreeHandleEntry( ds, handle );
1149         return 0;  /* OK */
1150     }
1151 }
1152
1153
1154 /***********************************************************************
1155  *           LOCAL_Alloc
1156  *
1157  * Implementation of LocalAlloc().
1158  *
1159  */
1160 HLOCAL16 LOCAL_Alloc( HANDLE16 ds, WORD flags, WORD size )
1161 {
1162     char *ptr;
1163     HLOCAL16 handle;
1164
1165     TRACE("%04x %d ds=%04x\n", flags, size, ds );
1166
1167     if(size > 0 && size <= 4) size = 5;
1168     if (flags & LMEM_MOVEABLE)
1169     {
1170         LOCALHANDLEENTRY *plhe;
1171         HLOCAL16 hmem;
1172
1173         if(size)
1174         {
1175             if (!(hmem = LOCAL_GetBlock( ds, size + MOVEABLE_PREFIX, flags )))
1176                 return 0;
1177         }
1178         else /* We just need to allocate a discarded handle */
1179             hmem = 0;
1180         if (!(handle = LOCAL_GetNewHandleEntry( ds )))
1181         {
1182             WARN("Couldn't get handle.\n");
1183             if(hmem)
1184                 LOCAL_FreeArena( ds, ARENA_HEADER(hmem) );
1185             return 0;
1186         }
1187         ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1188         plhe = (LOCALHANDLEENTRY *)(ptr + handle);
1189         plhe->lock = 0;
1190         if(hmem)
1191         {
1192             plhe->addr = hmem + MOVEABLE_PREFIX;
1193             plhe->flags = (BYTE)((flags & 0x0f00) >> 8);
1194             *(HLOCAL16 *)(ptr + hmem) = handle;
1195         }
1196         else
1197         {
1198             plhe->addr = 0;
1199             plhe->flags = LMEM_DISCARDED >> 8;
1200         }
1201     }
1202     else /* FIXED */
1203     {
1204         if(!size)
1205             return 0;
1206         handle = LOCAL_GetBlock( ds, size, flags );
1207     }
1208     return handle;
1209 }
1210
1211
1212 /***********************************************************************
1213  *           LOCAL_ReAlloc
1214  *
1215  * Implementation of LocalReAlloc().
1216  */
1217 HLOCAL16 LOCAL_ReAlloc( HANDLE16 ds, HLOCAL16 handle, WORD size, WORD flags )
1218 {
1219     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1220     LOCALHEAPINFO *pInfo;
1221     LOCALARENA *pArena, *pNext;
1222     LOCALHANDLEENTRY *pEntry = NULL;
1223     WORD arena, oldsize;
1224     HLOCAL16 hmem, blockhandle;
1225     LONG nextarena;
1226
1227     if (!handle) return 0;
1228     if(HANDLE_MOVEABLE(handle) &&
1229      ((LOCALHANDLEENTRY *)(ptr + handle))->lock == 0xff) /* An unused handle */
1230         return 0;
1231
1232     TRACE("%04x %d %04x ds=%04x\n",
1233                    handle, size, flags, ds );
1234     if (!(pInfo = LOCAL_GetHeap( ds ))) return 0;
1235
1236     if (HANDLE_FIXED( handle ))
1237         blockhandle = handle;
1238     else
1239     {
1240         pEntry = (LOCALHANDLEENTRY *) (ptr + handle);
1241         if(pEntry->flags == (LMEM_DISCARDED >> 8))
1242         {
1243             HLOCAL16 hl;
1244             if(pEntry->addr)
1245                 WARN("Dicarded block has non-zero addr.\n");
1246             TRACE("ReAllocating discarded block\n");
1247             if(size <= 4) size = 5;
1248             if (!(hl = LOCAL_GetBlock( ds, size + MOVEABLE_PREFIX, flags)))
1249                 return 0;
1250             ptr = MapSL( MAKESEGPTR( ds, 0 ) );  /* Reload ptr */
1251             pEntry = (LOCALHANDLEENTRY *) (ptr + handle);
1252             pEntry->addr = hl + MOVEABLE_PREFIX;
1253             pEntry->flags = 0;
1254             pEntry->lock = 0;
1255             *(HLOCAL16 *)(ptr + hl) = handle;
1256             return handle;
1257         }
1258         if (((blockhandle = pEntry->addr - MOVEABLE_PREFIX) & 3) != 0)
1259         {
1260             ERR("(%04x,%04x): invalid handle\n",
1261                      ds, handle );
1262             return 0;
1263         }
1264         if (*(HLOCAL16 *)(ptr + blockhandle) != handle) {
1265             ERR("Back ptr to handle is invalid\n");
1266             return 0;
1267         }
1268     }
1269
1270     if (flags & LMEM_MODIFY)
1271     {
1272         if (HANDLE_MOVEABLE(handle))
1273         {
1274             pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1275             pEntry->flags = (flags & 0x0f00) >> 8;
1276             TRACE("Changing flags to %x.\n", pEntry->flags);
1277         }
1278         return handle;
1279     }
1280
1281     if (!size)
1282     {
1283         if (flags & LMEM_MOVEABLE)
1284         {
1285             if (HANDLE_FIXED(handle))
1286             {
1287                 TRACE("Freeing fixed block.\n");
1288                 return LOCAL_Free( ds, handle );
1289             }
1290             else /* Moveable block */
1291             {
1292                 pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1293                 if (pEntry->lock == 0)
1294                 {
1295                     /* discards moveable blocks */
1296                     TRACE("Discarding block\n");
1297                     LOCAL_FreeArena(ds, ARENA_HEADER(pEntry->addr - MOVEABLE_PREFIX));
1298                     pEntry->addr = 0;
1299                     pEntry->flags = (LMEM_DISCARDED >> 8);
1300                     return handle;
1301                 }
1302             }
1303             return 0;
1304         }
1305         else if(flags == 0)
1306         {
1307             pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1308             if (pEntry->lock == 0)
1309             {
1310                 /* Frees block */
1311                 return LOCAL_Free( ds, handle );
1312             }
1313         }
1314         return 0;
1315     }
1316
1317     arena = ARENA_HEADER( blockhandle );
1318     TRACE("arena is %04x\n", arena );
1319     pArena = ARENA_PTR( ptr, arena );
1320
1321     if(size <= 4) size = 5;
1322     if(HANDLE_MOVEABLE(handle)) size += MOVEABLE_PREFIX;
1323     oldsize = pArena->next - arena - ARENA_HEADER_SIZE;
1324     nextarena = LALIGN(blockhandle + size);
1325
1326       /* Check for size reduction */
1327
1328     if (nextarena <= pArena->next)
1329     {
1330         TRACE("size reduction, making new free block\n");
1331         LOCAL_ShrinkArena(ds, arena, nextarena - arena);
1332         TRACE("returning %04x\n", handle );
1333         return handle;
1334     }
1335
1336       /* Check if the next block is free and large enough */
1337
1338     pNext = ARENA_PTR( ptr, pArena->next );
1339     if (((pNext->prev & 3) == LOCAL_ARENA_FREE) &&
1340         (nextarena <= pNext->next))
1341     {
1342         TRACE("size increase, making new free block\n");
1343         LOCAL_GrowArenaUpward(ds, arena, nextarena - arena);
1344         TRACE("returning %04x\n", handle );
1345         return handle;
1346     }
1347
1348     /* Now we have to allocate a new block, but not if (fixed block or locked
1349        block) and no LMEM_MOVEABLE */
1350
1351     if (!(flags & LMEM_MOVEABLE))
1352     {
1353         if (HANDLE_FIXED(handle))
1354         {
1355             ERR("Needed to move fixed block, but LMEM_MOVEABLE not specified.\n");
1356             return 0;
1357         }
1358         else
1359         {
1360             if(((LOCALHANDLEENTRY *)(ptr + handle))->lock != 0)
1361             {
1362                 ERR("Needed to move locked block, but LMEM_MOVEABLE not specified.\n");
1363                 return 0;
1364             }
1365         }
1366     }
1367
1368     hmem = LOCAL_GetBlock( ds, size, flags );
1369     ptr = MapSL( MAKESEGPTR( ds, 0 ));  /* Reload ptr                             */
1370     if(HANDLE_MOVEABLE(handle))         /* LOCAL_GetBlock might have triggered    */
1371     {                                   /* a compaction, which might in turn have */
1372       blockhandle = pEntry->addr - MOVEABLE_PREFIX; /* moved the very block we are resizing */
1373       arena = ARENA_HEADER( blockhandle );   /* thus, we reload arena, too        */
1374     }
1375     if (!hmem)
1376     {
1377         /* Remove the block from the heap and try again */
1378         LPSTR buffer = HeapAlloc( GetProcessHeap(), 0, oldsize );
1379         if (!buffer) return 0;
1380         memcpy( buffer, ptr + arena + ARENA_HEADER_SIZE, oldsize );
1381         LOCAL_FreeArena( ds, arena );
1382         if (!(hmem = LOCAL_GetBlock( ds, size, flags )))
1383         {
1384             if (!(hmem = LOCAL_GetBlock( ds, oldsize, flags )))
1385             {
1386                 ERR("Can't restore saved block\n" );
1387                 HeapFree( GetProcessHeap(), 0, buffer );
1388                 return 0;
1389             }
1390             size = oldsize;
1391         }
1392         ptr = MapSL( MAKESEGPTR( ds, 0 ) );  /* Reload ptr */
1393         memcpy( ptr + hmem, buffer, oldsize );
1394         HeapFree( GetProcessHeap(), 0, buffer );
1395     }
1396     else
1397     {
1398         memcpy( ptr + hmem, ptr + (arena + ARENA_HEADER_SIZE), oldsize );
1399         LOCAL_FreeArena( ds, arena );
1400     }
1401     if (HANDLE_MOVEABLE( handle ))
1402     {
1403         TRACE("fixing handle\n");
1404         pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1405         pEntry->addr = hmem + MOVEABLE_PREFIX;
1406         /* Back ptr should still be correct */
1407         if(*(HLOCAL16 *)(ptr + hmem) != handle)
1408             ERR("back ptr is invalid.\n");
1409         hmem = handle;
1410     }
1411     if (size == oldsize) hmem = 0;  /* Realloc failed */
1412     TRACE("returning %04x\n", hmem );
1413     return hmem;
1414 }
1415
1416
1417 /***********************************************************************
1418  *           LOCAL_InternalLock
1419  */
1420 static HLOCAL16 LOCAL_InternalLock( LPSTR heap, HLOCAL16 handle )
1421 {
1422     HLOCAL16 old_handle = handle;
1423
1424     if (HANDLE_MOVEABLE(handle))
1425     {
1426         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(heap + handle);
1427         if (pEntry->flags == (LMEM_DISCARDED >> 8)) return 0;
1428         if (pEntry->lock < 0xfe) pEntry->lock++;
1429         handle = pEntry->addr;
1430     }
1431     TRACE("%04x returning %04x\n",
1432                    old_handle, handle );
1433     return handle;
1434 }
1435
1436
1437 /***********************************************************************
1438  *           LOCAL_Lock
1439  */
1440 void *LOCAL_Lock( HANDLE16 ds, HLOCAL16 handle )
1441 {
1442     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1443     return handle ? ptr + LOCAL_InternalLock( ptr, handle ) : NULL;
1444 }
1445
1446
1447 /***********************************************************************
1448  *           LOCAL_Unlock
1449  */
1450 BOOL16 LOCAL_Unlock( HANDLE16 ds, HLOCAL16 handle )
1451 {
1452     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1453
1454     TRACE("%04x\n", handle );
1455     if (HANDLE_MOVEABLE(handle))
1456     {
1457         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1458         if (!pEntry->lock || (pEntry->lock == 0xff)) return FALSE;
1459         /* For moveable block, return the new lock count */
1460         /* (see _Windows_Internals_ p. 197) */
1461         return --pEntry->lock;
1462     }
1463     else return FALSE;
1464 }
1465
1466
1467 /***********************************************************************
1468  *           LOCAL_Size
1469  *
1470  * Implementation of LocalSize().
1471  */
1472 WORD LOCAL_Size( HANDLE16 ds, HLOCAL16 handle )
1473 {
1474     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1475     LOCALARENA *pArena;
1476
1477     TRACE("%04x ds=%04x\n", handle, ds );
1478
1479     if (!handle) return 0;
1480     if (HANDLE_MOVEABLE( handle ))
1481     {
1482         handle = *(WORD *)(ptr + handle);
1483         if (!handle) return 0;
1484         pArena = ARENA_PTR( ptr, ARENA_HEADER(handle - MOVEABLE_PREFIX) );
1485     }
1486     else
1487         pArena = ARENA_PTR( ptr, ARENA_HEADER(handle) );
1488
1489     return pArena->next - handle;
1490 }
1491
1492
1493 /***********************************************************************
1494  *           LOCAL_Flags
1495  *
1496  * Implementation of LocalFlags().
1497  */
1498 WORD LOCAL_Flags( HANDLE16 ds, HLOCAL16 handle )
1499 {
1500     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1501
1502     if (HANDLE_MOVEABLE(handle))
1503     {
1504         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY *)(ptr + handle);
1505         TRACE("(%04x,%04x): returning %04x\n",
1506                        ds, handle, pEntry->lock | (pEntry->flags << 8) );
1507         return pEntry->lock | (pEntry->flags << 8);
1508     }
1509     else
1510     {
1511         TRACE("(%04x,%04x): returning 0\n",
1512                        ds, handle );
1513         return 0;
1514     }
1515 }
1516
1517
1518 /***********************************************************************
1519  *           LOCAL_HeapSize
1520  *
1521  * Implementation of LocalHeapSize().
1522  */
1523 WORD LOCAL_HeapSize( HANDLE16 ds )
1524 {
1525     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
1526     if (!pInfo) return 0;
1527     return pInfo->last - pInfo->first;
1528 }
1529
1530
1531 /***********************************************************************
1532  *           LOCAL_CountFree
1533  *
1534  * Implementation of LocalCountFree().
1535  */
1536 WORD LOCAL_CountFree( HANDLE16 ds )
1537 {
1538     WORD arena, total;
1539     LOCALARENA *pArena;
1540     LOCALHEAPINFO *pInfo;
1541     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1542
1543     if (!(pInfo = LOCAL_GetHeap( ds )))
1544     {
1545         ERR("(%04x): Local heap not found\n", ds );
1546         LOCAL_PrintHeap( ds );
1547         return 0;
1548     }
1549
1550     total = 0;
1551     arena = pInfo->first;
1552     pArena = ARENA_PTR( ptr, arena );
1553     for (;;)
1554     {
1555         arena = pArena->free_next;
1556         pArena = ARENA_PTR( ptr, arena );
1557         if (arena == pArena->free_next) break;
1558         total += pArena->size;
1559     }
1560     TRACE("(%04x): returning %d\n", ds, total);
1561     return total;
1562 }
1563
1564
1565 /***********************************************************************
1566  *           LOCAL_Handle
1567  *
1568  * Implementation of LocalHandle().
1569  */
1570 HLOCAL16 LOCAL_Handle( HANDLE16 ds, WORD addr )
1571 {
1572     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1573     LOCALHEAPINFO *pInfo;
1574     WORD table;
1575
1576     if (!(pInfo = LOCAL_GetHeap( ds )))
1577     {
1578         ERR("(%04x): Local heap not found\n", ds );
1579         LOCAL_PrintHeap( ds );
1580         return 0;
1581     }
1582
1583     /* Find the address in the entry tables */
1584
1585     table = pInfo->htable;
1586     while (table)
1587     {
1588         WORD count = *(WORD *)(ptr + table);
1589         LOCALHANDLEENTRY *pEntry = (LOCALHANDLEENTRY*)(ptr+table+sizeof(WORD));
1590         for (; count > 0; count--, pEntry++)
1591             if (pEntry->addr == addr) return (HLOCAL16)((char *)pEntry - ptr);
1592         table = *(WORD *)pEntry;
1593     }
1594
1595     return (HLOCAL16)addr;  /* Fixed block handle is addr */
1596 }
1597
1598
1599 /***********************************************************************
1600  *           LocalAlloc   (KERNEL.5)
1601  */
1602 HLOCAL16 WINAPI LocalAlloc16( UINT16 flags, WORD size )
1603 {
1604     HLOCAL16 ret = LOCAL_Alloc( CURRENT_DS, flags, size );
1605     CURRENT_STACK16->ecx = ret;  /* must be returned in cx too */
1606     return ret;
1607 }
1608
1609
1610 /***********************************************************************
1611  *           LocalReAlloc   (KERNEL.6)
1612  */
1613 HLOCAL16 WINAPI LocalReAlloc16( HLOCAL16 handle, WORD size, UINT16 flags )
1614 {
1615     return LOCAL_ReAlloc( CURRENT_DS, handle, size, flags );
1616 }
1617
1618
1619 /***********************************************************************
1620  *           LocalFree   (KERNEL.7)
1621  */
1622 HLOCAL16 WINAPI LocalFree16( HLOCAL16 handle )
1623 {
1624     return LOCAL_Free( CURRENT_DS, handle );
1625 }
1626
1627
1628 /***********************************************************************
1629  *           LocalLock   (KERNEL.8)
1630  *
1631  * Note: only the offset part of the pointer is returned by the relay code.
1632  */
1633 SEGPTR WINAPI LocalLock16( HLOCAL16 handle )
1634 {
1635     WORD ds = CURRENT_DS;
1636     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1637     return MAKESEGPTR( ds, LOCAL_InternalLock( ptr, handle ) );
1638 }
1639
1640
1641 /***********************************************************************
1642  *           LocalUnlock   (KERNEL.9)
1643  */
1644 BOOL16 WINAPI LocalUnlock16( HLOCAL16 handle )
1645 {
1646     return LOCAL_Unlock( CURRENT_DS, handle );
1647 }
1648
1649
1650 /***********************************************************************
1651  *           LocalSize   (KERNEL.10)
1652  */
1653 UINT16 WINAPI LocalSize16( HLOCAL16 handle )
1654 {
1655     return LOCAL_Size( CURRENT_DS, handle );
1656 }
1657
1658
1659 /***********************************************************************
1660  *           LocalHandle   (KERNEL.11)
1661  */
1662 HLOCAL16 WINAPI LocalHandle16( WORD addr )
1663 {
1664     return LOCAL_Handle( CURRENT_DS, addr );
1665 }
1666
1667
1668 /***********************************************************************
1669  *           LocalFlags   (KERNEL.12)
1670  */
1671 UINT16 WINAPI LocalFlags16( HLOCAL16 handle )
1672 {
1673     return LOCAL_Flags( CURRENT_DS, handle );
1674 }
1675
1676
1677 /***********************************************************************
1678  *           LocalCompact   (KERNEL.13)
1679  */
1680 UINT16 WINAPI LocalCompact16( UINT16 minfree )
1681 {
1682     TRACE("%04x\n", minfree );
1683     return LOCAL_Compact( CURRENT_DS, minfree, 0 );
1684 }
1685
1686
1687 /***********************************************************************
1688  *           LocalNotify   (KERNEL.14)
1689  *
1690  * Installs a callback function that is called for local memory events
1691  * Callback function prototype is
1692  * BOOL16 NotifyFunc(WORD wMsg, HLOCAL16 hMem, WORD wArg)
1693  * wMsg:
1694  * - LN_OUTOFMEM
1695  *   NotifyFunc seems to be responsible for allocating some memory,
1696  *   returns TRUE for success.
1697  *   wArg = number of bytes needed additionally
1698  * - LN_MOVE
1699  *   hMem = handle; wArg = old mem location
1700  * - LN_DISCARD
1701  *   NotifyFunc seems to be strongly encouraged to return TRUE,
1702  *   otherwise LogError() gets called.
1703  *   hMem = handle; wArg = flags
1704  */
1705 FARPROC16 WINAPI LocalNotify16( FARPROC16 func )
1706 {
1707     LOCALHEAPINFO *pInfo;
1708     FARPROC16 oldNotify;
1709     HANDLE16 ds = CURRENT_DS;
1710
1711     if (!(pInfo = LOCAL_GetHeap( ds )))
1712     {
1713         ERR("(%04x): Local heap not found\n", ds );
1714         LOCAL_PrintHeap( ds );
1715         return 0;
1716     }
1717     TRACE("(%04x): %08lx\n", ds, (DWORD)func );
1718     FIXME("Half implemented\n");
1719     oldNotify = pInfo->notify;
1720     pInfo->notify = func;
1721     return oldNotify;
1722 }
1723
1724
1725 /***********************************************************************
1726  *           LocalShrink   (KERNEL.121)
1727  */
1728 UINT16 WINAPI LocalShrink16( HGLOBAL16 handle, UINT16 newsize )
1729 {
1730     TRACE("%04x %04x\n", handle, newsize );
1731     return 0;
1732 }
1733
1734
1735 /***********************************************************************
1736  *           GetHeapSpaces   (KERNEL.138)
1737  */
1738 DWORD WINAPI GetHeapSpaces16( HMODULE16 module )
1739 {
1740     NE_MODULE *pModule;
1741     WORD ds;
1742
1743     if (!(pModule = NE_GetPtr( module ))) return 0;
1744     ds =
1745     GlobalHandleToSel16((NE_SEG_TABLE( pModule ) + pModule->dgroup - 1)->hSeg);
1746     return MAKELONG( LOCAL_CountFree( ds ), LOCAL_HeapSize( ds ) );
1747 }
1748
1749
1750 /***********************************************************************
1751  *           LocalCountFree   (KERNEL.161)
1752  */
1753 WORD WINAPI LocalCountFree16(void)
1754 {
1755     return LOCAL_CountFree( CURRENT_DS );
1756 }
1757
1758
1759 /***********************************************************************
1760  *           LocalHeapSize   (KERNEL.162)
1761  */
1762 WORD WINAPI LocalHeapSize16(void)
1763 {
1764     TRACE("(void)\n" );
1765     return LOCAL_HeapSize( CURRENT_DS );
1766 }
1767
1768
1769 /***********************************************************************
1770  *           LocalHandleDelta   (KERNEL.310)
1771  */
1772 WORD WINAPI LocalHandleDelta16( WORD delta )
1773 {
1774     LOCALHEAPINFO *pInfo;
1775
1776     if (!(pInfo = LOCAL_GetHeap( CURRENT_DS )))
1777     {
1778         ERR("Local heap not found\n");
1779         LOCAL_PrintHeap( CURRENT_DS );
1780         return 0;
1781     }
1782     if (delta) pInfo->hdelta = delta;
1783     TRACE("returning %04x\n", pInfo->hdelta);
1784     return pInfo->hdelta;
1785 }
1786
1787
1788 /***********************************************************************
1789  *           LocalInfo   (TOOLHELP.56)
1790  */
1791 BOOL16 WINAPI LocalInfo16( LOCALINFO *pLocalInfo, HGLOBAL16 handle )
1792 {
1793     LOCALHEAPINFO *pInfo = LOCAL_GetHeap(SELECTOROF(K32WOWGlobalLock16(handle)));
1794     if (!pInfo) return FALSE;
1795     pLocalInfo->wcItems = pInfo->items;
1796     return TRUE;
1797 }
1798
1799
1800 /***********************************************************************
1801  *           LocalFirst   (TOOLHELP.57)
1802  */
1803 BOOL16 WINAPI LocalFirst16( LOCALENTRY *pLocalEntry, HGLOBAL16 handle )
1804 {
1805     WORD ds = GlobalHandleToSel16( handle );
1806     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1807     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
1808     if (!pInfo) return FALSE;
1809
1810     pLocalEntry->hHandle   = pInfo->first + ARENA_HEADER_SIZE;
1811     pLocalEntry->wAddress  = pLocalEntry->hHandle;
1812     pLocalEntry->wFlags    = LF_FIXED;
1813     pLocalEntry->wcLock    = 0;
1814     pLocalEntry->wType     = LT_NORMAL;
1815     pLocalEntry->hHeap     = handle;
1816     pLocalEntry->wHeapType = NORMAL_HEAP;
1817     pLocalEntry->wNext     = ARENA_PTR(ptr,pInfo->first)->next;
1818     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
1819     return TRUE;
1820 }
1821
1822
1823 /***********************************************************************
1824  *           LocalNext   (TOOLHELP.58)
1825  */
1826 BOOL16 WINAPI LocalNext16( LOCALENTRY *pLocalEntry )
1827 {
1828     WORD ds = GlobalHandleToSel16( pLocalEntry->hHeap );
1829     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
1830     LOCALARENA *pArena;
1831
1832     if (!LOCAL_GetHeap( ds )) return FALSE;
1833     if (!pLocalEntry->wNext) return FALSE;
1834     pArena = ARENA_PTR( ptr, pLocalEntry->wNext );
1835
1836     pLocalEntry->hHandle   = pLocalEntry->wNext + ARENA_HEADER_SIZE;
1837     pLocalEntry->wAddress  = pLocalEntry->hHandle;
1838     pLocalEntry->wFlags    = (pArena->prev & 3) + 1;
1839     pLocalEntry->wcLock    = 0;
1840     pLocalEntry->wType     = LT_NORMAL;
1841     if (pArena->next != pLocalEntry->wNext)  /* last one? */
1842         pLocalEntry->wNext = pArena->next;
1843     else
1844         pLocalEntry->wNext = 0;
1845     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
1846     return TRUE;
1847 }
1848
1849
1850 /***********************************************************************
1851  * 32-bit local heap functions (Win95; undocumented)
1852  */
1853
1854 /***********************************************************************
1855  *           K208   (KERNEL.208)
1856  */
1857 HANDLE WINAPI Local32Init16( WORD segment, DWORD tableSize,
1858                              DWORD heapSize, DWORD flags )
1859 {
1860     DWORD totSize, segSize = 0;
1861     LPBYTE base;
1862     LOCAL32HEADER *header;
1863     HANDLE heap;
1864     WORD *selectorTable;
1865     WORD selectorEven, selectorOdd;
1866     int i, nrBlocks;
1867
1868     /* Determine new heap size */
1869
1870     if ( segment )
1871     {
1872         if ( (segSize = GetSelectorLimit16( segment )) == 0 )
1873             return 0;
1874         else
1875             segSize++;
1876     }
1877
1878     if ( heapSize == -1L )
1879         heapSize = 1024L*1024L;   /* FIXME */
1880
1881     heapSize = (heapSize + 0xffff) & 0xffff0000;
1882     segSize  = (segSize  + 0x0fff) & 0xfffff000;
1883     totSize  = segSize + HTABLE_SIZE + heapSize;
1884
1885
1886     /* Allocate memory and initialize heap */
1887
1888     if ( !(base = VirtualAlloc( NULL, totSize, MEM_RESERVE, PAGE_READWRITE )) )
1889         return 0;
1890
1891     if ( !VirtualAlloc( base, segSize + HTABLE_PAGESIZE,
1892                         MEM_COMMIT, PAGE_READWRITE ) )
1893     {
1894         VirtualFree( base, 0, MEM_RELEASE );
1895         return 0;
1896     }
1897
1898     if (!(heap = RtlCreateHeap( 0, base + segSize + HTABLE_SIZE, heapSize, 0x10000, NULL, NULL )))
1899     {
1900         VirtualFree( base, 0, MEM_RELEASE );
1901         return 0;
1902     }
1903
1904
1905     /* Set up header and handle table */
1906
1907     header = (LOCAL32HEADER *)(base + segSize);
1908     header->base    = base;
1909     header->limit   = HTABLE_PAGESIZE-1;
1910     header->flags   = 0;
1911     header->magic   = LOCAL32_MAGIC;
1912     header->heap    = heap;
1913
1914     header->freeListFirst[0] = sizeof(LOCAL32HEADER);
1915     header->freeListLast[0]  = HTABLE_PAGESIZE - 4;
1916     header->freeListSize[0]  = (HTABLE_PAGESIZE - sizeof(LOCAL32HEADER)) / 4;
1917
1918     for (i = header->freeListFirst[0]; i < header->freeListLast[0]; i += 4)
1919         *(DWORD *)((LPBYTE)header + i) = i+4;
1920
1921     header->freeListFirst[1] = 0xffff;
1922
1923
1924     /* Set up selector table */
1925
1926     nrBlocks      = (totSize + 0x7fff) >> 15;
1927     selectorTable = (LPWORD) HeapAlloc( header->heap,  0, nrBlocks * 2 );
1928     selectorEven  = SELECTOR_AllocBlock( base, totSize, WINE_LDT_FLAGS_DATA );
1929     selectorOdd   = SELECTOR_AllocBlock( base + 0x8000, totSize - 0x8000, WINE_LDT_FLAGS_DATA );
1930     if ( !selectorTable || !selectorEven || !selectorOdd )
1931     {
1932         if ( selectorTable ) HeapFree( header->heap, 0, selectorTable );
1933         if ( selectorEven  ) SELECTOR_FreeBlock( selectorEven );
1934         if ( selectorOdd   ) SELECTOR_FreeBlock( selectorOdd );
1935         HeapDestroy( header->heap );
1936         VirtualFree( base, 0, MEM_RELEASE );
1937         return 0;
1938     }
1939
1940     header->selectorTableOffset = (LPBYTE)selectorTable - header->base;
1941     header->selectorTableSize   = nrBlocks * 4;  /* ??? Win95 does it this way! */
1942     header->selectorDelta       = selectorEven - selectorOdd;
1943     header->segment             = segment? segment : selectorEven;
1944
1945     for (i = 0; i < nrBlocks; i++)
1946         selectorTable[i] = (i & 1)? selectorOdd  + ((i >> 1) << __AHSHIFT)
1947                                   : selectorEven + ((i >> 1) << __AHSHIFT);
1948
1949     /* Move old segment */
1950
1951     if ( segment )
1952     {
1953         /* FIXME: This is somewhat ugly and relies on implementation
1954                   details about 16-bit global memory handles ... */
1955
1956         LPBYTE oldBase = (LPBYTE)GetSelectorBase( segment );
1957         memcpy( base, oldBase, segSize );
1958         GLOBAL_MoveBlock( segment, base, totSize );
1959         HeapFree( GetProcessHeap(), 0, oldBase );
1960     }
1961
1962     return (HANDLE)header;
1963 }
1964
1965 /***********************************************************************
1966  *           Local32_SearchHandle
1967  */
1968 static LPDWORD Local32_SearchHandle( LOCAL32HEADER *header, DWORD addr )
1969 {
1970     LPDWORD handle;
1971
1972     for ( handle = (LPDWORD)((LPBYTE)header + sizeof(LOCAL32HEADER));
1973           handle < (LPDWORD)((LPBYTE)header + header->limit);
1974           handle++)
1975     {
1976         if (*handle == addr)
1977             return handle;
1978     }
1979
1980     return NULL;
1981 }
1982
1983 /***********************************************************************
1984  *           Local32_ToHandle
1985  */
1986 static VOID Local32_ToHandle( LOCAL32HEADER *header, INT16 type,
1987                               DWORD addr, LPDWORD *handle, LPBYTE *ptr )
1988 {
1989     *handle = NULL;
1990     *ptr    = NULL;
1991
1992     switch (type)
1993     {
1994         case -2:    /* 16:16 pointer, no handles */
1995             *ptr    = MapSL( addr );
1996             *handle = (LPDWORD)*ptr;
1997             break;
1998
1999         case -1:    /* 32-bit offset, no handles */
2000             *ptr    = header->base + addr;
2001             *handle = (LPDWORD)*ptr;
2002             break;
2003
2004         case 0:     /* handle */
2005             if (    addr >= sizeof(LOCAL32HEADER)
2006                  && addr <  header->limit && !(addr & 3)
2007                  && *(LPDWORD)((LPBYTE)header + addr) >= HTABLE_SIZE )
2008             {
2009                 *handle = (LPDWORD)((LPBYTE)header + addr);
2010                 *ptr    = header->base + **handle;
2011             }
2012             break;
2013
2014         case 1:     /* 16:16 pointer */
2015             *ptr    = MapSL( addr );
2016             *handle = Local32_SearchHandle( header, *ptr - header->base );
2017             break;
2018
2019         case 2:     /* 32-bit offset */
2020             *ptr    = header->base + addr;
2021             *handle = Local32_SearchHandle( header, *ptr - header->base );
2022             break;
2023     }
2024 }
2025
2026 /***********************************************************************
2027  *           Local32_FromHandle
2028  */
2029 static VOID Local32_FromHandle( LOCAL32HEADER *header, INT16 type,
2030                                 DWORD *addr, LPDWORD handle, LPBYTE ptr )
2031 {
2032     switch (type)
2033     {
2034         case -2:    /* 16:16 pointer */
2035         case  1:
2036         {
2037             WORD *selTable = (LPWORD)(header->base + header->selectorTableOffset);
2038             DWORD offset   = (LPBYTE)ptr - header->base;
2039             *addr = MAKELONG( offset & 0x7fff, selTable[offset >> 15] );
2040         }
2041         break;
2042
2043         case -1:    /* 32-bit offset */
2044         case  2:
2045             *addr = ptr - header->base;
2046             break;
2047
2048         case  0:    /* handle */
2049             *addr = (LPBYTE)handle - (LPBYTE)header;
2050             break;
2051     }
2052 }
2053
2054 /***********************************************************************
2055  *           K209   (KERNEL.209)
2056  */
2057 DWORD WINAPI Local32Alloc16( HANDLE heap, DWORD size, INT16 type, DWORD flags )
2058 {
2059     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
2060     LPDWORD handle;
2061     LPBYTE ptr;
2062     DWORD addr;
2063
2064     /* Allocate memory */
2065     ptr = HeapAlloc( header->heap,
2066                      (flags & LMEM_MOVEABLE)? HEAP_ZERO_MEMORY : 0, size );
2067     if (!ptr) return 0;
2068
2069
2070     /* Allocate handle if requested */
2071     if (type >= 0)
2072     {
2073         int page, i;
2074
2075         /* Find first page of handle table with free slots */
2076         for (page = 0; page < HTABLE_NPAGES; page++)
2077             if (header->freeListFirst[page] != 0)
2078                 break;
2079         if (page == HTABLE_NPAGES)
2080         {
2081             WARN("Out of handles!\n" );
2082             HeapFree( header->heap, 0, ptr );
2083             return 0;
2084         }
2085
2086         /* If virgin page, initialize it */
2087         if (header->freeListFirst[page] == 0xffff)
2088         {
2089             if ( !VirtualAlloc( (LPBYTE)header + (page << 12),
2090                                 0x1000, MEM_COMMIT, PAGE_READWRITE ) )
2091             {
2092                 WARN("Cannot grow handle table!\n" );
2093                 HeapFree( header->heap, 0, ptr );
2094                 return 0;
2095             }
2096
2097             header->limit += HTABLE_PAGESIZE;
2098
2099             header->freeListFirst[page] = 0;
2100             header->freeListLast[page]  = HTABLE_PAGESIZE - 4;
2101             header->freeListSize[page]  = HTABLE_PAGESIZE / 4;
2102
2103             for (i = 0; i < HTABLE_PAGESIZE; i += 4)
2104                 *(DWORD *)((LPBYTE)header + i) = i+4;
2105
2106             if (page < HTABLE_NPAGES-1)
2107                 header->freeListFirst[page+1] = 0xffff;
2108         }
2109
2110         /* Allocate handle slot from page */
2111         handle = (LPDWORD)((LPBYTE)header + header->freeListFirst[page]);
2112         if (--header->freeListSize[page] == 0)
2113             header->freeListFirst[page] = header->freeListLast[page] = 0;
2114         else
2115             header->freeListFirst[page] = *handle;
2116
2117         /* Store 32-bit offset in handle slot */
2118         *handle = ptr - header->base;
2119     }
2120     else
2121     {
2122         handle = (LPDWORD)ptr;
2123         header->flags |= 1;
2124     }
2125
2126
2127     /* Convert handle to requested output type */
2128     Local32_FromHandle( header, type, &addr, handle, ptr );
2129     return addr;
2130 }
2131
2132 /***********************************************************************
2133  *           K210   (KERNEL.210)
2134  */
2135 DWORD WINAPI Local32ReAlloc16( HANDLE heap, DWORD addr, INT16 type,
2136                              DWORD size, DWORD flags )
2137 {
2138     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
2139     LPDWORD handle;
2140     LPBYTE ptr;
2141
2142     if (!addr)
2143         return Local32Alloc16( heap, size, type, flags );
2144
2145     /* Retrieve handle and pointer */
2146     Local32_ToHandle( header, type, addr, &handle, &ptr );
2147     if (!handle) return FALSE;
2148
2149     /* Reallocate memory block */
2150     ptr = HeapReAlloc( header->heap,
2151                        (flags & LMEM_MOVEABLE)? HEAP_ZERO_MEMORY : 0,
2152                        ptr, size );
2153     if (!ptr) return 0;
2154
2155     /* Modify handle */
2156     if (type >= 0)
2157         *handle = ptr - header->base;
2158     else
2159         handle = (LPDWORD)ptr;
2160
2161     /* Convert handle to requested output type */
2162     Local32_FromHandle( header, type, &addr, handle, ptr );
2163     return addr;
2164 }
2165
2166 /***********************************************************************
2167  *           K211   (KERNEL.211)
2168  */
2169 BOOL WINAPI Local32Free16( HANDLE heap, DWORD addr, INT16 type )
2170 {
2171     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
2172     LPDWORD handle;
2173     LPBYTE ptr;
2174
2175     /* Retrieve handle and pointer */
2176     Local32_ToHandle( header, type, addr, &handle, &ptr );
2177     if (!handle) return FALSE;
2178
2179     /* Free handle if necessary */
2180     if (type >= 0)
2181     {
2182         int offset = (LPBYTE)handle - (LPBYTE)header;
2183         int page   = offset >> 12;
2184
2185         /* Return handle slot to page free list */
2186         if (header->freeListSize[page]++ == 0)
2187             header->freeListFirst[page] = header->freeListLast[page]  = offset;
2188         else
2189             *(LPDWORD)((LPBYTE)header + header->freeListLast[page]) = offset,
2190             header->freeListLast[page] = offset;
2191
2192         *handle = 0;
2193
2194         /* Shrink handle table when possible */
2195         while (page > 0 && header->freeListSize[page] == HTABLE_PAGESIZE / 4)
2196         {
2197             if ( VirtualFree( (LPBYTE)header +
2198                               (header->limit & ~(HTABLE_PAGESIZE-1)),
2199                               HTABLE_PAGESIZE, MEM_DECOMMIT ) )
2200                 break;
2201
2202             header->limit -= HTABLE_PAGESIZE;
2203             header->freeListFirst[page] = 0xffff;
2204             page--;
2205         }
2206     }
2207
2208     /* Free memory */
2209     return HeapFree( header->heap, 0, ptr );
2210 }
2211
2212 /***********************************************************************
2213  *           K213   (KERNEL.213)
2214  */
2215 DWORD WINAPI Local32Translate16( HANDLE heap, DWORD addr, INT16 type1, INT16 type2 )
2216 {
2217     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
2218     LPDWORD handle;
2219     LPBYTE ptr;
2220
2221     Local32_ToHandle( header, type1, addr, &handle, &ptr );
2222     if (!handle) return 0;
2223
2224     Local32_FromHandle( header, type2, &addr, handle, ptr );
2225     return addr;
2226 }
2227
2228 /***********************************************************************
2229  *           K214   (KERNEL.214)
2230  */
2231 DWORD WINAPI Local32Size16( HANDLE heap, DWORD addr, INT16 type )
2232 {
2233     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
2234     LPDWORD handle;
2235     LPBYTE ptr;
2236
2237     Local32_ToHandle( header, type, addr, &handle, &ptr );
2238     if (!handle) return 0;
2239
2240     return HeapSize( header->heap, 0, ptr );
2241 }
2242
2243 /***********************************************************************
2244  *           K215   (KERNEL.215)
2245  */
2246 BOOL WINAPI Local32ValidHandle16( HANDLE heap, WORD addr )
2247 {
2248     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
2249     LPDWORD handle;
2250     LPBYTE ptr;
2251
2252     Local32_ToHandle( header, 0, addr, &handle, &ptr );
2253     return handle != NULL;
2254 }
2255
2256 /***********************************************************************
2257  *           K229   (KERNEL.229)
2258  */
2259 WORD WINAPI Local32GetSegment16( HANDLE heap )
2260 {
2261     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
2262     return header->segment;
2263 }
2264
2265 /***********************************************************************
2266  *           Local32_GetHeap
2267  */
2268 static LOCAL32HEADER *Local32_GetHeap( HGLOBAL16 handle )
2269 {
2270     WORD selector = GlobalHandleToSel16( handle );
2271     DWORD base  = GetSelectorBase( selector );
2272     DWORD limit = GetSelectorLimit16( selector );
2273
2274     /* Hmmm. This is a somewhat stupid heuristic, but Windows 95 does
2275        it this way ... */
2276
2277     if ( limit > 0x10000 && ((LOCAL32HEADER *)base)->magic == LOCAL32_MAGIC )
2278         return (LOCAL32HEADER *)base;
2279
2280     base  += 0x10000;
2281     limit -= 0x10000;
2282
2283     if ( limit > 0x10000 && ((LOCAL32HEADER *)base)->magic == LOCAL32_MAGIC )
2284         return (LOCAL32HEADER *)base;
2285
2286     return NULL;
2287 }
2288
2289 /***********************************************************************
2290  *           Local32Info   (KERNEL.444)
2291  *           Local32Info   (TOOLHELP.84)
2292  */
2293 BOOL16 WINAPI Local32Info16( LOCAL32INFO *pLocal32Info, HGLOBAL16 handle )
2294 {
2295     PROCESS_HEAP_ENTRY entry;
2296     int i;
2297
2298     LOCAL32HEADER *header = Local32_GetHeap( handle );
2299     if ( !header ) return FALSE;
2300
2301     if ( !pLocal32Info || pLocal32Info->dwSize < sizeof(LOCAL32INFO) )
2302         return FALSE;
2303
2304     pLocal32Info->dwMemReserved = 0;
2305     pLocal32Info->dwMemCommitted = 0;
2306     pLocal32Info->dwTotalFree = 0;
2307     pLocal32Info->dwLargestFreeBlock = 0;
2308
2309     while (HeapWalk( header->heap, &entry ))
2310     {
2311         if (entry.wFlags & PROCESS_HEAP_REGION)
2312         {
2313             pLocal32Info->dwMemReserved += entry.u.Region.dwCommittedSize
2314                                            + entry.u.Region.dwUnCommittedSize;
2315             pLocal32Info->dwMemCommitted = entry.u.Region.dwCommittedSize;
2316         }
2317         else if (!(entry.wFlags & PROCESS_HEAP_ENTRY_BUSY))
2318         {
2319             DWORD size = entry.cbData + entry.cbOverhead;
2320             pLocal32Info->dwTotalFree += size;
2321             if (size > pLocal32Info->dwLargestFreeBlock) pLocal32Info->dwLargestFreeBlock = size;
2322         }
2323     }
2324
2325     pLocal32Info->dwcFreeHandles = 0;
2326     for ( i = 0; i < HTABLE_NPAGES; i++ )
2327     {
2328         if ( header->freeListFirst[i] == 0xffff ) break;
2329         pLocal32Info->dwcFreeHandles += header->freeListSize[i];
2330     }
2331     pLocal32Info->dwcFreeHandles += (HTABLE_NPAGES - i) * HTABLE_PAGESIZE/4;
2332
2333     return TRUE;
2334 }
2335
2336 /***********************************************************************
2337  *           Local32First   (KERNEL.445)
2338  *           Local32First   (TOOLHELP.85)
2339  */
2340 BOOL16 WINAPI Local32First16( LOCAL32ENTRY *pLocal32Entry, HGLOBAL16 handle )
2341 {
2342     FIXME("(%p, %04X): stub!\n", pLocal32Entry, handle );
2343     return FALSE;
2344 }
2345
2346 /***********************************************************************
2347  *           Local32Next   (KERNEL.446)
2348  *           Local32Next   (TOOLHELP.86)
2349  */
2350 BOOL16 WINAPI Local32Next16( LOCAL32ENTRY *pLocal32Entry )
2351 {
2352     FIXME("(%p): stub!\n", pLocal32Entry );
2353     return FALSE;
2354 }