1
0
Fork 0
forked from len0rd/rockbox

malloc(), best-fit for big blocks, small blocks treated separately, all

details in THOUGHTS. No headers and stuff added yet.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@569 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Daniel Stenberg 2002-05-13 19:35:10 +00:00
parent e14e13df5c
commit 1dd21edacf
12 changed files with 2110 additions and 0 deletions

View file

@ -0,0 +1,51 @@
TARGET = libdmalloc.a
LIBOBJS = dmalloc.o bmalloc.o bysize.o
OBJS1 = mytest.o
TARGET1 = mytest
OBJS2 = Malloc.o
TARGET2 = mtest
OBJS3 = dmytest.o
TARGET3 = dmytest
# define this to talk a lot in runtime
# -DDEBUG_VERBOSE
CFLAGS = -g -DUNIX -DBMALLOC -Wall -DDEBUG
CC = gcc
AR = ar
LDFLAGS = -L. -ldmalloc
all: $(TARGET) $(TARGET1) $(TARGET2) $(TARGET3)
clean:
rm -f core *~ $(TARGET) $(TARGET1) $(TARGET2) $(TARGET3) \
$(LIBOBJS) $(OBJS1) $(OBJS2) $(OBJS3)
$(TARGET): $(LIBOBJS)
$(AR) ruv $(TARGET) $(LIBOBJS)
$(TARGET1): $(OBJS1)
$(CC) -g -o $(TARGET1) $(OBJS1) $(LDFLAGS)
$(TARGET2): $(OBJS2)
$(CC) -g -o $(TARGET2) $(OBJS2) $(LDFLAGS)
$(TARGET3): $(OBJS3)
$(CC) -g -o $(TARGET3) $(OBJS3) $(LDFLAGS)
bmalloc.o: bmalloc.c bysize.h
bysize.o: bysize.c
dmalloc.o: dmalloc.c
dmytest.o: dmytest.c dmalloc.h bmalloc.h
Malloc.o: Malloc.c
mytest.o: mytest.c bmalloc.h
tgz:
@(dir=`pwd`;name=`basename $$dir`;echo Creates $$name.tar.gz; cd .. ; \
tar -cf $$name.tar `cat $$name/FILES | sed "s:^/:$$name/:g"` ; \
gzip $$name.tar ; chmod a+r $$name.tar.gz ; mv $$name.tar.gz $$name/)

View file

