core: add ringbuffer data structure
This commit is contained in:
356
core/ringbuffer.c
Normal file
356
core/ringbuffer.c
Normal file
@@ -0,0 +1,356 @@
|
||||
#include <blue/core/ringbuffer.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BUF_LOCKED(buf) \
|
||||
(((buf)->r_flags & (B_RINGBUFFER_READ_LOCKED | B_RINGBUFFER_WRITE_LOCKED)) \
|
||||
!= 0)
|
||||
|
||||
struct b_ringbuffer *b_ringbuffer_create(size_t capacity)
|
||||
{
|
||||
struct b_ringbuffer *buf = malloc(sizeof *buf);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *p = malloc(capacity);
|
||||
if (!p) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(buf, 0x0, sizeof *buf);
|
||||
|
||||
buf->r_flags = B_RINGBUFFER_SELF_MALLOC | B_RINGBUFFER_BUFFER_MALLOC;
|
||||
buf->r_buf = p;
|
||||
buf->r_capacity = capacity;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
struct b_ringbuffer *b_ringbuffer_create_with_buffer(void *ptr, size_t capacity)
|
||||
{
|
||||
struct b_ringbuffer *buf = malloc(sizeof *buf);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(buf, 0x0, sizeof *buf);
|
||||
|
||||
buf->r_flags = B_RINGBUFFER_SELF_MALLOC;
|
||||
buf->r_buf = ptr;
|
||||
buf->r_capacity = capacity;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_init(struct b_ringbuffer *buf, size_t capacity)
|
||||
{
|
||||
void *p = malloc(capacity);
|
||||
if (!p) {
|
||||
free(buf);
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
memset(buf, 0x0, sizeof *buf);
|
||||
|
||||
buf->r_flags = B_RINGBUFFER_BUFFER_MALLOC;
|
||||
buf->r_buf = p;
|
||||
buf->r_capacity = capacity;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_init_with_buffer(
|
||||
struct b_ringbuffer *buf, void *ptr, size_t capacity)
|
||||
{
|
||||
memset(buf, 0x0, sizeof *buf);
|
||||
|
||||
buf->r_flags = 0;
|
||||
buf->r_buf = ptr;
|
||||
buf->r_capacity = capacity;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_reset(struct b_ringbuffer *buf)
|
||||
{
|
||||
if (BUF_LOCKED(buf)) {
|
||||
return B_ERR_BUSY;
|
||||
}
|
||||
|
||||
buf->r_read_ptr = buf->r_write_ptr = 0;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_destroy(struct b_ringbuffer *buf)
|
||||
{
|
||||
if (BUF_LOCKED(buf)) {
|
||||
return B_ERR_BUSY;
|
||||
}
|
||||
|
||||
if (buf->r_flags & B_RINGBUFFER_BUFFER_MALLOC) {
|
||||
free(buf->r_buf);
|
||||
}
|
||||
|
||||
if (buf->r_flags & B_RINGBUFFER_SELF_MALLOC) {
|
||||
free(buf);
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_read(
|
||||
struct b_ringbuffer *buf, void *p, size_t count, size_t *nr_read)
|
||||
{
|
||||
if (BUF_LOCKED(buf)) {
|
||||
return B_ERR_BUSY;
|
||||
}
|
||||
|
||||
size_t r = 0;
|
||||
unsigned char *dest = p;
|
||||
size_t remaining = count;
|
||||
|
||||
while (remaining > 0) {
|
||||
void *src;
|
||||
size_t available;
|
||||
enum b_status status
|
||||
= b_ringbuffer_open_read_buffer(buf, &src, &available);
|
||||
|
||||
if (status == B_ERR_NO_DATA) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!B_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t to_copy = remaining;
|
||||
if (to_copy > available) {
|
||||
to_copy = available;
|
||||
}
|
||||
|
||||
memcpy(dest, src, to_copy);
|
||||
|
||||
remaining -= to_copy;
|
||||
dest += to_copy;
|
||||
r += to_copy;
|
||||
|
||||
b_ringbuffer_close_read_buffer(buf, &src, to_copy);
|
||||
}
|
||||
|
||||
*nr_read = r;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_write(
|
||||
struct b_ringbuffer *buf, const void *p, size_t count, size_t *nr_written)
|
||||
{
|
||||
if (BUF_LOCKED(buf)) {
|
||||
return B_ERR_BUSY;
|
||||
}
|
||||
|
||||
size_t w = 0;
|
||||
const unsigned char *src = p;
|
||||
size_t remaining = count;
|
||||
|
||||
while (remaining > 0) {
|
||||
void *dest;
|
||||
size_t available;
|
||||
enum b_status status
|
||||
= b_ringbuffer_open_write_buffer(buf, &dest, &available);
|
||||
|
||||
if (status == B_ERR_NO_SPACE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!B_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t to_copy = remaining;
|
||||
if (to_copy > available) {
|
||||
to_copy = available;
|
||||
}
|
||||
|
||||
memcpy(dest, src, to_copy);
|
||||
|
||||
remaining -= to_copy;
|
||||
src += to_copy;
|
||||
w += to_copy;
|
||||
|
||||
b_ringbuffer_close_write_buffer(buf, &dest, to_copy);
|
||||
}
|
||||
|
||||
*nr_written = w;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
int b_ringbuffer_getc(struct b_ringbuffer *buf)
|
||||
{
|
||||
size_t available = b_ringbuffer_available_data_remaining(buf);
|
||||
if (available == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *p = buf->r_buf;
|
||||
char c = p[buf->r_read_ptr++];
|
||||
|
||||
if (buf->r_read_ptr >= buf->r_capacity) {
|
||||
buf->r_read_ptr = 0;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_putc(struct b_ringbuffer *buf, int c)
|
||||
{
|
||||
size_t available = b_ringbuffer_write_capacity_remaining(buf);
|
||||
if (available == 0) {
|
||||
return B_ERR_NO_SPACE;
|
||||
}
|
||||
|
||||
char *p = buf->r_buf;
|
||||
p[buf->r_write_ptr++] = c;
|
||||
|
||||
if (buf->r_write_ptr >= buf->r_capacity) {
|
||||
buf->r_write_ptr = 0;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
size_t b_ringbuffer_write_capacity_remaining(const struct b_ringbuffer *buf)
|
||||
{
|
||||
if (buf->r_read_ptr > buf->r_write_ptr) {
|
||||
return buf->r_read_ptr - buf->r_write_ptr - 1;
|
||||
} else {
|
||||
return buf->r_capacity - buf->r_write_ptr + buf->r_read_ptr - 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t b_ringbuffer_available_data_remaining(const struct b_ringbuffer *buf)
|
||||
{
|
||||
if (buf->r_read_ptr < buf->r_write_ptr) {
|
||||
return buf->r_write_ptr - buf->r_read_ptr;
|
||||
} else if (buf->r_read_ptr > buf->r_write_ptr) {
|
||||
return buf->r_capacity - buf->r_read_ptr + buf->r_write_ptr;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_open_read_buffer(
|
||||
struct b_ringbuffer *buf, void **ptr, size_t *length)
|
||||
{
|
||||
if (BUF_LOCKED(buf)) {
|
||||
return B_ERR_BUSY;
|
||||
}
|
||||
|
||||
size_t contiguous_capacity = 0;
|
||||
if (buf->r_read_ptr > buf->r_write_ptr) {
|
||||
contiguous_capacity = buf->r_capacity - buf->r_read_ptr;
|
||||
} else {
|
||||
contiguous_capacity = buf->r_write_ptr - buf->r_read_ptr;
|
||||
}
|
||||
|
||||
if (contiguous_capacity == 0) {
|
||||
return B_ERR_NO_DATA;
|
||||
}
|
||||
|
||||
buf->r_opened_buf = (char *)buf->r_buf + buf->r_read_ptr;
|
||||
buf->r_opened_capacity = contiguous_capacity;
|
||||
|
||||
buf->r_flags |= B_RINGBUFFER_READ_LOCKED;
|
||||
*ptr = buf->r_opened_buf;
|
||||
*length = contiguous_capacity;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_close_read_buffer(
|
||||
struct b_ringbuffer *buf, void **ptr, size_t nr_read)
|
||||
{
|
||||
if (!(buf->r_flags & B_RINGBUFFER_READ_LOCKED)) {
|
||||
return B_ERR_BAD_STATE;
|
||||
}
|
||||
|
||||
if (*ptr != buf->r_opened_buf) {
|
||||
return B_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (nr_read > buf->r_opened_capacity) {
|
||||
return B_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
buf->r_read_ptr += nr_read;
|
||||
if (buf->r_read_ptr >= buf->r_capacity) {
|
||||
buf->r_read_ptr = 0;
|
||||
}
|
||||
|
||||
buf->r_opened_buf = NULL;
|
||||
buf->r_opened_capacity = 0;
|
||||
buf->r_flags &= ~B_RINGBUFFER_READ_LOCKED;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_open_write_buffer(
|
||||
struct b_ringbuffer *buf, void **ptr, size_t *capacity)
|
||||
{
|
||||
if (BUF_LOCKED(buf)) {
|
||||
return B_ERR_BUSY;
|
||||
}
|
||||
|
||||
size_t contiguous_capacity = 0;
|
||||
if (buf->r_write_ptr >= buf->r_read_ptr) {
|
||||
contiguous_capacity = buf->r_capacity - buf->r_write_ptr - 1;
|
||||
|
||||
if (buf->r_read_ptr > 0) {
|
||||
contiguous_capacity++;
|
||||
}
|
||||
} else {
|
||||
contiguous_capacity = buf->r_read_ptr - buf->r_write_ptr - 1;
|
||||
}
|
||||
|
||||
if (contiguous_capacity == 0) {
|
||||
return B_ERR_NO_SPACE;
|
||||
}
|
||||
|
||||
buf->r_opened_buf = (char *)buf->r_buf + buf->r_write_ptr;
|
||||
buf->r_opened_capacity = contiguous_capacity;
|
||||
|
||||
buf->r_flags |= B_RINGBUFFER_WRITE_LOCKED;
|
||||
*ptr = buf->r_opened_buf;
|
||||
*capacity = contiguous_capacity;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_ringbuffer_close_write_buffer(
|
||||
struct b_ringbuffer *buf, void **ptr, size_t nr_written)
|
||||
{
|
||||
if (!(buf->r_flags & B_RINGBUFFER_WRITE_LOCKED)) {
|
||||
return B_ERR_BAD_STATE;
|
||||
}
|
||||
|
||||
if (*ptr != buf->r_opened_buf) {
|
||||
return B_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (nr_written > buf->r_opened_capacity) {
|
||||
return B_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
buf->r_write_ptr += nr_written;
|
||||
if (buf->r_write_ptr >= buf->r_capacity) {
|
||||
buf->r_write_ptr = 0;
|
||||
}
|
||||
|
||||
buf->r_opened_buf = NULL;
|
||||
buf->r_opened_capacity = 0;
|
||||
buf->r_flags &= ~B_RINGBUFFER_WRITE_LOCKED;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user