Files
bluelib/core/ringbuffer.c

443 lines
9.9 KiB
C
Raw Permalink Normal View History

2025-07-28 22:13:41 +01:00
#include <blue/core/ringbuffer.h>
#include <stdlib.h>
#include <string.h>
#define BUF_LOCKED(buf) \
(((buf)->r_flags & (RINGBUFFER_READ_LOCKED | RINGBUFFER_WRITE_LOCKED)) \
2025-07-28 22:13:41 +01:00
!= 0)
/*** PRIVATE DATA *************************************************************/
enum ringbuffer_flags {
RINGBUFFER_BUFFER_MALLOC = 0x02u,
RINGBUFFER_READ_LOCKED = 0x04u,
RINGBUFFER_WRITE_LOCKED = 0x08u,
};
struct b_ringbuffer_p {
enum 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;
};
/*** PRIVATE FUNCTIONS ********************************************************/
static enum b_status ringbuffer_clear(struct b_ringbuffer_p *buf)
2025-07-28 22:13:41 +01:00
{
if (BUF_LOCKED(buf)) {
return B_ERR_BUSY;
2025-07-28 22:13:41 +01:00
}
buf->r_read_ptr = buf->r_write_ptr = 0;
2025-07-28 22:13:41 +01:00
return B_SUCCESS;
}
2025-07-28 22:13:41 +01:00
static size_t ringbuffer_write_capacity_remaining(const struct b_ringbuffer_p *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;
}
}
2025-07-28 22:13:41 +01:00
static size_t ringbuffer_available_data_remaining(const struct b_ringbuffer_p *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;
}
2025-07-28 22:13:41 +01:00
}
static enum b_status ringbuffer_open_read_buffer(
struct b_ringbuffer_p *buf, const void **ptr, size_t *length)
2025-07-28 22:13:41 +01:00
{
if (BUF_LOCKED(buf)) {
return B_ERR_BUSY;
2025-07-28 22:13:41 +01:00
}
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;
}
2025-07-28 22:13:41 +01:00
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 |= RINGBUFFER_READ_LOCKED;
*ptr = buf->r_opened_buf;
*length = contiguous_capacity;
2025-07-28 22:13:41 +01:00
return B_SUCCESS;
2025-07-28 22:13:41 +01:00
}
static enum b_status ringbuffer_close_read_buffer(
struct b_ringbuffer_p *buf, const void **ptr, size_t nr_read)
2025-07-28 22:13:41 +01:00
{
if (!(buf->r_flags & RINGBUFFER_READ_LOCKED)) {
return B_ERR_BAD_STATE;
2025-07-28 22:13:41 +01:00
}
if (*ptr != buf->r_opened_buf) {
return B_ERR_INVALID_ARGUMENT;
}
2025-07-28 22:13:41 +01:00
if (nr_read > buf->r_opened_capacity) {
return B_ERR_INVALID_ARGUMENT;
}
2025-07-28 22:13:41 +01:00
buf->r_read_ptr += nr_read;
if (buf->r_read_ptr >= buf->r_capacity) {
buf->r_read_ptr = 0;
}
2025-07-28 22:13:41 +01:00
if (buf->r_read_ptr == buf->r_write_ptr) {
/* the ringbuffer is now empty. set both pointers to 0.
* this ensures that the whole buffer will be available
* contiguously to the next call to open_write_buffer */
buf->r_read_ptr = 0;
buf->r_write_ptr = 0;
}
2025-07-28 22:13:41 +01:00
buf->r_opened_buf = NULL;
buf->r_opened_capacity = 0;
buf->r_flags &= ~RINGBUFFER_READ_LOCKED;
2025-07-28 22:13:41 +01:00
return B_SUCCESS;
}
static enum b_status ringbuffer_open_write_buffer(
struct b_ringbuffer_p *buf, void **ptr, size_t *capacity)
2025-07-28 22:13:41 +01:00
{
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 |= RINGBUFFER_WRITE_LOCKED;
*ptr = buf->r_opened_buf;
*capacity = contiguous_capacity;
2025-07-28 22:13:41 +01:00
return B_SUCCESS;
}
static enum b_status ringbuffer_close_write_buffer(
struct b_ringbuffer_p *buf, void **ptr, size_t nr_written)
2025-07-28 22:13:41 +01:00
{
if (!(buf->r_flags & RINGBUFFER_WRITE_LOCKED)) {
return B_ERR_BAD_STATE;
2025-07-28 22:13:41 +01:00
}
if (*ptr != buf->r_opened_buf) {
return B_ERR_INVALID_ARGUMENT;
2025-07-28 22:13:41 +01:00
}
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;
2025-07-28 22:13:41 +01:00
}
buf->r_opened_buf = NULL;
buf->r_opened_capacity = 0;
buf->r_flags &= ~RINGBUFFER_WRITE_LOCKED;
2025-07-28 22:13:41 +01:00
return B_SUCCESS;
}
static enum b_status ringbuffer_read(
struct b_ringbuffer_p *buf, void *p, size_t count, size_t *nr_read)
2025-07-28 22:13:41 +01:00
{
if (BUF_LOCKED(buf)) {
return B_ERR_BUSY;
}
size_t r = 0;
unsigned char *dest = p;
size_t remaining = count;
while (remaining > 0) {
const void *src;
2025-07-28 22:13:41 +01:00
size_t available;
enum b_status status
= ringbuffer_open_read_buffer(buf, &src, &available);
2025-07-28 22:13:41 +01:00
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;
ringbuffer_close_read_buffer(buf, &src, to_copy);
2025-07-28 22:13:41 +01:00
}
*nr_read = r;
return B_SUCCESS;
}
static enum b_status ringbuffer_write(
struct b_ringbuffer_p *buf, const void *p, size_t count, size_t *nr_written)
2025-07-28 22:13:41 +01:00
{
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
= ringbuffer_open_write_buffer(buf, &dest, &available);
2025-07-28 22:13:41 +01:00
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;
ringbuffer_close_write_buffer(buf, &dest, to_copy);
2025-07-28 22:13:41 +01:00
}
*nr_written = w;
return B_SUCCESS;
}
static int ringbuffer_getc(struct b_ringbuffer_p *buf)
2025-07-28 22:13:41 +01:00
{
size_t available = ringbuffer_available_data_remaining(buf);
2025-07-28 22:13:41 +01:00
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;
}
static enum b_status ringbuffer_putc(struct b_ringbuffer_p *buf, int c)
2025-07-28 22:13:41 +01:00
{
size_t available = ringbuffer_write_capacity_remaining(buf);
2025-07-28 22:13:41 +01:00
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;
}
/*** PUBLIC FUNCTIONS *********************************************************/
2025-07-28 22:13:41 +01:00
b_ringbuffer *b_ringbuffer_create(size_t capacity)
2025-07-28 22:13:41 +01:00
{
b_ringbuffer *ringbuf = b_object_create(B_TYPE_RINGBUFFER);
if (!ringbuf) {
return NULL;
2025-07-28 22:13:41 +01:00
}
struct b_ringbuffer_p *p
= b_object_get_private(ringbuf, B_TYPE_RINGBUFFER);
2025-07-28 22:13:41 +01:00
void *buffer = malloc(capacity);
if (!buffer) {
b_ringbuffer_unref(ringbuf);
return NULL;
2025-07-28 22:13:41 +01:00
}
p->r_flags = RINGBUFFER_BUFFER_MALLOC;
p->r_buf = buffer;
p->r_capacity = capacity;
2025-07-28 22:13:41 +01:00
return ringbuf;
2025-07-28 22:13:41 +01:00
}
b_ringbuffer *b_ringbuffer_create_with_buffer(void *ptr, size_t capacity)
2025-07-28 22:13:41 +01:00
{
b_ringbuffer *ringbuf = b_object_create(B_TYPE_RINGBUFFER);
if (!ringbuf) {
return NULL;
2025-07-28 22:13:41 +01:00
}
struct b_ringbuffer_p *p
= b_object_get_private(ringbuf, B_TYPE_RINGBUFFER);
2025-07-28 22:13:41 +01:00
p->r_flags = 0;
p->r_buf = ptr;
p->r_capacity = capacity;
2025-07-28 22:13:41 +01:00
return ringbuf;
}
2025-07-28 22:13:41 +01:00
enum b_status b_ringbuffer_clear(b_ringbuffer *buf)
{
B_CLASS_DISPATCH_STATIC_0(B_TYPE_RINGBUFFER, ringbuffer_clear, buf);
}
enum b_status b_ringbuffer_read(
b_ringbuffer *buf, void *p, size_t count, size_t *nr_read)
{
B_CLASS_DISPATCH_STATIC(
B_TYPE_RINGBUFFER, ringbuffer_read, buf, p, count, nr_read);
}
2025-07-28 22:13:41 +01:00
enum b_status b_ringbuffer_write(
b_ringbuffer *buf, const void *p, size_t count, size_t *nr_written)
{
B_CLASS_DISPATCH_STATIC(
B_TYPE_RINGBUFFER, ringbuffer_write, buf, p, count, nr_written);
2025-07-28 22:13:41 +01:00
}
int b_ringbuffer_getc(b_ringbuffer *buf)
2025-07-28 22:13:41 +01:00
{
B_CLASS_DISPATCH_STATIC_0(B_TYPE_RINGBUFFER, ringbuffer_getc, buf);
}
2025-07-28 22:13:41 +01:00
enum b_status b_ringbuffer_putc(b_ringbuffer *buf, int c)
{
B_CLASS_DISPATCH_STATIC(B_TYPE_RINGBUFFER, ringbuffer_putc, buf, c);
}
2025-07-28 22:13:41 +01:00
size_t b_ringbuffer_write_capacity_remaining(const b_ringbuffer *buf)
{
B_CLASS_DISPATCH_STATIC_0(
B_TYPE_RINGBUFFER, ringbuffer_write_capacity_remaining, buf);
}
2025-07-28 22:13:41 +01:00
size_t b_ringbuffer_available_data_remaining(const b_ringbuffer *buf)
{
B_CLASS_DISPATCH_STATIC_0(
B_TYPE_RINGBUFFER, ringbuffer_available_data_remaining, buf);
}
2025-07-28 22:13:41 +01:00
enum b_status b_ringbuffer_open_read_buffer(
b_ringbuffer *buf, const void **ptr, size_t *length)
{
B_CLASS_DISPATCH_STATIC(
B_TYPE_RINGBUFFER, ringbuffer_open_read_buffer, buf, ptr, length);
}
2025-07-28 22:13:41 +01:00
enum b_status b_ringbuffer_close_read_buffer(
b_ringbuffer *buf, const void **ptr, size_t nr_read)
{
B_CLASS_DISPATCH_STATIC(
B_TYPE_RINGBUFFER, ringbuffer_close_read_buffer, buf, ptr, nr_read);
}
2025-07-28 22:13:41 +01:00
enum b_status b_ringbuffer_open_write_buffer(
b_ringbuffer *buf, void **ptr, size_t *capacity)
{
B_CLASS_DISPATCH_STATIC(
B_TYPE_RINGBUFFER, ringbuffer_open_write_buffer, buf, ptr,
capacity);
2025-07-28 22:13:41 +01:00
}
enum b_status b_ringbuffer_close_write_buffer(
b_ringbuffer *buf, void **ptr, size_t nr_written)
2025-07-28 22:13:41 +01:00
{
B_CLASS_DISPATCH_STATIC(
B_TYPE_RINGBUFFER, ringbuffer_close_write_buffer, buf, ptr,
nr_written);
}
2025-07-28 22:13:41 +01:00
/*** VIRTUAL FUNCTIONS ********************************************************/
2025-07-28 22:13:41 +01:00
static void ringbuffer_init(b_object *obj, void *priv)
{
struct b_ringbuffer_p *buf = priv;
}
2025-07-28 22:13:41 +01:00
static void ringbuffer_fini(b_object *obj, void *priv)
{
struct b_ringbuffer_p *buf = priv;
2025-07-28 22:13:41 +01:00
if (buf->r_flags & RINGBUFFER_BUFFER_MALLOC) {
free(buf->r_buf);
}
2025-07-28 22:13:41 +01:00
}
/*** CLASS DEFINITION *********************************************************/
B_TYPE_CLASS_DEFINITION_BEGIN(b_ringbuffer)
B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT)
B_INTERFACE_ENTRY(to_string) = NULL;
B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT)
B_TYPE_CLASS_DEFINITION_END(b_ringbuffer)
B_TYPE_DEFINITION_BEGIN(b_ringbuffer)
B_TYPE_ID(0xb0493774, 0xef13, 0x4905, 0xa865, 0x1595607ccad9);
B_TYPE_CLASS(b_ringbuffer_class);
B_TYPE_INSTANCE_PRIVATE(struct b_ringbuffer_p);
B_TYPE_INSTANCE_INIT(ringbuffer_init);
B_TYPE_INSTANCE_FINI(ringbuffer_fini);
B_TYPE_DEFINITION_END(b_ringbuffer)