@ -0,0 +1,196 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* Storleken på allokeringen bestäms genom att först slumpas en position i
"size_table" ut, sedan slumpas en storlek mellan den postionen och nästa värde
i tabellen. Genom att ha tabellen koncentrerad med låga värden, skapas
flest såna. Rutinen håller tills minnet en allokeringen nekas. Den kommer
aldrig att ha mer än MAXIMAL_MEMORY_TO_ALLOCATE allokerat samtidigt. Maximalt
har den MAX_ALLOCATIONS allokeringar samtidigt.
Statistiskt sätt kommer efter ett tag MAX_ALLOCATIONS/2 allokeringar finnas
samtidigt, med varje allokering i median med värdet av halva "size_table".
När minnet är slut (malloc()=NULL), frågas användaren om han ska fortsätta.
Med jämna mellanrum skrivs statisktik ut skärmen. (DISPLAY_WHEN)
För att stressa systemet med fler små allokeringar, kan man öka
MAX_ALLOCATIONS. AMOUNT_OF_MEMORY bör den att slå i taket fortare om man
minskar det.
Ingen initiering görs av slumptalen, allt är upprepbart (men plocka bort
kommentaren srand() och det löser sig.
*/
/*#undef BMALLOC*/
#ifdef BMALLOC
#include "dmalloc.h"
#include "bmalloc.h"
#endif
#define MAX_ALLOCATIONS 100000
#define AMOUNT_OF_MEMORY 100000 /* bytes */
#define MAXIMAL_MEMORY_TO_ALLOCATE 49000 /* Sätt den här högre än
AMOUNT_OF_MEMORY, och malloc() bör
returnera NULL förr eller senare */
#define DISPLAY_WHEN (10000) /* When to display statistic */
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define BOOL char
#define TRUE 1
#define FALSE 0
typedef struct {
char *memory;
long size;
char filled_with;
long table_position;
} MallocStruct;
/*
Skapar en lista med MAX_ALLOCATIONS storlek där det slumpvis allokeras
eller reallokeras i.
*/
MallocStruct my_mallocs[MAX_ALLOCATIONS];
long size_table[]={5,8,10,11,12,14,16,18,20,26,33,50,70,90,120,150,200,400,800,1000,2000,4000,8000};
#define TABLESIZE ((sizeof(size_table)-1)/sizeof(long))
long size_allocs[TABLESIZE];
int main(void)
{
long count=-1;
long count_free=0, count_malloc=0, count_realloc=0;
long total_memory=0;
long out_of_memory=FALSE;
dmalloc_initialize();
#ifdef BMALLOC
void *thisisourheap;
thisisourheap = (malloc)(AMOUNT_OF_MEMORY);
if(!thisisourheap)
return -1; /* can't get memory */
bmalloc_add_pool(thisisourheap, AMOUNT_OF_MEMORY);
#endif
srand( 0 ); /* Initialize to a fixed random */
while (!out_of_memory) {
long number=rand()%MAX_ALLOCATIONS;
long size;
long table_position=rand()%TABLESIZE;
char fill_with=rand()&255;
count++;
size=rand()%(size_table[table_position+1]-
size_table[table_position])+
size_table[table_position];
/* fprintf(stderr, "number %d size %d\n", number, size); */
if (my_mallocs[number].size) { /* Om allokering redan finns på den här
positionen, reallokerar vi eller
friar. */
long old_size=my_mallocs[number].size;
if (my_mallocs[number].size && fill_with<40) {
free(my_mallocs[number].memory);
total_memory -= my_mallocs[number].size;
count_free++;
size_allocs[my_mallocs[number].table_position]--;
size=0;
} else {
/*
* realloc() part
*
*/
char *temp;
#if 0
if(my_mallocs[number].size > size) {
printf("*** %d is realloc()ed to %d\n",
my_mallocs[number].size, size);
}
#endif
if (total_memory-old_size+size>MAXIMAL_MEMORY_TO_ALLOCATE)
goto output; /* for-loop */
temp = (char *)realloc(my_mallocs[number].memory, size);
if (!temp)
out_of_memory=size;
else {
my_mallocs[number].memory = temp;
my_mallocs[number].size=size;
size_allocs[my_mallocs[number].table_position]--;
size_allocs[table_position]++;
total_memory -= old_size;
total_memory += size;
old_size=min(old_size, size);
while (--old_size>0) {
if (my_mallocs[number].memory[old_size]!=my_mallocs[number].filled_with)
fprintf(stderr, "Wrong filling!\n");
}
count_realloc++;
}
}
} else {
if (total_memory+size>MAXIMAL_MEMORY_TO_ALLOCATE) {
goto output; /* for-loop */
}
my_mallocs[number].memory=(char *)malloc(size); /* Allokera! */
if (!my_mallocs[number].memory)
out_of_memory=size;
else {
size_allocs[table_position]++;
count_malloc++;
total_memory += size;
}
}
if(!out_of_memory) {
my_mallocs[number].table_position=table_position;
my_mallocs[number].size=size;
my_mallocs[number].filled_with=fill_with;
memset(my_mallocs[number].memory, fill_with, size);
}
output:
if (out_of_memory || !(count%DISPLAY_WHEN)) {
printf("(%d) malloc %d, realloc %d, free %d, total size %d\n",
count, count_malloc, count_realloc, count_free, total_memory);
{
int count;
printf("[size bytes]=[number of allocations]\n");
for (count=0; count<TABLESIZE; count++) {
printf(" %ld=%ld\n", size_table[count], size_allocs[count]);
}
printf("\n\n");
}
}
if (out_of_memory) {
if(out_of_memory)
printf("Couldn't get %d bytes\n", out_of_memory);
dmalloc_status();
bmalloc_status();
fprintf(stderr, "Memory is out! Continue (y/n)");
switch (getchar()) {
case 'y':
case 'Y':
out_of_memory=FALSE;
break;
}
fprintf(stderr, "\n");
}
}
printf("\n");
return 0;
}

View file

@ -0,0 +1,21 @@
Package: dbestfit - a dynamic best-fit memory allocator
Date: 1996 - 2002
Version: 3.3
Author: Daniel Stenberg <daniel@haxx.se>
License: MIT
I wrote the dmalloc part for small allocation sizes to improve the behavior
of the built-in (first-fit) allocator found in pSOS, during late 1996 and
spring 1997.
I wrote the bmalloc part (best-fit with optional splay-tree sorting) just for
the fun of it and to see how good malloc() implementation I could make. The
quality of my implementation is still left to be judged in real-world tests.
TODO:
* Remove the final not-so-very-nice loop in dmalloc.c that checks for a block
with free fragments (when the list gets longer too much time might be spent
in that loop).
* Make a separate application that samples the memory usage of a program
and is capable of replaying it (in order to test properly).

View file

@ -0,0 +1,170 @@
=====================================
Memory Allocation Algorithm Theories.
=====================================
GOAL
It is intended to be a 100% working memory allocation system. It should be
capable of replacing an ordinary Operating System's own routines. It should
work good in a multitasking, shared memory, non-virtual memory environment
without clogging the memory. Primary aimed for small machines, CPUs and
memory amounts.
I use a best-fit algorithm with a slight overhead in order to increase speed
a lot. It should remain scalable and work good with very large amount of
memory and free/used memory blocks too.
TERMINOLOGY
FRAGMENT - small identically sized parts of a larger BLOCK, they are when
travered in lists etc _not_ allocated.
BLOCK - large memory area, if used for FRAGMENTS, they are linked in a
lists. One list for each FRAGMENT size supported.
TOP - head struct that holds information about and points to a chain
of BLOCKS for a particular FRAGMENT size.
CHUNK - a contiguous area of free memory
MEMORY SYSTEM
We split the system in two parts. One part allocates small memory amounts
and one part allocates large memory amounts, but all allocations are done
"through" the small-part-system. There is an option to use only the small
system (and thus use the OS for large blocks) or the complete package.
##############################################################################
SMALL SIZE ALLOCATIONS
##############################################################################
Keywords for this system is 'Deferred Coalescing' and 'quick lists'.
ALLOC
* Small allocations are "aligned" upwards to a set of preset sizes. In the
current implementation I use 20, 28, 52, 116, 312, 580, 812, 2028 bytes.
Memory allocations of these sizes are refered to as FRAGMENTS.
(The reason for these specific sizes is the requirement that they must be
32-bit aligned and fit as good as possible within 4060 bytes.)
* Allocations larger than 2028 will get a BLOCK for that allocation only.
* Each of these sizes has it's own TOP. When a FRAGMENT is requested, a
larger BLOCK will be allocated and divided into many FRAGMENTS (all of the
same size). TOP points to a list with BLOCKS that contains FRAGMENTS of
the same size. Each BLOCK has a 'number of free FRAGMENTS' counter and so
has each TOP (for the entire chain).
* A BLOCK is around 4060 bytes plus the size of the information header. This
size is adjusted to make the allocation of the big block not require more
than 4096 bytes. (This might not be so easy to be sure of, if you don't
know how the big-block system works, but the BMALLOC system uses an
extra header of 12 bytes and the header for the FRAGMENT BLOCK is 20 bytes
in a general 32-bit unix environment.)
* In case the allocation of a BLOCK fails when a FRAGMENT is required, the
next size of FRAGMENTS will be checked for a free FRAGMENT. First when the
larger size lists have been tested without success it will fail for real.
FREE
* When FRAGMENTS are freed so that a BLOCK becomes non-used, it is returned
to the system.
* FREEing a fragment adds the buffer in a LIFO-order. That means that the
next request for a fragment from the same list, the last freed buffer will
be returned first.
REALLOC
* REALLOCATION of a FRAGMENT does first check if the new size would fit
within the same FRAGMENT and if it would use the same FRAGMENT size. If it
does and would, the same pointer is returned.
OVERHEAD
Yes, there is an overhead on small allocations (internal fragmentation).
Yet, I do believe that small allocations more often than larger ones are
used dynamically. I believe that a large overhead is not a big problem if it
remains only for a while. The big gain is with the extreme speed we can GET
and RETURN small allocations. This has yet to be proven. I am open to other
systems of dealing with the small ones, but I don`t believe in using the
same system for all sizes of allocations.
IMPROVEMENT
An addition to the above described algorithm is the `save-empty-BLOCKS-a-
while-afterwards`. It will be used when the last used FRAGMENT within a
BLOCK is freed. The BLOCK will then not get returned to the system until "a
few more" FRAGMENTS have been freed in case the last [few] freed FRAGMENTS
are allocated yet again (and thus prevent the huge overhead of making
FRAGMENTS in a BLOCK). The "only" drawback of such a SEBAWA concept is
that it would mean an even bigger overhead...
HEADERS (in allocated data)
FRAGMENTS - 32-bit pointer to its parent BLOCK (lowest bit must be 0)
BLOCK - 32-bit size (lowest bit must be 1 to separate this from
FRAGMENTS)
##############################################################################
LARGER ALLOCATIONS
##############################################################################
If the requested size is larger than the largest FRAGMENT size supported,
the allocation will be made for this memory area alone, or if a BLOCK is
allocated to fit lots of FRAGMENTS a large block is also desired.
* We add memory to the "system" with the add_pool() function call. It
specifies the start and size of the new block of memory that will be
used in this memory allocation system. Several add_pool() calls are
supported and they may or may not add contiguous memory.
* Make all blocks get allocated aligned to BLOCKSIZE (sometimes referred to
as 'grain size'), 64 bytes in my implementation. Reports tell us there is
no real gain in increasing the size of the align.
* We link *all* pieces of memory (AREAS), free or not free. We keep the list
in address order and thus when a FREE() occurs we know instanstly if there
are FREE CHUNKS wall-to-wall. No list "travels" needed. Requires some
extra space in every allocated BLOCK. Still needs to put the new CHUNK in
the right place in size-sorted list/tree. All memory areas, allocated or
not, contain the following header:
- size of this memory area
- FREE status
- pointer to the next AREA closest in memory
- pointer to the prev AREA closest in memory
(12 bytes)
* Sort all FREE CHUNKS in size-order. We use a SPLAY TREE algorithm for
maximum speed. Data/structs used for the size-sorting functions are kept
in an abstraction layer away from this since it is really not changing
anything (except executing speed).
ALLOC (RSIZE - requested size, aligned properly)
* Fetch a CHUNK that RSIZE fits within. If the found CHUNK is larger than
RSIZE, split it and return the RSIZE to the caller. Link the new CHUNK
into the list/tree.
FREE (AREA - piece of memory that is returned to the system)
* Since the allocated BLOCK has kept its link-pointers, we can without
checking any list instantly see if there are any FREE CHUNKS that are
wall-to-wall with the AREA (both sides). If the AREA *is* wall-to-wall
with one or two CHUNKS that or they are unlinked from the lists, enlarged
and re-linked into the lists.
REALLOC
* There IS NO realloc() of large blocks, they are performed in the higher
layer (dmalloc).
##############################################################################
FURTHER READING
##############################################################################
* "Dynamic Storage Allocation: A Survey and Critical Review" (Paul R. Wilson,
Mark S. Johnstone, Michael Neely, David Boles)
ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps
* "A Memory Allocator" (Doug Lea)
http://g.oswego.edu/dl/html/malloc.html

View file

@ -0,0 +1,366 @@
/*****************************************************************************
*
* Big (best-fit) Memory Allocation
*
* Author: Daniel Stenberg <daniel@haxx.se>
*
* Read THOUGHTS for theories and details on implementation.
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "bysize.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* #define DEBUG */
#define BMEM_ALIGN 64 /* resolution */
#define BMEMERR_TOOSMALL -1
/* this struct will be stored in all CHUNKS and AREAS */
struct BlockInfo {
struct BlockInfo *lower; /* previous block in memory (lower address) */
struct BlockInfo *higher; /* next block in memory (higher address) */
unsigned long info; /* 31 bits size: 1 bit free boolean */
#define INFO_FREE 1
#define INFO_SIZE (~ INFO_FREE) /* inverted FREE bit pattern */
/* FREE+SIZE Could be written to use ordinary bitfields if using a smart
(like gcc) compiler in a manner like:
int size:31;
int free:1;
The 'higher' pointer COULD be removed completely if the size is used as
an index to the higher one. This would then REQUIRE the entire memory
pool to be contiguous and it needs a 'terminating' "node" or an extra
flag that informs about the end of the list.
*/
};
/* the BLOCK list should be sorted in a lower to higher address order */
struct BlockInfo *blockHead=NULL; /* nothing from the start */
void bmalloc_status(void);
/***********************************************************************
*
* remove_block()
*
* Remove the block from the address-sorted list.
*
***********************************************************************/
static
void remove_block(struct BlockInfo *block)
{
if(block->lower)
block->lower->higher = block->higher;
else
blockHead = block->higher;
if(block->higher)
block->higher->lower = block->lower;
}
/****************************************************************************
*
* add_blocktolists()
*
* Adds the specified block at the specified place in the address-sorted
* list and at the appropriate place in the size-sorted.
*
***************************************************************************/
static
void add_blocktolists(struct BlockInfo *block,
struct BlockInfo *newblock,
size_t newsize)
{
struct BlockInfo *temp; /* temporary storage variable */
if(block) {
/* `block' is now a lower address than 'newblock' */
/*
* Check if the new CHUNK is wall-to-wall with the lower addressed
* one (if *that* is free)
*/
if(block->info&INFO_FREE) {
if((char *)block + (block->info&INFO_SIZE) == (char *)newblock) {
/* yes sir, this is our lower address neighbour, enlarge that one
pick it out from the list and recursively add that chunk and
then we escape */
/* remove from size-sorted list: */
bmalloc_remove_chunksize((char*)block+sizeof(struct BlockInfo));
block->info += newsize; /* newsize is an even number and thus the FREE
bit is untouched */
remove_block(block); /* unlink the block address-wise */
/* recursively check our lower friend(s) */
add_blocktolists(block->lower, block, block->info&INFO_SIZE);
return;
}
}
temp = block->higher;
block->higher = newblock;
newblock->lower = block;
newblock->higher = temp;
}
else {
/* this block should preceed the heading one */
temp = blockHead;
/* check if this is our higher addressed neighbour */
if((char *)newblock + newsize == (char *)temp) {
/* yes, we are wall-to-wall with the higher CHUNK */
if(temp->info&INFO_FREE) {
/* and the neighbour is even free, remove that one and enlarge
ourselves, call add_pool() recursively and then escape */
remove_block(temp); /* unlink 'temp' from list */
/* remove from size-sorted list: */
bmalloc_remove_chunksize((char*)temp+sizeof(struct BlockInfo) );
/* add the upper block's size on ourselves */
newsize += temp->info&INFO_SIZE;
/* add the new, bigger block */
add_blocktolists(block, newblock, newsize);
return;
}
}
blockHead = newblock;
newblock->higher = temp;
newblock->lower = NULL; /* there is no lower one */
}
newblock->info = newsize | INFO_FREE; /* we do assume size isn't using the
FREE bit */
bmalloc_insert_bysize((char *)newblock+sizeof(struct BlockInfo), newsize);
}
/***********************************************************************
*
* findblockbyaddr()
*
* Find the block that is just before the input block in memory. Returns NULL
* if none is.
*
***********************************************************************/
static
struct BlockInfo *findblockbyaddr(struct BlockInfo *block)
{
struct BlockInfo *test = blockHead;
struct BlockInfo *lower = NULL;
while(test && (test < block)) {
lower = test;
test = test->higher;
}
return lower;
}
/***********************************************************************
*
* bmalloc_add_pool()
*
* This function should be the absolutely first function to call. It sets up
* the memory bounds of the [first] CHUNK(s). It is possible to call this
* function several times to add more CHUNKs to the pool of free memory. This
* allows the bmalloc system to deal with non-contigous memory areas.
*
* Returns non-zero if an error occured. The memory was not added then.
*
***********************************************************************/
int bmalloc_add_pool(void *start,
size_t size)
{
struct BlockInfo *newblock = (struct BlockInfo *)start;
struct BlockInfo *block;
if(size < BMEM_ALIGN)
return BMEMERR_TOOSMALL;
block = findblockbyaddr( newblock );
/* `block' is now a lower address than 'newblock' or NULL */
if(size&1)
size--; /* only add even sizes */
add_blocktolists(block, newblock, size);
return 0;
}
#ifdef DEBUG_VERBOSE
static void bmalloc_failed(size_t size)
{
printf("*** " __FILE__ " Couldn't allocate %d bytes\n", size);
bmalloc_status();
}
#else
#define bmalloc_failed(x)
#endif
void bmalloc_status()
{
struct BlockInfo *block = blockHead;
long mem_free = 0;
long mem_used = 0;
#if 1
printf("List of BLOCKS (in address order):\n");
while(block) {
printf(" START %p END %p SIZE %ld FLAG %s\n",
block,
(char *)block+(block->info&INFO_SIZE),
block->info&INFO_SIZE,
(block->info&INFO_FREE)?"free":"used");
if(block->info&INFO_FREE)
mem_free += block->info&INFO_SIZE;
else
mem_used += block->info&INFO_SIZE;
block = block->higher;
}
printf(" Used mem: %ld , free mem: %ld (total %ld)\n",
mem_used, mem_free, mem_used + mem_free);
#endif
bmalloc_print_sizes();
}
void *bmalloc(size_t size)
{
void *mem;
#ifdef DEBUG_VERBOSE
{
static int count=0;
int realsize = size + sizeof(struct BlockInfo);
if(realsize%4096)
realsize = ((size / BMEM_ALIGN)+1) * BMEM_ALIGN;
printf("%d bmalloc(%d) [%d]\n", count++, size, realsize);
}
#endif
size += sizeof(struct BlockInfo); /* add memory for our header */
if(size&(BMEM_ALIGN-1)) /* a lot faster than %BMEM_ALIGN but this MUST be
changed if the BLOCKSIZE is not 2^X ! */
size = ((size / BMEM_ALIGN)+1) * BMEM_ALIGN; /* align like this */
/* get a CHUNK from the list with this size */
mem = bmalloc_obtainbysize ( size );
if(mem) {
/* the memory block we have got is the "best-fit" and it is already
un-linked from the free list */
/* now do the math to get the proper block pointer */
struct BlockInfo *block= (struct BlockInfo *)
((char *)mem - sizeof(struct BlockInfo));
block->info &= ~INFO_FREE;
/* not free anymore */
if( size != (block->info&INFO_SIZE)) {
/* split this chunk into two pieces and return the one that fits us */
size_t othersize = (block->info&INFO_SIZE) - size;
if(othersize > BMEM_ALIGN) {
/* prevent losing small pieces of memory due to weird alignments
of the memory pool */
block->info = size; /* set new size (leave FREE bit cleared) */
/* Add the new chunk to the lists: */
add_blocktolists(block->lower,
(struct BlockInfo *)((char *)block + size),
othersize );
}
}
/* Return the memory our parent may use: */
return (char *)block+sizeof(struct BlockInfo);
}
else {
bmalloc_failed(size);
return NULL; /* can't find any memory, fail hard */
}
#ifdef DEBUG_VERBOSE
bmalloc_status();
#endif
return mem;
}
void bfree(void *ptr)
{
struct BlockInfo *block = (struct BlockInfo *)
((char *)ptr - sizeof(struct BlockInfo));
size_t size;
/* setup our initial higher and lower pointers */
struct BlockInfo *lower = block->lower;
struct BlockInfo *higher = block->higher;
#ifdef DEBUG_VERBOSE
static int freecount=0;
printf("%d bfree(%p)\n", freecount++, ptr);
#endif
/* bind together lower addressed FREE CHUNKS */
if(lower && (lower->info&INFO_FREE) &&
((char *)lower + (lower->info&INFO_SIZE) == (char *)block)) {
size = block->info&INFO_SIZE; /* original size */
/* remove from size-link: */
bmalloc_remove_chunksize((char *)lower+sizeof(struct BlockInfo));
remove_block(block); /* unlink from address list */
block = lower; /* new base area pointer */
block->info += size; /* append the new size (the FREE bit
will remain untouched) */
lower = lower->lower; /* new lower pointer */
}
/* bind together higher addressed FREE CHUNKS */
if(higher && (higher->info&INFO_FREE) &&
((char *)block + (block->info&INFO_SIZE) == (char *)higher)) {
/* append higher size, the FREE bit won't be affected */
block->info += (higher->info&INFO_SIZE);
/* unlink from size list: */
bmalloc_remove_chunksize(higher+sizeof(struct BlockInfo));
remove_block(higher); /* unlink from address list */
higher = higher->higher; /* the new higher link */
block->higher = higher; /* new higher link */
}
block->info &= ~INFO_FREE; /* consider this FREE! */
block->lower = lower;
block->higher = higher;
bmalloc_insert_bysize((char *)block+sizeof(struct BlockInfo),
block->info&INFO_SIZE);
#ifdef DEBUG_VERBOSE
bmalloc_status();
#endif
}

