ByteRingBuffer 接口完善
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
#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;
|
||||
@@ -106,4 +110,434 @@ c_bool_t c_ByteRingBuffer_IsEmpty(const c_ByteRingBuffer_t* self) {
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user