Release 940510
[wine/multimedia.git] / memory / global.c
blob29717e07c1b670dc73ba6105caf6e9a68d322a7d
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";
4 #define GLOBAL_SOURCE
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include "prototypes.h"
10 #include "heap.h"
11 #include "segmem.h"
13 GDESC *GlobalList = NULL;
14 static unsigned short next_unused_handle = 1;
16 /**********************************************************************
17 * GlobalGetGDesc
19 GDESC *GlobalGetGDesc(unsigned int block)
21 GDESC *g;
23 if (block == 0)
24 return NULL;
27 * Find GDESC for this block.
29 if (block & 0xffff0000)
31 for (g = GlobalList; g != NULL; g = g->next)
32 if (g->handle > 0 && (unsigned int) g->addr == block)
33 break;
35 else
37 for (g = GlobalList; g != NULL; g = g->next)
38 if (g->handle == block)
39 break;
42 return g;
45 /**********************************************************************
46 * GlobalGetFreeSegments
48 GDESC *
49 GlobalGetFreeSegments(unsigned int flags, int n_segments)
51 struct segment_descriptor_s *s;
52 GDESC *g;
53 GDESC *g_start;
54 GDESC *g_prev;
55 int count, i;
58 * Try to find some empty segments in our list.
60 count = 0;
61 for (g = GlobalList; g != NULL && count != n_segments; g = g->next)
63 if ((int) g->sequence == -1)
65 if (count > 0)
67 if (g->prev->handle + 8 != g->handle)
68 count = 0;
69 else
70 count++;
72 else
74 g_start = g;
75 count = 1;
78 else if (count)
79 count = 0;
83 * If we couldn't find enough segments, then we need to create some.
85 if (count != n_segments)
88 * Find list tail.
90 g_prev = NULL;
91 for (g = GlobalList; g != NULL; g = g->next)
92 g_prev = g;
95 * Allocate segments.
97 s = CreateNewSegments(0, 0, 0x10000, n_segments);
98 if (s == NULL)
100 printf("GlobalGetFreeSegments // bad CreateNewSegments !\n");
101 return NULL;
103 for (count = 0; count < n_segments; count++, s++)
105 g = (GDESC *) malloc(sizeof(*g));
106 if (g == NULL) {
107 printf("GlobalGetFreeSegments // bad GDESC malloc !\n");
108 return NULL;
110 g->prev = g_prev;
111 g->next = NULL;
112 g->handle = s->selector;
113 g->sequence = -1;
114 g->addr = s->base_addr;
115 g->length = s->length;
116 g->linear_addr = NULL;
117 g->linear_key = 0;
118 g->linear_count = 0;
119 if (!(flags & GLOBAL_FLAGS_MOVEABLE))
120 g->lock_count = 1;
121 else
122 g->lock_count = 0;
124 if (count == 0) g_start = g;
126 if (g_prev != NULL)
128 g_prev->next = g;
130 else
131 GlobalList = g;
132 g_prev = g;
137 * We have all of the segments we need. Let's adjust their contents.
139 g = g_start;
140 for (i = 0; i < n_segments; i++, g = g->next)
142 if (g == NULL) {
143 printf("GlobalGetFreeSegments // bad Segments chain !\n");
144 return NULL;
146 g->sequence = i + 1;
147 g->length = n_segments;
148 g->linear_addr = NULL;
149 g->linear_key = 0;
150 g->linear_count = 0;
153 return g_start;
156 /**********************************************************************
157 * GlobalAlloc
159 HANDLE
160 GlobalAlloc(unsigned int flags, unsigned long size)
162 GDESC *g;
163 GDESC *g_prev;
164 void *m;
166 #ifdef DEBUG_HEAP
167 printf("GlobalAlloc flags %4X, size %d\n", flags, size);
168 #endif
171 * If this block is fixed or very big we need to allocate entire
172 * segments.
174 if (size > 0x8000 || !(flags & GLOBAL_FLAGS_MOVEABLE))
176 int segments = (size >> 16) + 1;
178 g = GlobalGetFreeSegments(flags, segments);
179 if (g == NULL)
180 return 0;
181 else
182 return g->handle;
185 * Otherwise we just need a little piece of a segment.
187 else
190 * Try to allocate from active free lists.
192 for (g = GlobalList; g != NULL; g = g->next)
194 if (g->handle == 0 && g->sequence == 0)
196 m = HEAP_Alloc((MDESC **) g->addr, 0, size);
197 if (m != NULL)
198 break;
203 * If we couldn't get the memory there, then we need to create
204 * a new free list.
206 if (g == NULL)
208 g = GlobalGetFreeSegments(0, 1);
209 if (g == NULL)
210 return 0;
212 g->handle = 0;
213 g->sequence = 0;
214 HEAP_Init((MDESC **) g->addr, (MDESC **) g->addr + 1,
215 0x10000 - sizeof(MDESC **));
216 m = HEAP_Alloc((MDESC **) g->addr, flags & GLOBAL_FLAGS_ZEROINIT,
217 size);
218 if (m == NULL)
219 return 0;
223 * Save position of heap descriptor.
225 g_prev = g;
228 * We have a new block. Let's create a GDESC entry for it.
230 g = malloc(sizeof(*g));
231 #ifdef DEBUG_HEAP
232 printf("New GDESC %08x\n", g);
233 #endif
234 if (g == NULL)
235 return 0;
237 g->handle = next_unused_handle;
238 g->sequence = 0;
239 g->addr = m;
240 g->linear_addr = NULL;
241 g->linear_key = 0;
242 g->linear_count = 0;
243 g->length = size;
244 g->next = g_prev->next;
245 if (g->next) g->next->prev = g;
246 g->lock_count = 0;
248 g_prev->next = g;
249 g->prev = g_prev;
251 next_unused_handle++;
252 if ((next_unused_handle & 7) == 7)
253 next_unused_handle++;
255 #ifdef DEBUG_HEAP
256 printf("GlobalAlloc: returning %04x\n", g->handle);
257 #endif
258 return g->handle;
262 /**********************************************************************
263 * GlobalFree
265 * Windows programs will pass a handle in the "block" parameter, but
266 * this function will also accept a 32-bit address.
268 HANDLE
269 GlobalFree(unsigned int block)
271 GDESC *g;
273 if (block == 0)
274 return 0;
277 * Find GDESC for this block.
279 g = GlobalGetGDesc(block);
280 if (g == NULL)
281 return block;
284 * If the sequence number is zero then use HEAP_Free to deallocate
285 * memory, and throw away this descriptor.
287 if (g->sequence == 0)
289 HEAP_Free((MDESC **) ((int) g->addr & 0xffff0000), (void *) g->addr);
291 g->prev->next = g->next;
293 if (g->next != NULL)
294 g->next->prev = g->prev;
296 free(g);
300 * Otherwise just mark these descriptors as free.
302 else
304 int i, limit;
306 limit = g->length;
307 for (i = g->sequence - 1; i < limit && g != NULL; i++, g = g->next)
309 g->sequence = -1;
310 g->length = 0x10000;
314 return 0;
317 /**********************************************************************
318 * GlobalLock
321 void *
322 GlobalLock(unsigned int block)
324 GDESC *g;
326 if (block == 0)
327 return 0;
330 * Find GDESC for this block.
332 for (g = GlobalList; g != NULL; g = g->next)
334 if (g->handle == block)
336 g->lock_count++;
337 #ifdef DEBUG_HEAP
338 printf("GlobalLock: returning %08x\n", g->addr);
339 #endif
340 return g->addr;
344 #ifdef DEBUG_HEAP
345 printf("GlobalLock: returning %08x\n", 0);
346 #endif
347 return NULL;
350 /**********************************************************************
351 * GlobalUnlock
355 GlobalUnlock(unsigned int block)
357 GDESC *g;
359 if (block == 0)
360 return 0;
363 * Find GDESC for this block.
365 for (g = GlobalList; g != NULL; g = g->next)
367 if (g->handle == block && g->lock_count > 0)
369 g->lock_count--;
370 return 0;
374 return 1;
377 /**********************************************************************
378 * GlobalFlags
381 unsigned int
382 GlobalFlags(unsigned int block)
384 GDESC *g;
386 if (block == 0)
387 return 0;
390 * Find GDESC for this block.
392 for (g = GlobalList; g != NULL; g = g->next)
394 if (g->handle == block)
395 return g->lock_count;
398 return 0;
401 /**********************************************************************
402 * GlobalSize
405 unsigned int
406 GlobalSize(unsigned int block)
408 GDESC *g;
410 if (block == 0)
411 return 0;
414 * Find GDESC for this block.
416 for (g = GlobalList; g != NULL; g = g->next)
418 if (g->handle == block)
419 return g->length;
422 return 0;
425 /**********************************************************************
426 * GlobalHandle
428 * This routine is not strictly correct. MS Windows creates a selector
429 * for every locked global block. We do not. If the allocation is small
430 * enough, we only give out a little piece of a selector. Thus this
431 * function cannot be implemented.
433 unsigned int
434 GlobalHandle(unsigned int selector)
436 GDESC *g;
438 if (selector == 0)
439 return 0;
442 * Find GDESC for this block.
444 for (g = GlobalList; g != NULL; g = g->next)
446 if (g->handle == selector)
448 if (g->sequence > 0)
449 return g->handle;
450 else
452 fprintf(stderr, "Attempt to get a handle "
453 "from a selector to a far heap.\n");
454 return 0;
459 return 0;
462 /**********************************************************************
463 * GlobalCompact
466 unsigned int
467 GlobalCompact(unsigned int desired)
469 GDESC *g;
470 unsigned char free_map[512];
471 unsigned int max_selector_used = 0;
472 unsigned int i;
473 unsigned int selector;
474 int current_free;
475 int max_free;
478 * Initialize free list to all items not controlled by GlobalAlloc()
480 for (i = 0; i < 512; i++)
481 free_map[i] = -1;
484 * Traverse table looking for used and free selectors.
486 for (g = GlobalList; g != NULL; g = g->next)
489 * Check for free segments.
491 if (g->sequence == -1)
493 free_map[g->handle >> 3] = 1;
494 if (g->handle > max_selector_used)
495 max_selector_used = g->handle;
499 * Check for heap allocated segments.
501 else if (g->handle == 0)
503 selector = (unsigned int) g->addr >> 16;
504 free_map[selector >> 3] = 0;
505 if (selector > max_selector_used)
506 max_selector_used = selector;
511 * All segments past the biggest selector used are free.
513 for (i = (max_selector_used >> 3) + 1; i < 512; i++)
514 free_map[i] = 1;
517 * Find the largest free block of segments
519 current_free = 0;
520 max_free = 0;
521 for (i = 0; i < 512; i++)
523 if (free_map[i] == 1)
525 current_free++;
527 else
529 if (current_free > max_free)
530 max_free = current_free;
531 current_free = 0;
535 return max_free << 16;
538 /**********************************************************************
539 * GlobalReAlloc
542 unsigned int
543 GlobalReAlloc(unsigned int block, unsigned int new_size, unsigned int flags)
545 GDESC *g;
546 unsigned int n_segments;
547 int i;
549 if (block == 0)
550 return 0;
553 * Find GDESC for this block.
555 g = GlobalGetGDesc(block);
556 if (g == NULL)
557 return 0;
560 * If this is a heap allocated block, then use HEAP_ReAlloc() to
561 * reallocate the block. If this fails, call GlobalAlloc() to get
562 * a new block.
564 if (g->sequence == 0)
566 MDESC **free_list;
567 void *p;
569 free_list = (MDESC **) ((unsigned int) g->addr & 0xffff0000);
570 p = HEAP_ReAlloc(free_list, g->addr, new_size, flags) ;
571 if (p == NULL)
573 unsigned int handle = GlobalAlloc(flags, new_size);
574 if (handle == 0)
575 return 0;
576 p = GlobalLock(handle);
577 memcpy(p, g->addr, g->length);
578 GlobalUnlock(handle);
579 GlobalFree(g->handle);
581 return handle;
583 else
585 g->addr = p;
586 g->length = new_size;
587 return g->handle;
592 * Otherwise, we need to do the work ourselves. First verify the
593 * handle.
595 else
597 if (g->sequence != 1)
598 return 0;
601 * Do we need more memory? Segments are in ascending order in
602 * the GDESC list.
604 n_segments = (new_size >> 16) + 1;
605 if (n_segments > g->length)
607 GDESC *g_new;
608 GDESC *g_start = g;
609 int old_segments = g_start->length;
610 unsigned short next_handle = g_start->handle;
612 for (i = 1; i <= n_segments; i++, g = g->next)
615 * If we run into a block allocated to something else,
616 * try GlobalGetFreeSegments() and memcpy(). (Yuk!)
618 if (g->sequence != i || g->handle != next_handle)
620 g = GlobalGetFreeSegments(flags, n_segments);
621 if (g == NULL)
622 return 0;
624 memcpy(g->addr, g_start->addr,
625 g_start->length << 16);
627 GlobalFree(block);
628 return g->handle;
632 * Otherwise this block is used by us or free. So,
633 * snatch it. If this block is new and we are supposed to
634 * zero init, then do some erasing.
636 if (g->sequence == -1 && (flags & GLOBAL_FLAGS_ZEROINIT))
637 memset(g->addr, 0, 0x10000);
639 g->sequence = i;
640 g->length = n_segments;
641 next_handle += 8;
644 * If the next descriptor is non-existant, then use
645 * GlobalGetFreeSegments to create them.
647 if (i != n_segments && g->next == NULL)
649 g_new = GlobalGetFreeSegments(flags, n_segments - i);
650 if (g_new == NULL)
651 return 0;
652 GlobalFree(g_new->handle);
656 return g_start->handle;
660 * Do we need less memory?
662 else if (n_segments < g->length)
664 GDESC *g_free;
666 g_free = g;
667 for (i = 0; i < n_segments; i++)
669 if (g_free->sequence != i + 1)
670 return 0;
671 g_free = g_free->next;
676 * We already have exactly the right amount of memory.
678 else
679 return block;
683 * If we fall through it must be an error.
685 return 0;
688 /**********************************************************************
689 * GlobalQuickAlloc
691 void *
692 GlobalQuickAlloc(int size)
694 unsigned int hmem;
696 hmem = GlobalAlloc(GLOBAL_FLAGS_MOVEABLE, size);
697 if (hmem == 0)
698 return NULL;
699 else
700 return GlobalLock(hmem);
703 /**********************************************************************
704 * GlobalHandleFromPointer
707 unsigned int
708 GlobalHandleFromPointer(void *block)
710 GDESC *g;
712 if (block == NULL)
713 return 0;
716 * Find GDESC for this block.
718 for (g = GlobalList; g != NULL; g = g->next)
719 if (g->handle > 0 && g->addr == block)
720 break;
722 if (g == NULL)
723 return 0;
724 else
725 return g->handle;
728 /**********************************************************************
729 * GetFreeSpace (kernel.169)
732 DWORD GetFreeSpace(UINT wFlags)
733 /* windows 3.1 doesn't use the wFlags parameter !!
734 (so I won't either) */
736 GDESC *g;
737 unsigned char free_map[512];
738 unsigned int max_selector_used = 0;
739 unsigned int i;
740 unsigned int selector;
741 int total_free;
744 * Initialize free list to all items not controlled by GlobalAlloc()
746 for (i = 0; i < 512; i++)
747 free_map[i] = -1;
750 * Traverse table looking for used and free selectors.
752 for (g = GlobalList; g != NULL; g = g->next)
755 * Check for free segments.
757 if (g->sequence == -1)
759 free_map[g->handle >> 3] = 1;
760 if (g->handle > max_selector_used)
761 max_selector_used = g->handle;
765 * Check for heap allocated segments.
767 else if (g->handle == 0)
769 selector = (unsigned int) g->addr >> 16;
770 free_map[selector >> 3] = 0;
771 if (selector > max_selector_used)
772 max_selector_used = selector;
777 * All segments past the biggest selector used are free.
779 for (i = (max_selector_used >> 3) + 1; i < 512; i++)
780 free_map[i] = 1;
783 * Add up the total free segments (obviously this amount of memory
784 may not be contiguous, use GlobalCompact to get largest contiguous
785 memory available).
787 total_free=0;
788 for (i = 0; i < 512; i++)
789 if (free_map[i] == 1)
790 total_free++;
792 return total_free << 16;