View file

@ -0,0 +1,6 @@
int bmalloc_add_pool(void *start, size_t size);
void bmalloc_status(void);
void *bmalloc(size_t size);
void bfree(void *ptr);

View file

@ -0,0 +1,434 @@
/*****************************************************************************
*
* Size-sorted list/tree functions.
*
* Author: Daniel Stenberg
* Date: March 7, 1997
* Version: 2.0
* Email: Daniel.Stenberg@sth.frontec.se
*
*
* v2.0
* - Added SPLAY TREE functionality.
*
* Adds and removes CHUNKS from a list or tree.
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#define SPLAY /* we use the splay version as that is much faster when the
amount of blocks grow */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef SPLAY /* these routines are for the non-splay version */
struct ChunkInfo {
struct ChunkInfo *larger;
struct ChunkInfo *smaller;
size_t size;
};
/* the CHUNK list anchor */
struct ChunkInfo *chunkHead=NULL;
/***********************************************************************
findchunkbysize()
Find the chunk that is smaller than the input size. Returns
NULL if none is.
**********************************************************************/
static struct ChunkInfo *findchunkbysize(size_t size)
{
struct ChunkInfo *test = chunkHead;
struct ChunkInfo *smaller = NULL;
while(test && (test->size < size)) {
smaller = test;
test = test->larger;
}
return smaller;
}
/***********************************************************************
remove_chunksize()
Remove the chunk from the size-sorted list.
***********************************************************************/
void bmalloc_remove_chunksize(void *data)
{
struct ChunkInfo *chunk = (struct ChunkInfo *)data;
if(chunk->smaller)
chunk->smaller->larger = chunk->larger;
else {
/* if this has no smaller, this is the head */
chunkHead = chunk->larger; /* new head */
}
if(chunk->larger)
chunk->larger->smaller = chunk->smaller;
}
void bmalloc_insert_bysize(char *data, size_t size)
{
struct ChunkInfo *newchunk = (struct ChunkInfo *)data;
struct ChunkInfo *chunk = findchunkbysize ( size );
newchunk->size = size;
if(chunk) {
/* 'chunk' is smaller than size, append the new chunk ahead of this */
newchunk->smaller = chunk;
newchunk->larger = chunk->larger;
if(chunk->larger)
chunk->larger->smaller = newchunk;
chunk->larger = newchunk;
}
else {
/* smallest CHUNK around, append first in the list */
newchunk->larger = chunkHead;
newchunk->smaller = NULL;
if(chunkHead)
chunkHead->smaller = newchunk;
chunkHead = newchunk;
}
}
char *bmalloc_obtainbysize( size_t size)
{
struct ChunkInfo *chunk = findchunkbysize( size );
if(!chunk) {
if(size <= (chunkHead->size))
/* there is no smaller CHUNK, use the first one (if we fit within that)
*/
chunk = chunkHead;
}
else
/* we're on the last CHUNK that is smaller than requested, step onto
the bigger one */
chunk = chunk->larger;
if(chunk) {
bmalloc_remove_chunksize( chunk ); /* unlink size-wise */
return (char *)chunk;
}
else
return NULL;
}
void bmalloc_print_sizes(void)
{
struct ChunkInfo *chunk = chunkHead;
printf("List of CHUNKS (in size order):\n");
#if 1
while(chunk) {
printf(" START %p END %p SIZE %d\n",
chunk, (char *)chunk+chunk->size, chunk->size);
chunk = chunk->larger;
}
#endif
printf("End of CHUNKS:\n");
}
#else /* Here follows all routines dealing with the SPLAY TREES */
typedef struct tree_node Tree;
struct tree_node {
Tree *smaller; /* smaller node */
Tree *larger; /* larger node */
Tree *same; /* points to a node with identical key */
int key; /* the "sort" key */
};
Tree *chunkHead = NULL; /* the root */
#define compare(i,j) ((i)-(j))
/* Set this to a key value that will *NEVER* appear otherwise */
#define KEY_NOTUSED -1
/*
* Splay using the key i (which may or may not be in the tree.) The starting
* root is t. Weight fields are maintained.
*/
static
Tree * splay (int i, Tree *t)
{
Tree N, *l, *r, *y;
int comp;
if (t == NULL)
return t;
N.smaller = N.larger = NULL;
l = r = &N;
for (;;) {
comp = compare(i, t->key);
if (comp < 0) {
if (t->smaller == NULL)
break;
if (compare(i, t->smaller->key) < 0) {
y = t->smaller; /* rotate smaller */
t->smaller = y->larger;
y->larger = t;
t = y;
if (t->smaller == NULL)
break;
}
r->smaller = t; /* link smaller */
r = t;
t = t->smaller;
}
else if (comp > 0) {
if (t->larger == NULL)
break;
if (compare(i, t->larger->key) > 0) {
y = t->larger; /* rotate larger */
t->larger = y->smaller;
y->smaller = t;
t = y;
if (t->larger == NULL)
break;
}
l->larger = t; /* link larger */
l = t;
t = t->larger;
}
else {
break;
}
}
l->larger = r->smaller = NULL;
l->larger = t->smaller; /* assemble */
r->smaller = t->larger;
t->smaller = N.larger;
t->larger = N.smaller;
return t;
}
/* Insert key i into the tree t. Return a pointer to the resulting tree or
NULL if something went wrong. */
static
Tree *insert(int i, Tree *t, Tree *new)
{
if (new == NULL) {
return t;
}
if (t != NULL) {
t = splay(i,t);
if (compare(i, t->key)==0) {
/* it already exists one of this size */
new->same = t;
new->key = i;
new->smaller = t->smaller;
new->larger = t->larger;
t->smaller = new;
t->key = KEY_NOTUSED;
return new; /* new root node */
}
}
if (t == NULL) {
new->smaller = new->larger = NULL;
}
else if (compare(i, t->key) < 0) {
new->smaller = t->smaller;
new->larger = t;
t->smaller = NULL;
}
else {
new->larger = t->larger;
new->smaller = t;
t->larger = NULL;
}
new->key = i;
new->same = NULL; /* no identical node (yet) */
return new;
}
/* Finds and deletes the best-fit node from the tree. Return a pointer to the
resulting tree. best-fit means the smallest node that fits the requested
size. */
static
Tree *removebestfit(int i, Tree *t, Tree **removed)
{
Tree *x;
if (t==NULL)
return NULL;
t = splay(i,t);
if(compare(i, t->key) > 0) {
/* too small node, try the larger chain */
if(t->larger)
t=splay(t->larger->key, t);
else {
/* fail */
*removed = NULL;
return t;
}
}
if (compare(i, t->key) <= 0) { /* found it */
/* FIRST! Check if there is a list with identical sizes */
x = t->same;
if(x) {
/* there is, pick one from the list */
/* 'x' is the new root node */
x->key = t->key;
x->larger = t->larger;
x->smaller = t->smaller;
*removed = t;
return x; /* new root */
}
if (t->smaller == NULL) {
x = t->larger;
}
else {
x = splay(i, t->smaller);
x->larger = t->larger;
}
*removed = t;
return x;
}
else {
*removed = NULL; /* no match */
return t; /* It wasn't there */
}
}
/* Deletes the node we point out from the tree if it's there. Return a pointer
to the resulting tree. */
static
Tree *removebyaddr(Tree *t, Tree *remove)
{
Tree *x;
if (!t || !remove)
return NULL;
if(KEY_NOTUSED == remove->key) {
/* just unlink ourselves nice and quickly: */
remove->smaller->same = remove->same;
if(remove->same)
remove->same->smaller = remove->smaller;
/* voila, we're done! */
return t;
}
t = splay(remove->key,t);
/* Check if there is a list with identical sizes */
x = t->same;
if(x) {
/* 'x' is the new root node */
x->key = t->key;
x->larger = t->larger;
x->smaller = t->smaller;
return x; /* new root */
}
/* Remove the actualy root node: */
if (t->smaller == NULL) {
x = t->larger;
}
else {
x = splay(remove->key, t->smaller);
x->larger = t->larger;
}
return x;
}
#ifdef DEBUG
static
int printtree(Tree * t, int d, char output)
{
int distance=0;
Tree *node;
int i;
if (t == NULL)
return 0;
distance += printtree(t->larger, d+1, output);
for (i=0; i<d; i++)
if(output)
printf(" ");
if(output) {
printf("%d[%d]", t->key, i);
}
for(node = t->same; node; node = node->same) {
distance += i; /* this has the same "virtual" distance */
if(output)
printf(" [+]");
}
if(output)
puts("");
distance += i;
distance += printtree(t->smaller, d+1, output);
return distance;
}
#endif
/* Here follow the look-alike interface so that the tree-function names are
the same as the list-ones to enable easy interchange */
void bmalloc_remove_chunksize(void *data)
{
chunkHead = removebyaddr(chunkHead, data);
}
void bmalloc_insert_bysize(char *data, size_t size)
{
chunkHead = insert(size, chunkHead, (Tree *)data);
}
char *bmalloc_obtainbysize( size_t size)
{
Tree *receive;
chunkHead = removebestfit(size, chunkHead, &receive);
return (char *)receive;
}
#ifdef DEBUG
void bmalloc_print_sizes(void)
{
printtree(chunkHead, 0, 1);
}
#endif
#endif

