Release 0.4.10
[wine/multimedia.git] / memory / global.c
blob169ebc3878aea60ab6009edb24b2c36e63a0d89a
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 #include <stdio.h>
5 #include <stdlib.h>
6 #include "prototypes.h"
7 #include "heap.h"
8 #include "segmem.h"
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.
15 * handle = 0, this descriptor contains the address of a free pool.
16 * != 0, this describes an allocated block.
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
22 * addr - address of this memory block.
24 * length - used to maintain huge blocks.
27 typedef struct global_mem_desc_s
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;
38 GDESC *GlobalList = NULL;
39 static unsigned short next_unused_handle = 1;
42 /**********************************************************************
43 * GlobalGetFreeSegments
45 GDESC *
46 GlobalGetFreeSegments(unsigned int flags, int n_segments)
48 struct segment_descriptor_s *s;
49 GDESC *g;
50 GDESC *g_start;
51 GDESC *g_prev;
52 int count, i;
55 * Try to find some empty segments in our list.
57 count = 0;
58 for (g = GlobalList; g != NULL && count != n_segments; g = g->next)
60 if ((int) g->sequence == -1)
62 if (count > 0)
64 if (g->prev->handle + 8 != g->handle)
65 count = 0;
66 else
67 count++;
69 else
71 g_start = g;
72 count = 1;
75 else if (count)
76 count = 0;
80 * If we couldn't find enough segments, then we need to create some.
82 if (count != n_segments)
85 * Find list tail.
87 g_prev = NULL;
88 for (g = GlobalList; g != NULL; g = g->next)
89 g_prev = g;
92 * Allocate segments.
94 for (count = 0; count < n_segments; count++)
96 s = GetNextSegment(flags, 0x10000);
97 if (s == NULL)
98 return NULL;
100 g = (GDESC *) malloc(sizeof(*g));
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;
113 free(s);
115 if (count == 0)
116 g_start = g;
118 if (g_prev != NULL)
120 g_prev->next = g;
121 g->prev = g_prev;
123 else
124 GlobalList = g;
129 * We have all of the segments we need. Let's adjust their contents.
131 g = g_start;
132 for (i = 0; i < n_segments; i++, g = g->next)
134 g->sequence = i + 1;
135 g->length = n_segments;
138 return g_start;
141 /**********************************************************************
142 * GlobalAlloc
144 unsigned int
145 GlobalAlloc(unsigned int flags, unsigned long size)
147 GDESC *g;
148 GDESC *g_prev;
149 void *m;
152 * If this block is fixed or very big we need to allocate entire
153 * segments.
155 if (size > 0x8000 || !(flags & GLOBAL_FLAGS_MOVEABLE))
157 int segments = (size >> 16) + 1;
159 g = GlobalGetFreeSegments(flags, segments);
160 if (g == NULL)
161 return 0;
162 else
163 return g->handle;
166 * Otherwise we just need a little piece of a segment.
168 else
171 * Try to allocate from active free lists.
173 for (g = GlobalList; g != NULL; g = g->next)
175 if (g->handle == 0 && g->sequence == 0)
177 m = HEAP_Alloc((MDESC **) g->addr, 0, size);
178 if (m != NULL)
179 break;
184 * If we couldn't get the memory there, then we need to create
185 * a new free list.
187 if (g == NULL)
189 g = GlobalGetFreeSegments(0, 1);
190 if (g == NULL)
191 return 0;
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;
204 * Save position of heap descriptor.
206 g_prev = g;
209 * We have a new block. Let's create a GDESC entry for it.
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;
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;
226 g_prev->next = g;
227 g->prev = g_prev;
229 next_unused_handle++;
230 if ((next_unused_handle & 7) == 7)
231 next_unused_handle++;
233 #ifdef DEBUG_HEAP
234 printf("GlobalAlloc: returning %04x\n", g->handle);
235 #endif
236 return g->handle;
240 /**********************************************************************
241 * GlobalFree
243 * Windows programs will pass a handle in the "block" parameter, but
244 * this function will also accept a 32-bit address.
246 unsigned int
247 GlobalFree(unsigned int block)
249 GDESC *g;
251 if (block == 0)
252 return 0;
255 * Find GDESC for this block.
257 if (block & 0xffff0000)
259 for (g = GlobalList; g != NULL; g = g->next)
260 if (g->handle > 0 && (unsigned int) g->addr == block)
261 break;
263 else
265 for (g = GlobalList; g != NULL; g = g->next)
266 if (g->handle == block)
267 break;
269 if (g == NULL)
270 return block;
273 * If the sequence number is zero then use HEAP_Free to deallocate
274 * memory, and throw away this descriptor.
276 if (g->sequence == 0)
278 HEAP_Free((MDESC **) ((int) g->addr & 0xffff0000), (void *) g->addr);
280 g->prev->next = g->next;
282 if (g->next != NULL)
283 g->next->prev = g->prev;
285 free(g);
289 * Otherwise just mark these descriptors as free.
291 else
293 int i, limit;
295 limit = g->length;
296 for (i = g->sequence - 1; i < limit && g != NULL; i++, g = g->next)
298 g->sequence = -1;
299 g->length = 0x10000;
303 return 0;
306 /**********************************************************************
307 * GlobalLock
310 void *
311 GlobalLock(unsigned int block)
313 GDESC *g;
315 if (block == 0)
316 return 0;
319 * Find GDESC for this block.
321 for (g = GlobalList; g != NULL; g = g->next)
323 if (g->handle == block)
325 g->lock_count++;
326 #ifdef DEBUG_HEAP
327 printf("GlobalLock: returning %08x\n", g->addr);
328 #endif
329 return g->addr;
333 #ifdef DEBUG_HEAP
334 printf("GlobalLock: returning %08x\n", 0);
335 #endif
336 return NULL;
339 /**********************************************************************
340 * GlobalUnlock
344 GlobalUnlock(unsigned int block)
346 GDESC *g;
348 if (block == 0)
349 return 0;
352 * Find GDESC for this block.
354 for (g = GlobalList; g != NULL; g = g->next)
356 if (g->handle == block && g->lock_count > 0)
358 g->lock_count--;
359 return 0;
363 return 1;
366 /**********************************************************************
367 * GlobalFlags
370 unsigned int
371 GlobalFlags(unsigned int block)
373 GDESC *g;
375 if (block == 0)
376 return 0;
379 * Find GDESC for this block.
381 for (g = GlobalList; g != NULL; g = g->next)
383 if (g->handle == block)
384 return g->lock_count;
387 return 0;
390 /**********************************************************************
391 * GlobalSize
394 unsigned int
395 GlobalSize(unsigned int block)
397 GDESC *g;
399 if (block == 0)
400 return 0;
403 * Find GDESC for this block.
405 for (g = GlobalList; g != NULL; g = g->next)
407 if (g->handle == block)
408 return g->length;
411 return 0;
414 /**********************************************************************
415 * GlobalHandle
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.
422 unsigned int
423 GlobalHandle(unsigned int selector)
425 GDESC *g;
427 if (selector == 0)
428 return 0;
431 * Find GDESC for this block.
433 for (g = GlobalList; g != NULL; g = g->next)
435 if (g->handle == selector)
437 if (g->sequence > 0)
438 return g->handle;
439 else
441 fprintf(stderr, "Attempt to get a handle "
442 "from a selector to a far heap.\n");
443 return 0;
448 return 0;
451 /**********************************************************************
452 * GlobalCompact
455 unsigned int
456 GlobalCompact(unsigned int desired)
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;
467 * Initialize free list to all items not controlled by GlobalAlloc()
469 for (i = 0; i < 512; i++)
470 free_map[i] = -1;
473 * Traverse table looking for used and free selectors.
475 for (g = GlobalList; g != NULL; g = g->next)
478 * Check for free segments.
480 if (g->sequence == -1)
482 free_map[g->handle >> 3] = 1;
483 if (g->handle > max_selector_used)
484 max_selector_used = g->handle;
488 * Check for heap allocated segments.
490 else if (g->handle == 0)
492 selector = (unsigned int) g->addr >> 16;
493 free_map[selector >> 3] = 0;
494 if (selector > max_selector_used)
495 max_selector_used = selector;
500 * All segments past the biggest selector used are free.
502 for (i = (max_selector_used >> 3) + 1; i < 512; i++)
503 free_map[i] = 1;
506 * Find the largest free block of segments
508 current_free = 0;
509 max_free = 0;
510 for (i = 0; i < 512; i++)
512 if (free_map[i] == 1)
514 current_free++;
516 else
518 if (current_free > max_free)
519 max_free = current_free;
520 current_free = 0;
524 return max_free << 16;
527 /**********************************************************************
528 * GlobalReAlloc
531 unsigned int
532 GlobalReAlloc(unsigned int block, unsigned int new_size, unsigned int flags)
534 GDESC *g;
535 unsigned int n_segments;
536 int i;
538 if (block == 0)
539 return 0;
542 * Find GDESC for this block.
544 for (g = GlobalList; g != NULL; g = g->next)
546 if (g->handle == block)
547 break;
550 if (g == NULL)
551 return 0;
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.
558 if (g->sequence == 0)
560 MDESC **free_list;
561 void *p;
563 free_list = (MDESC **) ((unsigned int) g->addr & 0xffff0000);
564 p = HEAP_ReAlloc(free_list, g->addr, new_size, flags) ;
565 if (p == NULL)
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);
575 return handle;
577 else
579 g->addr = p;
580 g->length = new_size;
581 return g->handle;
586 * Otherwise, we need to do the work ourselves. First verify the
587 * handle.
589 else
591 if (g->sequence != 1)
592 return 0;
595 * Do we need more memory? Segments are in ascending order in
596 * the GDESC list.
598 n_segments = (new_size >> 16) + 1;
599 if (n_segments > g->length)
601 GDESC *g_new;
602 GDESC *g_start = g;
603 int old_segments = g_start->length;
604 unsigned short next_handle = g_start->handle;
606 for (i = 1; i <= n_segments; i++, g = g->next)
609 * If we run into a block allocated to something else,
610 * try GlobalGetFreeSegments() and memcpy(). (Yuk!)
612 if (g->sequence != i || g->handle != next_handle)
614 g = GlobalGetFreeSegments(flags, n_segments);
615 if (g == NULL)
616 return 0;
618 memcpy(g->addr, g_start->addr,
619 g_start->length << 16);
621 GlobalFree(block);
622 return g->handle;
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.
630 if (g->sequence == -1 && (flags & GLOBAL_FLAGS_ZEROINIT))
631 memset(g->addr, 0, 0x10000);
633 g->sequence = i;
634 g->length = n_segments;
635 next_handle += 8;
638 * If the next descriptor is non-existant, then use
639 * GlobalGetFreeSegments to create them.
641 if (i != n_segments && g->next == NULL)
643 g_new = GlobalGetFreeSegments(flags, n_segments - i);
644 if (g_new == NULL)
645 return 0;
646 GlobalFree(g_new->handle);
650 return g_start->handle;
654 * Do we need less memory?
656 else if (n_segments < g->length)
658 GDESC *g_free;
660 g_free = g;
661 for (i = 0; i < n_segments; i++)
663 if (g_free->sequence != i + 1)
664 return 0;
665 g_free = g_free->next;
670 * We already have exactly the right amount of memory.
672 else
673 return block;
677 * If we fall through it must be an error.
679 return 0;
682 /**********************************************************************
683 * GlobalQuickAlloc
685 void *
686 GlobalQuickAlloc(int size)
688 unsigned int hmem;
690 hmem = GlobalAlloc(GLOBAL_FLAGS_MOVEABLE, size);
691 if (hmem == 0)
692 return NULL;
693 else
694 return GlobalLock(hmem);
697 /**********************************************************************
698 * GlobalHandleFromPointer
701 unsigned int
702 GlobalHandleFromPointer(void *block)
704 GDESC *g;
706 if (block == NULL)
707 return 0;
710 * Find GDESC for this block.
712 for (g = GlobalList; g != NULL; g = g->next)
713 if (g->handle > 0 && g->addr == block)
714 break;
716 if (g == NULL)
717 return 0;
718 else
719 return g->handle;