reworked the ringbuffer. should work like it should now.

This commit is contained in:
Magnus Auvinen 2008-10-16 17:08:58 +00:00
parent 03f6d24ba1
commit 07fc7ec8f8
2 changed files with 278 additions and 162 deletions

View file

@ -2,33 +2,29 @@
#include "e_ringbuffer.h"
enum
typedef struct RBITEM
{
RBFLAG_FREE=1
};
struct RBITEM *prev;
struct RBITEM *next;
int free;
int size;
} RBITEM;
/*
*/
struct RINGBUFFER
{
struct RBITEM_t *next_alloc;
struct RBITEM_t *last_alloc;
struct RBITEM_t *first;
struct RBITEM_t *last;
RBITEM *produce;
RBITEM *consume;
RBITEM *first;
RBITEM *last;
void *memory;
int size;
int flags;
};
typedef struct RBITEM_t
{
struct RBITEM_t *prev;
struct RBITEM_t *next;
int flags;
int size;
} RBITEM;
RINGBUFFER *ringbuf_init(void *memory, int size, int flags)
{
RINGBUFFER *rb = (RINGBUFFER *)memory;
@ -37,115 +33,80 @@ RINGBUFFER *ringbuf_init(void *memory, int size, int flags)
rb->memory = rb+1;
rb->size = (size-sizeof(RINGBUFFER))/sizeof(RBITEM)*sizeof(RBITEM);
rb->first = (RBITEM *)rb->memory;
rb->first->flags = RBFLAG_FREE;
rb->first->free = 1;
rb->first->size = rb->size;
rb->last = rb->first;
rb->next_alloc = rb->first;
rb->produce = rb->first;
rb->consume = rb->first;
rb->flags = flags;
return rb;
}
static RBITEM *ringbuf_free(RINGBUFFER *rb, RBITEM *item)
static RBITEM *ringbuf_nextblock(RINGBUFFER *rb, RBITEM *item)
{
dbg_assert(!(item->flags&RBFLAG_FREE), "trying to free element that is already freed");
item->flags |= RBFLAG_FREE;
if(item->next)
return item->next;
return rb->first;
}
/* TODO: this should be handled better */
if(item == rb->last_alloc)
rb->last_alloc = 0;
/* merge with all free items backwards */
while(item->prev && (item->prev->flags&RBFLAG_FREE))
static RBITEM *ringbuf_prevblock(RINGBUFFER *rb, RBITEM *item)
{
if(item->prev)
return item->prev;
return rb->last;
}
static RBITEM *ringbuf_mergeback(RINGBUFFER *rb, RBITEM *item)
{
/* make sure that this block and previous block is free */
if(!item->free || !item->prev || !item->prev->free)
return item;
/* merge the blocks */
item->prev->size += item->size;
item->prev->next = item->next;
item = item->prev;
/* fixup pointers */
if(item->next)
item->next->prev = item;
}
/* merge with all free items forwards */
while(item->next && (item->next->flags&RBFLAG_FREE))
{
item->size += item->next->size;
item->next = item->next->next;
if(item->next)
item->next->prev = item;
}
if(!item->next)
rb->last = item;
return item;
}
void ringbuf_validate(RINGBUFFER *rb)
{
RBITEM *prev = 0;
RBITEM *cur = rb->first;
int freechunks = 0;
while(cur)
{
if(cur->flags&RBFLAG_FREE)
freechunks++;
dbg_assert(freechunks <= 2, "too many free chunks");
dbg_assert(cur->prev == prev, "pointers doesn't match");
dbg_assert(!prev || prev->next == cur, "pointers doesn't match");
dbg_assert(cur->next || cur == rb->last, "last isn't last");
prev = cur;
cur = cur->next;
}
}
static RBITEM *ringbuf_try_allocate(RINGBUFFER *rb, int wanted_size)
{
RBITEM *item = rb->next_alloc;
/* check for space */
if(!(item->flags&RBFLAG_FREE) || item->size < wanted_size)
return 0x0;
/* split the block if needed */
if(item->size > wanted_size)
{
RBITEM *new_item = (RBITEM *)((char *)item + wanted_size);
new_item->prev = item;
new_item->next = item->next;
if(new_item->next)
new_item->next->prev = new_item;
item->next = new_item;
new_item->flags = RBFLAG_FREE;
new_item->size = item->size - wanted_size;
item->size = wanted_size;
if(!new_item->next)
rb->last = new_item;
}
item->flags &= ~RBFLAG_FREE;
rb->next_alloc = item->next;
if(!rb->next_alloc)
rb->next_alloc = rb->first;
rb->last_alloc = item;
return item;
}
void ringbuf_popfirst(RINGBUFFER *rb)
{
if(rb->next_alloc->next)
rb->next_alloc = ringbuf_free(rb, rb->next_alloc->next);
item->next->prev = item->prev;
else
{
rb->next_alloc = rb->first;
rb->next_alloc = ringbuf_free(rb, rb->next_alloc);
rb->last = item->prev;
if(item == rb->produce)
rb->produce = item->prev;
if(item == rb->consume)
rb->consume = item->prev;
/* return the current block */
return item->prev;
}
int ringbuf_popfirst(RINGBUFFER *rb)
{
if(rb->consume->free)
return 0;
/* set the free flag */
rb->consume->free = 1;
/* previous block is also free, merge them */
rb->consume = ringbuf_mergeback(rb, rb->consume);
/* advance the consume pointer */
rb->consume = ringbuf_nextblock(rb, rb->consume);
while(rb->consume->free && rb->consume != rb->produce)
{
rb->consume = ringbuf_mergeback(rb, rb->consume);
rb->consume = ringbuf_nextblock(rb, rb->consume);
}
/* in the case that we have catched up with the produce pointer */
/* we might stand on a free block so merge em */
ringbuf_mergeback(rb, rb->consume);
return 1;
}
void *ringbuf_allocate(RINGBUFFER *rb, int size)
@ -157,87 +118,245 @@ void *ringbuf_allocate(RINGBUFFER *rb, int size)
if(wanted_size > rb->size)
return 0;
/* free the block if it is in use */
if(!(rb->next_alloc->flags&RBFLAG_FREE))
rb->next_alloc = ringbuf_free(rb, rb->next_alloc);
/* first attempt */
block = ringbuf_try_allocate(rb, wanted_size);
if(block)
return block+1;
/* check if we just should return null */
if(!(rb->flags&RINGBUF_FLAG_RECYCLE))
return 0;
/* ok, we need to wipe some blocks in order to get space */
while(1)
{
/* pop one */
ringbuf_popfirst(rb);
/* try allocate again */
block = ringbuf_try_allocate(rb, wanted_size);
if(block)
return block+1;
/* check for space */
if(rb->produce->free)
{
if(rb->produce->size >= wanted_size)
block = rb->produce;
else
{
/* wrap around to try to find a block */
if(rb->first->free && rb->first->size >= wanted_size)
block = rb->first;
}
}
if(block)
break;
else
{
/* we have no block, check our policy and see what todo */
if(rb->flags&RINGBUF_FLAG_RECYCLE)
{
if(!ringbuf_popfirst(rb))
return 0;
}
else
return 0;
}
}
/* okey, we have our block */
/* split the block if needed */
if(block->size > wanted_size)
{
RBITEM *new_item = (RBITEM *)((char *)block + wanted_size);
new_item->prev = block;
new_item->next = block->next;
if(new_item->next)
new_item->next->prev = new_item;
block->next = new_item;
new_item->free = 1;
new_item->size = block->size - wanted_size;
block->size = wanted_size;
if(!new_item->next)
rb->last = new_item;
}
/* set next block */
rb->produce = ringbuf_nextblock(rb, block);
/* set as used and return the item pointer */
block->free = 0;
return block+1;
}
void *ringbuf_prev(RINGBUFFER *rb, void *current)
{
RBITEM *item = ((RBITEM *)current) - 1;
while(1)
{
/* back up one step */
item = item->prev;
if(!item)
item = rb->last;
/* we have gone around */
if(item == rb->last_alloc)
item = ringbuf_prevblock(rb, item);
if(item == rb->produce)
return 0;
if(!(item->flags&RBFLAG_FREE))
if(!item->free)
return item+1;
}
}
void *ringbuf_next(RINGBUFFER *rb, void *current)
{
RBITEM *item = ((RBITEM *)current) - 1;
/* we have gone around */
if(item == rb->last_alloc)
return 0;
while(1)
{
/* back up one step */
item = item->next;
if(!item)
item = rb->first;
if(!(item->flags&RBFLAG_FREE))
item = ringbuf_nextblock(rb, item);
if(item == rb->produce)
return 0;
if(!item->free)
return item+1;
}
}
void *ringbuf_item_ptr(void *p)
{
return ((RBITEM *)p) - 1;
}
void *ringbuf_first(RINGBUFFER *rb)
{
if(rb->last_alloc && rb->last_alloc->next)
return ringbuf_next(rb, rb->last_alloc->next+1);
return 0x0;
if(rb->consume->free)
return 0;
return rb->consume+1;
}
void *ringbuf_last(RINGBUFFER *rb)
{
if(rb->last_alloc == 0)
return 0;
return rb->last_alloc+1;
if(!rb->produce->free)
return rb->produce+1;
return ringbuf_prev(rb, rb->produce+1);
}
/* debugging and testing stuff */
static void ringbuf_debugdump(RINGBUFFER *rb, const char *msg)
{
RBITEM *cur = rb->first;
dbg_msg("ringbuf", "-- dumping --");
while(cur)
{
char flags[4] = " ";
if(cur->free)
flags[0] = 'F';
if(cur == rb->consume)
flags[1] = '>';
if(cur == rb->produce)
flags[2] = '<';
dbg_msg("ringbuf", "%s %d", flags, cur->size);
cur = cur->next;
}
dbg_msg("ringbuf", "-- --");
if(msg)
dbg_assert(0, msg);
}
static void ringbuf_validate(RINGBUFFER *rb)
{
RBITEM *prev = 0;
RBITEM *cur = rb->first;
int freechunks = 0;
int got_consume = 0;
int got_produce = 0;
while(cur)
{
if(cur->free)
freechunks++;
if(freechunks > 2) ringbuf_debugdump(rb, "too many free chunks");
if(prev && prev->free && cur->free) ringbuf_debugdump(rb, "two free chunks next to each other");
if(cur == rb->consume) got_consume = 1;
if(cur == rb->produce) got_produce = 1;
dbg_assert(cur->prev == prev, "prev pointers doesn't match");
dbg_assert(!prev || prev->next == cur, "next pointers doesn't match");
dbg_assert(cur->next || cur == rb->last, "last isn't last");
prev = cur;
cur = cur->next;
}
if(!got_consume) ringbuf_debugdump(rb, "consume pointer isn't pointing at a valid block");
if(!got_produce) ringbuf_debugdump(rb, "produce pointer isn't pointing at a valid block");
}
int ringbuf_test()
{
char buffer[256];
RINGBUFFER *rb;
int i, s, k, m;
int count;
int testcount = 0;
void *item;
int before;
for(k = 100; k < sizeof(buffer); k++)
{
if((k%10) == 0)
dbg_msg("ringbuf", "testing at %d", k);
rb = ringbuf_init(buffer, k, 0);
count = 0;
for(s = 1; s < sizeof(buffer); s++)
{
for(i = 0; i < k*8; i++, testcount++)
{
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
before = m;
if(ringbuf_allocate(rb, s))
{
count++;
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
if(before+1 != m) ringbuf_debugdump(rb, "alloc error");
if(count != m) ringbuf_debugdump(rb, "count error");
}
else
{
if(ringbuf_popfirst(rb))
{
count--;
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
if(before-1 != m) dbg_msg("", "popping error %d %d", before, m);
if(count != m) ringbuf_debugdump(rb, "count error");
}
}
/* remove an item every 10 */
if((i%10) == 0)
{
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
before = m;
if(ringbuf_popfirst(rb))
{
count--;
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
if(before-1 != m) dbg_msg("", "popping error %d %d", before, m);
dbg_assert(count == m, "count error");
}
}
/* count items */
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
if(m != count) ringbuf_debugdump(rb, "wrong number of items during forward count");
for(m = 0, item = ringbuf_last(rb); item; item = ringbuf_prev(rb, item), m++);
if(m != count) ringbuf_debugdump(rb, "wrong number of items during backward count");
ringbuf_validate(rb);
}
/* empty the ring buffer */
while(ringbuf_first(rb))
ringbuf_popfirst(rb);
ringbuf_validate(rb);
count = 0;
}
}
return 0;
}

View file

@ -12,15 +12,12 @@ enum
RINGBUFFER *ringbuf_init(void *memory, int size, int flags);
void ringbuf_clear(RINGBUFFER *rb);
void *ringbuf_allocate(RINGBUFFER *rb, int size);
void ringbuf_validate(RINGBUFFER *rb);
void *ringbuf_item_ptr(void *p);
void *ringbuf_prev(RINGBUFFER *rb, void *current);
void *ringbuf_next(RINGBUFFER *rb, void *current);
void *ringbuf_first(RINGBUFFER *rb);
void *ringbuf_last(RINGBUFFER *rb);
void ringbuf_popfirst(RINGBUFFER *rb);
int ringbuf_popfirst(RINGBUFFER *rb);
#endif