View file

@ -0,0 +1,6 @@
void bmalloc_remove_chunksize(void *data);
void bmalloc_insert_bysize(char *data, size_t size);
char *bmalloc_obtainbysize( size_t size);
#ifdef DEBUG
void bmalloc_print_sizes(void);
#endif

View file

@ -0,0 +1,604 @@
/*****************************************************************************
*
* Dynamic small-blocks Memory Allocation
*
* Author: Daniel Stenberg <daniel@haxx.se>
*
* Read THOUGHTS for theories and details on the implementation.
*
*****************************************************************************/
#include <stdio.h>
#include <string.h> /* memcpy */
#ifdef DEBUG
#include <stdarg.h>
#endif
#ifdef PSOS
#include <psos.h>
#define SEMAPHORE /* the PSOS routines use semaphore protection */
#else
#include <stdlib.h> /* makes the PSOS complain on the 'size_t' typedef */
#endif
#ifdef BMALLOC
#include "bmalloc.h"
#endif
/* Each TOP takes care of a chain of BLOCKS */
struct MemTop {
struct MemBlock *chain; /* pointer to the BLOCK chain */
long nfree; /* total number of free FRAGMENTS in the chain */
short nmax; /* total number of FRAGMENTS in this kind of BLOCK */
size_t fragsize; /* the size of each FRAGMENT */
#ifdef SEMAPHORE /* if we're protecting the list with SEMAPHORES */
long semaphore_id; /* semaphore used to lock this particular list */
#endif
};
/* Each BLOCK takes care of an amount of FRAGMENTS */
struct MemBlock {
struct MemTop *top; /* our TOP struct */
struct MemBlock *next; /* next BLOCK */
struct MemBlock *prev; /* prev BLOCK */
struct MemFrag *first; /* the first free FRAGMENT in this block */
short nfree; /* number of free FRAGMENTS in this BLOCK */
};
/* This is the data kept in all _free_ FRAGMENTS */
struct MemFrag {
struct MemFrag *next; /* next free FRAGMENT */
struct MemFrag *prev; /* prev free FRAGMENT */
};
/* This is the data kept in all _allocated_ FRAGMENTS and BLOCKS. We add this
to the allocation size first thing in the ALLOC function to make room for
this smoothly. */
struct MemInfo {
void *block;
/* which BLOCK is our father, if BLOCK_BIT is set it means this is a
stand-alone, large allocation and then the rest of the bits should be
treated as the size of the block */
#define BLOCK_BIT 1
};
/* ---------------------------------------------------------------------- */
/* Defines */
/* ---------------------------------------------------------------------- */
#ifdef DEBUG_VERBOSE
#define MEMINCR(addr,x) memchange(addr, x)
#define MEMDECR(addr,x) memchange(addr,-(x))
#else
#define MEMINCR(a,x)
#define MEMDECR(a,x)
#endif
/* The low level functions used to get memory from the OS and to return memory
to the OS, we may also define a stub that does the actual allocation and
free, these are the defined function names used in the dmalloc system
anyway: */
#ifdef PSOS
#ifdef DEBUG
#define DMEM_OSALLOCMEM(size,pointer,type) pointer=(type)dbgmalloc(size)
#define DMEM_OSFREEMEM(x) dbgfree(x)
#else
#define DMEM_OSALLOCMEM(size,pointer,type) rn_getseg(0,size,RN_NOWAIT,0,(void **)&pointer)
/* Similar, but this returns the memory */
#define DMEM_OSFREEMEM(x) rn_retseg(0, x)
#endif
/* Argument: <id> */
#define SEMAPHOREOBTAIN(x) sm_p(x, SM_WAIT, 0)
/* Argument: <id> */
#define SEMAPHORERETURN(x) sm_v(x)
/* Argument: <name> <id-variable name> */
#define SEMAPHORECREATE(x,y) sm_create(x, 1, SM_FIFO, (ULONG *)&(y))
#else
#ifdef BMALLOC /* use our own big-memory-allocation system */
#define DMEM_OSALLOCMEM(size,pointer,type) pointer=(type)bmalloc(size)
#define DMEM_OSFREEMEM(x) bfree(x)
#elif DEBUG
#define DMEM_OSALLOCMEM(size,pointer,type) pointer=(type)dbgmalloc(size)
#define DMEM_OSFREEMEM(x) dbgfree(x)
#else
#define DMEM_OSALLOCMEM(size,pointer,type) pointer=(type)malloc(size)
#define DMEM_OSFREEMEM(x) free(x)
#endif
#endif
/* the largest memory allocation that is made a FRAGMENT: (grab the highest
number from the list below) */
#define DMEM_LARGESTSIZE 2032
/* The total size of a BLOCK used for FRAGMENTS
In order to make this use only *1* even alignment from the big-block-
allocation-system (possible the bmalloc() system also written by me)
we need to subtract the [maximum] struct sizes that could get added all
the way through to the grab from the memory. */
#define DMEM_BLOCKSIZE 4064 /* (4096 - sizeof(struct MemBlock) - 12) */
/* Since the blocksize isn't an even 2^X story anymore, we make a table with
the FRAGMENT sizes and amounts that fills up a BLOCK nicely */
/* a little 'bc' script that helps us maximize the usage:
- for 32-bit aligned addresses (SPARC crashes otherwise):
for(i=20; i<2040; i++) { a=4064/i; if(a*i >= 4060) { if(i%4==0) {i;} } }
I try to approximate a double of each size, starting with ~20. We don't do
ODD sizes since several CPU flavours dump core when accessing such
addresses. We try to do 32-bit aligned to make ALL kinds of CPUs to remain
happy with us.
*/
short qinfo[]= { 20, 28, 52, 116, 312, 580, 1016, 2032};
/* 52 and 312 only make use of 4056 bytes, but without them there are too
wide gaps */
#define MIN(x,y) ((x)<(y)?(x):(y))
/* ---------------------------------------------------------------------- */
/* Globals */
/* ---------------------------------------------------------------------- */
/* keeper of the chain of BLOCKS */
static struct MemTop top[ sizeof(qinfo)/sizeof(qinfo[0]) ];
/* ---------------------------------------------------------------------- */
/* Start of the real code */
/* ---------------------------------------------------------------------- */
#ifdef DEBUG
/************
* A few functions that are verbose and tells us about the current status
* of the dmalloc system
***********/
void dmalloc_status(void)
{
int i;
int used;
int num;
int totalfree=0;
struct MemBlock *block;
for(i=0; i<sizeof(qinfo)/sizeof(qinfo[0]);i++) {
block = top[i].chain;
used = 0;
num = 0;
while(block) {
used += top[i].nmax-block->nfree;
num++;
block = block->next;
}
printf("Q %d (FRAG %4d), USED %4d FREE %4ld (SIZE %4ld) BLOCKS %d\n",
i, top[i].fragsize, used, top[i].nfree,
top[i].nfree*top[i].fragsize, num);
totalfree += top[i].nfree*top[i].fragsize;
}
printf("Total unused memory stolen by dmalloc: %d\n", totalfree);
}
#endif
#ifdef DEBUG_VERBOSE
static void dmalloc_failed(size_t size)
{
printf("*** " __FILE__ " Couldn't allocate %d bytes\n", size);
dmalloc_status();
}
#else
#define dmalloc_failed(x)
#endif
#ifdef DEBUG_VERBOSE
#define DBG(x) syslog x
void syslog(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stdout, fmt, ap);
va_end(ap);
}
void memchange(void *a, int x)
{
static int memory=0;
static int count=0;
static int max=0;
if(memory > max)
max = memory;
memory += x;
DBG(("%d. PTR %p / %d TOTAL %d MAX %d\n", ++count, a, x, memory, max));
}
#else
#define DBG(x)
#endif
/****************************************************************************
*
* FragBlock()
*
* This function makes FRAGMENTS of the BLOCK sent as argument.
*
***************************************************************************/
static void FragBlock(char *memp, int size)
{
struct MemFrag *frag=(struct MemFrag *)memp;
struct MemFrag *prev=NULL; /* no previous in the first round */
int count=0;
while((count+size) <= DMEM_BLOCKSIZE) {
frag->next = (struct MemFrag *)((char *)frag + size);
frag->prev = prev;
prev = frag;
(char *)frag += size;
count += size;
}
prev->next = NULL; /* the last one has no next struct */
}
/***************************************************************************
*
* dmalloc_initialize();
*
* Call before the first dmalloc(). Inits a few memory things.
*
**************************************************************************/
void dmalloc_initialize(void)
{
int i;
/* Setup the nmax and fragsize fields of the top structs */
for(i=0; i< sizeof(qinfo)/sizeof(qinfo[0]); i++) {
top[i].fragsize = qinfo[i];
top[i].nmax = DMEM_BLOCKSIZE/qinfo[i];
#ifdef PSOS
/* for some reason, these aren't nulled from start: */
top[i].chain = NULL; /* no BLOCKS */
top[i].nfree = 0; /* no FRAGMENTS */
#endif
#ifdef SEMAPHORE
{
char name[7];
sprintf(name, "MEM%d", i);
SEMAPHORECREATE(name, top[i].semaphore_id);
/* doesn't matter if it failed, we continue anyway ;-( */
}
#endif
}
}
/****************************************************************************
*
* fragfromblock()
*
* This should return a fragment from the block and mark it as used
* accordingly.
*
***************************************************************************/
static void *fragfromblock(struct MemBlock *block)
{
/* make frag point to the first free FRAGMENT */
struct MemFrag *frag = block->first;
struct MemInfo *mem = (struct MemInfo *)frag;
/*
* Remove the FRAGMENT from the list and decrease the free counters.
*/
block->first = frag->next; /* new first free FRAGMENT */
block->nfree--; /* BLOCK counter */
block->top->nfree--; /* TOP counter */
/* heal the FRAGMENT list */
if(frag->prev) {
frag->prev->next = frag->next;
}
if(frag->next) {
frag->next->prev = frag->prev;
}
mem->block = block; /* no block bit set here */
return ((char *)mem)+sizeof(struct MemInfo);
}
/***************************************************************************
*
* dmalloc()
*
* This needs no explanation. A malloc() look-alike.
*
**************************************************************************/
void *dmalloc(size_t size)
{
void *mem;
DBG(("dmalloc(%d)\n", size));
/* First, we make room for the space needed in every allocation */
size += sizeof(struct MemInfo);
if(size < DMEM_LARGESTSIZE) {
/* get a FRAGMENT */
struct MemBlock *block=NULL; /* SAFE */
struct MemBlock *newblock=NULL; /* SAFE */
struct MemTop *memtop=NULL; /* SAFE */
/* Determine which queue to use */
int queue;
for(queue=0; size > qinfo[queue]; queue++)
;
do {
/* This is the head master of our chain: */
memtop = &top[queue];
DBG(("Top info: CHAIN %p FREE %d MAX %d FRAGSIZE %d\n",
memtop->chain,
memtop->nfree,
memtop->nmax,
memtop->fragsize));
#ifdef SEMAPHORE
if(SEMAPHOREOBTAIN(memtop->semaphore_id))
return NULL; /* failed somehow */
#endif
/* get the first BLOCK in the chain */
block = memtop->chain;
/* check if we have a free FRAGMENT */
if(memtop->nfree) {
/* there exists a free FRAGMENT in this chain */
/* I WANT THIS LOOP OUT OF HERE! */
/* search for the free FRAGMENT */
while(!block->nfree)
block = block->next; /* check next BLOCK */
/*
* Now 'block' is the first BLOCK with a free FRAGMENT
*/
mem = fragfromblock(block);
}
else {
/* we do *not* have a free FRAGMENT but need to get us a new BLOCK */
DMEM_OSALLOCMEM(DMEM_BLOCKSIZE + sizeof(struct MemBlock),
newblock,
struct MemBlock *);
if(!newblock) {
if(++queue < sizeof(qinfo)/sizeof(qinfo[0])) {
/* There are queues for bigger FRAGMENTS that we should check
before we fail this for real */
#ifdef DEBUG_VERBOSE
printf("*** " __FILE__ " Trying a bigger Q: %d\n", queue);
#endif
mem = NULL;
}
else {
dmalloc_failed(size- sizeof(struct MemInfo));
return NULL; /* not enough memory */
}
}
else {
/* allocation of big BLOCK was successful */
MEMINCR(newblock, DMEM_BLOCKSIZE + sizeof(struct MemBlock));
memtop->chain = newblock; /* attach this BLOCK to the chain */
newblock->next = block; /* point to the previous first BLOCK */
if(block)
block->prev = newblock; /* point back on this new BLOCK */
newblock->prev = NULL; /* no previous */
newblock->top = memtop; /* our head master */
/* point to the new first FRAGMENT */
newblock->first = (struct MemFrag *)
((char *)newblock+sizeof(struct MemBlock));
/* create FRAGMENTS of the BLOCK: */
FragBlock((char *)newblock->first, memtop->fragsize);
/* fix the nfree counters */
newblock->nfree = memtop->nmax;
memtop->nfree += memtop->nmax;
/* get a FRAGMENT from the BLOCK */
mem = fragfromblock(newblock);
}
}
#ifdef SEMAPHORE
SEMAPHORERETURN(memtop->semaphore_id); /* let it go */
#endif
} while(NULL == mem); /* if we should retry a larger FRAGMENT */
}
else {
/* get a stand-alone BLOCK */
struct MemInfo *meminfo;
if(size&1)
/* don't leave this with an odd size since we'll use that bit for
information */
size++;
DMEM_OSALLOCMEM(size, meminfo, struct MemInfo *);
if(meminfo) {
MEMINCR(meminfo, size);
meminfo->block = (void *)(size|BLOCK_BIT);
mem = (char *)meminfo + sizeof(struct MemInfo);
}
else {
dmalloc_failed(size);
mem = NULL;
}
}
return (void *)mem;
}
/***************************************************************************
*
* dfree()
*
* This needs no explanation. A free() look-alike.
*
**************************************************************************/
void dfree(void *memp)
{
struct MemInfo *meminfo = (struct MemInfo *)
((char *)memp- sizeof(struct MemInfo));
DBG(("dfree(%p)\n", memp));
if(!((size_t)meminfo->block&BLOCK_BIT)) {
/* this is a FRAGMENT we have to deal with */
struct MemBlock *block=meminfo->block;
struct MemTop *memtop = block->top;
#ifdef SEMAPHORE
SEMAPHOREOBTAIN(memtop->semaphore_id);
#endif
/* increase counters */
block->nfree++;
memtop->nfree++;
/* is this BLOCK completely empty now? */
if(block->nfree == memtop->nmax) {
/* yes, return the BLOCK to the system */
if(block->prev)
block->prev->next = block->next;
else
memtop->chain = block->next;
if(block->next)
block->next->prev = block->prev;
memtop->nfree -= memtop->nmax; /* total counter subtraction */
MEMDECR(block, DMEM_BLOCKSIZE + sizeof(struct MemBlock));
DMEM_OSFREEMEM((void *)block); /* return the whole block */
}
else {
/* there are still used FRAGMENTS in the BLOCK, link this one
into the chain of free ones */
struct MemFrag *frag = (struct MemFrag *)meminfo;
frag->prev = NULL;
frag->next = block->first;
if(block->first)
block->first->prev = frag;
block->first = frag;
}
#ifdef SEMAPHORE
SEMAPHORERETURN(memtop->semaphore_id);
#endif
}
else {
/* big stand-alone block, just give it back to the OS: */
MEMDECR(meminfo->block, (size_t)meminfo->block&~BLOCK_BIT); /* clean BLOCK_BIT */
DMEM_OSFREEMEM((void *)meminfo);
}
}
/***************************************************************************
*
* drealloc()
*
* This needs no explanation. A realloc() look-alike.
*
**************************************************************************/
void *drealloc(char *ptr, size_t size)
{
struct MemInfo *meminfo = (struct MemInfo *)
((char *)ptr- sizeof(struct MemInfo));
/*
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* NOTE: the ->size field of the meminfo will now contain the MemInfo
* struct size too!
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
void *mem=NULL; /* SAFE */
size_t prevsize;
/* NOTE that this is only valid if BLOCK_BIT isn't set: */
struct MemBlock *block;
DBG(("drealloc(%p, %d)\n", ptr, size));
if(NULL == ptr)
return dmalloc( size );
block = meminfo->block;
if(!((size_t)meminfo->block&BLOCK_BIT) &&
(size + sizeof(struct MemInfo) <
(prevsize = block->top->fragsize) )) {
/* This is a FRAGMENT and new size is possible to retain within the same
FRAGMENT */
if((prevsize > qinfo[0]) &&
/* this is not the smallest memory Q */
(size < (block->top-1)->fragsize))
/* this fits in a smaller Q */
;
else
mem = ptr; /* Just return the same pointer as we got in. */
}
if(!mem) {
/* This is a stand-alone BLOCK or a realloc that no longer fits within
the same FRAGMENT */
if((size_t)meminfo->block&BLOCK_BIT) {
prevsize = ((size_t)meminfo->block&~BLOCK_BIT) -
sizeof(struct MemInfo);
}
else
prevsize -= sizeof(struct MemInfo);
/* No tricks involved here, just grab a new bite of memory, copy the data
* from the old place and free the old memory again. */
mem = dmalloc(size);
if(mem) {
memcpy(mem, ptr, MIN(size, prevsize) );
dfree(ptr);
}
}
return mem;
}
/***************************************************************************
*
* dcalloc()
*
* This needs no explanation. A calloc() look-alike.
*
**************************************************************************/
/* Allocate an array of NMEMB elements each SIZE bytes long.
The entire array is initialized to zeros. */
void *
dcalloc (size_t nmemb, size_t size)
{
void *result = dmalloc (nmemb * size);
if (result != NULL)
memset (result, 0, nmemb * size);
return result;
}

