From 5bac4db7edad5990b45365ce3e4d4b6e9f76ac76 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Mon, 28 Jul 2025 22:13:41 +0100 Subject: [PATCH] core: add ringbuffer data structure --- core/include/blue/core/ringbuffer.h | 52 ++++ core/include/blue/core/status.h | 6 +- core/ringbuffer.c | 356 ++++++++++++++++++++++++++++ 3 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 core/include/blue/core/ringbuffer.h create mode 100644 core/ringbuffer.c diff --git a/core/include/blue/core/ringbuffer.h b/core/include/blue/core/ringbuffer.h new file mode 100644 index 0000000..67d8e03 --- /dev/null +++ b/core/include/blue/core/ringbuffer.h @@ -0,0 +1,52 @@ +#ifndef BLUELIB_CORE_RINGBUFFER_H_ +#define BLUELIB_CORE_RINGBUFFER_H_ + +#include +#include + +typedef enum b_ringbuffer_flags { + B_RINGBUFFER_SELF_MALLOC = 0x01u, + B_RINGBUFFER_BUFFER_MALLOC = 0x02u, + B_RINGBUFFER_READ_LOCKED = 0x04u, + B_RINGBUFFER_WRITE_LOCKED = 0x08u, +} b_ringbuffer_flags; + +typedef struct b_ringbuffer { + b_ringbuffer_flags r_flags; + void *r_buf, *r_opened_buf; + unsigned long r_capacity, r_opened_capacity; + unsigned long r_write_ptr, r_read_ptr; +} b_ringbuffer; + +BLUE_API b_ringbuffer *b_ringbuffer_create(size_t capacity); +BLUE_API b_ringbuffer *b_ringbuffer_create_with_buffer(void *ptr, size_t capacity); + +BLUE_API b_status b_ringbuffer_init(b_ringbuffer *buf, size_t capacity); +BLUE_API b_status b_ringbuffer_init_with_buffer( + b_ringbuffer *buf, void *ptr, size_t capacity); + +BLUE_API b_status b_ringbuffer_reset(b_ringbuffer *buf); +BLUE_API b_status b_ringbuffer_destroy(b_ringbuffer *buf); + +BLUE_API b_status b_ringbuffer_read( + b_ringbuffer *buf, void *p, size_t count, size_t *nr_read); +BLUE_API b_status b_ringbuffer_write( + b_ringbuffer *buf, const void *p, size_t count, size_t *nr_written); + +BLUE_API int b_ringbuffer_getc(b_ringbuffer *buf); +BLUE_API b_status b_ringbuffer_putc(b_ringbuffer *buf, int c); + +BLUE_API size_t b_ringbuffer_write_capacity_remaining(const b_ringbuffer *buf); +BLUE_API size_t b_ringbuffer_available_data_remaining(const b_ringbuffer *buf); + +BLUE_API b_status b_ringbuffer_open_read_buffer( + b_ringbuffer *buf, void **ptr, size_t *length); +BLUE_API b_status b_ringbuffer_close_read_buffer( + b_ringbuffer *buf, void **ptr, size_t nr_read); + +BLUE_API b_status b_ringbuffer_open_write_buffer( + b_ringbuffer *buf, void **ptr, size_t *capacity); +BLUE_API b_status b_ringbuffer_close_write_buffer( + b_ringbuffer *buf, void **ptr, size_t nr_written); + +#endif diff --git a/core/include/blue/core/status.h b/core/include/blue/core/status.h index 3248fd7..dce1634 100644 --- a/core/include/blue/core/status.h +++ b/core/include/blue/core/status.h @@ -3,7 +3,7 @@ #include -#define B_OK(status) ((status) == B_SUCCESS) +#define B_OK(status) ((enum b_status)((uintptr_t)(status)) == B_SUCCESS) #define B_ERR(status) ((status) != B_SUCCESS) typedef enum b_status { @@ -16,14 +16,18 @@ typedef enum b_status { B_ERR_BAD_STATE, B_ERR_NO_ENTRY, B_ERR_NO_DATA, + B_ERR_NO_SPACE, B_ERR_UNKNOWN_FUNCTION, B_ERR_BAD_FORMAT, B_ERR_IO_FAILURE, B_ERR_IS_DIRECTORY, B_ERR_NOT_DIRECTORY, B_ERR_PERMISSION_DENIED, + B_ERR_BUSY, + B_ERR_COMPRESSION_FAILURE, } b_status; BLUE_API const char *b_status_to_string(b_status status); +BLUE_API const char *b_status_description(b_status status); #endif diff --git a/core/ringbuffer.c b/core/ringbuffer.c new file mode 100644 index 0000000..ec6a36e --- /dev/null +++ b/core/ringbuffer.c @@ -0,0 +1,356 @@ +#include +#include +#include + +#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; +}