Release 950319
[wine] / memory / local.c
1 /*
2  * Local heap functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 /*
8  * Note:
9  * All local heap functions need the current DS as first parameter
10  * when called from the emulation library, so they take one more
11  * parameter than usual.
12  */
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include "windows.h"
17 #include "ldt.h"
18 #include "instance.h"
19 #include "local.h"
20 #include "stackframe.h"
21 #include "toolhelp.h"
22 #include "stddebug.h"
23 #include "debug.h"
24
25
26 #ifndef WINELIB
27 #pragma pack(1)
28 #endif
29
30 typedef struct
31 {
32 /* Arena header */
33     WORD prev;          /* Previous arena | arena type */
34     WORD next;          /* Next arena */
35 /* Start of the memory block or free-list info */
36     WORD size;          /* Size of the free block */
37     WORD free_prev;     /* Previous free block */
38     WORD free_next;     /* Next free block */
39 } LOCALARENA;
40
41 #define ARENA_HEADER_SIZE      4
42
43   /* Arena types (stored in 'prev' field of the arena) */
44 #define LOCAL_ARENA_FREE       0
45 #define LOCAL_ARENA_FIXED      1
46 #define LOCAL_ARENA_MOVEABLE   3
47
48
49
50 typedef struct
51 {
52     WORD addr;                /* Address of the MOVEABLE block */
53     BYTE flags;               /* Flags for this block */
54     BYTE lock;                /* Lock count */
55 } LOCALHANDLEENTRY;
56
57 typedef struct
58 {
59     WORD check;               /* Heap checking flag */
60     WORD freeze;              /* Heap frozen flag */
61     WORD items;               /* Count of items on the heap */
62     WORD first;               /* First item of the heap */
63     WORD pad1;                /* Always 0 */
64     WORD last;                /* Last item of the heap */
65     WORD pad2;                /* Always 0 */
66     BYTE ncompact;            /* Compactions counter */
67     BYTE dislevel;            /* Discard level */
68     DWORD distotal;           /* Total bytes discarded */
69     WORD htable;              /* Pointer to handle table */
70     WORD hfree;               /* Pointer to free handle table */
71     WORD hdelta;              /* Delta to expand the handle table */
72     WORD expand;              /* Pointer to expand function (unused) */
73     WORD pstat;               /* Pointer to status structure (unused) */
74     DWORD notify WINE_PACKED; /* Pointer to LocalNotify() function */
75     WORD lock;                /* Lock count for the heap */
76     WORD extra;               /* Extra bytes to allocate when expanding */
77     WORD minsize;             /* Minimum size of the heap */
78     WORD magic;               /* Magic number */
79 } LOCALHEAPINFO;
80
81 #ifndef WINELIB
82 #pragma pack(4)
83 #endif
84
85 #define LOCAL_HEAP_MAGIC  0x484c  /* 'LH' */
86
87
88   /* All local heap allocations are aligned on 4-byte boundaries */
89 #define LALIGN(word)          (((word) + 3) & ~3)
90
91 #define ARENA_PTR(ptr,arena)       ((LOCALARENA *)((char*)(ptr)+(arena)))
92 #define ARENA_PREV(ptr,arena)      (ARENA_PTR(ptr,arena)->prev & ~3)
93 #define ARENA_NEXT(ptr,arena)      (ARENA_PTR(ptr,arena)->next)
94 #define ARENA_FLAGS(ptr,arena)     (ARENA_PTR(ptr,arena)->prev & 3)
95
96
97 /***********************************************************************
98  *           LOCAL_GetHeap
99  *
100  * Return a pointer to the local heap, making sure it exists.
101  */
102 static LOCALHEAPINFO *LOCAL_GetHeap( WORD ds )
103 {
104     LOCALHEAPINFO *pInfo;
105     INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( ds, 0 );
106     if (!ptr->heap) return 0;
107     pInfo = (LOCALHEAPINFO*)((char*)ptr + ptr->heap);
108     if (pInfo->magic != LOCAL_HEAP_MAGIC) return NULL;
109     return pInfo;
110 }
111
112
113 /***********************************************************************
114  *           LOCAL_AddFreeBlock
115  *
116  * Make a block free, inserting it in the free-list.
117  * 'block' is the handle of the block arena; 'baseptr' points to
118  * the beginning of the data segment containing the heap.
119  */
120 static void LOCAL_AddFreeBlock( char *baseptr, WORD block )
121 {
122     LOCALARENA *pArena, *pNext;
123     WORD next;
124
125       /* Mark the block as free */
126
127     pArena = ARENA_PTR( baseptr, block );
128     pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FREE;
129     pArena->size = pArena->next - block;
130     
131       /* Find the next free block (last block is always free) */
132
133     next = pArena->next;
134     for (;;)
135     {
136         pNext = ARENA_PTR( baseptr, next );
137         if ((pNext->prev & 3) == LOCAL_ARENA_FREE) break;
138         next = pNext->next;
139     }
140
141       /* Insert the free block in the free-list */
142
143     pArena->free_prev = pNext->free_prev;
144     pArena->free_next = next;
145     ARENA_PTR(baseptr,pNext->free_prev)->free_next = block;
146     pNext->free_prev  = block;
147 }
148
149
150 /***********************************************************************
151  *           LOCAL_RemoveFreeBlock
152  *
153  * Remove a block from the free-list.
154  * 'block' is the handle of the block arena; 'baseptr' points to
155  * the beginning of the data segment containing the heap.
156  */
157 static void LOCAL_RemoveFreeBlock( char *baseptr, WORD block )
158 {
159       /* Mark the block as fixed */
160
161     LOCALARENA *pArena = ARENA_PTR( baseptr, block );
162     pArena->prev = (pArena->prev & ~3) | LOCAL_ARENA_FIXED;
163
164       /* Remove it from the list */
165
166     ARENA_PTR(baseptr,pArena->free_prev)->free_next = pArena->free_next;
167     ARENA_PTR(baseptr,pArena->free_next)->free_prev = pArena->free_prev;
168 }
169
170
171 /***********************************************************************
172  *           LOCAL_AddBlock
173  *
174  * Insert a new block in the heap.
175  * 'new' is the handle of the new block arena; 'baseptr' points to
176  * the beginning of the data segment containing the heap; 'prev' is
177  * the block before the new one.
178  */
179 static void LOCAL_AddBlock( char *baseptr, WORD prev, WORD new )
180 {
181     LOCALARENA *pPrev = ARENA_PTR( baseptr, prev );
182     LOCALARENA *pNew  = ARENA_PTR( baseptr, new );
183
184     pNew->prev = prev | LOCAL_ARENA_FIXED;
185     pNew->next = pPrev->next;
186     ARENA_PTR(baseptr,pPrev->next)->prev &= 3;
187     ARENA_PTR(baseptr,pPrev->next)->prev |= new;
188     pPrev->next = new;
189 }
190
191
192 /***********************************************************************
193  *           LOCAL_RemoveBlock
194  *
195  * Remove a block from the heap.
196  * 'block' is the handle of the block arena; 'baseptr' points to
197  * the beginning of the data segment containing the heap.
198  */
199 static void LOCAL_RemoveBlock( char *baseptr, WORD block )
200 {
201     LOCALARENA *pArena, *pTmp;
202
203       /* Remove the block from the free-list */
204
205     pArena = ARENA_PTR( baseptr, block );
206     if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
207         LOCAL_RemoveFreeBlock( baseptr, block );
208
209       /* If the previous block is free, expand its size */
210
211     pTmp = ARENA_PTR( baseptr, pArena->prev & ~3 );
212     if ((pTmp->prev & 3) == LOCAL_ARENA_FREE)
213         pTmp->size += pArena->next - block;
214
215       /* Remove the block from the linked list */
216
217     pTmp->next = pArena->next;
218     pTmp = ARENA_PTR( baseptr, pArena->next );
219     pTmp->prev = (pTmp->prev & 3) | (pArena->prev & ~3);
220 }
221
222
223 /***********************************************************************
224  *           LOCAL_PrintHeap
225  */
226 static void LOCAL_PrintHeap( WORD ds )
227 {
228     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
229     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
230     WORD arena;
231
232     if (!pInfo)
233     {
234         printf( "Local Heap corrupted!  ds=%04x\n", ds );
235         return;
236     }
237     printf( "Local Heap  ds=%04x first=%04x last=%04x items=%d\n",
238             ds, pInfo->first, pInfo->last, pInfo->items );
239
240     arena = pInfo->first;
241     for (;;)
242     {
243         LOCALARENA *pArena = ARENA_PTR(ptr,arena);
244         printf( "  %04x: prev=%04x next=%04x type=%d\n", arena,
245                 pArena->prev & ~3, pArena->next, pArena->prev & 3 );
246         if ((pArena->prev & 3) == LOCAL_ARENA_FREE)
247         {
248             printf( "        size=%d free_prev=%04x free_next=%04x\n",
249                     pArena->size, pArena->free_prev, pArena->free_next );
250             if (pArena->next == arena) break;  /* last one */
251             if (ARENA_PTR(ptr,pArena->free_next)->free_prev != arena)
252             {
253                 printf( "*** arena->free_next->free_prev != arena\n" );
254                 break;
255             }
256         }
257         if (pArena->next == arena)
258         {
259             printf( "*** last block is not marked free\n" );
260             break;
261         }
262         if ((ARENA_PTR(ptr,pArena->next)->prev & ~3) != arena)
263         {
264             printf( "*** arena->next->prev != arena\n" );
265             break;
266         }
267         arena = pArena->next;
268     }
269 }
270
271
272 /***********************************************************************
273  *           LocalInit   (KERNEL.4)
274  */
275 HLOCAL LocalInit( WORD selector, WORD start, WORD end )
276 {
277     char *ptr;
278     WORD heapInfoArena, freeArena, lastArena;
279     LOCALHEAPINFO *pHeapInfo;
280     LOCALARENA *pArena, *pFirstArena, *pLastArena;
281
282       /* The initial layout of the heap is: */
283       /* - first arena         (FIXED)      */
284       /* - heap info structure (FIXED)      */
285       /* - large free block    (FREE)       */
286       /* - last arena          (FREE)       */
287
288       /* FIXME: What should be done if there's already */
289       /* a local heap in this segment? */
290     dprintf_local(stddeb, "LocalInit: %04x %04x-%04x\n", selector, start, end);
291     if (!selector) selector = CURRENT_DS;
292     ptr = PTR_SEG_OFF_TO_LIN( selector, 0 );
293     start = LALIGN( max( start, sizeof(INSTANCEDATA) ) );
294     heapInfoArena = LALIGN(start + sizeof(LOCALARENA) );
295     freeArena = LALIGN( heapInfoArena + ARENA_HEADER_SIZE
296                         + sizeof(LOCALHEAPINFO) );
297     lastArena = (end - sizeof(LOCALARENA)) & ~3;
298
299       /* Make sure there's enough space.       */
300
301     if (freeArena + sizeof(LOCALARENA) >= lastArena) return FALSE;
302
303       /* Initialise the first arena */
304
305     pFirstArena = ARENA_PTR( ptr, start );
306     pFirstArena->prev      = start | LOCAL_ARENA_FIXED;
307     pFirstArena->next      = heapInfoArena;
308     pFirstArena->size      = LALIGN(sizeof(LOCALARENA));
309     pFirstArena->free_prev = start;  /* this one */
310     pFirstArena->free_next = freeArena;
311
312       /* Initialise the arena of the heap info structure */
313
314     pArena = ARENA_PTR( ptr, heapInfoArena );
315     pArena->prev = start | LOCAL_ARENA_FIXED;
316     pArena->next = freeArena;
317
318       /* Initialise the heap info structure */
319
320     pHeapInfo = (LOCALHEAPINFO *) (ptr + heapInfoArena + ARENA_HEADER_SIZE );
321     memset( pHeapInfo, 0, sizeof(LOCALHEAPINFO) );
322     pHeapInfo->items   = 4;
323     pHeapInfo->first   = start;
324     pHeapInfo->last    = lastArena;
325     pHeapInfo->hdelta  = 0x20;
326     pHeapInfo->extra   = 0x200;
327     pHeapInfo->minsize = lastArena - freeArena;
328     pHeapInfo->magic   = LOCAL_HEAP_MAGIC;
329
330       /* Initialise the large free block */
331
332     pArena = ARENA_PTR( ptr, freeArena );
333     pArena->prev      = heapInfoArena | LOCAL_ARENA_FREE;
334     pArena->next      = lastArena;
335     pArena->size      = lastArena - freeArena;
336     pArena->free_prev = start;
337     pArena->free_next = lastArena;
338
339       /* Initialise the last block */
340
341     pLastArena = ARENA_PTR( ptr, lastArena );
342     pLastArena->prev      = heapInfoArena | LOCAL_ARENA_FREE;
343     pLastArena->next      = lastArena;  /* this one */
344     pLastArena->size      = LALIGN(sizeof(LOCALARENA));
345     pLastArena->free_prev = freeArena;
346     pLastArena->free_next = lastArena;  /* this one */
347
348       /* Store the local heap address in the instance data */
349
350     ((INSTANCEDATA *)ptr)->heap = heapInfoArena + ARENA_HEADER_SIZE;
351     return TRUE;
352 }
353
354
355 /***********************************************************************
356  *           LOCAL_Alloc
357  *
358  * Implementation of LocalAlloc().
359  */
360 HLOCAL LOCAL_Alloc( WORD ds, WORD flags, WORD size )
361 {
362     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
363     LOCALHEAPINFO *pInfo;
364     LOCALARENA *pArena;
365     WORD arena;
366
367     dprintf_local( stddeb, "LocalAlloc: %04x %d ds=%04x\n", flags, size, ds );
368
369       /* Find a suitable free block */
370
371     if (!(pInfo = LOCAL_GetHeap( ds ))) return 0;
372     size += ARENA_HEADER_SIZE;
373     size = LALIGN( max( size, sizeof(LOCALARENA) ) );
374     arena = pInfo->first;
375     pArena = ARENA_PTR( ptr, arena );
376     for (;;)
377     {
378         if (arena == pArena->free_next) return 0;  /* not found */
379         arena = pArena->free_next;
380         pArena = ARENA_PTR( ptr, arena );
381         if (pArena->size >= size) break;
382     }
383
384       /* Make a block out of the free arena */
385
386     if (pArena->size > size + LALIGN(sizeof(LOCALARENA)))
387     {
388         LOCAL_AddBlock( ptr, arena, arena+size );
389         LOCAL_AddFreeBlock( ptr, arena+size );
390         pInfo->items++;
391     }
392     LOCAL_RemoveFreeBlock( ptr, arena );
393
394     dprintf_local( stddeb, "LocalAlloc: returning %04x\n",
395                    arena + ARENA_HEADER_SIZE );
396     return arena + ARENA_HEADER_SIZE;
397 }
398
399
400 /***********************************************************************
401  *           LOCAL_ReAlloc
402  *
403  * Implementation of LocalReAlloc().
404  */
405 HLOCAL LOCAL_ReAlloc( WORD ds, HLOCAL handle, WORD size, WORD flags )
406 {
407     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
408     LOCALHEAPINFO *pInfo;
409     LOCALARENA *pArena, *pNext;
410     WORD arena, newhandle;
411
412     dprintf_local( stddeb, "LocalReAlloc: %04x %d %04x ds=%04x\n",
413                    handle, size, flags, ds );
414     if (!(pInfo = LOCAL_GetHeap( ds ))) return 0;
415     arena = handle - ARENA_HEADER_SIZE;
416     pArena = ARENA_PTR( ptr, arena );
417     if (!size) size = 1;
418     size = LALIGN( size );
419
420       /* Check for size reduction */
421
422     if (size < pArena->next - handle)
423     {
424         if (handle + size < pArena->next - LALIGN(sizeof(LOCALARENA)))
425         {
426               /* It is worth making a new free block */
427             LOCAL_AddBlock( ptr, arena, handle + size );
428             LOCAL_AddFreeBlock( ptr, handle + size );
429             pInfo->items++;
430         }
431         dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
432         return handle;
433     }
434
435       /* Check if the next block is free */
436
437     pNext = ARENA_PTR( ptr, pArena->next );
438     if (((pNext->prev & 3) == LOCAL_ARENA_FREE) &&
439         (size <= pNext->next - handle))
440     {
441         LOCAL_RemoveBlock( ptr, pArena->next );
442         if (handle + size < pArena->next - LALIGN(sizeof(LOCALARENA)))
443         {
444               /* It is worth making a new free block */
445             LOCAL_AddBlock( ptr, arena, handle + size );
446             LOCAL_AddFreeBlock( ptr, handle + size );
447             pInfo->items++;
448         }
449         dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", handle );
450         return handle;
451     }
452
453       /* Now we have to allocate a new block */
454
455     newhandle = LOCAL_Alloc( ds, flags, size );
456     if (!newhandle) return 0;
457     memcpy( ptr + newhandle, ptr + handle, pArena->next - handle );
458     LOCAL_Free( ds, handle );
459     dprintf_local( stddeb, "LocalReAlloc: returning %04x\n", newhandle );
460     return newhandle;
461 }
462
463
464 /***********************************************************************
465  *           LOCAL_Free
466  *
467  * Implementation of LocalFree().
468  */
469 HLOCAL LOCAL_Free( WORD ds, HLOCAL handle )
470 {
471     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
472     LOCALHEAPINFO *pInfo;
473     LOCALARENA *pArena, *pPrev, *pNext;
474     WORD arena;
475
476     dprintf_local( stddeb, "LocalFree: %04x ds=%04x\n", handle, ds );
477     if (!(pInfo = LOCAL_GetHeap( ds ))) return handle;
478     arena = handle - ARENA_HEADER_SIZE;
479     pArena = ARENA_PTR( ptr, arena );
480     if ((pArena->prev & 3) == LOCAL_ARENA_FREE) return handle;
481
482       /* Check if we can merge with the previous block */
483
484     pPrev = ARENA_PTR( ptr, pArena->prev & ~3 );
485     pNext = ARENA_PTR( ptr, pArena->next );
486     if ((pPrev->prev & 3) == LOCAL_ARENA_FREE)
487     {
488         arena  = pArena->prev & ~3;
489         pArena = pPrev;
490         LOCAL_RemoveBlock( ptr, pPrev->next );
491         pInfo->items--;
492     }
493     else  /* Make a new free block */
494     {
495         LOCAL_AddFreeBlock( ptr, arena );
496     }
497
498       /* Check if we can merge with the next block */
499
500     if ((pArena->next == pArena->free_next) &&
501         (pArena->next != pInfo->last))
502     {
503         LOCAL_RemoveBlock( ptr, pArena->next );
504         pInfo->items--;
505     }
506     return 0;
507 }
508
509
510 /***********************************************************************
511  *           LOCAL_Size
512  *
513  * Implementation of LocalSize().
514  */
515 WORD LOCAL_Size( WORD ds, HLOCAL handle )
516 {
517     LOCALARENA *pArena = PTR_SEG_OFF_TO_LIN( ds, handle - ARENA_HEADER_SIZE );
518     return pArena->next - handle;
519 }
520
521
522 /***********************************************************************
523  *           LOCAL_HeapSize
524  *
525  * Implementation of LocalHeapSize().
526  */
527 WORD LOCAL_HeapSize( WORD ds )
528 {
529     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
530     if (!pInfo) return 0;
531     return pInfo->last - pInfo->first;
532 }
533
534
535 /***********************************************************************
536  *           LocalAlloc   (KERNEL.5)
537  */
538 HLOCAL LocalAlloc( WORD flags, WORD size )
539 {
540     return LOCAL_Alloc( CURRENT_DS, flags, size );
541 }
542
543
544 /***********************************************************************
545  *           LocalReAlloc   (KERNEL.6)
546  */
547 HLOCAL LocalReAlloc( HLOCAL handle, WORD flags, WORD size )
548 {
549     return LOCAL_ReAlloc( CURRENT_DS, handle, flags, size );
550 }
551
552
553 /***********************************************************************
554  *           LocalFree   (KERNEL.7)
555  */
556 HLOCAL LocalFree( HLOCAL handle )
557 {
558     return LOCAL_Free( CURRENT_DS, handle );
559 }
560
561
562 /***********************************************************************
563  *           LocalLock   (KERNEL.8)
564  */
565 WORD LocalLock( HLOCAL handle )
566 {
567     return handle;
568 }
569
570
571 /***********************************************************************
572  *           LocalUnlock   (KERNEL.9)
573  */
574 BOOL LocalUnlock( HLOCAL handle )
575 {
576     return TRUE;
577 }
578
579
580 /***********************************************************************
581  *           LocalSize   (KERNEL.10)
582  */
583 WORD LocalSize( HLOCAL handle )
584 {
585     return LOCAL_Size( CURRENT_DS, handle );
586 }
587
588
589 /***********************************************************************
590  *           LocalHandle   (KERNEL.11)
591  */
592 HLOCAL LocalHandle( WORD addr )
593 {
594     dprintf_local( stddeb, "LocalHandle: %04x\n", addr );
595     return addr;
596 }
597
598
599 /***********************************************************************
600  *           LocalFlags   (KERNEL.12)
601  */
602 WORD LocalFlags( HLOCAL handle )
603 {
604     dprintf_local( stddeb, "LocalFlags: %04x\n", handle );
605     return 0;
606 }
607
608
609 /***********************************************************************
610  *           LocalCompact   (KERNEL.13)
611  */
612 WORD LocalCompact( WORD minfree )
613 {
614 }
615
616
617 /***********************************************************************
618  *           LocalNotify   (KERNEL.14)
619  */
620 FARPROC LocalNotify( FARPROC func )
621 {
622 }
623
624
625 /***********************************************************************
626  *           LocalShrink   (KERNEL.121)
627  */
628 WORD LocalShrink( HLOCAL handle, WORD newsize )
629 {
630 }
631
632
633 /***********************************************************************
634  *           GetHeapSpaces   (KERNEL.138)
635  */
636 DWORD GetHeapSpaces( HMODULE module )
637 {
638 }
639
640
641 /***********************************************************************
642  *           LocalCountFree   (KERNEL.161)
643  */
644 void LocalCountFree()
645 {
646 }
647
648
649 /***********************************************************************
650  *           LocalHeapSize   (KERNEL.162)
651  */
652 WORD LocalHeapSize()
653 {
654     return LOCAL_HeapSize( CURRENT_DS );
655 }
656
657
658 /***********************************************************************
659  *           LocalHandleDelta   (KERNEL.310)
660  */
661 WORD LocalHandleDelta( WORD delta )
662 {
663 }
664
665
666 /***********************************************************************
667  *           LocalInfo   (TOOLHELP.56)
668  */
669 BOOL LocalInfo( LOCALINFO *pLocalInfo, HGLOBAL handle )
670 {
671     LOCALHEAPINFO *pInfo = LOCAL_GetHeap(SELECTOROF(WIN16_GlobalLock(handle)));
672     if (!pInfo) return FALSE;
673     pLocalInfo->wcItems = pInfo->items;
674     return TRUE;
675 }
676
677
678 /***********************************************************************
679  *           LocalFirst   (TOOLHELP.57)
680  */
681 BOOL LocalFirst( LOCALENTRY *pLocalEntry, HGLOBAL handle )
682 {
683     WORD ds = SELECTOROF( WIN16_GlobalLock( handle ) );
684     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
685     LOCALHEAPINFO *pInfo = LOCAL_GetHeap( ds );
686     if (!pInfo) return FALSE;
687
688     pLocalEntry->hHandle   = pInfo->first + ARENA_HEADER_SIZE;
689     pLocalEntry->wAddress  = pLocalEntry->hHandle;
690     pLocalEntry->wFlags    = LF_FIXED;
691     pLocalEntry->wcLock    = 0;
692     pLocalEntry->wType     = LT_NORMAL;
693     pLocalEntry->hHeap     = handle;
694     pLocalEntry->wHeapType = NORMAL_HEAP;
695     pLocalEntry->wNext     = ARENA_PTR(ptr,pInfo->first)->next;
696     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
697     return TRUE;
698 }
699
700
701 /***********************************************************************
702  *           LocalNext   (TOOLHELP.58)
703  */
704 BOOL LocalNext( LOCALENTRY *pLocalEntry )
705 {
706     WORD ds = SELECTOROF( pLocalEntry->hHeap );
707     char *ptr = PTR_SEG_OFF_TO_LIN( ds, 0 );
708     LOCALARENA *pArena;
709
710     if (!LOCAL_GetHeap( ds )) return FALSE;
711     if (!pLocalEntry->wNext) return FALSE;
712     pArena = ARENA_PTR( ptr, pLocalEntry->wNext );
713
714     pLocalEntry->hHandle   = pLocalEntry->wNext + ARENA_HEADER_SIZE;
715     pLocalEntry->wAddress  = pLocalEntry->hHandle;
716     pLocalEntry->wFlags    = (pArena->prev & 3) + 1;
717     pLocalEntry->wcLock    = 0;
718     pLocalEntry->wType     = LT_NORMAL;
719     if (pArena->next != pLocalEntry->wNext)  /* last one? */
720         pLocalEntry->wNext = pArena->next;
721     else
722         pLocalEntry->wNext = 0;
723     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
724     return TRUE;
725 }