#include <ncbi.h>
#include <gishlib.h>

#define POOLBLK_SIZE (sizeof(VoidPtr) * HOWMANY(sizeof(PoolBlk),sizeof(VoidPtr)))

PoolBlkPtr LIBCALL
PoolNew(nelem, size, nextoff, pdata)
	size_t	nelem, /* number of elements to allocate in each pool block */
			size, /* size of the structure */
			nextoff; /* offset of "next" field (VoidPtr) in item */
	ValNodePtr	pdata; /* personal, private data field */
{
	PoolBlkPtr	pbp;
	size_t	totsize;

	if (size < sizeof(VoidPtr) || nelem == 0 || nextoff > (size - sizeof(VoidPtr)))
		return NULL;

	/* ensure our private "next" pointer will fit and is properly aligned */
	size = sizeof(PoolItemPtr) * HOWMANY(size, sizeof(PoolItemPtr));
	pbp = (PoolBlkPtr)mem_malloc(totsize = POOLBLK_SIZE + nelem * size);
	if (pbp == NULL)
		return NULL;

	pbp->nelem = nelem;
	pbp->size = size;
	pbp->nextoff = nextoff;
	pbp->totsize = totsize;
	if (pdata != NULL)
		pbp->pdata = *pdata;
	else {
		MemSet(&pbp->pdata, 0, sizeof(pbp->pdata));
	}
	pbp->item0 = (PoolItemPtr) ((CharPtr)pbp + POOLBLK_SIZE);
	pbp->nextblkp = NULL;

	PoolInit(pbp);

	return pbp;
}

void LIBCALL
PoolDestruct(pbp)
	PoolBlkPtr	pbp;
{
	PoolBlkPtr	pbp2;

	while (pbp != NULL) {
		pbp2 = pbp->nextblkp;
		mem_free(pbp);
		pbp = pbp2;
	}
	return;
}

void LIBCALL
PoolInit(pbp)
	PoolBlkPtr	pbp;
{
	register PoolItemPtr	pip, pip2, pipmax;
	register size_t	size;

	for (pip = NULL; pbp != NULL; pbp = pbp->nextblkp) {
		pipmax = (PoolItemPtr) ((CharPtr)pbp + pbp->totsize);
		size = pbp->size;
		pip2 = pbp->item0;
		if (pip == NULL) {
			pip = pbp->avail = pip2;
			pip2 = (PoolItemPtr) ((CharPtr)pip2 + size);
		}
		while (pip2 < pipmax) {
			pip->next = pip2;
			pip = pip2;
			pip2 = (PoolItemPtr) ((CharPtr)pip2 + size);
		}
		pip->next = NULL;
	}

	return;
}

/* PoolGet -- obtain one item from the free pool */
VoidPtr LIBCALL
PoolGet(pbp)
	register PoolBlkPtr	pbp;
{
	register PoolBlkPtr	pbp_new;
	register PoolItemPtr	pip;

	if (pbp == NULL)
		return NULL;

	if ((pip = pbp->avail) != NULL) {
		pbp->avail = pip->next;
		return (VoidPtr)pip;
	}

	pbp_new = PoolNew(pbp->nelem, pbp->size, pbp->nextoff, &pbp->pdata);
	if (pbp_new == NULL)
		return NULL;
	/* Insert new block in the chain */
	pbp_new->nextblkp = pbp->nextblkp;
	pbp->nextblkp = pbp_new;
	pbp->avail = pbp_new->avail;
	return PoolGet(pbp); /* recursive */
}

/* PoolGetClr -- obtain one item from the free pool and clear it */
VoidPtr LIBCALL
PoolGetClr(pbp)
	register PoolBlkPtr	pbp;
{
	register PoolBlkPtr	pbp_new;
	register PoolItemPtr	pip;

	if (pbp == NULL)
		return NULL;

	if ((pip = pbp->avail) != NULL) {
		pbp->avail = pip->next;
		Nlm_MemSet((VoidPtr)pip, 0, pbp->size);
		return (VoidPtr)pip;
	}

	pbp_new = PoolNew(pbp->nelem, pbp->size, pbp->nextoff, &pbp->pdata);
	if (pbp_new == NULL)
		return NULL;
	/* Insert new block in the chain */
	pbp_new->nextblkp = pbp->nextblkp;
	pbp->nextblkp = pbp_new;
	pbp->avail = pbp_new->avail;
	return PoolGetClr(pbp); /* recursive */
}

/* PoolPut -- return one item to the free pool */
void LIBCALL
PoolPut(pbp, vp)
	register PoolBlkPtr	pbp;
	VoidPtr	vp;
{
	register PoolItemPtr	pip = (PoolItemPtr)vp;

	if (pbp == NULL || pip == NULL)
		return;

	pip->next = pbp->avail;
	pbp->avail = (PoolItemPtr)pip;
}

/* PoolPutLink -- return an entire linked list to the free pool */
void LIBCALL
PoolPutLink(pbp, vp)
	PoolBlkPtr	pbp;
	VoidPtr		vp;
{
	register PoolItemPtr	pip = (PoolItemPtr)vp;
	register PoolItemPtr	pipnext;
	register size_t	nextoff;
	PoolItemPtr	avail_orig;

	if (pbp == NULL || pip == NULL)
		return;

	nextoff = pbp->nextoff;
	
	avail_orig = pbp->avail;
	pbp->avail = pip;
	while ((pipnext = *(PoolItemPtr PNTR)((CharPtr)pip + nextoff)) != NULL) {
		pip->next = pipnext;
		pip = pipnext;
	}
	pip->next = avail_orig;
}

int LIBCALL
PoolContains(pbp, vp)
	register PoolBlkPtr	pbp;
	register VoidPtr		vp;
{
	register PoolItemPtr	pipmax;

	for (; pbp != NULL; pbp = pbp->nextblkp) {
		pipmax = (PoolItemPtr) ((CharPtr)pbp + pbp->totsize);
		if (vp >= (VoidPtr)pbp->item0 && vp < (VoidPtr)pipmax)
			return 1;
	}
	return 0;
}

