7#include <ascii-chat/ringbuffer.h>
8#include <ascii-chat/common.h>
9#include <ascii-chat/asciichat_errno.h>
10#include <ascii-chat/buffer_pool.h>
11#include <ascii-chat/util/math.h>
12#include <ascii-chat/util/bits.h>
29 if (element_size == 0 || capacity == 0) {
30 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid ring buffer parameters: element_size=%zu, capacity=%zu", element_size,
36 rb = SAFE_CALLOC(1,
sizeof(ringbuffer_t), ringbuffer_t *);
39 size_t actual_capacity = math_next_power_of_two(capacity);
41 rb->buffer = SAFE_CALLOC(actual_capacity, element_size,
char *);
43 rb->element_size = element_size;
44 rb->capacity = actual_capacity;
45 rb->is_power_of_two =
true;
46 rb->capacity_mask = actual_capacity - 1;
47 atomic_init(&rb->head, 0);
48 atomic_init(&rb->tail, 0);
49 atomic_init(&rb->size, 0);
56 SAFE_FREE(rb->buffer);
65 size_t current_size = atomic_load(&rb->size);
66 if (current_size >= rb->capacity) {
70 size_t head = atomic_load(&rb->head);
71 size_t next_head = (head + 1) & rb->capacity_mask;
74 SAFE_MEMCPY(rb->buffer + (head * rb->element_size), rb->element_size, data, rb->element_size);
77 atomic_store(&rb->head, next_head);
78 atomic_fetch_add(&rb->size, 1);
87 size_t current_size = atomic_load(&rb->size);
88 if (current_size == 0) {
92 size_t tail = atomic_load(&rb->tail);
93 size_t next_tail = (tail + 1) & rb->capacity_mask;
96 SAFE_MEMCPY(data, rb->element_size, rb->buffer + (tail * rb->element_size), rb->element_size);
99 atomic_store(&rb->tail, next_tail);
100 atomic_fetch_sub(&rb->size, 1);
109 size_t current_size = atomic_load(&rb->size);
110 if (current_size == 0) {
114 size_t tail = atomic_load(&rb->tail);
117 SAFE_MEMCPY(data, rb->element_size, rb->buffer + (tail * rb->element_size), rb->element_size);
123 return rb ? atomic_load(&rb->size) : 0;
136 atomic_store(&rb->head, 0);
137 atomic_store(&rb->tail, 0);
138 atomic_store(&rb->size, 0);
149 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid frame buffer parameters");
154 fb = SAFE_CALLOC(1,
sizeof(framebuffer_t), framebuffer_t *);
158 SET_ERRNO(ERROR_THREAD,
"Failed to initialize framebuffer mutex");
166 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate frame buffer");
177 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid capacity: %zu", capacity);
182 fb = SAFE_CALLOC(1,
sizeof(framebuffer_t), framebuffer_t *);
186 SET_ERRNO(ERROR_THREAD,
"Failed to initialize framebuffer mutex");
208 if (fb->rb == (ringbuffer_t *)0xDEADBEEF) {
209 SET_ERRNO(ERROR_INVALID_STATE,
"DOUBLE-FREE DETECTED: framebuffer %p already destroyed!", fb);
218 fb->rb = (ringbuffer_t *)0xDEADBEEF;
223 if (!fb || !frame_data || frame_size == 0)
227 if (frame_size > 10 * 1024 * 1024) {
228 SET_ERRNO(ERROR_INVALID_PARAM,
"Rejecting oversized frame: %zu bytes", frame_size);
233 mutex_lock(&fb->mutex);
240 if (IS_FRAME_VALID(&old_frame)) {
241 MARK_FRAME_FREED(&old_frame);
244 }
else if (!IS_MAGIC_VALID(old_frame.magic, FRAME_MAGIC)) {
245 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Invalid old frame magic 0x%x when dropping", old_frame.magic);
253 mutex_unlock(&fb->mutex);
254 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate %zu bytes from buffer pool for frame", frame_size + 1);
258 SAFE_MEMCPY(frame_copy, frame_size, frame_data, frame_size);
259 frame_copy[frame_size] =
'\0';
262 frame_t frame = {.magic = MAGIC_FRAME_VALID, .size = frame_size + 1, .data = frame_copy};
269 SET_ERRNO(ERROR_INVALID_STATE,
"Failed to write frame to ringbuffer even after dropping oldest");
272 mutex_unlock(&fb->mutex);
287 mutex_lock(&fb->mutex);
293 if (!IS_MAGIC_VALID(frame->magic, FRAME_MAGIC)) {
294 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Invalid frame magic 0x%x (expected 0x%x)", frame->magic, FRAME_MAGIC);
297 mutex_unlock(&fb->mutex);
301 if (IS_FRAME_FREED(frame)) {
302 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Reading already-freed frame!");
305 mutex_unlock(&fb->mutex);
309 if (frame->size > 10 * 1024 * 1024) {
310 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Frame size too large: %zu", frame->size);
311 SAFE_FREE(frame->data);
314 mutex_unlock(&fb->mutex);
319 mutex_unlock(&fb->mutex);
328 mutex_lock(&fb->mutex);
331 if (fb->rb->element_size ==
sizeof(multi_source_frame_t)) {
333 multi_source_frame_t multi_frame;
335 if (IS_FRAME_VALID((frame_t *)&multi_frame)) {
336 MARK_FRAME_FREED((frame_t *)&multi_frame);
338 }
else if (!IS_MAGIC_VALID(multi_frame.magic, FRAME_MAGIC) && multi_frame.magic != 0) {
339 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Invalid multi-source frame magic 0x%x during clear",
343 }
else if (fb->rb->element_size ==
sizeof(frame_t)) {
347 if (IS_FRAME_VALID(&frame)) {
348 MARK_FRAME_FREED(&frame);
350 }
else if (!IS_MAGIC_VALID(frame.magic, FRAME_MAGIC) && frame.magic != 0) {
351 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Invalid frame magic 0x%x during clear", frame.magic);
355 SET_ERRNO(ERROR_INVALID_STATE,
"Unknown frame buffer type with element size %zu", fb->rb->element_size);
362 if (fb->rb->buffer) {
364 if (fb->rb->capacity > SIZE_MAX / fb->rb->element_size) {
365 SET_ERRNO(ERROR_INVALID_PARAM,
"Buffer size would overflow: capacity=%zu, element_size=%zu", fb->rb->capacity,
366 fb->rb->element_size);
367 mutex_unlock(&fb->mutex);
370 size_t buffer_size = fb->rb->capacity * fb->rb->element_size;
374 mutex_unlock(&fb->mutex);
380 uint32_t source_client_id, uint32_t frame_sequence, uint32_t timestamp) {
381 if (!fb || !fb->rb || !frame_data || frame_size == 0) {
388 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate %zu bytes from buffer pool for multi-source frame", frame_size);
393 SAFE_MEMCPY(data_copy, frame_size, frame_data, frame_size);
396 multi_source_frame_t multi_frame = {.magic = MAGIC_FRAME_VALID,
397 .source_client_id = source_client_id,
398 .frame_sequence = frame_sequence,
399 .timestamp = timestamp,
404 mutex_lock(&fb->mutex);
411 log_debug(
"Frame buffer full, dropping multi-source frame from client %u", source_client_id);
414 mutex_unlock(&fb->mutex);
419 if (!fb || !fb->rb || !frame) {
424 mutex_lock(&fb->mutex);
430 if (!IS_MAGIC_VALID(frame->magic, FRAME_MAGIC)) {
431 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Invalid multi-source frame magic 0x%x (expected 0x%x)", frame->magic,
435 frame->source_client_id = 0;
436 mutex_unlock(&fb->mutex);
441 if (frame->size == 0 || !frame->data) {
442 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Invalid multi-source frame data (size=%zu, data=%p)", frame->size,
444 mutex_unlock(&fb->mutex);
449 mutex_unlock(&fb->mutex);
454 if (!fb || !fb->rb || !frame) {
459 mutex_lock(&fb->mutex);
466 if (!IS_MAGIC_VALID(frame->magic, FRAME_MAGIC)) {
467 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Invalid multi-source frame magic 0x%x (expected 0x%x) in peek",
468 frame->magic, FRAME_MAGIC);
471 frame->source_client_id = 0;
472 mutex_unlock(&fb->mutex);
477 if (frame->size == 0 || !frame->data) {
478 SET_ERRNO(ERROR_INVALID_STATE,
"CORRUPTION: Invalid multi-source frame data (size=%zu, data=%p) in peek",
479 frame->size, frame->data);
480 mutex_unlock(&fb->mutex);
490 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate memory for frame data copy in peek");
491 mutex_unlock(&fb->mutex);
494 SAFE_MEMCPY(data_copy, frame->size, frame->data, frame->size);
495 frame->data = data_copy;
498 mutex_unlock(&fb->mutex);
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
int buffer_size
Size of circular buffer.
void ringbuffer_clear(ringbuffer_t *rb)
void framebuffer_destroy(framebuffer_t *fb)
void framebuffer_clear(framebuffer_t *fb)
bool framebuffer_read_multi_frame(framebuffer_t *fb, multi_source_frame_t *frame)
framebuffer_t * framebuffer_create_multi(size_t capacity)
ringbuffer_t * ringbuffer_create(size_t element_size, size_t capacity)
framebuffer_t * framebuffer_create(size_t capacity)
size_t ringbuffer_size(const ringbuffer_t *rb)
bool framebuffer_read_frame(framebuffer_t *fb, frame_t *frame)
bool ringbuffer_is_full(const ringbuffer_t *rb)
void ringbuffer_destroy(ringbuffer_t *rb)
bool ringbuffer_is_empty(const ringbuffer_t *rb)
bool framebuffer_write_frame(framebuffer_t *fb, const char *frame_data, size_t frame_size)
bool framebuffer_write_multi_frame(framebuffer_t *fb, const char *frame_data, size_t frame_size, uint32_t source_client_id, uint32_t frame_sequence, uint32_t timestamp)
bool framebuffer_peek_latest_multi_frame(framebuffer_t *fb, multi_source_frame_t *frame)
bool ringbuffer_read(ringbuffer_t *rb, void *data)
bool ringbuffer_write(ringbuffer_t *rb, const void *data)
bool ringbuffer_peek(ringbuffer_t *rb, void *data)
int mutex_init(mutex_t *mutex)
int mutex_destroy(mutex_t *mutex)