1 /*****************************************************************************
3 * Big (best-fit) Memory Allocation
5 * Author: Daniel Stenberg
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
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 ****************************************************************************/
39 #define BMEM_ALIGN 64 /* resolution */
41 #define BMEMERR_TOOSMALL -1
43 /* this struct will be stored in all CHUNKS and AREAS */
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 */
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:
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 /***********************************************************************
73 * Remove the block from the address-sorted list.
75 ***********************************************************************/
77 void remove_block(struct BlockInfo
*block
)
80 block
->lower
->higher
= block
->higher
;
82 blockHead
= block
->higher
;
84 block
->higher
->lower
= block
->lower
;
87 /****************************************************************************
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
,
99 struct BlockInfo
*temp
; /* temporary storage variable */
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
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
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
);
127 temp
= block
->higher
;
129 block
->higher
= newblock
;
130 newblock
->lower
= block
;
131 newblock
->higher
= temp
;
133 newblock
->higher
->lower
= newblock
;
136 /* this block should preceed the heading one */
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
);
161 blockHead
= newblock
;
162 newblock
->higher
= temp
;
163 newblock
->lower
= NULL
; /* there is no lower one */
165 newblock
->higher
->lower
= newblock
;
168 newblock
->info
= newsize
| INFO_FREE
; /* we do assume size isn't using the
170 insert_bysize((char *)newblock
+sizeof(struct BlockInfo
), newsize
);
173 /***********************************************************************
177 * Find the block that is just before the input block in memory. Returns NULL
180 ***********************************************************************/
182 static struct BlockInfo
*findblockbyaddr(struct BlockInfo
*block
)
184 struct BlockInfo
*test
= blockHead
;
185 struct BlockInfo
*lower
= NULL
;
187 while(test
&& (test
< block
)) {
194 /***********************************************************************
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
204 * Returns non-zero if an error occured. The memory was not added then.
206 ***********************************************************************/
208 int add_pool(void *start
,
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 */
221 size
--; /* only add even sizes */
223 add_blocktolists(block
, newblock
, size
);
230 static void bmalloc_failed(size_t size
)
232 printf("*** " __FILE__
" Couldn't allocate %d bytes\n", size
);
236 #define bmalloc_failed(x)
241 struct BlockInfo
*block
= blockHead
;
243 printf("List of BLOCKS (in address order):\n");
245 printf(" START %p END %p SIZE %ld FLAG %s\n",
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");
256 void *bmalloc(size_t size
)
263 int realsize
= size
+ sizeof(struct BlockInfo
);
265 realsize
= ((size
/ BMEM_ALIGN
)+1) * BMEM_ALIGN
;
266 printf("%d bmalloc(%d) [%d]\n", count
++, size
, realsize
);
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
);
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
),
306 /* Return the memory our parent may use: */
307 return (char *)block
+sizeof(struct BlockInfo
);
310 bmalloc_failed(size
);
311 return NULL
; /* can't find any memory, fail hard */
316 void bfree(void *ptr
)
318 struct BlockInfo
*block
= (struct BlockInfo
*)
319 ((char *)ptr
- sizeof(struct BlockInfo
));
322 /* setup our initial higher and lower pointers */
323 struct BlockInfo
*lower
= block
->lower
;
324 struct BlockInfo
*higher
= block
->higher
;
327 static int freecount
=0;
328 printf("%d bfree(%p)\n", freecount
++, ptr
);
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
);
371 /*****************************************************************************
373 * Big (best-fit) Memory Allocation
375 * Author: Daniel Stenberg
376 * Date: March 5, 1997
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
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 ****************************************************************************/
409 #define BMEM_ALIGN 64 /* resolution */
411 #define BMEMERR_TOOSMALL -1
413 /* this struct will be stored in all CHUNKS and AREAS */
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 */
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:
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 /***********************************************************************
443 * Remove the block from the address-sorted list.
445 ***********************************************************************/
447 void remove_block(struct BlockInfo
*block
)
450 block
->lower
->higher
= block
->higher
;
452 blockHead
= block
->higher
;
454 block
->higher
->lower
= block
->lower
;
457 /****************************************************************************
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
,
469 struct BlockInfo
*temp
; /* temporary storage variable */
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
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
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
);
497 temp
= block
->higher
;
499 block
->higher
= newblock
;
500 newblock
->lower
= block
;
501 newblock
->higher
= temp
;
503 newblock
->higher
->lower
= newblock
;
506 /* this block should preceed the heading one */
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
);
531 blockHead
= newblock
;
532 newblock
->higher
= temp
;
533 newblock
->lower
= NULL
; /* there is no lower one */
535 newblock
->higher
->lower
= newblock
;
538 newblock
->info
= newsize
| INFO_FREE
; /* we do assume size isn't using the
540 insert_bysize((char *)newblock
+sizeof(struct BlockInfo
), newsize
);
543 /***********************************************************************
547 * Find the block that is just before the input block in memory. Returns NULL
550 ***********************************************************************/
552 static struct BlockInfo
*findblockbyaddr(struct BlockInfo
*block
)
554 struct BlockInfo
*test
= blockHead
;
555 struct BlockInfo
*lower
= NULL
;
557 while(test
&& (test
< block
)) {
564 /***********************************************************************
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
574 * Returns non-zero if an error occured. The memory was not added then.
576 ***********************************************************************/
578 int add_pool(void *start
,
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 */
591 size
--; /* only add even sizes */
593 add_blocktolists(block
, newblock
, size
);
600 static void bmalloc_failed(size_t size
)
602 printf("*** " __FILE__
" Couldn't allocate %d bytes\n", size
);
606 #define bmalloc_failed(x)
611 struct BlockInfo
*block
= blockHead
;
613 printf("List of BLOCKS (in address order):\n");
615 printf(" START %p END %p SIZE %ld FLAG %s\n",
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");
626 void *bmalloc(size_t size
)
633 int realsize
= size
+ sizeof(struct BlockInfo
);
635 realsize
= ((size
/ BMEM_ALIGN
)+1) * BMEM_ALIGN
;
636 printf("%d bmalloc(%d) [%d]\n", count
++, size
, realsize
);
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
);
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
),
676 /* Return the memory our parent may use: */
677 return (char *)block
+sizeof(struct BlockInfo
);
680 bmalloc_failed(size
);
681 return NULL
; /* can't find any memory, fail hard */
686 void bfree(void *ptr
)
688 struct BlockInfo
*block
= (struct BlockInfo
*)
689 ((char *)ptr
- sizeof(struct BlockInfo
));
692 /* setup our initial higher and lower pointers */
693 struct BlockInfo
*lower
= block
->lower
;
694 struct BlockInfo
*higher
= block
->higher
;
697 static int freecount
=0;
698 printf("%d bfree(%p)\n", freecount
++, ptr
);
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
);