View file

@ -0,0 +1,13 @@
void *dmalloc(size_t);
void dfree(void *);
void *drealloc(void *, size_t);
#define malloc(x) dmalloc(x)
#define free(x) dfree(x)
#define realloc(x,y) drealloc(x,y)
#define calloc(x,y) dcalloc(x,y)
/* use this to intialize the internals of the dmalloc engine */
void dmalloc_initialize(void);

View file

@ -0,0 +1,173 @@
#include <stdio.h>
#include <stdlib.h>
#include "dmalloc.h"
#include "bmalloc.h"
#define MAX 500
#define MAX2 1000
#define MAXC 2
#define TESTA
#define TESTB
#define TESTC
#define TESTD
int test1(void)
{
#define MAXK 100
int i;
void *wow[MAXK];
for(i=0; i<MAXK; i++)
if(!(wow[i]=malloc(412))) {
printf("*** Couldn't allocate memory, exiting\n");
return -2;
}
for(i=MAXK-1; i>=0; i-=2)
free(wow[i]);
return 0;
}
int test2(void)
{
#define MAXS 10
#define MAXS1 0
int i;
void *ptr[MAXS];
for(i=MAXS1; i< MAXS; i++) {
printf("%d malloc(%d)\n", i, i*55);
ptr[i] = malloc (i*55);
}
for(i=MAXS1; i< MAXS; i++) {
void *tmp;
printf("%d realloc(%d)\n", i, i*155);
tmp=realloc(ptr[i], i*155);
if(tmp)
ptr[i] = tmp;
}
for(i=MAXS1; i< MAXS; i++) {
if(ptr[i]) {
printf("%d free(%d)\n", i, i*155);
free(ptr[i]);
}
}
return 0;
}
int test3(void)
{
int i;
void *ptr[MAXC];
printf("This is test C:\n");
for(i=0; i< MAXC; i++) {
printf("%d malloc(100)\n", i+1);
ptr[i] = malloc(100);
printf(" ...returned %p\n", ptr[i]);
}
for(i=0; i< MAXC; i++) {
printf("%d free()\n", i+1);
if(ptr[i])
free(ptr[i]);
}
printf("End of test C:\n");
return 0;
}
int test4(void)
{
int i;
int memory = 0;
void *pointers[MAX];
printf("This is test I:\n");
for(i=0; i<MAX; i++) {
printf("%d attempts malloc(%d)\n", i, i*6);
pointers[i]=malloc(i*6);
if(!pointers[i]) {
printf("cant get more memory!");
return(0);
}
memory += (i*6);
}
printf("\namount: %d\n", memory);
memory = 0;
for(i=0; i<MAX; i++) {
printf("%d attempts realloc(%d)\n", i, i*7);
pointers[i]=realloc(pointers[i], i*7);
memory += i*7;
}
printf("\namount: %d\n", memory);
for(i=0; i<MAX; i++) {
printf("%d attempts free(%d)\n", i, i*7);
free(pointers[i]);
}
printf("\nend of test 1\n");
return 0;
}
int test5(void)
{
int memory = 0;
int i;
void *pointers2[MAX2];
memory = 0;
printf("\nTest II\n");
for(i=0; i< MAX2; i++) {
printf("%d attempts malloc(%d)\n", i, 7);
pointers2[i] = malloc(7);
memory += 7;
}
printf("\namount: %d\n", memory);
for(i=0; i< MAX2; i++) {
if(pointers2[i])
free(pointers2[i]);
}
printf("\nend of test II\n");
return 0;
}
#define HEAPSIZE 10000
void smallblocks(void)
{
void *ptr;
int i=0;
do {
ptr = malloc(16);
i++;
} while(ptr);
printf("I: %d\n", i);
}
int main(int argc, char **argv)
{
void *heap = (malloc)(HEAPSIZE);
if(!heap)
return -1;
dmalloc_initialize();
bmalloc_add_pool(heap, HEAPSIZE);
smallblocks();
bmalloc_status();
dmalloc_status();
return 0;
test1();
test2();
test3();
test4();
test5();
return 0;
}

