Release 0.3.0
[wine] / memory / global.c
1 static char RCSId[] = "$Id: global.c,v 1.2 1993/07/04 04:04:21 root Exp root $";
2 static char Copyright[] = "Copyright  Robert J. Amstadt, 1993";
3
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include "prototypes.h"
7 #include "heap.h"
8 #include "segmem.h"
9
10 /*
11  * Global memory pool descriptor.  Segments MUST be maintained in segment
12  * ascending order.  If not the reallocation routine will die a horrible
13  * death.
14  *
15  * handle  = 0, this descriptor contains the address of a free pool.
16  *        != 0, this describes an allocated block.
17  *
18  * sequence = 0, this is not a huge block
19  *          > 0, this is a portion of a huge block
20  *          =-1, this is a free segment
21  *
22  * addr       - address of this memory block.
23  *
24  * length     - used to maintain huge blocks.
25  *
26  */
27 typedef struct global_mem_desc_s
28 {
29     struct global_mem_desc_s *next;
30     struct global_mem_desc_s *prev;
31     unsigned short handle;
32     short sequence;
33     void *addr;
34     int length;
35     int lock_count;
36 } GDESC;
37
38 GDESC *GlobalList = NULL;
39 static unsigned short next_unused_handle = 1;
40
41 \f
42 /**********************************************************************
43  *                                      GlobalGetFreeSegments
44  */
45 GDESC *
46 GlobalGetFreeSegments(unsigned int flags, int n_segments)
47 {
48     struct segment_descriptor_s *s;
49     GDESC *g;
50     GDESC *g_start;
51     GDESC *g_prev;
52     int count, i;
53
54     /*
55      * Try to find some empty segments in our list.
56      */
57     count = 0;
58     for (g = GlobalList; g != NULL && count != n_segments; g = g->next)
59     {
60         if ((int) g->sequence == -1)
61         {
62             if (count > 0)
63             {
64                 if (g->prev->handle + 8 != g->handle)
65                     count = 0;
66                 else
67                     count++;
68             }
69             else
70             {
71                 g_start = g;
72                 count = 1;
73             }
74         }
75         else if (count)
76             count = 0;
77     }
78
79     /*
80      * If we couldn't find enough segments, then we need to create some.
81      */
82     if (count != n_segments)
83     {
84         /*
85          * Find list tail.
86          */
87         g_prev = NULL;
88         for (g = GlobalList; g != NULL; g = g->next)
89             g_prev = g;
90
91         /*
92          * Allocate segments.
93          */
94         for (count = 0; count < n_segments; count++)
95         {
96             s = GetNextSegment(flags, 0x10000);
97             if (s == NULL)
98                 return NULL;
99
100             g = (GDESC *) malloc(sizeof(*g));
101             
102             g->prev = g_prev;
103             g->next = NULL;
104             g->handle = s->selector;
105             g->sequence = -1;
106             g->addr = s->base_addr;
107             g->length = s->length;
108             if (!(flags & GLOBAL_FLAGS_MOVEABLE))
109                 g->lock_count = 1;
110             else
111                 g->lock_count = 0;
112             
113             free(s);
114
115             if (count == 0)
116                 g_start = g;
117
118             if (g_prev != NULL)
119             {
120                 g_prev->next = g;
121                 g->prev = g_prev;
122             }
123             else
124                 GlobalList = g;
125         }
126     }
127
128     /*
129      * We have all of the segments we need.  Let's adjust their contents.
130      */
131     g = g_start;
132     for (i = 0; i < n_segments; i++, g = g->next)
133     {
134         g->sequence = i + 1;
135         g->length = n_segments;
136     }
137
138     return g_start;
139 }
140 \f
141 /**********************************************************************
142  *                                      GlobalAlloc
143  */
144 unsigned int
145 GlobalAlloc(unsigned int flags, unsigned long size)
146 {
147     GDESC *g;
148     GDESC *g_prev;
149     void *m;
150
151     /*
152      * If this block is fixed or very big we need to allocate entire
153      * segments.
154      */
155     if (size > 0x8000 || !(flags & GLOBAL_FLAGS_MOVEABLE))
156     {
157         int segments = (size >> 16) + 1;
158
159         g = GlobalGetFreeSegments(flags, segments);
160         if (g == NULL)
161             return 0;
162         else
163             return g->handle;
164     }
165     /*
166      * Otherwise we just need a little piece of a segment.
167      */
168     else
169     {
170         /*
171          * Try to allocate from active free lists.
172          */
173         for (g = GlobalList; g != NULL; g = g->next)
174         {
175             if (g->handle == 0 && g->sequence == 0)
176             {
177                 m = HEAP_Alloc((MDESC **) g->addr, 0, size);
178                 if (m != NULL)
179                     break;
180             }
181         }
182
183         /*
184          * If we couldn't get the memory there, then we need to create
185          * a new free list.
186          */
187         if (g == NULL)
188         {
189             g = GlobalGetFreeSegments(0, 1);
190             if (g == NULL)
191                 return 0;
192
193             g->handle = 0;
194             g->sequence = 0;
195             HEAP_Init((MDESC **) g->addr, (MDESC **) g->addr + 1, 
196                       0x10000 - sizeof(MDESC **));
197             m = HEAP_Alloc((MDESC **) g->addr, flags & GLOBAL_FLAGS_ZEROINIT, 
198                            size);
199             if (m == NULL)
200                 return 0;
201         }
202
203         /*
204          * Save position of heap descriptor.
205          */
206         g_prev = g;
207
208         /*
209          * We have a new block.  Let's create a GDESC entry for it.
210          */
211         g = malloc(sizeof(*g));
212 #ifdef DEBUG_HEAP
213         printf("New GDESC %08x\n", g);
214 #endif
215         if (g == NULL)
216             return 0;
217
218         g->handle = next_unused_handle;
219         g->sequence = 0;
220         g->addr = m;
221         g->length = size;
222         g->next = g_prev->next;
223         if (g->next) g->next->prev = g;
224         g->lock_count = 0;
225
226         g_prev->next = g;
227         g->prev = g_prev;
228
229         next_unused_handle++;
230         if ((next_unused_handle & 7) == 7)
231             next_unused_handle++;
232         
233 #ifdef DEBUG_HEAP
234         printf("GlobalAlloc: returning %04x\n", g->handle);
235 #endif
236         return g->handle;
237     }
238 }
239 \f
240 /**********************************************************************
241  *                                      GlobalFree
242  *
243  * Windows programs will pass a handle in the "block" parameter, but
244  * this function will also accept a 32-bit address.
245  */
246 unsigned int
247 GlobalFree(unsigned int block)
248 {
249     GDESC *g;
250
251     if (block == 0)
252         return 0;
253
254     /*
255      * Find GDESC for this block.
256      */
257     if (block & 0xffff0000)
258     {
259         for (g = GlobalList; g != NULL; g = g->next)
260             if (g->handle > 0 && (unsigned int) g->addr == block)
261                 break;
262     }
263     else
264     {
265         for (g = GlobalList; g != NULL; g = g->next)
266             if (g->handle == block)
267                 break;
268     }
269     if (g == NULL)
270         return block;
271
272     /*
273      * If the sequence number is zero then use HEAP_Free to deallocate
274      * memory, and throw away this descriptor.
275      */
276     if (g->sequence == 0)
277     {
278         HEAP_Free((MDESC **) ((int) g->addr & 0xffff0000), (void *) g->addr);
279
280         g->prev->next = g->next;
281         
282         if (g->next != NULL)
283             g->next->prev = g->prev;
284
285         free(g);
286     }
287     
288     /*
289      * Otherwise just mark these descriptors as free.
290      */
291     else
292     {
293         int i, limit;
294         
295         limit = g->length;
296         for (i = g->sequence - 1; i < limit && g != NULL; i++, g = g->next)
297         {
298             g->sequence = -1;
299             g->length = 0x10000;
300         }
301     }
302
303     return 0;
304 }
305 \f
306 /**********************************************************************
307  *                                      GlobalLock
308  *
309  */
310 void *
311 GlobalLock(unsigned int block)
312 {
313     GDESC *g;
314
315     if (block == 0)
316         return 0;
317
318     /*
319      * Find GDESC for this block.
320      */
321     for (g = GlobalList; g != NULL; g = g->next)
322     {
323         if (g->handle == block)
324         {
325             g->lock_count++;
326 #ifdef DEBUG_HEAP
327             printf("GlobalLock: returning %08x\n", g->addr);
328 #endif
329             return g->addr;
330         }
331     }
332
333 #ifdef DEBUG_HEAP
334     printf("GlobalLock: returning %08x\n", 0);
335 #endif
336     return NULL;
337 }
338 \f
339 /**********************************************************************
340  *                                      GlobalUnlock
341  *
342  */
343 int
344 GlobalUnlock(unsigned int block)
345 {
346     GDESC *g;
347
348     if (block == 0)
349         return 0;
350
351     /*
352      * Find GDESC for this block.
353      */
354     for (g = GlobalList; g != NULL; g = g->next)
355     {
356         if (g->handle == block && g->lock_count > 0)
357         {
358             g->lock_count--;
359             return 0;
360         }
361     }
362
363     return 1;
364 }
365 \f
366 /**********************************************************************
367  *                                      GlobalFlags
368  *
369  */
370 unsigned int
371 GlobalFlags(unsigned int block)
372 {
373     GDESC *g;
374
375     if (block == 0)
376         return 0;
377
378     /*
379      * Find GDESC for this block.
380      */
381     for (g = GlobalList; g != NULL; g = g->next)
382     {
383         if (g->handle == block)
384             return g->lock_count;
385     }
386
387     return 0;
388 }
389 \f
390 /**********************************************************************
391  *                                      GlobalSize
392  *
393  */
394 unsigned int
395 GlobalSize(unsigned int block)
396 {
397     GDESC *g;
398
399     if (block == 0)
400         return 0;
401
402     /*
403      * Find GDESC for this block.
404      */
405     for (g = GlobalList; g != NULL; g = g->next)
406     {
407         if (g->handle == block)
408             return g->length;
409     }
410
411     return 0;
412 }
413 \f
414 /**********************************************************************
415  *                                      GlobalHandle
416  *
417  * This routine is not strictly correct.  MS Windows creates a selector
418  * for every locked global block.  We do not.  If the allocation is small
419  * enough, we only give out a little piece of a selector.  Thus this
420  * function cannot be implemented.
421  */
422 unsigned int
423 GlobalHandle(unsigned int selector)
424 {
425     GDESC *g;
426
427     if (selector == 0)
428         return 0;
429
430     /*
431      * Find GDESC for this block.
432      */
433     for (g = GlobalList; g != NULL; g = g->next)
434     {
435         if (g->handle == selector)
436         {
437             if (g->sequence > 0)
438                 return g->handle;
439             else
440             {
441                 fprintf(stderr, "Attempt to get a handle "
442                         "from a selector to a far heap.\n");
443                 return 0;
444             }
445         }
446     }
447
448     return 0;
449 }
450 \f
451 /**********************************************************************
452  *                                      GlobalCompact
453  *
454  */
455 unsigned int
456 GlobalCompact(unsigned int desired)
457 {
458     GDESC *g;
459     unsigned char free_map[512];
460     unsigned int max_selector_used = 0;
461     unsigned int i;
462     unsigned int selector;
463     int current_free;
464     int max_free;
465
466     /*
467      * Initialize free list to all items not controlled by GlobalAlloc()
468      */
469     for (i = 0; i < 512; i++)
470         free_map[i] = -1;
471
472     /*
473      * Traverse table looking for used and free selectors.
474      */
475     for (g = GlobalList; g != NULL; g = g->next)
476     {
477         /*
478          * Check for free segments.
479          */
480         if (g->sequence == -1)
481         {
482             free_map[g->handle >> 3] = 1;
483             if (g->handle > max_selector_used)
484                 max_selector_used = g->handle;
485         }
486
487         /*
488          * Check for heap allocated segments.
489          */
490         else if (g->handle == 0)
491         {
492             selector = (unsigned int) g->addr >> 16;
493             free_map[selector >> 3] = 0;
494             if (selector > max_selector_used)
495                 max_selector_used = selector;
496         }
497     }
498
499     /*
500      * All segments past the biggest selector used are free.
501      */
502     for (i = (max_selector_used >> 3) + 1; i < 512; i++)
503         free_map[i] = 1;
504
505     /*
506      * Find the largest free block of segments
507      */
508     current_free = 0;
509     max_free = 0;
510     for (i = 0; i < 512; i++)
511     {
512         if (free_map[i] == 1)
513         {
514             current_free++;
515         }
516         else
517         {
518             if (current_free > max_free)
519                 max_free = current_free;
520             current_free = 0;
521         }
522     }
523     
524     return max_free << 16;
525 }
526 \f
527 /**********************************************************************
528  *                                      GlobalReAlloc
529  *
530  */
531 unsigned int
532 GlobalReAlloc(unsigned int block, unsigned int new_size, unsigned int flags)
533 {
534     GDESC *g;
535     unsigned int n_segments;
536     int i;
537
538     if (block == 0)
539         return 0;
540
541     /*
542      * Find GDESC for this block.
543      */
544     for (g = GlobalList; g != NULL; g = g->next)
545     {
546         if (g->handle == block)
547             break;
548     }
549
550     if (g == NULL)
551         return 0;
552     
553     /*
554      * If this is a heap allocated block,  then use HEAP_ReAlloc() to
555      * reallocate the block.  If this fails, call GlobalAlloc() to get
556      * a new block.
557      */
558     if (g->sequence = 0)
559     {
560         MDESC **free_list;
561         void *p;
562         
563         free_list = (MDESC **) ((unsigned int) g->addr & 0xffff0000);
564         p = HEAP_ReAlloc(free_list, g->addr, new_size, flags) ;
565         if (p == NULL)
566         {
567             unsigned int handle = GlobalAlloc(flags, new_size);
568             if (handle == 0)
569                 return 0;
570             p = GlobalLock(handle);
571             memcpy(p, g->addr, g->length);
572             GlobalUnlock(handle);
573             GlobalFree(g->handle);
574
575             return handle;
576         }
577         else
578         {
579             g->addr = p;
580             g->length = new_size;
581             return g->handle;
582         }
583     }
584     
585     /*
586      * Otherwise, we need to do the work ourselves.  First verify the
587      * handle.
588      */
589     else
590     {
591         if (g->sequence != 1)
592             return 0;
593
594         /*
595          * Do we need more memory?  Segments are in ascending order in 
596          * the GDESC list.
597          */
598         n_segments = (new_size >> 16) + 1;
599         if (n_segments > g->length)
600         {
601             GDESC *g_new;
602             GDESC *g_start = g;
603             int old_segments = g_start->length;
604             unsigned short next_handle = g_start->handle;
605             
606             for (i = 1; i <= n_segments; i++, g = g->next)
607             {
608                 /*
609                  * If we run into a block allocated to something else,
610                  * try GlobalGetFreeSegments() and memcpy(). (Yuk!)
611                  */
612                 if (g->sequence != i || g->handle != next_handle)
613                 {
614                     g = GlobalGetFreeSegments(flags, n_segments);
615                     if (g == NULL)
616                         return 0;
617                     
618                     memcpy(g->addr, g_start->addr,
619                            g_start->length << 16);
620
621                     GlobalFree(block);
622                     return g->handle;
623                 }
624
625                 /*
626                  * Otherwise this block is used by us or free.  So,
627                  * snatch it.  If this block is new and we are supposed to
628                  * zero init, then do some erasing.
629                  */
630                 if (g->sequence == -1 && (flags & GLOBAL_FLAGS_ZEROINIT))
631                     memset(g->addr, 0, 0x10000);
632                 
633                 g->sequence = i;
634                 g->length = n_segments;
635                 next_handle += 8;
636
637                 /*
638                  * If the next descriptor is non-existant, then use
639                  * GlobalGetFreeSegments to create them.
640                  */
641                 if (i != n_segments && g->next == NULL)
642                 {
643                     g_new = GlobalGetFreeSegments(flags, n_segments - i);
644                     if (g_new == NULL)
645                         return 0;
646                     GlobalFree(g_new->handle);
647                 }
648             }
649
650             return g_start->handle;
651         }
652         
653         /*
654          * Do we need less memory?
655          */
656         else if (n_segments < g->length)
657         {
658             GDESC *g_free;
659             
660             g_free = g;
661             for (i = 0; i < n_segments; i++)
662             {
663                 if (g_free->sequence != i + 1)
664                     return 0;
665                 g_free = g_free->next;
666             }
667         }
668         
669         /*
670          * We already have exactly the right amount of memory.
671          */
672         else
673             return block;
674     }
675
676     /*
677      * If we fall through it must be an error.
678      */
679     return 0;
680 }
681 \f
682 /**********************************************************************
683  *                                      GlobalQuickAlloc
684  */
685 void *
686 GlobalQuickAlloc(int size)
687 {
688     unsigned int hmem;
689     
690     hmem = GlobalAlloc(GLOBAL_FLAGS_MOVEABLE, size);
691     if (hmem == 0)
692         return NULL;
693     else
694         return GlobalLock(hmem);
695 }
696 \f
697 /**********************************************************************
698  *                                      GlobalHandleFromPointer
699
700  */
701 unsigned int
702 GlobalHandleFromPointer(void *block)
703 {
704     GDESC *g;
705
706     if (block == NULL)
707         return 0;
708
709     /*
710      * Find GDESC for this block.
711      */
712     for (g = GlobalList; g != NULL; g = g->next)
713         if (g->handle > 0 && g->addr == block)
714             break;
715
716     if (g == NULL)
717         return 0;
718     else
719         return g->handle;
720 }
721
722
723