mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-22 11:03:12 +00:00
278 lines
6.7 KiB
C
278 lines
6.7 KiB
C
#include "ringbuffer.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#define ALIGN(s) (((s) + 3 ) & ~3)
|
|
|
|
struct ringbuffer {
|
|
int size;
|
|
int head;
|
|
};
|
|
|
|
static inline int
|
|
block_offset(struct ringbuffer * rb, struct ringbuffer_block * blk) {
|
|
char * start = (char *)(rb + 1);
|
|
return (char *)blk - start;
|
|
}
|
|
|
|
static inline struct ringbuffer_block *
|
|
block_ptr(struct ringbuffer * rb, int offset) {
|
|
char * start = (char *)(rb + 1);
|
|
return (struct ringbuffer_block *)(start + offset);
|
|
}
|
|
|
|
static inline struct ringbuffer_block *
|
|
block_next(struct ringbuffer * rb, struct ringbuffer_block * blk) {
|
|
int align_length = ALIGN(blk->length);
|
|
int head = block_offset(rb, blk);
|
|
if (align_length + head == rb->size) {
|
|
return NULL;
|
|
}
|
|
assert(align_length + head < rb->size);
|
|
return block_ptr(rb, head + align_length);
|
|
}
|
|
|
|
struct ringbuffer *
|
|
ringbuffer_new(int size) {
|
|
struct ringbuffer * rb = malloc(sizeof(*rb) + size);
|
|
rb->size = size;
|
|
rb->head = 0;
|
|
struct ringbuffer_block * blk = block_ptr(rb, 0);
|
|
blk->length = size;
|
|
blk->id = -1;
|
|
return rb;
|
|
}
|
|
|
|
void
|
|
ringbuffer_delete(struct ringbuffer * rb) {
|
|
free(rb);
|
|
}
|
|
|
|
void
|
|
ringbuffer_link(struct ringbuffer *rb , struct ringbuffer_block * head, struct ringbuffer_block * next) {
|
|
while (head->next >=0) {
|
|
head = block_ptr(rb, head->next);
|
|
}
|
|
next->id = head->id;
|
|
head->next = block_offset(rb, next);
|
|
}
|
|
|
|
static struct ringbuffer_block *
|
|
_alloc(struct ringbuffer * rb, int total_size , int size) {
|
|
struct ringbuffer_block * blk = block_ptr(rb, rb->head);
|
|
int align_length = ALIGN(sizeof(struct ringbuffer_block) + size);
|
|
blk->length = sizeof(struct ringbuffer_block) + size;
|
|
blk->offset = 0;
|
|
blk->next = -1;
|
|
blk->id = -1;
|
|
struct ringbuffer_block * next = block_next(rb, blk);
|
|
rb->head = block_offset(rb, next);
|
|
if (align_length < total_size) {
|
|
next->length = total_size - align_length;
|
|
if (next->length >= sizeof(struct ringbuffer_block)) {
|
|
next->id = -1;
|
|
}
|
|
}
|
|
return blk;
|
|
}
|
|
|
|
struct ringbuffer_block *
|
|
ringbuffer_alloc(struct ringbuffer * rb, int size) {
|
|
int align_length = ALIGN(sizeof(struct ringbuffer_block) + size);
|
|
int i;
|
|
for (i=0;i<2;i++) {
|
|
int free_size = 0;
|
|
struct ringbuffer_block * blk = block_ptr(rb, rb->head);
|
|
do {
|
|
if (blk->length >= sizeof(struct ringbuffer_block) && blk->id >= 0)
|
|
return NULL;
|
|
free_size += ALIGN(blk->length);
|
|
if (free_size >= align_length) {
|
|
return _alloc(rb, free_size , size);
|
|
}
|
|
blk = block_next(rb, blk);
|
|
} while(blk);
|
|
rb->head = 0;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
_last_id(struct ringbuffer * rb) {
|
|
int i;
|
|
for (i=0;i<2;i++) {
|
|
struct ringbuffer_block * blk = block_ptr(rb, rb->head);
|
|
do {
|
|
if (blk->length >= sizeof(struct ringbuffer_block) && blk->id >= 0)
|
|
return blk->id;
|
|
blk = block_next(rb, blk);
|
|
} while(blk);
|
|
rb->head = 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
ringbuffer_collect(struct ringbuffer * rb) {
|
|
int id = _last_id(rb);
|
|
struct ringbuffer_block *blk = block_ptr(rb, 0);
|
|
do {
|
|
if (blk->length >= sizeof(struct ringbuffer_block) && blk->id == id) {
|
|
blk->id = -1;
|
|
}
|
|
blk = block_next(rb, blk);
|
|
} while(blk);
|
|
return id;
|
|
}
|
|
|
|
void
|
|
ringbuffer_resize(struct ringbuffer * rb, struct ringbuffer_block * blk, int size) {
|
|
if (size == 0) {
|
|
rb->head = block_offset(rb, blk);
|
|
return;
|
|
}
|
|
int align_length = ALIGN(sizeof(struct ringbuffer_block) + size);
|
|
int old_length = ALIGN(blk->length);
|
|
assert(align_length <= old_length);
|
|
blk->length = size + sizeof(struct ringbuffer_block);
|
|
if (align_length == old_length) {
|
|
return;
|
|
}
|
|
blk = block_next(rb, blk);
|
|
blk->length = old_length - align_length;
|
|
if (blk->length >= sizeof(struct ringbuffer_block)) {
|
|
blk->id = -1;
|
|
}
|
|
rb->head = block_offset(rb, blk);
|
|
}
|
|
|
|
static int
|
|
_block_id(struct ringbuffer_block * blk) {
|
|
assert(blk->length >= sizeof(struct ringbuffer_block));
|
|
int id = blk->id;
|
|
assert(id>=0);
|
|
return id;
|
|
}
|
|
|
|
void
|
|
ringbuffer_free(struct ringbuffer * rb, struct ringbuffer_block * blk) {
|
|
if (blk == NULL)
|
|
return;
|
|
int id = _block_id(blk);
|
|
blk->id = -1;
|
|
while (blk->next >= 0) {
|
|
blk = block_ptr(rb, blk->next);
|
|
assert(_block_id(blk) == id);
|
|
blk->id = -1;
|
|
}
|
|
}
|
|
|
|
int
|
|
ringbuffer_data(struct ringbuffer * rb, struct ringbuffer_block * blk, int size, int skip, void **ptr) {
|
|
int length = blk->length - sizeof(struct ringbuffer_block) - blk->offset;
|
|
for (;;) {
|
|
if (length > skip) {
|
|
if (length - skip >= size) {
|
|
char * start = (char *)(blk + 1);
|
|
*ptr = (start + blk->offset + skip);
|
|
return size;
|
|
}
|
|
*ptr = NULL;
|
|
int ret = length - skip;
|
|
while (blk->next >= 0) {
|
|
blk = block_ptr(rb, blk->next);
|
|
ret += blk->length - sizeof(struct ringbuffer_block);
|
|
if (ret >= size)
|
|
return size;
|
|
}
|
|
return ret;
|
|
}
|
|
if (blk->next < 0) {
|
|
assert(length == skip);
|
|
*ptr = NULL;
|
|
return 0;
|
|
}
|
|
blk = block_ptr(rb, blk->next);
|
|
assert(blk->offset == 0);
|
|
skip -= length;
|
|
length = blk->length - sizeof(struct ringbuffer_block);
|
|
}
|
|
}
|
|
|
|
void *
|
|
ringbuffer_copy(struct ringbuffer * rb, struct ringbuffer_block * from, int skip, struct ringbuffer_block * to) {
|
|
int size = to->length - sizeof(struct ringbuffer_block);
|
|
int length = from->length - sizeof(struct ringbuffer_block) - from->offset;
|
|
char * ptr = (char *)(to+1);
|
|
for (;;) {
|
|
if (length > skip) {
|
|
char * src = (char *)(from + 1);
|
|
src += from->offset + skip;
|
|
length -= skip;
|
|
while (length < size) {
|
|
memcpy(ptr, src, length);
|
|
assert(from->next >= 0);
|
|
from = block_ptr(rb , from->next);
|
|
assert(from->offset == 0);
|
|
ptr += length;
|
|
size -= length;
|
|
length = from->length - sizeof(struct ringbuffer_block);
|
|
src = (char *)(from + 1);
|
|
}
|
|
memcpy(ptr, src , size);
|
|
to->id = from->id;
|
|
return (char *)(to + 1);
|
|
}
|
|
assert(from->next >= 0);
|
|
from = block_ptr(rb, from->next);
|
|
assert(from->offset == 0);
|
|
skip -= length;
|
|
length = from->length - sizeof(struct ringbuffer_block);
|
|
}
|
|
}
|
|
|
|
struct ringbuffer_block *
|
|
ringbuffer_yield(struct ringbuffer * rb, struct ringbuffer_block *blk, int skip) {
|
|
int length = blk->length - sizeof(struct ringbuffer_block) - blk->offset;
|
|
for (;;) {
|
|
if (length > skip) {
|
|
blk->offset += skip;
|
|
return blk;
|
|
}
|
|
blk->id = -1;
|
|
if (blk->next < 0) {
|
|
return NULL;
|
|
}
|
|
blk = block_ptr(rb, blk->next);
|
|
assert(blk->offset == 0);
|
|
skip -= length;
|
|
length = blk->length - sizeof(struct ringbuffer_block);
|
|
}
|
|
}
|
|
|
|
void
|
|
ringbuffer_dump(struct ringbuffer * rb) {
|
|
struct ringbuffer_block *blk = block_ptr(rb,0);
|
|
int i=0;
|
|
printf("total size= %d\n",rb->size);
|
|
while (blk) {
|
|
++i;
|
|
if (i>10)
|
|
break;
|
|
if (blk->length >= sizeof(*blk)) {
|
|
printf("[%u : %d]", (unsigned)(blk->length - sizeof(*blk)), block_offset(rb,blk));
|
|
printf(" id=%d",blk->id);
|
|
if (blk->id >=0) {
|
|
printf(" offset=%d next=%d",blk->offset, blk->next);
|
|
}
|
|
} else {
|
|
printf("<%u : %d>", blk->length, block_offset(rb,blk));
|
|
}
|
|
printf("\n");
|
|
blk = block_next(rb, blk);
|
|
}
|
|
}
|