Add FS #10214. Initial commit of the original PDa code for the GSoC Pure Data plugin...
[kugel-rb.git] / apps / plugins / pdbox / dbestfit-3.3 / bmalloc.c
blobf95b4f6125f42b7f3c746f077b26fbe6f9cd6a16
1 /*****************************************************************************
3 * Big (best-fit) Memory Allocation
5 * Author: Daniel Stenberg
6 * Date: March 5, 1997
7 * Version: 2.0
8 * Email: Daniel.Stenberg@sth.frontec.se
11 * Read 'thoughts' for theories and details in implementation.
13 * Routines meant to replace the most low level functions of an Operting
14 * System
16 * v2.0
17 * - Made all size-routines get moved out from this file. This way, the size
18 * functions can much more easily get replaced.
19 * - Improved how new memory blocks get added to the size-sorted list. When
20 * not adding new pools, there should never ever be any list traversing
21 * since all information is dynamically gathered.
23 ****************************************************************************/
25 #include <stdio.h>
26 #include <stdlib.h>
28 #include "bysize.h"
30 #ifndef TRUE
31 #define TRUE 1
32 #endif
33 #ifndef FALSE
34 #define FALSE 0
35 #endif
37 /* #define DEBUG */
39 #define BMEM_ALIGN 64 /* resolution */
41 #define BMEMERR_TOOSMALL -1
43 /* this struct will be stored in all CHUNKS and AREAS */
44 struct BlockInfo {
45 struct BlockInfo *lower; /* previous block in memory (lower address) */
46 struct BlockInfo *higher; /* next block in memory (higher address) */
47 unsigned long info; /* 31 bits size: 1 bit free boolean */
48 #define INFO_FREE 1
49 #define INFO_SIZE (~ INFO_FREE) /* inverted FREE bit pattern */
51 /* FREE+SIZE Could be written to use ordinary bitfields if using a smart
52 (like gcc) compiler in a manner like:
53 int size:31;
54 int free:1;
56 The 'higher' pointer COULD be removed completely if the size is used as
57 an index to the higher one. This would then REQUIRE the entire memory
58 pool to be contiguous and it needs a 'terminating' "node" or an extra
59 flag that informs about the end of the list.
63 /* the BLOCK list should be sorted in a lower to higher address order */
64 struct BlockInfo *blockHead=NULL; /* nothing from the start */
66 void print_lists(void);
69 /***********************************************************************
71 * remove_block()
73 * Remove the block from the address-sorted list.
75 ***********************************************************************/
77 void remove_block(struct BlockInfo *block)
79 if(block->lower)
80 block->lower->higher = block->higher;
81 else
82 blockHead = block->higher;
83 if(block->higher)
84 block->higher->lower = block->lower;
87 /****************************************************************************
89 * add_blocktolists()
91 * Adds the specified block at the specified place in the address-sorted
92 * list and at the appropriate place in the size-sorted.
94 ***************************************************************************/
95 void add_blocktolists(struct BlockInfo *block,
96 struct BlockInfo *newblock,
97 size_t newsize)
99 struct BlockInfo *temp; /* temporary storage variable */
100 if(block) {
101 /* `block' is now a lower address than 'newblock' */
104 * Check if the new CHUNK is wall-to-wall with the lower addressed
105 * one (if *that* is free)
107 if(block->info&INFO_FREE) {
108 if((char *)block + (block->info&INFO_SIZE) == (char *)newblock) {
109 /* yes sir, this is our lower address neighbour, enlarge that one
110 pick it out from the list and recursively add that chunk and
111 then we escape */
113 /* remove from size-sorted list: */
114 remove_chunksize((char*)block+sizeof(struct BlockInfo));
116 block->info += newsize; /* newsize is an even number and thus the FREE
117 bit is untouched */
119 remove_block(block); /* unlink the block address-wise */
121 /* recursively check our lower friend(s) */
122 add_blocktolists(block->lower, block, block->info&INFO_SIZE);
123 return;
127 temp = block->higher;
129 block->higher = newblock;
130 newblock->lower = block;
131 newblock->higher = temp;
132 if(newblock->higher)
133 newblock->higher->lower = newblock;
135 else {
136 /* this block should preceed the heading one */
137 temp = blockHead;
139 /* check if this is our higher addressed neighbour */
140 if((char *)newblock + newsize == (char *)temp) {
142 /* yes, we are wall-to-wall with the higher CHUNK */
143 if(temp->info&INFO_FREE) {
144 /* and the neighbour is even free, remove that one and enlarge
145 ourselves, call add_pool() recursively and then escape */
147 remove_block(temp); /* unlink 'temp' from list */
149 /* remove from size-sorted list: */
150 remove_chunksize((char*)temp+sizeof(struct BlockInfo) );
152 /* add the upper block's size on ourselves */
153 newsize += temp->info&INFO_SIZE;
155 /* add the new, bigger block */
156 add_blocktolists(block, newblock, newsize);
157 return;
161 blockHead = newblock;
162 newblock->higher = temp;
163 newblock->lower = NULL; /* there is no lower one */
164 if(newblock->higher)
165 newblock->higher->lower = newblock;
168 newblock->info = newsize | INFO_FREE; /* we do assume size isn't using the
169 FREE bit */
170 insert_bysize((char *)newblock+sizeof(struct BlockInfo), newsize);
173 /***********************************************************************
175 * findblockbyaddr()
177 * Find the block that is just before the input block in memory. Returns NULL
178 * if none is.
180 ***********************************************************************/
182 static struct BlockInfo *findblockbyaddr(struct BlockInfo *block)
184 struct BlockInfo *test = blockHead;
185 struct BlockInfo *lower = NULL;
187 while(test && (test < block)) {
188 lower = test;
189 test = test->higher;
191 return lower;
194 /***********************************************************************
196 * add_pool()
198 * This function should be the absolutely first function to call. It sets up
199 * the memory bounds of the [first] CHUNK(s). It should be possible to call
200 * this function several times to add more CHUNKs to the pool of free
201 * memory. This allows the bmalloc system to deal with a non-contigous memory
202 * area.
204 * Returns non-zero if an error occured. The memory was not added then.
206 ***********************************************************************/
208 int add_pool(void *start,
209 size_t size)
211 struct BlockInfo *newblock = (struct BlockInfo *)start;
212 struct BlockInfo *block;
214 if(size < BMEM_ALIGN)
215 return BMEMERR_TOOSMALL;
217 block = findblockbyaddr( newblock );
218 /* `block' is now a lower address than 'newblock' or NULL */
220 if(size&1)
221 size--; /* only add even sizes */
223 add_blocktolists(block, newblock, size);
225 return 0;
229 #ifdef DEBUG
230 static void bmalloc_failed(size_t size)
232 printf("*** " __FILE__ " Couldn't allocate %d bytes\n", size);
233 print_lists();
235 #else
236 #define bmalloc_failed(x)
237 #endif
239 void print_lists()
241 struct BlockInfo *block = blockHead;
242 #if 1
243 printf("List of BLOCKS (in address order):\n");
244 while(block) {
245 printf(" START %p END %p SIZE %ld FLAG %s\n",
246 (char *)block,
247 (char *)block+(block->info&INFO_SIZE), block->info&INFO_SIZE,
248 (block->info&INFO_FREE)?"free":"used");
249 block = block->higher;
251 printf("End of BLOCKS:\n");
252 #endif
253 print_sizes();
256 void *bmalloc(size_t size)
258 void *mem;
260 #ifdef DEBUG
262 static int count=0;
263 int realsize = size + sizeof(struct BlockInfo);
264 if(realsize%4096)
265 realsize = ((size / BMEM_ALIGN)+1) * BMEM_ALIGN;
266 printf("%d bmalloc(%d) [%d]\n", count++, size, realsize);
268 #endif
270 size += sizeof(struct BlockInfo); /* add memory for our header */
272 if(size&(BMEM_ALIGN-1)) /* a lot faster than %BMEM_ALIGN but this MUST be
273 changed if the BLOCKSIZE is not 2^X ! */
274 size = ((size / BMEM_ALIGN)+1) * BMEM_ALIGN; /* align like this */
276 /* get a CHUNK from the list with this size */
277 mem = obtainbysize ( size );
278 if(mem) {
279 /* the memory block we have got is the "best-fit" and it is already
280 un-linked from the free list */
282 /* now do the math to get the proper block pointer */
283 struct BlockInfo *block= (struct BlockInfo *)
284 ((char *)mem - sizeof(struct BlockInfo));
286 block->info &= ~INFO_FREE;
287 /* not free anymore */
289 if( size != (block->info&INFO_SIZE)) {
290 /* split this chunk into two pieces and return the one that fits us */
291 size_t othersize = (block->info&INFO_SIZE) - size;
293 if(othersize > BMEM_ALIGN) {
294 /* prevent losing small pieces of memory due to weird alignments
295 of the memory pool */
297 block->info = size; /* set new size (leave FREE bit cleared) */
299 /* Add the new chunk to the lists: */
300 add_blocktolists(block,
301 (struct BlockInfo *)((char *)block + size),
302 othersize );
306 /* Return the memory our parent may use: */
307 return (char *)block+sizeof(struct BlockInfo);
309 else {
310 bmalloc_failed(size);
311 return NULL; /* can't find any memory, fail hard */
313 return NULL;
316 void bfree(void *ptr)
318 struct BlockInfo *block = (struct BlockInfo *)
319 ((char *)ptr - sizeof(struct BlockInfo));
320 size_t size;
322 /* setup our initial higher and lower pointers */
323 struct BlockInfo *lower = block->lower;
324 struct BlockInfo *higher = block->higher;
326 #ifdef DEBUG
327 static int freecount=0;
328 printf("%d bfree(%p)\n", freecount++, ptr);
329 #endif
331 /* bind together lower addressed FREE CHUNKS */
332 if(lower && (lower->info&INFO_FREE) &&
333 ((char *)lower + (lower->info&INFO_SIZE) == (char *)block)) {
334 size = block->info&INFO_SIZE; /* original size */
336 /* remove from size-link: */
337 remove_chunksize((char *)lower+sizeof(struct BlockInfo));
339 remove_block(block); /* unlink from address list */
340 block = lower; /* new base area pointer */
341 block->info += size; /* append the new size (the FREE bit
342 will remain untouched) */
344 lower = lower->lower; /* new lower pointer */
346 /* bind together higher addressed FREE CHUNKS */
347 if(higher && (higher->info&INFO_FREE) &&
348 ((char *)block + (block->info&INFO_SIZE) == (char *)higher)) {
349 /* append higher size, the FREE bit won't be affected */
350 block->info += (higher->info&INFO_SIZE);
352 /* unlink from size list: */
353 remove_chunksize((char *)higher+sizeof(struct BlockInfo));
354 remove_block(higher); /* unlink from address list */
355 higher = higher->higher; /* the new higher link */
356 block->higher = higher; /* new higher link */
358 block->info |= INFO_FREE; /* consider this FREE! */
360 block->lower = lower;
361 block->higher = higher;
363 insert_bysize((char *)block+sizeof(struct BlockInfo), block->info&INFO_SIZE);
365 #ifdef DEBUG
366 print_lists();
367 #endif
371 /*****************************************************************************
373 * Big (best-fit) Memory Allocation
375 * Author: Daniel Stenberg
376 * Date: March 5, 1997
377 * Version: 2.0
378 * Email: Daniel.Stenberg@sth.frontec.se
381 * Read 'thoughts' for theories and details in implementation.
383 * Routines meant to replace the most low level functions of an Operting
384 * System
386 * v2.0
387 * - Made all size-routines get moved out from this file. This way, the size
388 * functions can much more easily get replaced.
389 * - Improved how new memory blocks get added to the size-sorted list. When
390 * not adding new pools, there should never ever be any list traversing
391 * since all information is dynamically gathered.
393 ****************************************************************************/
395 #include <stdio.h>
396 #include <stdlib.h>
398 #include "bysize.h"
400 #ifndef TRUE
401 #define TRUE 1
402 #endif
403 #ifndef FALSE
404 #define FALSE 0
405 #endif
407 /* #define DEBUG */
409 #define BMEM_ALIGN 64 /* resolution */
411 #define BMEMERR_TOOSMALL -1
413 /* this struct will be stored in all CHUNKS and AREAS */
414 struct BlockInfo {
415 struct BlockInfo *lower; /* previous block in memory (lower address) */
416 struct BlockInfo *higher; /* next block in memory (higher address) */
417 unsigned long info; /* 31 bits size: 1 bit free boolean */
418 #define INFO_FREE 1
419 #define INFO_SIZE (~ INFO_FREE) /* inverted FREE bit pattern */
421 /* FREE+SIZE Could be written to use ordinary bitfields if using a smart
422 (like gcc) compiler in a manner like:
423 int size:31;
424 int free:1;
426 The 'higher' pointer COULD be removed completely if the size is used as
427 an index to the higher one. This would then REQUIRE the entire memory
428 pool to be contiguous and it needs a 'terminating' "node" or an extra
429 flag that informs about the end of the list.
433 /* the BLOCK list should be sorted in a lower to higher address order */
434 struct BlockInfo *blockHead=NULL; /* nothing from the start */
436 void print_lists(void);
439 /***********************************************************************
441 * remove_block()
443 * Remove the block from the address-sorted list.
445 ***********************************************************************/
447 void remove_block(struct BlockInfo *block)
449 if(block->lower)
450 block->lower->higher = block->higher;
451 else
452 blockHead = block->higher;
453 if(block->higher)
454 block->higher->lower = block->lower;
457 /****************************************************************************
459 * add_blocktolists()
461 * Adds the specified block at the specified place in the address-sorted
462 * list and at the appropriate place in the size-sorted.
464 ***************************************************************************/
465 void add_blocktolists(struct BlockInfo *block,
466 struct BlockInfo *newblock,
467 size_t newsize)
469 struct BlockInfo *temp; /* temporary storage variable */
470 if(block) {
471 /* `block' is now a lower address than 'newblock' */
474 * Check if the new CHUNK is wall-to-wall with the lower addressed
475 * one (if *that* is free)
477 if(block->info&INFO_FREE) {
478 if((char *)block + (block->info&INFO_SIZE) == (char *)newblock) {
479 /* yes sir, this is our lower address neighbour, enlarge that one
480 pick it out from the list and recursively add that chunk and
481 then we escape */
483 /* remove from size-sorted list: */
484 remove_chunksize((char*)block+sizeof(struct BlockInfo));
486 block->info += newsize; /* newsize is an even number and thus the FREE
487 bit is untouched */
489 remove_block(block); /* unlink the block address-wise */
491 /* recursively check our lower friend(s) */
492 add_blocktolists(block->lower, block, block->info&INFO_SIZE);
493 return;
497 temp = block->higher;
499 block->higher = newblock;
500 newblock->lower = block;
501 newblock->higher = temp;
502 if(newblock->higher)
503 newblock->higher->lower = newblock;
505 else {
506 /* this block should preceed the heading one */
507 temp = blockHead;
509 /* check if this is our higher addressed neighbour */
510 if((char *)newblock + newsize == (char *)temp) {
512 /* yes, we are wall-to-wall with the higher CHUNK */
513 if(temp->info&INFO_FREE) {
514 /* and the neighbour is even free, remove that one and enlarge
515 ourselves, call add_pool() recursively and then escape */
517 remove_block(temp); /* unlink 'temp' from list */
519 /* remove from size-sorted list: */
520 remove_chunksize((char*)temp+sizeof(struct BlockInfo) );
522 /* add the upper block's size on ourselves */
523 newsize += temp->info&INFO_SIZE;
525 /* add the new, bigger block */
526 add_blocktolists(block, newblock, newsize);
527 return;
531 blockHead = newblock;
532 newblock->higher = temp;
533 newblock->lower = NULL; /* there is no lower one */
534 if(newblock->higher)
535 newblock->higher->lower = newblock;
538 newblock->info = newsize | INFO_FREE; /* we do assume size isn't using the
539 FREE bit */
540 insert_bysize((char *)newblock+sizeof(struct BlockInfo), newsize);
543 /***********************************************************************
545 * findblockbyaddr()
547 * Find the block that is just before the input block in memory. Returns NULL
548 * if none is.
550 ***********************************************************************/
552 static struct BlockInfo *findblockbyaddr(struct BlockInfo *block)
554 struct BlockInfo *test = blockHead;
555 struct BlockInfo *lower = NULL;
557 while(test && (test < block)) {
558 lower = test;
559 test = test->higher;
561 return lower;
564 /***********************************************************************
566 * add_pool()
568 * This function should be the absolutely first function to call. It sets up
569 * the memory bounds of the [first] CHUNK(s). It should be possible to call
570 * this function several times to add more CHUNKs to the pool of free
571 * memory. This allows the bmalloc system to deal with a non-contigous memory
572 * area.
574 * Returns non-zero if an error occured. The memory was not added then.
576 ***********************************************************************/
578 int add_pool(void *start,
579 size_t size)
581 struct BlockInfo *newblock = (struct BlockInfo *)start;
582 struct BlockInfo *block;
584 if(size < BMEM_ALIGN)
585 return BMEMERR_TOOSMALL;
587 block = findblockbyaddr( newblock );
588 /* `block' is now a lower address than 'newblock' or NULL */
590 if(size&1)
591 size--; /* only add even sizes */
593 add_blocktolists(block, newblock, size);
595 return 0;
599 #ifdef DEBUG
600 static void bmalloc_failed(size_t size)
602 printf("*** " __FILE__ " Couldn't allocate %d bytes\n", size);
603 print_lists();
605 #else
606 #define bmalloc_failed(x)
607 #endif
609 void print_lists()
611 struct BlockInfo *block = blockHead;
612 #if 1
613 printf("List of BLOCKS (in address order):\n");
614 while(block) {
615 printf(" START %p END %p SIZE %ld FLAG %s\n",
616 (char *)block,
617 (char *)block+(block->info&INFO_SIZE), block->info&INFO_SIZE,
618 (block->info&INFO_FREE)?"free":"used");
619 block = block->higher;
621 printf("End of BLOCKS:\n");
622 #endif
623 print_sizes();
626 void *bmalloc(size_t size)
628 void *mem;
630 #ifdef DEBUG
632 static int count=0;
633 int realsize = size + sizeof(struct BlockInfo);
634 if(realsize%4096)
635 realsize = ((size / BMEM_ALIGN)+1) * BMEM_ALIGN;
636 printf("%d bmalloc(%d) [%d]\n", count++, size, realsize);
638 #endif
640 size += sizeof(struct BlockInfo); /* add memory for our header */
642 if(size&(BMEM_ALIGN-1)) /* a lot faster than %BMEM_ALIGN but this MUST be
643 changed if the BLOCKSIZE is not 2^X ! */
644 size = ((size / BMEM_ALIGN)+1) * BMEM_ALIGN; /* align like this */
646 /* get a CHUNK from the list with this size */
647 mem = obtainbysize ( size );
648 if(mem) {
649 /* the memory block we have got is the "best-fit" and it is already
650 un-linked from the free list */
652 /* now do the math to get the proper block pointer */
653 struct BlockInfo *block= (struct BlockInfo *)
654 ((char *)mem - sizeof(struct BlockInfo));
656 block->info &= ~INFO_FREE;
657 /* not free anymore */
659 if( size != (block->info&INFO_SIZE)) {
660 /* split this chunk into two pieces and return the one that fits us */
661 size_t othersize = (block->info&INFO_SIZE) - size;
663 if(othersize > BMEM_ALIGN) {
664 /* prevent losing small pieces of memory due to weird alignments
665 of the memory pool */
667 block->info = size; /* set new size (leave FREE bit cleared) */
669 /* Add the new chunk to the lists: */
670 add_blocktolists(block,
671 (struct BlockInfo *)((char *)block + size),
672 othersize );
676 /* Return the memory our parent may use: */
677 return (char *)block+sizeof(struct BlockInfo);
679 else {
680 bmalloc_failed(size);
681 return NULL; /* can't find any memory, fail hard */
683 return NULL;
686 void bfree(void *ptr)
688 struct BlockInfo *block = (struct BlockInfo *)
689 ((char *)ptr - sizeof(struct BlockInfo));
690 size_t size;
692 /* setup our initial higher and lower pointers */
693 struct BlockInfo *lower = block->lower;
694 struct BlockInfo *higher = block->higher;
696 #ifdef DEBUG
697 static int freecount=0;
698 printf("%d bfree(%p)\n", freecount++, ptr);
699 #endif
701 /* bind together lower addressed FREE CHUNKS */
702 if(lower && (lower->info&INFO_FREE) &&
703 ((char *)lower + (lower->info&INFO_SIZE) == (char *)block)) {
704 size = block->info&INFO_SIZE; /* original size */
706 /* remove from size-link: */
707 remove_chunksize((char *)lower+sizeof(struct BlockInfo));
709 remove_block(block); /* unlink from address list */
710 block = lower; /* new base area pointer */
711 block->info += size; /* append the new size (the FREE bit
712 will remain untouched) */
714 lower = lower->lower; /* new lower pointer */
716 /* bind together higher addressed FREE CHUNKS */
717 if(higher && (higher->info&INFO_FREE) &&
718 ((char *)block + (block->info&INFO_SIZE) == (char *)higher)) {
719 /* append higher size, the FREE bit won't be affected */
720 block->info += (higher->info&INFO_SIZE);
722 /* unlink from size list: */
723 remove_chunksize((char *)higher+sizeof(struct BlockInfo));
724 remove_block(higher); /* unlink from address list */
725 higher = higher->higher; /* the new higher link */
726 block->higher = higher; /* new higher link */
728 block->info |= INFO_FREE; /* consider this FREE! */
730 block->lower = lower;
731 block->higher = higher;
733 insert_bysize((char *)block+sizeof(struct BlockInfo), block->info&INFO_SIZE);
735 #ifdef DEBUG
736 print_lists();
737 #endif