Release 0.2.0
[wine/hacks.git] / global.c
blob0bef480ee1fd4c36d06c13352c2290efb8944163
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 g->lock_count = 0;
225 g_prev->next = g;
226 g->prev = g_prev;
228 next_unused_handle++;
229 if ((next_unused_handle & 7) == 7)
230 next_unused_handle++;
232 #ifdef DEBUG_HEAP
233 printf("GlobalAlloc: returning %04x\n", g->handle);
234 #endif
235 return g->handle;
239 /**********************************************************************
240 * GlobalFree
242 * Windows programs will pass a handle in the "block" parameter, but
243 * this function will also accept a 32-bit address.
245 unsigned int
246 GlobalFree(unsigned int block)
248 GDESC *g;
250 if (block == 0)
251 return 0;
254 * Find GDESC for this block.
256 if (block & 0xffff0000)
258 for (g = GlobalList; g != NULL; g = g->next)
259 if (g->handle > 0 && (unsigned int) g->addr == block)
260 break;
262 else
264 for (g = GlobalList; g != NULL; g = g->next)
265 if (g->handle == block)
266 break;
268 if (g == NULL)
269 return block;
272 * If the sequence number is zero then use HEAP_Free to deallocate
273 * memory, and throw away this descriptor.
275 if (g->sequence == 0)
277 HEAP_Free((MDESC **) (block & 0xffff0000), (void *) block);
279 g->prev->next = g->next;
281 if (g->next != NULL)
282 g->next->prev = g->prev;
284 free(g);
288 * Otherwise just mark these descriptors as free.
290 else
292 int i, limit;
294 limit = g->length;
295 for (i = g->sequence - 1; i < limit && g != NULL; i++, g = g->next)
297 g->sequence = -1;
298 g->length = 0x10000;
302 return 0;
305 /**********************************************************************
306 * GlobalLock
309 void *
310 GlobalLock(unsigned int block)
312 GDESC *g;
314 if (block == 0)
315 return 0;
318 * Find GDESC for this block.
320 for (g = GlobalList; g != NULL; g = g->next)
322 if (g->handle == block)
324 g->lock_count++;
325 #ifdef DEBUG_HEAP
326 printf("GlobalLock: returning %08x\n", g->addr);
327 #endif
328 return g->addr;
332 #ifdef DEBUG_HEAP
333 printf("GlobalLock: returning %08x\n", 0);
334 #endif
335 return NULL;
338 /**********************************************************************
339 * GlobalUnlock
343 GlobalUnlock(unsigned int block)
345 GDESC *g;
347 if (block == 0)
348 return 0;
351 * Find GDESC for this block.
353 for (g = GlobalList; g != NULL; g = g->next)
355 if (g->handle == block && g->lock_count > 0)
357 g->lock_count--;
358 return 0;
362 return 1;
365 /**********************************************************************
366 * GlobalFlags
369 unsigned int
370 GlobalFlags(unsigned int block)
372 GDESC *g;
374 if (block == 0)
375 return 0;
378 * Find GDESC for this block.
380 for (g = GlobalList; g != NULL; g = g->next)
382 if (g->handle == block)
383 return g->lock_count;
386 return 0;
389 /**********************************************************************
390 * GlobalSize
393 unsigned int
394 GlobalSize(unsigned int block)
396 GDESC *g;
398 if (block == 0)
399 return 0;
402 * Find GDESC for this block.
404 for (g = GlobalList; g != NULL; g = g->next)
406 if (g->handle == block)
407 return g->length;
410 return 0;
413 /**********************************************************************
414 * GlobalHandle
416 * This routine is not strictly correct. MS Windows creates a selector
417 * for every locked global block. We do not. If the allocation is small
418 * enough, we only give out a little piece of a selector. Thus this
419 * function cannot be implemented.
421 unsigned int
422 GlobalHandle(unsigned int selector)
424 GDESC *g;
426 if (selector == 0)
427 return 0;
430 * Find GDESC for this block.
432 for (g = GlobalList; g != NULL; g = g->next)
434 if (g->handle == selector)
436 if (g->sequence > 0)
437 return g->handle;
438 else
440 fprintf(stderr, "Attempt to get a handle "
441 "from a selector to a far heap.\n");
442 return 0;
447 return 0;
450 /**********************************************************************
451 * GlobalCompact
454 unsigned int
455 GlobalCompact(unsigned int desired)
457 GDESC *g;
458 unsigned char free_map[512];
459 unsigned int max_selector_used = 0;
460 unsigned int i;
461 unsigned int selector;
462 int current_free;
463 int max_free;
466 * Initialize free list to all items not controlled by GlobalAlloc()
468 for (i = 0; i < 512; i++)
469 free_map[i] = -1;
472 * Traverse table looking for used and free selectors.
474 for (g = GlobalList; g != NULL; g = g->next)
477 * Check for free segments.
479 if (g->sequence == -1)
481 free_map[g->handle >> 3] = 1;
482 if (g->handle > max_selector_used)
483 max_selector_used = g->handle;
487 * Check for heap allocated segments.
489 else if (g->handle == 0)
491 selector = (unsigned int) g->addr >> 16;
492 free_map[selector >> 3] = 0;
493 if (selector > max_selector_used)
494 max_selector_used = selector;
499 * All segments past the biggest selector used are free.
501 for (i = (max_selector_used >> 3) + 1; i < 512; i++)
502 free_map[i] = 1;
505 * Find the largest free block of segments
507 current_free = 0;
508 max_free = 0;
509 for (i = 0; i < 512; i++)
511 if (free_map[i] == 1)
513 current_free++;
515 else
517 if (current_free > max_free)
518 max_free = current_free;
519 current_free = 0;
523 return max_free << 16;
526 /**********************************************************************
527 * GlobalReAlloc
530 unsigned int
531 GlobalReAlloc(unsigned int block, unsigned int new_size, unsigned int flags)
533 GDESC *g;
534 unsigned int n_segments;
535 int i;
537 if (block == 0)
538 return 0;
541 * Find GDESC for this block.
543 for (g = GlobalList; g != NULL; g = g->next)
545 if (g->handle == block)
546 break;
549 if (g == NULL)
550 return 0;
553 * If this is a heap allocated block, then use HEAP_ReAlloc() to
554 * reallocate the block. If this fails, call GlobalAlloc() to get
555 * a new block.
557 if (g->sequence = 0)
559 MDESC **free_list;
560 void *p;
562 free_list = (MDESC **) ((unsigned int) g->addr & 0xffff0000);
563 p = HEAP_ReAlloc(free_list, g->addr, new_size, flags) ;
564 if (p == NULL)
566 unsigned int handle = GlobalAlloc(flags, new_size);
567 if (handle == 0)
568 return 0;
569 p = GlobalLock(handle);
570 memcpy(p, g->addr, g->length);
571 GlobalUnlock(handle);
572 GlobalFree(g->handle);
574 return handle;
576 else
578 g->addr = p;
579 g->length = new_size;
580 return g->handle;
585 * Otherwise, we need to do the work ourselves. First verify the
586 * handle.
588 else
590 if (g->sequence != 1)
591 return 0;
594 * Do we need more memory? Segments are in ascending order in
595 * the GDESC list.
597 n_segments = (new_size >> 16) + 1;
598 if (n_segments > g->length)
600 GDESC *g_new;
601 GDESC *g_start = g;
602 int old_segments = g_start->length;
603 unsigned short next_handle = g_start->handle;
605 for (i = 1; i <= n_segments; i++, g = g->next)
608 * If we run into a block allocated to something else,
609 * try GlobalGetFreeSegments() and memcpy(). (Yuk!)
611 if (g->sequence != i || g->handle != next_handle)
613 g = GlobalGetFreeSegments(flags, n_segments);
614 if (g == NULL)
615 return 0;
617 memcpy(g->addr, g_start->addr,
618 g_start->length << 16);
620 GlobalFree(block);
621 return g->handle;
625 * Otherwise this block is used by us or free. So,
626 * snatch it. If this block is new and we are supposed to
627 * zero init, then do some erasing.
629 if (g->sequence == -1 && (flags & GLOBAL_FLAGS_ZEROINIT))
630 memset(g->addr, 0, 0x10000);
632 g->sequence = i;
633 g->length = n_segments;
634 next_handle += 8;
637 * If the next descriptor is non-existant, then use
638 * GlobalGetFreeSegments to create them.
640 if (i != n_segments && g->next == NULL)
642 g_new = GlobalGetFreeSegments(flags, n_segments - i);
643 if (g_new == NULL)
644 return 0;
645 GlobalFree(g_new->handle);
649 return g_start->handle;
653 * Do we need less memory?
655 else if (n_segments < g->length)
657 GDESC *g_free;
659 g_free = g;
660 for (i = 0; i < n_segments; i++)
662 if (g_free->sequence != i + 1)
663 return 0;
664 g_free = g_free->next;
669 * We already have exactly the right amount of memory.
671 else
672 return block;
676 * If we fall through it must be an error.
678 return 0;