Files
cAI/cKit/Foundation/c_ByteRingBuffer.c
2026-08-10 11:55:59 +08:00

544 lines
19 KiB
C

#include <c_ByteRingBuffer.h>
#include <c_Memory.h>
#include <c_Alignment.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
c_err_t c_ByteRingBuffer_Init(c_ByteRingBuffer_t* self, c_size_t capacity) {
if (!self || capacity == 0) return C_ERR_PARAM;
self->capacity = capacity;
self->head = 0;
self->tail = 0;
self->is_full = C_FALSE;
self->buffer = (uint8_t*)C_ALLOC(self->capacity);
if (!self->buffer) {
self->capacity = 0;
return C_ERR_NOMEM;
}
return C_ERR_SUCCESS;
}
void c_ByteRingBuffer_Destroy(c_ByteRingBuffer_t* self) {
if (!self) return;
C_FREE(self->buffer);
self->capacity = 0;
self->head = 0;
self->tail = 0;
self->is_full = C_FALSE;
}
// Writes a single byte. O(1) performance.
c_err_t c_ByteRingBuffer_WriteByte(c_ByteRingBuffer_t* self, uint8_t byte) {
if (!self || !self->buffer) return C_ERR_PARAM;
if (self->is_full) return C_ERR_FULL;
self->buffer[self->tail] = byte;
self->tail = (self->tail + 1) % self->capacity;
if (self->tail == self->head) {
self->is_full = C_TRUE;
}
return C_ERR_SUCCESS;
}
// Reads a single byte. O(1) performance.
c_err_t c_ByteRingBuffer_ReadByte(c_ByteRingBuffer_t* self, uint8_t* out_byte) {
if (!self || !self->buffer || !out_byte) return C_ERR_PARAM;
if (c_ByteRingBuffer_IsEmpty(self)) return C_ERR_EMPTY;
*out_byte = self->buffer[self->head];
self->head = (self->head + 1) % self->capacity;
self->is_full = C_FALSE;
return C_ERR_SUCCESS;
}
// Writes an entire chunk of bytes. Returns the total number of bytes successfully written.
c_size_t c_ByteRingBuffer_WriteBuffer(c_ByteRingBuffer_t* self, const uint8_t* src, c_size_t len) {
if (!self || !self->buffer || !src || len == 0) return 0;
c_size_t bytes_written = 0;
while (bytes_written < len && !self->is_full) {
self->buffer[self->tail] = src[bytes_written];
self->tail = (self->tail + 1) % self->capacity;
if (self->tail == self->head) {
self->is_full = C_TRUE;
}
bytes_written++;
}
return bytes_written;
}
// Reads an entire chunk of bytes. Returns the total number of bytes successfully read.
c_size_t c_ByteRingBuffer_ReadBuffer(c_ByteRingBuffer_t* self, uint8_t* dest, c_size_t len) {
if (!self || !self->buffer || !dest || len == 0) return 0;
c_size_t bytes_read = 0;
while (bytes_read < len && !c_ByteRingBuffer_IsEmpty(self)) {
dest[bytes_read] = self->buffer[self->head];
self->head = (self->head + 1) % self->capacity;
self->is_full = C_FALSE;
bytes_read++;
}
return bytes_read;
}
c_size_t c_ByteRingBuffer_GetSize(const c_ByteRingBuffer_t* self) {
if (!self || !self->buffer) return 0;
if (self->is_full) return self->capacity;
if (self->tail >= self->head) {
return self->tail - self->head;
} else {
return self->capacity + self->tail - self->head;
}
}
c_bool_t c_ByteRingBuffer_IsEmpty(const c_ByteRingBuffer_t* self) {
if (!self) return C_TRUE;
return (self->head == self->tail) && !self->is_full;
}
c_bool_t c_ByteRingBuffer_IsFull(const c_ByteRingBuffer_t* self) {
if (!self) return C_FALSE;
return self->is_full;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
void c_ByteRingBuffer_WriteByteOverwrite(c_ByteRingBuffer_t* self, uint8_t byte) {
if (!self || !self->buffer) return;
if (self->is_full) {
// Advance head position to discard the oldest item before placing the new data
self->head = (self->head + 1) % self->capacity;
}
self->buffer[self->tail] = byte;
self->tail = (self->tail + 1) % self->capacity;
if (self->tail == self->head) {
self->is_full = C_TRUE;
}
}
c_size_t c_ByteRingBuffer_WriteBufferOverwrite(c_ByteRingBuffer_t* self, const uint8_t* src, c_size_t len) {
if (!self || !self->buffer || !src || len == 0) return 0;
// Boundary edge-case optimization: If incoming payload exceeds entire capacity,
// only the absolute last subset window matching 'capacity' will survive anyway.
if (len >= self->capacity) {
src += (len - self->capacity);
len = self->capacity;
// Blindly overwrite entire buffer matrix instantly
memcpy(self->buffer, src, len);
self->head = 0;
self->tail = 0;
self->is_full = C_TRUE;
return len;
}
c_size_t size = c_ByteRingBuffer_GetSize(self);
c_size_t free_space = self->capacity - size;
// Track if writing this string length overflows existing configurations
if (len > free_space) {
c_size_t overwrite_count = len - free_space;
self->head = (self->head + overwrite_count) % self->capacity;
}
// Segment 1: Physical top copy action
c_size_t space_to_end = self->capacity - self->tail;
c_size_t first_chunk = (len < space_to_end) ? len : space_to_end;
memcpy(self->buffer + self->tail, src, first_chunk);
// Segment 2: Physical circular bottom split copy action
c_size_t second_chunk = len - first_chunk;
if (second_chunk > 0) {
memcpy(self->buffer, src + first_chunk, second_chunk);
self->tail = second_chunk;
} else {
self->tail = (self->tail + first_chunk) % self->capacity;
}
if (self->tail == self->head) {
self->is_full = C_TRUE;
} else {
self->is_full = C_FALSE; // Only set false if it didn't completely fill out layout thresholds
}
return len;
}
c_err_t c_ByteRingBuffer_PeekByte(const c_ByteRingBuffer_t* self, uint8_t* out_byte) {
if (!self || !self->buffer || !out_byte) return C_ERR_INVALID_PARAM;
if (c_ByteRingBuffer_IsEmpty(self)) return -3; // Underflow
*out_byte = self->buffer[self->head];
return C_SUCCESS;
}
c_size_t c_ByteRingBuffer_PeekBuffer(const c_ByteRingBuffer_t* self, uint8_t* dest, c_size_t len) {
if (!self || !self->buffer || !dest || len == 0) return 0;
c_size_t available_bytes = c_ByteRingBuffer_GetSize(self);
if (len > available_bytes) {
len = available_bytes;
}
if (len == 0) return 0;
// Duplicate standard ReadBuffer chunk copy patterns without changing the 'head' cursor state
c_size_t bytes_to_end = self->capacity - self->head;
c_size_t first_chunk = (len < bytes_to_end) ? len : bytes_to_end;
memcpy(dest, self->buffer + self->head, first_chunk);
c_size_t second_chunk = len - first_chunk;
if (second_chunk > 0) {
memcpy(dest + first_chunk, self->buffer, second_chunk);
}
return len;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_size_t c_ByteRingBuffer_Discard(c_ByteRingBuffer_t* self, c_size_t len) {
if (!self || !self->buffer || len == 0) return 0;
c_size_t available_bytes = c_ByteRingBuffer_GetSize(self);
if (len > available_bytes) {
len = available_bytes;
}
if (len == 0) return 0;
self->head = (self->head + len) % self->capacity;
self->is_full = C_FALSE; // Dropping bytes guarantees it is no longer full
return len;
}
const uint8_t* c_ByteRingBuffer_GetReadPtr(const c_ByteRingBuffer_t* self, c_size_t* out_contiguous_len) {
if (!self || !self->buffer || !out_contiguous_len) return NULL;
*out_contiguous_len = 0;
if (c_ByteRingBuffer_IsEmpty(self)) return NULL;
if (self->tail > self->head) {
// Data path is completely linear up to the tail index position
*out_contiguous_len = self->tail - self->head;
} else {
// Data path wraps around; the first contiguous stretch runs to the array tail boundary
*out_contiguous_len = self->capacity - self->head;
}
return self->buffer + self->head;
}
uint8_t* c_ByteRingBuffer_GetWritePtr(const c_ByteRingBuffer_t* self, c_size_t* out_contiguous_len) {
if (!self || !self->buffer || !out_contiguous_len) return NULL;
*out_contiguous_len = 0;
if (self->is_full) return NULL;
if (self->tail >= self->head) {
// Free space path runs from tail up to the absolute array wrap boundary
// Special case adjustment: If head is exactly at index 0, we can write up to capacity - 1
// but since is_full flag tracking isolates capacity limits, write blocks up to standard edge boundaries.
*out_contiguous_len = self->capacity - self->tail;
// Minor modification check: If head index configuration is further up but tail wraps,
// don't overlap onto the head index area until the next subsequent hardware layout fetch pass.
if (self->head == 0 && *out_contiguous_len == self->capacity) {
// Full allocation window available
}
} else {
// Free space path is bounded cleanly between tail position and head position index spaces
*out_contiguous_len = self->head - self->tail;
}
return self->buffer + self->tail;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
C_STATIC_FORCE_INLINE
uint8_t c_ByteRingBuffer_GetAtRelativeInternal(const c_ByteRingBuffer_t* self, c_size_t relative_offset) {
c_size_t absolute_index = (self->head + relative_offset) % self->capacity;
return self->buffer[absolute_index];
}
c_index_t c_ByteRingBuffer_IndexOfBuffer(const c_ByteRingBuffer_t* self, const uint8_t* pattern, c_size_t pattern_len) {
if (!self || !self->buffer || !pattern || pattern_len == 0) return C_ERR_NOT_FOUND;
c_size_t total_size = c_ByteRingBuffer_GetSize(self);
if (pattern_len > total_size) return C_ERR_NOT_FOUND;
// Slide search window across the total available size boundary
c_size_t max_search_offset = total_size - pattern_len;
for (c_size_t offset = 0; offset <= max_search_offset; offset++) {
c_bool_t match_found = C_TRUE;
// Perform relative window byte sequence comparison
for (c_size_t p_idx = 0; p_idx < pattern_len; p_idx++) {
if (c_ByteRingBuffer_GetAtRelativeInternal(self, offset + p_idx) != pattern[p_idx]) {
match_found = C_FALSE;
break;
}
}
if (match_found) {
return (c_index_t)offset; // Returns offset relative to current head position
}
}
return C_ERR_NOT_FOUND;
}
c_index_t c_ByteRingBuffer_IndexOfByte(const c_ByteRingBuffer_t* self, uint8_t target) {
if (!self || !self->buffer) return C_ERR_NOT_FOUND;
c_size_t total_size = c_ByteRingBuffer_GetSize(self);
if (total_size == 0) return C_ERR_NOT_FOUND;
// Linear pass mapping relative pointers natively across internal split layers
for (c_size_t offset = 0; offset < total_size; offset++) {
if (c_ByteRingBuffer_GetAtRelativeInternal(self, offset) == target) {
return (c_index_t)offset;
}
}
return C_ERR_NOT_FOUND;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_index_t c_ByteRingBuffer_LastIndexOfBuffer(const c_ByteRingBuffer_t* self, const uint8_t* pattern, c_size_t pattern_len) {
if (!self || !self->buffer || !pattern || pattern_len == 0) return C_ERR_NOT_FOUND;
c_size_t total_size = c_ByteRingBuffer_GetSize(self);
if (pattern_len > total_size) return C_ERR_NOT_FOUND;
// Scan backwards from the largest possible relative offset down to index 0
c_size_t max_search_offset = total_size - pattern_len;
for (c_size_t offset = max_search_offset; ; offset--) {
c_bool_t match_found = C_TRUE;
for (c_size_t p_idx = 0; p_idx < pattern_len; p_idx++) {
if (c_ByteRingBuffer_GetAtRelativeInternal(self, offset + p_idx) != pattern[p_idx]) {
match_found = C_FALSE;
break;
}
}
if (match_found) {
return (c_index_t)offset;
}
if (offset == 0) break; // Secure unsigned loop breakout guard rail
}
return C_ERR_NOT_FOUND;
}
c_size_t c_ByteRingBuffer_ReadUntilToken(c_ByteRingBuffer_t* self, const uint8_t* token, c_size_t token_len, uint8_t* dest, c_size_t dest_max_len) {
if (!self || !self->buffer || !token || token_len == 0 || !dest || dest_max_len == 0) return 0;
// Step 1: Scan for the token sequence location relative to the head
c_index_t match_offset = c_ByteRingBuffer_IndexOfBuffer(self, token, token_len);
if (match_offset == C_ERR_NOT_FOUND) {
return 0; // Token sequence not currently hosted inside the window pipeline
}
// Step 2: Compute total structural extraction requirements including the token depth
c_size_t aggregate_bytes = (c_size_t)match_offset + token_len;
// Absolute safety check: Safeguard against destination memory container overflows
if (aggregate_bytes > dest_max_len) {
return 0; // Reject processing since target container is too small to safely store the complete payload string
}
// Step 3: Perform standard destructive consumption via ReadBuffer
c_size_t bytes_extracted = c_ByteRingBuffer_ReadBuffer(self, dest, aggregate_bytes);
return bytes_extracted;
}
c_err_t c_ByteRingBuffer_GetAtRelative(const c_ByteRingBuffer_t* self, c_size_t relative_offset, uint8_t* out_byte) {
if (!self || !self->buffer || !out_byte) return C_ERR_PARAM;
// Validate that the request falls inside the populated byte array span
c_size_t active_size = c_ByteRingBuffer_GetSize(self);
if (relative_offset >= active_size) {
return C_ERR_OUT_OF_BOUNDS;
}
// Safely fold the relative offset over physical ring buffer wrapping thresholds
c_size_t absolute_index = (self->head + relative_offset) % self->capacity;
*out_byte = self->buffer[absolute_index];
return C_SUCCESS;
}
c_bool_t c_ByteRingBuffer_Is(const c_ByteRingBuffer_t* self, c_index_t offset, uint8_t value) {
// Return false immediately if the buffer is uninitialized or the offset is negative
if (!self || !self->buffer || offset < 0) {
return C_FALSE;
}
// Verify that the requested relative offset falls within the currently readable window
c_size_t active_size = c_ByteRingBuffer_GetSize(self);
if ((c_size_t)offset >= active_size) {
return C_FALSE;
}
// Map the relative offset to the physical, wrapped array index bounds
c_size_t absolute_index = (self->head + (c_size_t)offset) % self->capacity;
// Evaluate content identity match condition
return (self->buffer[absolute_index] == value) ? C_TRUE : C_FALSE;
}
int c_ByteRingBuffer_Memcmp(const c_ByteRingBuffer_t* self, c_size_t offset, const uint8_t* buffer, c_size_t len) {
if (!self || !self->buffer || !buffer) {
return C_ERR_INVALID_PARAM;
}
if (len == 0) {
return 0; // Empty comparison matches instantly
}
c_size_t active_size = c_ByteRingBuffer_GetSize(self);
// Boundary check: Verify the comparison window falls entirely within readable buffer constraints
if (offset >= active_size || (offset + len) > active_size) {
return C_ERR_OUT_OF_BOUNDS;
}
// Resolve the real physical starting index inside the memory array
c_size_t absolute_start = (self->head + offset) % self->capacity;
// Calculate how many contiguous bytes run in a straight line up to the array boundary edge
c_size_t bytes_to_end = self->capacity - absolute_start;
if (len <= bytes_to_end) {
// Scenario 1: The target verification window is completely contiguous
return memcmp(self->buffer + absolute_start, buffer, len);
} else {
// Scenario 2: The window wraps around the physical edge bounds. Execute a split-block comparison.
// Pass A: Compare up to the wrapping array edge boundary
int first_segment_match = memcmp(self->buffer + absolute_start, buffer, bytes_to_end);
if (first_segment_match != 0) {
return first_segment_match; // Return mismatch direction immediately
}
// Pass B: Wrap around to index 0 and compare the remainder sequence
c_size_t remaining_bytes = len - bytes_to_end;
return memcmp(self->buffer, buffer + bytes_to_end, remaining_bytes);
}
}
c_err_t c_ByteRingBuffer_Strtoul(const c_ByteRingBuffer_t* self, c_size_t offset, int base, unsigned long* out_value, c_size_t* out_end_offset) {
if (!self || !self->buffer || !out_value) return C_ERR_INVALID_PARAM;
c_size_t total_size = c_ByteRingBuffer_GetSize(self);
if (offset >= total_size) return C_ERR_OUT_OF_BOUNDS;
// 1. Skip leading whitespace using the internal helper function
c_size_t scan_idx = offset;
while (scan_idx < total_size) {
uint8_t byte = c_ByteRingBuffer_GetAtRelativeInternal(self, scan_idx);
if (!isspace(byte)) break;
scan_idx++;
}
if (scan_idx == total_size) return C_ERR_INVALID_PARAM; // Buffer contains only whitespace
// 2. Measure the exact alphanumeric chunk layout width boundary
c_size_t start_numeric_offset = scan_idx;
c_size_t numeric_len = 0;
while (scan_idx < total_size) {
uint8_t byte = c_ByteRingBuffer_GetAtRelativeInternal(self, scan_idx);
// Track hex modifiers (x, X), signs, and alphanumeric digits
if (!isalnum(byte) && byte != '+' && byte != '-') {
break;
}
numeric_len++;
scan_idx++;
}
if (numeric_len == 0) return C_ERR_INVALID_PARAM;
// 3. Compute absolute pointers and optimize memory operations based on wrap layouts
c_size_t absolute_start = (self->head + start_numeric_offset) % self->capacity;
c_size_t bytes_to_end = self->capacity - absolute_start;
unsigned long result = 0;
char* parse_end = NULL;
int current_errno = errno;
errno = 0;
if (numeric_len <= bytes_to_end) {
// Linear path optimization: Parse directly out of the contiguous array space
const char* flat_ptr = (const char*)(self->buffer + absolute_start);
result = strtoul(flat_ptr, &parse_end, base);
c_size_t parsed_bytes = (c_size_t)(parse_end - flat_ptr);
if (parsed_bytes == 0 || parse_end == flat_ptr) {
errno = current_errno;
return C_ERR_INVALID_PARAM;
}
if (errno == ERANGE) return C_ERR_OUT_OF_BOUNDS;
*out_value = result;
if (out_end_offset) {
*out_end_offset = start_numeric_offset + parsed_bytes;
}
} else {
// Fragmented Wrap handling path: Copy across loop slices onto a small stack array
if (numeric_len >= 64) return C_ERR_OUT_OF_BOUNDS; // Enforce safe parsing limits
char stack_scratch[64];
for (c_size_t i = 0; i < numeric_len; i++) {
stack_scratch[i] = (char)c_ByteRingBuffer_GetAtRelativeInternal(self, start_numeric_offset + i);
}
stack_scratch[numeric_len] = '\0'; // Guarantee safe string boundary termination
result = strtoul(stack_scratch, &parse_end, base);
c_size_t parsed_bytes = (c_size_t)(parse_end - stack_scratch);
if (parsed_bytes == 0 || parse_end == stack_scratch) {
errno = current_errno;
return C_ERR_INVALID_PARAM;
}
if (errno == ERANGE) return C_ERR_OUT_OF_BOUNDS;
*out_value = result;
if (out_end_offset) {
*out_end_offset = start_numeric_offset + parsed_bytes;
}
}
errno = current_errno; // Restore system state integrity flags cleanly
return C_SUCCESS;
}