about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-10-16 17:08:58 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-10-16 17:08:58 +0000
commit07fc7ec8f899abdf8f24f9e17d019ee6b3a33a68 (patch)
treef1991c68184933f138ba809bee7bc5d5b1774674
parent03f6d24ba11f72751bc8ad5119f3db1c0c73dbcc (diff)
downloadzcatch-07fc7ec8f899abdf8f24f9e17d019ee6b3a33a68.tar.gz
zcatch-07fc7ec8f899abdf8f24f9e17d019ee6b3a33a68.zip
reworked the ringbuffer. should work like it should now.
-rw-r--r--src/engine/e_ringbuffer.c423
-rw-r--r--src/engine/e_ringbuffer.h5
2 files changed, 272 insertions, 156 deletions
diff --git a/src/engine/e_ringbuffer.c b/src/engine/e_ringbuffer.c
index ed3326d4..39dfa3e9 100644
--- a/src/engine/e_ringbuffer.c
+++ b/src/engine/e_ringbuffer.c
@@ -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;
-
-	/* 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))
-	{
-		item->prev->size += item->size;
-		item->prev->next = item->next;
-		item = item->prev;
-		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;
+	if(item->next)
+		return item->next;
+	return rb->first;
 }
 
-void ringbuf_validate(RINGBUFFER *rb)
+static RBITEM *ringbuf_prevblock(RINGBUFFER *rb, RBITEM *item)
 {
-	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;
-	}
+	if(item->prev)
+		return item->prev;
+	return rb->last;
 }
 
-static RBITEM *ringbuf_try_allocate(RINGBUFFER *rb, int wanted_size)
+static RBITEM *ringbuf_mergeback(RINGBUFFER *rb, RBITEM *item)
 {
-	RBITEM *item = rb->next_alloc;
+	/* 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;
 	
-	/* 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;
+	/* fixup pointers */
+	if(item->next)
+		item->next->prev = item->prev;
+	else
+		rb->last = item->prev;
 		
-		if(!new_item->next)
-			rb->last = new_item;
-	}
+	if(item == rb->produce)
+		rb->produce = item->prev;
 	
-	item->flags &= ~RBFLAG_FREE;
-	rb->next_alloc = item->next;
-	if(!rb->next_alloc)
-		rb->next_alloc = rb->first;
-		
-	rb->last_alloc = item;
-	return item;
+	if(item == rb->consume)
+		rb->consume = item->prev;
+	
+	/* return the current block */
+	return item->prev;
 }
 
-void ringbuf_popfirst(RINGBUFFER *rb)
+int ringbuf_popfirst(RINGBUFFER *rb)
 {
-	if(rb->next_alloc->next)
-		rb->next_alloc = ringbuf_free(rb, rb->next_alloc->next);
-	else
+	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->next_alloc = rb->first;
-		rb->next_alloc = ringbuf_free(rb, rb->next_alloc);
+		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);
+	while(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;
+			}
+		}
 		
-	/* 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;
+		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;
+		}
+	}
 	
-	/* ok, we need to wipe some blocks in order to get space */
-	while(1)
+	/* okey, we have our block */
+	
+	/* split the block if needed */
+	if(block->size > wanted_size)
 	{
-		/* pop one */
-		ringbuf_popfirst(rb);
-
-		/* try allocate again */
-		block = ringbuf_try_allocate(rb, wanted_size);
-		if(block)
-			return block+1;
+		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)
+void *ringbuf_first(RINGBUFFER *rb)
 {
-	return ((RBITEM *)p) - 1;
+	if(rb->consume->free)
+		return 0;
+	return rb->consume+1;
 }
 
-void *ringbuf_first(RINGBUFFER *rb)
+void *ringbuf_last(RINGBUFFER *rb)
 {
-	if(rb->last_alloc && rb->last_alloc->next)
-		return ringbuf_next(rb, rb->last_alloc->next+1);
-	return 0x0;
+	if(!rb->produce->free)
+		return rb->produce+1;
+	return ringbuf_prev(rb, rb->produce+1);
 }
 
-void *ringbuf_last(RINGBUFFER *rb)
+
+/* debugging and testing stuff */
+
+static void ringbuf_debugdump(RINGBUFFER *rb, const char *msg)
 {
-	if(rb->last_alloc == 0)
-		return 0;
-	return rb->last_alloc+1;
+	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;
 }
diff --git a/src/engine/e_ringbuffer.h b/src/engine/e_ringbuffer.h
index 71b5831b..6113c19b 100644
--- a/src/engine/e_ringbuffer.h
+++ b/src/engine/e_ringbuffer.h
@@ -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