#include #include #include #define BUF_LOCKED(buf) \ (((buf)->r_flags & (RINGBUFFER_READ_LOCKED | RINGBUFFER_WRITE_LOCKED)) \ != 0) /*** PRIVATE DATA *************************************************************/ enum ringbuffer_flags { RINGBUFFER_BUFFER_MALLOC = 0x02u, RINGBUFFER_READ_LOCKED = 0x04u, RINGBUFFER_WRITE_LOCKED = 0x08u, }; struct fx_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 fx_status ringbuffer_clear(struct fx_ringbuffer_p *buf) { if (BUF_LOCKED(buf)) { return FX_ERR_BUSY; } buf->r_read_ptr = buf->r_write_ptr = 0; return FX_SUCCESS; } static size_t ringbuffer_write_capacity_remaining(const struct fx_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; } } static size_t ringbuffer_available_data_remaining(const struct fx_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; } } static enum fx_status ringbuffer_open_read_buffer( struct fx_ringbuffer_p *buf, const void **ptr, size_t *length) { if (BUF_LOCKED(buf)) { return FX_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 FX_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; return FX_SUCCESS; } static enum fx_status ringbuffer_close_read_buffer( struct fx_ringbuffer_p *buf, const void **ptr, size_t nr_read) { if (!(buf->r_flags & RINGBUFFER_READ_LOCKED)) { return FX_ERR_BAD_STATE; } if (*ptr != buf->r_opened_buf) { return FX_ERR_INVALID_ARGUMENT; } if (nr_read > buf->r_opened_capacity) { return FX_ERR_INVALID_ARGUMENT; } buf->r_read_ptr += nr_read; if (buf->r_read_ptr >= buf->r_capacity) { buf->r_read_ptr = 0; } 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; } buf->r_opened_buf = NULL; buf->r_opened_capacity = 0; buf->r_flags &= ~RINGBUFFER_READ_LOCKED; return FX_SUCCESS; } static enum fx_status ringbuffer_open_write_buffer( struct fx_ringbuffer_p *buf, void **ptr, size_t *capacity) { if (BUF_LOCKED(buf)) { return FX_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 FX_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; return FX_SUCCESS; } static enum fx_status ringbuffer_close_write_buffer( struct fx_ringbuffer_p *buf, void **ptr, size_t nr_written) { if (!(buf->r_flags & RINGBUFFER_WRITE_LOCKED)) { return FX_ERR_BAD_STATE; } if (*ptr != buf->r_opened_buf) { return FX_ERR_INVALID_ARGUMENT; } if (nr_written > buf->r_opened_capacity) { return FX_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 &= ~RINGBUFFER_WRITE_LOCKED; return FX_SUCCESS; } static enum fx_status ringbuffer_read( struct fx_ringbuffer_p *buf, void *p, size_t count, size_t *nr_read) { if (BUF_LOCKED(buf)) { return FX_ERR_BUSY; } size_t r = 0; unsigned char *dest = p; size_t remaining = count; while (remaining > 0) { const void *src; size_t available; enum fx_status status = ringbuffer_open_read_buffer(buf, &src, &available); if (status == FX_ERR_NO_DATA) { break; } if (!FX_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); } *nr_read = r; return FX_SUCCESS; } static enum fx_status ringbuffer_write( struct fx_ringbuffer_p *buf, const void *p, size_t count, size_t *nr_written) { if (BUF_LOCKED(buf)) { return FX_ERR_BUSY; } size_t w = 0; const unsigned char *src = p; size_t remaining = count; while (remaining > 0) { void *dest; size_t available; enum fx_status status = ringbuffer_open_write_buffer(buf, &dest, &available); if (status == FX_ERR_NO_SPACE) { break; } if (!FX_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); } *nr_written = w; return FX_SUCCESS; } static int ringbuffer_getc(struct fx_ringbuffer_p *buf) { size_t available = 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; } static enum fx_status ringbuffer_putc(struct fx_ringbuffer_p *buf, int c) { size_t available = ringbuffer_write_capacity_remaining(buf); if (available == 0) { return FX_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 *********************************************************/ fx_ringbuffer *fx_ringbuffer_create(size_t capacity) { fx_ringbuffer *ringbuf = fx_object_create(FX_TYPE_RINGBUFFER); if (!ringbuf) { return NULL; } struct fx_ringbuffer_p *p = fx_object_get_private(ringbuf, FX_TYPE_RINGBUFFER); void *buffer = malloc(capacity); if (!buffer) { fx_ringbuffer_unref(ringbuf); return NULL; } p->r_flags = RINGBUFFER_BUFFER_MALLOC; p->r_buf = buffer; p->r_capacity = capacity; return ringbuf; } fx_ringbuffer *fx_ringbuffer_create_with_buffer(void *ptr, size_t capacity) { fx_ringbuffer *ringbuf = fx_object_create(FX_TYPE_RINGBUFFER); if (!ringbuf) { return NULL; } struct fx_ringbuffer_p *p = fx_object_get_private(ringbuf, FX_TYPE_RINGBUFFER); p->r_flags = 0; p->r_buf = ptr; p->r_capacity = capacity; return ringbuf; } enum fx_status fx_ringbuffer_clear(fx_ringbuffer *buf) { FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_RINGBUFFER, ringbuffer_clear, buf); } enum fx_status fx_ringbuffer_read( fx_ringbuffer *buf, void *p, size_t count, size_t *nr_read) { FX_CLASS_DISPATCH_STATIC( FX_TYPE_RINGBUFFER, ringbuffer_read, buf, p, count, nr_read); } enum fx_status fx_ringbuffer_write( fx_ringbuffer *buf, const void *p, size_t count, size_t *nr_written) { FX_CLASS_DISPATCH_STATIC( FX_TYPE_RINGBUFFER, ringbuffer_write, buf, p, count, nr_written); } int fx_ringbuffer_getc(fx_ringbuffer *buf) { FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_RINGBUFFER, ringbuffer_getc, buf); } enum fx_status fx_ringbuffer_putc(fx_ringbuffer *buf, int c) { FX_CLASS_DISPATCH_STATIC(FX_TYPE_RINGBUFFER, ringbuffer_putc, buf, c); } size_t fx_ringbuffer_write_capacity_remaining(const fx_ringbuffer *buf) { FX_CLASS_DISPATCH_STATIC_0( FX_TYPE_RINGBUFFER, ringbuffer_write_capacity_remaining, buf); } size_t fx_ringbuffer_available_data_remaining(const fx_ringbuffer *buf) { FX_CLASS_DISPATCH_STATIC_0( FX_TYPE_RINGBUFFER, ringbuffer_available_data_remaining, buf); } enum fx_status fx_ringbuffer_open_read_buffer( fx_ringbuffer *buf, const void **ptr, size_t *length) { FX_CLASS_DISPATCH_STATIC( FX_TYPE_RINGBUFFER, ringbuffer_open_read_buffer, buf, ptr, length); } enum fx_status fx_ringbuffer_close_read_buffer( fx_ringbuffer *buf, const void **ptr, size_t nr_read) { FX_CLASS_DISPATCH_STATIC( FX_TYPE_RINGBUFFER, ringbuffer_close_read_buffer, buf, ptr, nr_read); } enum fx_status fx_ringbuffer_open_write_buffer( fx_ringbuffer *buf, void **ptr, size_t *capacity) { FX_CLASS_DISPATCH_STATIC( FX_TYPE_RINGBUFFER, ringbuffer_open_write_buffer, buf, ptr, capacity); } enum fx_status fx_ringbuffer_close_write_buffer( fx_ringbuffer *buf, void **ptr, size_t nr_written) { FX_CLASS_DISPATCH_STATIC( FX_TYPE_RINGBUFFER, ringbuffer_close_write_buffer, buf, ptr, nr_written); } /*** VIRTUAL FUNCTIONS ********************************************************/ static void ringbuffer_init(fx_object *obj, void *priv) { struct fx_ringbuffer_p *buf = priv; } static void ringbuffer_fini(fx_object *obj, void *priv) { struct fx_ringbuffer_p *buf = priv; if (buf->r_flags & RINGBUFFER_BUFFER_MALLOC) { free(buf->r_buf); } } /*** CLASS DEFINITION *********************************************************/ FX_TYPE_CLASS_DEFINITION_BEGIN(fx_ringbuffer) FX_TYPE_CLASS_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) FX_INTERFACE_ENTRY(to_string) = NULL; FX_TYPE_CLASS_INTERFACE_END(fx_object, FX_TYPE_OBJECT) FX_TYPE_CLASS_DEFINITION_END(fx_ringbuffer) FX_TYPE_DEFINITION_BEGIN(fx_ringbuffer) FX_TYPE_ID(0xb0493774, 0xef13, 0x4905, 0xa865, 0x1595607ccad9); FX_TYPE_CLASS(fx_ringbuffer_class); FX_TYPE_INSTANCE_PRIVATE(struct fx_ringbuffer_p); FX_TYPE_INSTANCE_INIT(ringbuffer_init); FX_TYPE_INSTANCE_FINI(ringbuffer_fini); FX_TYPE_DEFINITION_END(fx_ringbuffer)