ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
ringbuffer.c
Go to the documentation of this file.
1
8#include <ascii-chat/common.h>
9#include <ascii-chat/asciichat_errno.h> // For asciichat_errno system
11#include <ascii-chat/util/math.h> // For power-of-two utilities
12#include <ascii-chat/util/bits.h> // For is_power_of_two, next_power_of_two
13#include <ascii-chat/atomic.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <assert.h>
18
19/* ============================================================================
20 * Ring Buffer Implementation
21 * ============================================================================
22 *
23 * THREAD SAFETY NOTE: This ring buffer is designed for single-producer,
24 * single-consumer (SPSC) use only. The atomic operations provide memory
25 * ordering guarantees but do NOT support concurrent writes from multiple
26 * producers. For multi-writer scenarios, external synchronization is required.
27 */
28
29ringbuffer_t *ringbuffer_create(size_t element_size, size_t capacity) {
30 if (element_size == 0 || capacity == 0) {
31 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid ring buffer parameters: element_size=%zu, capacity=%zu", element_size,
32 capacity);
33 return NULL;
34 }
35
36 ringbuffer_t *rb;
37 rb = SAFE_CALLOC(1, sizeof(ringbuffer_t), ringbuffer_t *);
38
39 /* Round capacity up to power of 2 for optimization */
40 size_t actual_capacity = math_next_power_of_two(capacity);
41
42 rb->buffer = SAFE_CALLOC(actual_capacity, element_size, char *);
43
44 rb->element_size = element_size;
45 rb->capacity = actual_capacity;
46 rb->is_power_of_two = true;
47 rb->capacity_mask = actual_capacity - 1;
48 /* Atomic fields are zero-initialized by SAFE_CALLOC */
49
50 return rb;
51}
52
54 if (rb) {
55 SAFE_FREE(rb->buffer);
56 SAFE_FREE(rb);
57 }
58}
59
60bool ringbuffer_write(ringbuffer_t *rb, const void *data) {
61 if (!rb || !data)
62 return false;
63
64 size_t current_size = atomic_load_u64(&rb->size);
65 if (current_size >= rb->capacity) {
66 return false; /* Buffer full */
67 }
68
69 size_t head = atomic_load_u64(&rb->head);
70 size_t next_head = (head + 1) & rb->capacity_mask;
71
72 /* Copy data */
73 SAFE_MEMCPY(rb->buffer + (head * rb->element_size), rb->element_size, data, rb->element_size);
74
75 /* Update head and size atomically */
76 atomic_store_u64(&rb->head, next_head);
78
79 return true;
80}
81
82bool ringbuffer_read(ringbuffer_t *rb, void *data) {
83 if (!rb || !data)
84 return false;
85
86 size_t current_size = atomic_load_u64(&rb->size);
87 if (current_size == 0) {
88 return false; /* Buffer empty */
89 }
90
91 size_t tail = atomic_load_u64(&rb->tail);
92 size_t next_tail = (tail + 1) & rb->capacity_mask;
93
94 /* Copy data */
95 SAFE_MEMCPY(data, rb->element_size, rb->buffer + (tail * rb->element_size), rb->element_size);
96
97 /* Update tail and size atomically */
98 atomic_store_u64(&rb->tail, next_tail);
100
101 return true;
102}
103
104bool ringbuffer_peek(ringbuffer_t *rb, void *data) {
105 if (!rb || !data)
106 return false;
107
108 size_t current_size = atomic_load_u64(&rb->size);
109 if (current_size == 0) {
110 return false; /* Buffer empty */
111 }
112
113 size_t tail = atomic_load_u64(&rb->tail);
114
115 /* Copy data without updating tail */
116 SAFE_MEMCPY(data, rb->element_size, rb->buffer + (tail * rb->element_size), rb->element_size);
117
118 return true;
119}
120
121size_t ringbuffer_size(const ringbuffer_t *rb) {
122 return rb ? atomic_load_u64(&rb->size) : 0;
123}
124
126 return ringbuffer_size(rb) == 0;
127}
128
130 return rb ? ringbuffer_size(rb) >= rb->capacity : true;
131}
132
134 if (rb) {
135 atomic_store_u64(&rb->head, 0);
136 atomic_store_u64(&rb->tail, 0);
137 atomic_store_u64(&rb->size, 0);
138 }
139}
140
141/* ============================================================================
142 * Frame Buffer Implementation
143 * ============================================================================
144 */
145
147 if (capacity == 0) {
148 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid frame buffer parameters");
149 return NULL;
150 }
151
152 framebuffer_t *fb;
153 fb = SAFE_CALLOC(1, sizeof(framebuffer_t), framebuffer_t *);
154
155 // Initialize mutex for thread-safe access
156 if (mutex_init(&fb->mutex, "framebuffer") != 0) {
157 SET_ERRNO(ERROR_THREAD, "Failed to initialize framebuffer mutex");
158 SAFE_FREE(fb);
159 return NULL;
160 }
161
162 // Create ringbuffer to store frame_t structs
163 fb->rb = ringbuffer_create(sizeof(frame_t), capacity);
164 if (!fb->rb) {
165 SET_ERRNO(ERROR_MEMORY, "Failed to allocate frame buffer");
166 mutex_destroy(&fb->mutex);
167 SAFE_FREE(fb);
168 return NULL;
169 }
170
171 return fb;
172}
173
175 if (capacity == 0) {
176 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid capacity: %zu", capacity);
177 return NULL;
178 }
179
180 framebuffer_t *fb;
181 fb = SAFE_CALLOC(1, sizeof(framebuffer_t), framebuffer_t *);
182
183 // Initialize mutex for thread-safe access
184 if (mutex_init(&fb->mutex, "framebuffer_multi") != 0) {
185 SET_ERRNO(ERROR_THREAD, "Failed to initialize framebuffer mutex");
186 SAFE_FREE(fb);
187 return NULL;
188 }
189
190 // Create ringbuffer to store multi_source_frame_t structs
191 fb->rb = ringbuffer_create(sizeof(multi_source_frame_t), capacity);
192
193 if (!fb->rb) {
194 mutex_destroy(&fb->mutex);
195 SAFE_FREE(fb);
196 return NULL;
197 }
198
199 return fb;
200}
201
203 if (!fb)
204 return;
205
206 // Add magic number check to detect double-free using rb pointer
207 if (fb->rb == (ringbuffer_t *)0xDEADBEEF) {
208 SET_ERRNO(ERROR_INVALID_STATE, "DOUBLE-FREE DETECTED: framebuffer %p already destroyed!", fb);
209 return;
210 }
211
214 mutex_destroy(&fb->mutex);
215
216 // Mark as destroyed before freeing
217 fb->rb = (ringbuffer_t *)0xDEADBEEF;
218 SAFE_FREE(fb);
219}
220
221bool framebuffer_write_frame(framebuffer_t *fb, const char *frame_data, size_t frame_size) {
222 if (!fb || !frame_data || frame_size == 0)
223 return false;
224
225 // Validate frame size to prevent overflow
226 if (frame_size > 10 * 1024 * 1024) { // 10MB max for ASCII frames
227 SET_ERRNO(ERROR_INVALID_PARAM, "Rejecting oversized frame: %zu bytes", frame_size);
228 return false;
229 }
230
231 // Thread-safe access to framebuffer (was missing, causing race conditions)
232 mutex_lock(&fb->mutex);
233
234 // Check if buffer is full - if so, we need to drop the oldest frame
235 if (ringbuffer_size(fb->rb) >= fb->rb->capacity) {
236 // Buffer is full, read and free the oldest frame before writing new one
237 frame_t old_frame;
238 if (ringbuffer_read(fb->rb, &old_frame)) {
239 if (IS_FRAME_VALID(&old_frame)) {
240 MARK_FRAME_FREED(&old_frame);
241 // Use buffer_pool_free since data was allocated with buffer_pool_alloc
242 buffer_pool_free(NULL, old_frame.data, old_frame.size);
243 } else if (!IS_MAGIC_VALID(old_frame.magic, FRAME_MAGIC)) {
244 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Invalid old frame magic 0x%x when dropping", old_frame.magic);
245 }
246 }
247 }
248
249 // Allocate a copy of the frame data using buffer pool for better performance
250 char *frame_copy = (char *)buffer_pool_alloc(NULL, frame_size + 1);
251 if (!frame_copy) {
252 mutex_unlock(&fb->mutex);
253 SET_ERRNO(ERROR_MEMORY, "Failed to allocate %zu bytes from buffer pool for frame", frame_size + 1);
254 return false;
255 }
256
257 SAFE_MEMCPY(frame_copy, frame_size, frame_data, frame_size);
258 frame_copy[frame_size] = '\0'; // Ensure null termination
259
260 // Create a frame_t struct with the copy (store allocated size for proper cleanup)
261 frame_t frame = {.magic = MAGIC_FRAME_VALID, .size = frame_size + 1, .data = frame_copy};
262
263 bool result = ringbuffer_write(fb->rb, &frame);
264
265 if (!result) {
266 // If we still couldn't write to ringbuffer, return the buffer to pool
267 buffer_pool_free(NULL, frame_copy, frame_size + 1);
268 SET_ERRNO(ERROR_INVALID_STATE, "Failed to write frame to ringbuffer even after dropping oldest");
269 }
270
271 mutex_unlock(&fb->mutex);
272 return result;
273}
274
276 if (!fb || !frame) {
277 return false;
278 }
279
280 // Initialize frame to safe values
281 frame->magic = 0;
282 frame->data = NULL;
283 frame->size = 0;
284
285 // Thread-safe access to framebuffer (fixes TOCTOU race condition)
286 mutex_lock(&fb->mutex);
287
288 bool result = ringbuffer_read(fb->rb, frame);
289
290 // Validate the frame we just read
291 if (result) {
292 if (!IS_MAGIC_VALID(frame->magic, FRAME_MAGIC)) {
293 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Invalid frame magic 0x%x (expected 0x%x)", frame->magic, FRAME_MAGIC);
294 frame->data = NULL;
295 frame->size = 0;
296 mutex_unlock(&fb->mutex);
297 return false;
298 }
299
300 if (IS_FRAME_FREED(frame)) {
301 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Reading already-freed frame!");
302 frame->data = NULL;
303 frame->size = 0;
304 mutex_unlock(&fb->mutex);
305 return false;
306 }
307
308 if (frame->size > 10 * 1024 * 1024) {
309 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Frame size too large: %zu", frame->size);
310 SAFE_FREE(frame->data);
311 frame->data = NULL;
312 frame->size = 0;
313 mutex_unlock(&fb->mutex);
314 return false;
315 }
316 }
317
318 mutex_unlock(&fb->mutex);
319 return result;
320}
321
323 if (!fb || !fb->rb)
324 return;
325
326 // Thread-safe access to framebuffer (was missing, causing race conditions)
327 mutex_lock(&fb->mutex);
328
329 // Check the element size to determine frame type
330 if (fb->rb->element_size == sizeof(multi_source_frame_t)) {
331 // Multi-source frame buffer - read and free multi_source_frame_t
332 multi_source_frame_t multi_frame;
333 while (ringbuffer_read(fb->rb, &multi_frame)) {
334 if (IS_FRAME_VALID((frame_t *)&multi_frame)) {
335 MARK_FRAME_FREED((frame_t *)&multi_frame); // Mark as freed to detect use-after-free
336 buffer_pool_free(NULL, multi_frame.data, multi_frame.size);
337 } else if (!IS_MAGIC_VALID(multi_frame.magic, FRAME_MAGIC) && multi_frame.magic != 0) {
338 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Invalid multi-source frame magic 0x%x during clear",
339 multi_frame.magic);
340 }
341 }
342 } else if (fb->rb->element_size == sizeof(frame_t)) {
343 // Single-source frame buffer - read and free frame_t
345 while (ringbuffer_read(fb->rb, &frame)) {
346 if (IS_FRAME_VALID(&frame)) {
347 MARK_FRAME_FREED(&frame); // Mark as freed to detect use-after-free
348 buffer_pool_free(NULL, frame.data, frame.size);
349 } else if (!IS_MAGIC_VALID(frame.magic, FRAME_MAGIC) && frame.magic != 0) {
350 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Invalid frame magic 0x%x during clear", frame.magic);
351 }
352 }
353 } else {
354 SET_ERRNO(ERROR_INVALID_STATE, "Unknown frame buffer type with element size %zu", fb->rb->element_size);
355 }
356
357 // Clear the ringbuffer indices
358 ringbuffer_clear(fb->rb);
359
360 // Zero out the entire buffer to prevent any dangling pointers
361 if (fb->rb->buffer) {
362 // Check for integer overflow before multiplication
363 if (fb->rb->capacity > SIZE_MAX / fb->rb->element_size) {
364 SET_ERRNO(ERROR_INVALID_PARAM, "Buffer size would overflow: capacity=%zu, element_size=%zu", fb->rb->capacity,
365 fb->rb->element_size);
366 mutex_unlock(&fb->mutex);
367 return;
368 }
369 size_t buffer_size = fb->rb->capacity * fb->rb->element_size;
371 }
372
373 mutex_unlock(&fb->mutex);
374}
375
376// Multi-source frame functions for multi-user support
377
378bool framebuffer_write_multi_frame(framebuffer_t *fb, const char *frame_data, size_t frame_size,
379 uint32_t source_client_id, uint32_t frame_sequence, uint32_t timestamp) {
380 if (!fb || !fb->rb || !frame_data || frame_size == 0) {
381 return false;
382 }
383
384 // Allocate memory for frame data using buffer pool for better performance
385 char *data_copy = (char *)buffer_pool_alloc(NULL, frame_size);
386 if (!data_copy) {
387 SET_ERRNO(ERROR_MEMORY, "Failed to allocate %zu bytes from buffer pool for multi-source frame", frame_size);
388 return false;
389 }
390
391 // Copy frame data
392 SAFE_MEMCPY(data_copy, frame_size, frame_data, frame_size);
393
394 // Convert client ID number to string for storage in frame metadata
395 char client_id_str[64];
396 snprintf(client_id_str, sizeof(client_id_str), "%u", source_client_id);
397
398 // Create multi-source frame
399 multi_source_frame_t multi_frame = {
401 .source_client_id = {0},
402 .frame_sequence = frame_sequence,
403 .timestamp = timestamp,
404 .size = frame_size,
405 .data = data_copy,
406 };
407 SAFE_MEMCPY(multi_frame.source_client_id, sizeof(multi_frame.source_client_id), client_id_str,
408 strlen(client_id_str) + 1);
409
410 // Thread-safe access to framebuffer
411 mutex_lock(&fb->mutex);
412
413 // Try to write to ring buffer
414 bool success = ringbuffer_write(fb->rb, &multi_frame);
415 if (!success) {
416 // Buffer full, return the buffer to pool
417 buffer_pool_free(NULL, data_copy, frame_size);
418 log_debug("Frame buffer full, dropping multi-source frame from client %u", source_client_id);
419 }
420
421 mutex_unlock(&fb->mutex);
422 return success;
423}
424
426 if (!fb || !fb->rb || !frame) {
427 return false;
428 }
429
430 // Thread-safe access to framebuffer
431 mutex_lock(&fb->mutex);
432
433 bool result = ringbuffer_read(fb->rb, frame);
434
435 if (result) {
436 // Validate frame magic
437 if (!IS_MAGIC_VALID(frame->magic, FRAME_MAGIC)) {
438 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Invalid multi-source frame magic 0x%x (expected 0x%x)", frame->magic,
440 frame->data = NULL;
441 frame->size = 0;
442 memset(frame->source_client_id, 0, sizeof(frame->source_client_id));
443 mutex_unlock(&fb->mutex);
444 return false;
445 }
446
447 // Additional validation
448 if (frame->size == 0 || !frame->data) {
449 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Invalid multi-source frame data (size=%zu, data=%p)", frame->size,
450 frame->data);
451 mutex_unlock(&fb->mutex);
452 return false;
453 }
454 }
455
456 mutex_unlock(&fb->mutex);
457 return result;
458}
459
461 if (!fb || !fb->rb || !frame) {
462 return false;
463 }
464
465 // Thread-safe access to framebuffer
466 mutex_lock(&fb->mutex);
467
468 // Use ringbuffer_peek to get the frame without consuming it
469 bool result = ringbuffer_peek(fb->rb, frame);
470
471 if (result) {
472 // Validate frame magic
473 if (!IS_MAGIC_VALID(frame->magic, FRAME_MAGIC)) {
474 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Invalid multi-source frame magic 0x%x (expected 0x%x) in peek",
475 frame->magic, FRAME_MAGIC);
476 frame->data = NULL;
477 frame->size = 0;
478 memset(frame->source_client_id, 0, sizeof(frame->source_client_id));
479 mutex_unlock(&fb->mutex);
480 return false;
481 }
482
483 // Additional validation
484 if (frame->size == 0 || !frame->data) {
485 SET_ERRNO(ERROR_INVALID_STATE, "CORRUPTION: Invalid multi-source frame data (size=%zu, data=%p) in peek",
486 frame->size, frame->data);
487 mutex_unlock(&fb->mutex);
488 return false;
489 }
490
491 // IMPORTANT: We need to make a copy of the data since we're not consuming the frame
492 // The original data pointer will remain valid in the ring buffer
493 // Caller is responsible for freeing this copy with buffer_pool_free()
494 char *data_copy;
495 data_copy = (char *)buffer_pool_alloc(NULL, frame->size);
496 if (!data_copy) {
497 SET_ERRNO(ERROR_MEMORY, "Failed to allocate memory for frame data copy in peek");
498 mutex_unlock(&fb->mutex);
499 return false;
500 }
501 SAFE_MEMCPY(data_copy, frame->size, frame->data, frame->size);
502 frame->data = data_copy;
503 }
504
505 mutex_unlock(&fb->mutex);
506 return result;
507}
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
uint64_t atomic_fetch_add_u64(atomic_t *a, uint64_t delta)
Atomically add to a uint64_t and return the previous value.
Definition atomic.c:248
uint64_t atomic_fetch_sub_u64(atomic_t *a, uint64_t delta)
Atomically subtract from a uint64_t and return the previous value.
Definition atomic.c:256
void atomic_store_u64(atomic_t *a, uint64_t value)
Atomically store a uint64_t value.
Definition atomic.c:241
uint64_t atomic_load_u64(atomic_t *a)
Atomically load a uint64_t value.
Definition atomic.c:233
⚛️ Atomic operations abstraction layer with debug tracking
🔢 Bit Manipulation Utilities
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
⚙️ Common definitions, error codes, macros, and types shared throughout the application
int buffer_size
Size of circular buffer.
Definition grep.c:90
void buffer_pool_free(buffer_pool_t *pool, const void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
unsigned int uint32_t
Definition common.h:58
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MEMSET(dest, dest_size, ch, count)
Definition common.h:469
#define SAFE_CALLOC(count, size, cast)
Definition common.h:274
#define SAFE_MEMCPY(dest, dest_size, src, count)
Definition common.h:468
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_INVALID_STATE
@ ERROR_MEMORY
Definition error_codes.h:56
@ ERROR_INVALID_PARAM
@ ERROR_THREAD
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
int mutex_init(mutex_t *mutex, const char *name)
Initialize a mutex with a name.
Definition threading.c:16
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
Definition threading.c:22
void ringbuffer_clear(ringbuffer_t *rb)
Clear all elements from the buffer.
Definition ringbuffer.c:133
void framebuffer_destroy(framebuffer_t *fb)
Destroy a frame buffer.
Definition ringbuffer.c:202
void framebuffer_clear(framebuffer_t *fb)
Clear all frames from the buffer, freeing their data.
Definition ringbuffer.c:322
bool framebuffer_read_multi_frame(framebuffer_t *fb, multi_source_frame_t *frame)
Read a multi-source frame from the buffer.
Definition ringbuffer.c:425
framebuffer_t * framebuffer_create_multi(size_t capacity)
Create a multi-source frame buffer for multi-user support.
Definition ringbuffer.c:174
ringbuffer_t * ringbuffer_create(size_t element_size, size_t capacity)
Create a new ring buffer.
Definition ringbuffer.c:29
framebuffer_t * framebuffer_create(size_t capacity)
Create a frame buffer for ASCII frames.
Definition ringbuffer.c:146
size_t ringbuffer_size(const ringbuffer_t *rb)
Get current number of elements in the buffer.
Definition ringbuffer.c:121
bool framebuffer_read_frame(framebuffer_t *fb, frame_t *frame)
Read a frame from the buffer.
Definition ringbuffer.c:275
bool ringbuffer_is_full(const ringbuffer_t *rb)
Check if buffer is full.
Definition ringbuffer.c:129
void ringbuffer_destroy(ringbuffer_t *rb)
Destroy a ring buffer and free its memory.
Definition ringbuffer.c:53
#define FRAME_MAGIC
Magic number indicating a valid frame (0xDEADBEEF) - Alias for backward compatibility.
Definition ringbuffer.h:422
bool ringbuffer_is_empty(const ringbuffer_t *rb)
Check if buffer is empty.
Definition ringbuffer.c:125
bool framebuffer_write_frame(framebuffer_t *fb, const char *frame_data, size_t frame_size)
Write a frame to the buffer.
Definition ringbuffer.c:221
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)
Write a multi-source frame to the buffer (for multi-user support)
Definition ringbuffer.c:378
bool framebuffer_peek_latest_multi_frame(framebuffer_t *fb, multi_source_frame_t *frame)
Peek at the latest multi-source frame without removing it.
Definition ringbuffer.c:460
bool ringbuffer_read(ringbuffer_t *rb, void *data)
Try to read an element from the ring buffer (non-blocking)
Definition ringbuffer.c:82
bool ringbuffer_write(ringbuffer_t *rb, const void *data)
Try to write an element to the ring buffer (non-blocking)
Definition ringbuffer.c:60
bool ringbuffer_peek(ringbuffer_t *rb, void *data)
Peek at the next element without removing it.
Definition ringbuffer.c:104
#define IS_MAGIC_VALID(magic, expected)
Check if magic number is valid (true if matches expected)
Definition magic.h:36
#define IS_FRAME_VALID(frame)
Check if frame is valid and not freed.
Definition magic.h:39
#define MARK_FRAME_FREED(frame)
Mark a frame as freed.
Definition magic.h:49
#define IS_FRAME_FREED(frame)
Check if frame has been freed (corruption detection)
Definition magic.h:42
#define MAGIC_FRAME_VALID
Magic number for valid ring buffer frames (0xA5C11C4A1 = "ASCIICHAT" in hex)
Definition magic.h:17
🔢 Mathematical Utility Functions
Lock-Free Ring Buffer and Frame Buffer Management.
int frame
Definition splash.c:99
Frame structure that stores both data and actual size.
Definition ringbuffer.h:383
char * data
Pointer to frame data (not owned by this struct)
Definition ringbuffer.h:389
uint64_t magic
Magic number to detect corruption (FRAME_MAGIC when valid)
Definition ringbuffer.h:385
size_t size
Actual size of frame data in bytes.
Definition ringbuffer.h:387
Frame buffer structure for managing video frames.
Definition ringbuffer.h:436
mutex_t mutex
Mutex for thread-safe access to framebuffer operations.
Definition ringbuffer.h:440
ringbuffer_t * rb
Underlying ring buffer for frame storage.
Definition ringbuffer.h:438
Multi-source frame structure for multi-user support.
Definition ringbuffer.h:400
uint64_t magic
Magic number to detect corruption (FRAME_MAGIC when valid)
Definition ringbuffer.h:402
char source_client_id[64]
Client ID that sent this frame.
Definition ringbuffer.h:404
Lock-free ring buffer structure.
Definition ringbuffer.h:88
bool is_power_of_two
Whether capacity is power of 2 (enables bit masking optimization)
Definition ringbuffer.h:102
size_t capacity
Number of elements that can be stored.
Definition ringbuffer.h:94
atomic_t head
Write position (producer) - atomic for lock-free operations.
Definition ringbuffer.h:96
atomic_t size
Current number of elements - atomic for fast size checks.
Definition ringbuffer.h:100
size_t capacity_mask
Mask for fast modulo when capacity is power of 2.
Definition ringbuffer.h:104
atomic_t tail
Read position (consumer) - atomic for lock-free operations.
Definition ringbuffer.h:98
size_t element_size
Size of each element in bytes.
Definition ringbuffer.h:92
char * buffer
The actual buffer memory.
Definition ringbuffer.h:90