mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
reworked the ringbuffer. should work like it should now.
This commit is contained in:
parent
03f6d24ba1
commit
07fc7ec8f8
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue