Memory tracking

Here’s the code I’m using for video memory tracking.
Can anyone think of any improvements besides a free-list cache?

-Sam Lantinga				(slouken at devolution.com)

Lead Programmer, Loki Entertainment Software–
“Any sufficiently advanced bug is indistinguishable from a feature”
– Rich Kulawiec
-------------- next part --------------

#include <stdio.h>

/* This is the structure we use to keep track of video memory */
typedef struct vidmem_bucket {
struct vidmem_bucket *prev;
int used;
int base;
int size;
struct vidmem_bucket *next;
} vidmem_bucket;

vidmem_bucket surfaces;
int surface_memtotal;
int surface_memleft;

void dump_surfaces()
{
vidmem_bucket *bucket;

printf("Memory left: %d (%d total)\n", surface_memleft, surface_memtotal);
printf("\n");
printf("         Base  Size\n");
for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
	printf("Bucket:  %4.4d, %4.4d (%s)\n", bucket->base, bucket->size, bucket->used ? "used" : "free");
	if ( bucket->prev ) {
		if ( bucket->base != bucket->prev->base+bucket->prev->size ) {
			printf("Warning, corrupt bucket list! (prev)\n");
		}
	} else {
		if ( bucket != &surfaces ) {
			printf("Warning, corrupt bucket list! (!prev)\n");
		}
	}
	if ( bucket->next ) {
		if ( bucket->next->base != bucket->base+bucket->size ) {
			printf("Warning, corrupt bucket list! (next)\n");
		}
	}
}
printf("\n");

}

void init_surfaces(int base, int size)
{
surfaces.prev = NULL;
surfaces.used = 0;
surfaces.base = base;
surfaces.size = size;
surfaces.next = NULL;
surface_memtotal = size;
surface_memleft = size;
}

void free_surfaces()
{
vidmem_bucket *bucket, *freeable;

bucket = surfaces.next;
while ( bucket ) {
	freeable = bucket;
	bucket = bucket->next;
	free(freeable);
}

}

int alloc_surface(int size)
{
vidmem_bucket *bucket;
int extra;

printf(“Allocating bucket of %d bytes\n”, size);

/* Quick check for available mem */
if ( size > surface_memleft ) {
	printf("Not enough video memory\n");
	return(NULL);
}

/* Search for an empty bucket big enough */
for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
	if ( ! bucket->used && (size <= bucket->size) ) {
		break;
	}
}
if ( bucket == NULL ) {
	printf("Video memory too fragmented\n");
	return(NULL);
}

/* Create a new bucket for left-over memory */
extra = (bucket->size - size);
if ( extra ) {
	vidmem_bucket *newbucket;

printf(“Adding new free bucket of %d bytes\n”, extra);
newbucket = (vidmem_bucket *)malloc(sizeof(*newbucket));
if ( newbucket == NULL ) {
printf(“Out of memory\n”);
return(NULL);
}
newbucket->prev = bucket;
newbucket->used = 0;
newbucket->base = bucket->base+size;
newbucket->size = extra;
newbucket->next = bucket->next;
if ( bucket->next ) {
bucket->next->prev = newbucket;
}
bucket->next = newbucket;
}

/* Set the current bucket values and return it! */

printf(“Allocated %d bytes at %d\n”, bucket->size, bucket->base);
bucket->used = 1;
bucket->size = size;
surface_memleft -= size;
return(bucket->base);
}

int free_surface(int base)
{
vidmem_bucket *bucket, *freeable;

/* Look for the bucket in the current list */
for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
	if ( bucket->base == base ) {
		break;
	}
}
if ( (bucket == NULL) || ! bucket->used ) {
	printf("Bucket %d not found!\n", base);
	return(-1);
}

/* Add the memory back to the total */

printf(“Freeing bucket of %d bytes\n”, bucket->size);
surface_memleft += bucket->size;

/* Can we merge the space with surrounding buckets? */
freeable = NULL;
bucket->used = 0;
if ( bucket->next && ! bucket->next->used ) {

printf(“Merging with next bucket, for %d total bytes\n”, bucket->size+bucket->next->size);
freeable = bucket->next;
bucket->size += bucket->next->size;
bucket->next = bucket->next->next;
if ( bucket->next ) {
bucket->next->prev = bucket;
}
memset(freeable, 0, (sizeof *freeable));
free(freeable);
}
if ( bucket->prev && ! bucket->prev->used ) {
printf(“Merging with previous bucket, for %d total bytes\n”, bucket->prev->size+bucket->size);
freeable = bucket;
bucket->prev->size += bucket->size;
bucket->prev->next = bucket->next;
if ( bucket->next ) {
bucket->next->prev = bucket->prev;
}
memset(freeable, 0, (sizeof *freeable));
free(freeable);
}
return(0);
}

main()
{
char line[1024];
int base, size;

init_surfaces(0, 4096);
do {
	dump_surfaces();

	fgets(line, sizeof(line), stdin);
	switch(line[0]) {
		case 'a':
			sscanf(&line[1], "%d", &size);
			alloc_surface(size);
			break;
		case 'f':
			sscanf(&line[1], "%d", &base);
			free_surface(base);
			break;
		default:
			break;
	}
} while ( line[0] != 'q' );

free_surfaces();
exit(0);

}