2008-02-03 23:52:28 +00:00
|
|
|
#include <engine/e_system.h>
|
|
|
|
|
2008-02-03 22:42:03 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
RBFLAG_FREE=1
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct RBITEM_t
|
|
|
|
{
|
|
|
|
struct RBITEM_t *prev;
|
|
|
|
struct RBITEM_t *next;
|
|
|
|
int flags;
|
|
|
|
int size;
|
|
|
|
} RBITEM;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
/* what you need */
|
|
|
|
RBITEM *next_alloc;
|
|
|
|
RBITEM *last_alloc;
|
|
|
|
RBITEM *first;
|
|
|
|
RBITEM *last;
|
|
|
|
void *memory;
|
|
|
|
int size;
|
|
|
|
} RINGBUFFER;
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
RINGBUFFER *ringbuf_init(void *memory, int size)
|
2008-02-03 22:42:03 +00:00
|
|
|
{
|
|
|
|
RINGBUFFER *rb = (RINGBUFFER *)memory;
|
|
|
|
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->prev = 0;
|
|
|
|
rb->first->size = rb->size;
|
|
|
|
rb->last = rb->first;
|
|
|
|
rb->next_alloc = rb->first;
|
|
|
|
rb->last_alloc = 0;
|
|
|
|
return rb;
|
|
|
|
}
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
static RBITEM *ringbuf_free(RINGBUFFER *rb, RBITEM *item)
|
2008-02-03 22:42:03 +00:00
|
|
|
{
|
2008-02-03 23:52:28 +00:00
|
|
|
dbg_assert(!(item->flags&RBFLAG_FREE), "trying to free element that is already freed");
|
2008-02-03 22:42:03 +00:00
|
|
|
item->flags |= RBFLAG_FREE;
|
|
|
|
|
|
|
|
/* 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;
|
2008-02-03 23:52:28 +00:00
|
|
|
if(item->next)
|
|
|
|
item->next->prev = item;
|
2008-02-03 22:42:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* merge with all free items forwards */
|
|
|
|
while(item->next && (item->next->flags&RBFLAG_FREE))
|
|
|
|
{
|
|
|
|
item->size += item->next->size;
|
|
|
|
item->next = item->next->next;
|
2008-02-03 23:52:28 +00:00
|
|
|
if(item->next)
|
|
|
|
item->next->prev = item;
|
2008-02-03 22:42:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!item->next)
|
|
|
|
rb->last = item;
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
void ringbuf_validate(RINGBUFFER *rb)
|
2008-02-03 23:52:28 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
static RBITEM *ringbuf_try_allocate(RINGBUFFER *rb, int wanted_size)
|
2008-02-03 22:42:03 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-02-03 23:52:28 +00:00
|
|
|
item->flags &= ~RBFLAG_FREE;
|
2008-02-03 22:42:03 +00:00
|
|
|
rb->next_alloc = item->next;
|
|
|
|
if(!rb->next_alloc)
|
|
|
|
rb->next_alloc = rb->first;
|
|
|
|
|
|
|
|
rb->last_alloc = item;
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
void *ringbuf_allocate(RINGBUFFER *rb, int size)
|
2008-02-03 22:42:03 +00:00
|
|
|
{
|
|
|
|
int wanted_size = (size+sizeof(RBITEM)+sizeof(RBITEM)-1)/sizeof(RBITEM)*sizeof(RBITEM);
|
|
|
|
RBITEM *block = 0;
|
2008-02-03 23:52:28 +00:00
|
|
|
|
2008-02-03 22:42:03 +00:00
|
|
|
/* check if we even can fit this block */
|
|
|
|
if(wanted_size > rb->size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* free the block if it is in use */
|
|
|
|
if(!(rb->next_alloc->flags&RBFLAG_FREE))
|
2008-02-03 23:55:37 +00:00
|
|
|
rb->next_alloc = ringbuf_free(rb, rb->next_alloc);
|
2008-02-03 22:42:03 +00:00
|
|
|
|
|
|
|
/* first attempt */
|
2008-02-03 23:55:37 +00:00
|
|
|
block = ringbuf_try_allocate(rb, wanted_size);
|
2008-02-03 22:42:03 +00:00
|
|
|
if(block)
|
2008-02-03 23:52:28 +00:00
|
|
|
return block+1;
|
2008-02-03 22:42:03 +00:00
|
|
|
|
|
|
|
/* ok, we need to wipe some blocks in order to get space */
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
if(rb->next_alloc->next)
|
2008-02-03 23:55:37 +00:00
|
|
|
rb->next_alloc = ringbuf_free(rb, rb->next_alloc->next);
|
2008-02-03 22:42:03 +00:00
|
|
|
else
|
2008-02-03 23:52:28 +00:00
|
|
|
{
|
2008-02-03 22:42:03 +00:00
|
|
|
rb->next_alloc = rb->first;
|
2008-02-03 23:55:37 +00:00
|
|
|
rb->next_alloc = ringbuf_free(rb, rb->next_alloc);
|
2008-02-03 23:52:28 +00:00
|
|
|
}
|
2008-02-03 22:42:03 +00:00
|
|
|
|
|
|
|
/* try allocate again */
|
2008-02-03 23:55:37 +00:00
|
|
|
block = ringbuf_try_allocate(rb, wanted_size);
|
2008-02-03 22:42:03 +00:00
|
|
|
if(block)
|
2008-02-03 23:52:28 +00:00
|
|
|
return block+1;
|
2008-02-03 22:42:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
void *ringbuf_prev(RINGBUFFER *rb, void *current)
|
2008-02-03 22:42:03 +00:00
|
|
|
{
|
2008-02-03 23:52:28 +00:00
|
|
|
RBITEM *item = ((RBITEM *)current) - 1;
|
2008-02-03 22:42:03 +00:00
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
/* back up one step */
|
|
|
|
item = item->prev;
|
|
|
|
if(!item)
|
|
|
|
item = rb->last;
|
|
|
|
|
|
|
|
/* we have gone around */
|
|
|
|
if(item == rb->last_alloc)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if(!(item->flags&RBFLAG_FREE))
|
2008-02-03 23:52:28 +00:00
|
|
|
return item+1;
|
2008-02-03 22:42:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
void *ringbuf_next(RINGBUFFER *rb, void *current)
|
2008-02-03 22:42:03 +00:00
|
|
|
{
|
2008-02-03 23:52:28 +00:00
|
|
|
RBITEM *item = ((RBITEM *)current) - 1;
|
2008-02-03 22:42:03 +00:00
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
/* back up one step */
|
|
|
|
item = item->next;
|
|
|
|
if(!item)
|
|
|
|
item = rb->first;
|
|
|
|
|
|
|
|
/* we have gone around */
|
|
|
|
if(item == rb->last_alloc)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if(!(item->flags&RBFLAG_FREE))
|
2008-02-03 23:52:28 +00:00
|
|
|
return item+1;
|
2008-02-03 22:42:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
void *ringbuf_item_ptr(void *p)
|
2008-02-03 23:52:28 +00:00
|
|
|
{
|
|
|
|
return ((RBITEM *)p) - 1;
|
|
|
|
}
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
void *ringbuf_first(RINGBUFFER *rb)
|
2008-02-03 22:42:03 +00:00
|
|
|
{
|
2008-02-03 23:55:37 +00:00
|
|
|
return ringbuf_next(rb, rb->last_alloc+1);
|
2008-02-03 22:42:03 +00:00
|
|
|
}
|
|
|
|
|
2008-02-03 23:55:37 +00:00
|
|
|
void *ringbuf_last(RINGBUFFER *rb)
|
2008-02-03 22:42:03 +00:00
|
|
|
{
|
2008-02-03 23:52:28 +00:00
|
|
|
return rb->last_alloc+1;
|
2008-02-03 22:42:03 +00:00
|
|
|
}
|