View file

@ -0,0 +1,70 @@
#include <stdio.h>
#include <stdlib.h>
#include "bmalloc.h"
int main(int argc, char **argv)
{
void *pointers[5];
int i;
void *area;
for(i=0; i<5; i++)
pointers[i] = malloc(8000);
if(argc>1) {
switch(argv[1][0]) {
case '1':
for(i=0; i<5; i++) {
bmalloc_add_pool(pointers[i], 4000);
bmalloc_add_pool((char *)pointers[i]+4000, 4000);
}
break;
case '2':
area = malloc(20000);
bmalloc_add_pool(area, 3000);
bmalloc_add_pool((char *)area+6000, 3000);
bmalloc_add_pool((char *)area+3000, 3000);
bmalloc_add_pool((char *)area+12000, 3000);
bmalloc_add_pool((char *)area+9000, 3000);
break;
case '3':
{
void *ptr[10];
area = malloc(20000);
bmalloc_add_pool(area, 20000);
printf(" ** TEST USAGE\n");
for(i=0; i<9; i++)
ptr[i]=bmalloc(200);
bmalloc_status();
for(i=0; i<9; i++)
bfree(ptr[i]);
printf(" ** END OF TEST USAGE\n");
}
break;
case '4':
{
void *ptr[10];
area = malloc(20000);
bmalloc_add_pool(area, 20000);
ptr[0]=bmalloc(4080);
bmalloc_status();
bfree(ptr[0]);
printf(" ** END OF TEST USAGE\n");
}
break;
}
}
else
for(i=4; i>=0; i--)
bmalloc_add_pool(pointers[i], 8000-i*100);
bmalloc_status();
return 0;
}