bufpool.c revision 43ec799dbc8971afca055dd7f8fd479b7b72f338
/* 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
crBufferPoolCallbackFree(CRBufferPool *pool, CRBufferPoolDeleteCallback pfnDelete)
{
Buffer *b, *next;
CRASSERT(pfnDelete);
for (b = pool->head; b; b = next) {
next = b->next;
(*pfnDelete)(b->address);
crFree(b);
}
}
void
crBufferPoolPush( CRBufferPool *pool, void *buf, unsigned int bytes )
{
Buffer *b = crCalloc(sizeof(Buffer));
if (b) {
#ifdef DEBUG
/* 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;
}