/* 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 /* * 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; inumBuffers; 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; }