shithub: scc

ref: f2f8265148da2575342a68a2b55e3bade3551e33
dir: /src/libscc/alloc.c/

View raw version
static char sccsid[] = "@(#) ./lib/scc/alloc.c";
#include <stdlib.h>
#include <scc/scc.h>

/*
 * This is the most pedantic piece of code that I have written
 * in my life. The next union is used to enforce the aligmnet
 * of the address returned by new(). A union has the aligment
 * of the field with the biggest aligment. This union has all
 * the types that we use in scc, and we round all the address
 * to the aligment of this struct, so we can be sure that any
 * pointer using that address will be safe. The field ap is
 * in the union to be sure that struct pointers are included
 * in the list, although they will have the same aligment or
 * smaller than void *, but I wanted to be pedantic.
 */
union hdr {
	union hdr *next;
	struct arena *ap;
	char c;
	unsigned char uc;
	int i;
	short s;
	long l;
	long long ll;
	float f;
	double d;
	long double ld;
	void *vp;
};

struct arena {
	struct arena *next;
	union hdr *array;
};

struct alloc {
	size_t size;
	size_t nmemb;
	size_t padding;
	struct arena *arena;
	union hdr *freep;
};

static void
newarena(Alloc *allocp)
{
	struct arena *ap;
	union hdr *bp, *lim;
	size_t unit, n = allocp->nmemb;

	unit = (allocp->size-1) / sizeof(union hdr) + 1;
	ap = xmalloc(sizeof(struct arena));
	ap->array = xmalloc(unit * sizeof(union hdr) * n);

	bp = ap->array;
	for (lim = &bp[unit * (n-1)]; bp < lim; bp += unit)
		bp->next = bp + unit;
	bp->next = NULL;

	ap->next = allocp->arena;
	allocp->arena = ap;
	allocp->freep = ap->array;
}

Alloc *
alloc(size_t size, size_t nmemb)
{
	Alloc *allocp = xmalloc(sizeof(*allocp));

	allocp->size = size;
	allocp->nmemb = nmemb;
	allocp->arena = NULL;
	allocp->freep = NULL;

	return allocp;
}

void
dealloc(Alloc *allocp)
{
	struct arena *ap, *next;

	for (ap = allocp->arena; ap; ap = next) {
		next = ap->next;
		free(ap->array);
		free(ap);
	}
	free(allocp);
}

void *
new(Alloc *allocp)
{
	union hdr *bp;

	if (!allocp->freep)
		newarena(allocp);
	bp = allocp->freep;
	allocp->freep = bp->next;

	return bp;
}

void
delete(Alloc *allocp, void *p)
{
	union hdr *bp = p;

	bp->next = allocp->freep;
	allocp->freep = bp;
}