/* Copyright (c) 2001, Stanford University
 * All rights reserved
 *
 * See the file LICENSE.txt for information on redistributing this software.
 */

#include "cr_mem.h"
#include "cr_error.h"
#include "cr_bufpool.h"
#include <limits.h>

/*
 * New (version 1.4) buffer pool implementation.
 *
 * Now, each buffer in the pool can be a different size.
 * We only return a buffer from crBufferPoolPop() if we have exactly
 * the right size buffer.
 *
 * Note: the old implementation had a 'max buffers' parameter but it
 * really wasn't good for anything since we always grew the buffer pool
 * if we pushed a new buffer that would cause use to exceed the limit.
 * That's gone now.
 *
 * We're just using a simple linked list here.  Since we seldom have
 * more than about 10-15 buffers in the pool, that's OK.  A binary tree
 * would be nicer though.
 *
 * MCH: BufferPoolPop will now return the smallest buffer in the pool that
 *      is >= to the size required.  This fixes BufferPool overruns with lots
 *      of MTUs.
 */


#ifndef NULL
#define NULL  ((void *) 0)
#endif


typedef struct buffer
{
	void *address;
	unsigned int size;
	struct buffer *next;
} Buffer;


struct CRBufferPool_t
{
	unsigned int maxBuffers;
	int numBuffers;
	struct buffer *head;
};


int
crBufferPoolGetNumBuffers( CRBufferPool *pool )
{
	if ( pool )
		return pool->numBuffers;
	return 0;
}

int
crBufferPoolGetMaxBuffers( CRBufferPool *pool )
{
	if ( pool )
		return pool->maxBuffers;
	return 0;
}

CRBufferPool *
crBufferPoolInit( unsigned int maxBuffers )
{
	CRBufferPool *pool = crCalloc(sizeof(CRBufferPool));
	if (pool) {
		pool->head = NULL;
		pool->maxBuffers = maxBuffers;
		pool->numBuffers = 0;
	}
	return pool;
}

void
crBufferPoolFree( CRBufferPool *pool )
{
	Buffer *b, *next;

	for (b = pool->head; b; b = next) {
		next = b->next;
		crFree(b->address);
		crFree(b);
	}
}

void
crBufferPoolPush( CRBufferPool *pool, void *buf, unsigned int bytes )
{
	Buffer *b = crCalloc(sizeof(Buffer));
	if (b) {
#ifndef NDEBUG
		/* check that the buffer to push isn't already in the pool! */
		{
			const Buffer *b;
			for (b = pool->head; b; b = b->next) {
				CRASSERT(b->address != buf);
			}
		}
#endif

		b->address = buf;
		b->size = bytes;
		b->next = pool->head;
		pool->head = b;
		pool->numBuffers++;
	}
}

void *
crBufferPoolPop( CRBufferPool *pool, unsigned int bytes )
{
	Buffer *b, *prev, *prev_smallest;
	unsigned int smallest = UINT_MAX; /* size of smallest buffer >= bytes */
	int i;

	prev = NULL;
	prev_smallest = NULL;
	for (b = pool->head, i=0; i<pool->numBuffers; b = b->next, i++) {
		if (b->size == bytes) {
			/* we found an exact size match! */
			void *p = b->address;
			if (prev) {
				prev->next = b->next;
			}
			else {
				pool->head = b->next;
			}
			crFree(b);
			pool->numBuffers--;
			CRASSERT(pool->numBuffers >= 0);
			return p;
		}
		else if (b->size >= bytes){
			/* We found a buffer that's large enough, but keep looking
			 * for a smaller one that's large enough.
			 */
			if (b->size < smallest) {
				prev_smallest = prev; /* save ptr to previous! */
				smallest = b->size;
			}
		}
		prev = b;
	}

	if (smallest < UINT_MAX) {
		/* we found the smallest buffer whose size is > bytes */
		void *p;
		if (prev_smallest) {
			b = prev_smallest->next;
		}
		else {
			b = pool->head;
		}
		CRASSERT(b->size == smallest);
		CRASSERT(b->size >= bytes);
		p = b->address;
		if (prev_smallest) {
		  prev_smallest->next = b->next;
		}
		else {
		  pool->head = b->next;
		}
		crFree(b);
		pool->numBuffers--;
		CRASSERT(pool->numBuffers >= 0);
		return p;
	}

	/* found no buffer large enough */
	return NULL;
}
