ByteRingBuffer 接口完善

This commit is contained in:
2026-08-10 11:55:59 +08:00
parent 62cfd720c9
commit d6d3800830
6 changed files with 1627 additions and 116 deletions
+434
View File
@@ -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;
@@ -107,3 +111,433 @@ 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;
}
+106
View File
@@ -30,5 +30,111 @@ c_size_t c_ByteRingBuffer_GetSize(const c_ByteRingBuffer_t* self);
c_bool_t c_ByteRingBuffer_IsEmpty(const c_ByteRingBuffer_t* self);
c_bool_t c_ByteRingBuffer_IsFull(const c_ByteRingBuffer_t* self);
/*
* Writes a single byte, overwriting the oldest byte if the buffer is full.
*/
void c_ByteRingBuffer_WriteByteOverwrite(c_ByteRingBuffer_t* self, uint8_t byte);
/*
* Writes a buffer span, overwriting the oldest bytes continuously if capacity is exceeded.
*/
c_size_t c_ByteRingBuffer_WriteBufferOverwrite(c_ByteRingBuffer_t* self, const uint8_t* src, c_size_t len);
/*
* Inspects a single byte at the head position without removing it.
*/
c_err_t c_ByteRingBuffer_PeekByte(const c_ByteRingBuffer_t* self, uint8_t* out_byte);
/*
* Inspects up to 'len' bytes starting from the head position without removing them.
* Returns the actual number of bytes peeked.
*/
c_size_t c_ByteRingBuffer_PeekBuffer(const c_ByteRingBuffer_t* self, uint8_t* dest, c_size_t len);
/*
* Advances the head pointer to drop up to 'len' bytes without copying data.
* Returns the actual number of bytes dropped.
*/
c_size_t c_ByteRingBuffer_Discard(c_ByteRingBuffer_t* self, c_size_t len);
/*
* Returns the direct linear address to read the first available contiguous memory block.
* @param out_contiguous_len: Populated with the byte depth length of the straight chunk line.
* @return Pointer into the structural array channel, or NULL if buffer is empty.
*/
const uint8_t* c_ByteRingBuffer_GetReadPtr(const c_ByteRingBuffer_t* self, c_size_t* out_contiguous_len);
/*
* Returns the direct linear address to write into the first available contiguous free memory block.
* @param out_contiguous_len: Populated with the space depth length of the straight chunk line.
* @return Pointer into the structural array channel, or NULL if buffer is full.
*/
uint8_t* c_ByteRingBuffer_GetWritePtr(const c_ByteRingBuffer_t* self, c_size_t* out_contiguous_len);
/*
* Searches for the first occurrence of a byte sequence (pattern) within the ring buffer.
* Returns the relative offset from the current head pointer (0 to size-1), or C_ERR_NOT_FOUND.
*/
c_index_t c_ByteRingBuffer_IndexOfBuffer(const c_ByteRingBuffer_t* self, const uint8_t* pattern, c_size_t pattern_len);
/*
* Searches for the first occurrence of a single byte within the ring buffer.
* Returns the relative offset from the current head pointer (0 to size-1), or C_ERR_NOT_FOUND.
*/
c_index_t c_ByteRingBuffer_IndexOfByte(const c_ByteRingBuffer_t* self, uint8_t target);
/*
* Searches for the last occurrence of a byte sequence within the ring buffer.
* Returns the relative offset from the current head pointer (0 to size-1), or C_ERR_NOT_FOUND.
*/
c_index_t c_ByteRingBuffer_LastIndexOfBuffer(const c_ByteRingBuffer_t* self, const uint8_t* pattern, c_size_t pattern_len);
/*
* Consumes and extracts data into 'dest' up to and including the specified token sequence.
* Returns the total number of bytes read and placed into dest, or 0 if token is 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);
/*
* Retrieves a single byte from the buffer at a relative index position from the head.
* @param self: The ring buffer instance.
* @param relative_offset: The offset relative to the head pointer (0 = oldest unread byte, size-1 = newest byte).
* @param out_byte: Destination pointer for the extracted byte.
* @return C_SUCCESS on clean execution, C_ERR_INVALID_PARAM, or C_ERR_OUT_OF_BOUNDS.
*/
c_err_t c_ByteRingBuffer_GetAtRelative(const c_ByteRingBuffer_t* self, c_size_t relative_offset, uint8_t* out_byte);
/*
* Checks if the byte at a specific relative offset from the head matches the given value.
* @param self: The ring buffer instance.
* @param offset: The relative offset from the current head pointer (0 = oldest unread byte).
* @param value: The expected byte value to compare against.
* @return C_TRUE (1) if it matches perfectly, C_FALSE (0) if it mismatches, is empty, or out of bounds.
*/
c_bool_t c_ByteRingBuffer_Is(const c_ByteRingBuffer_t* self, c_index_t offset, uint8_t value);
/*
* Compares the contents of the ring buffer starting at a relative offset with an external flat buffer.
* @param self: The ring buffer instance.
* @param offset: The relative offset from the current head pointer to start comparing from.
* @param buffer: The external memory array to compare against.
* @param len: The number of bytes to compare.
* @return 0 if the memory blocks match exactly, < 0 if the ring buffer data is lexicographically smaller,
* > 0 if it is larger. Returns (C_ERR_INVALID_PARAM) or (C_ERR_OUT_OF_BOUNDS) on range violations.
*/
int c_ByteRingBuffer_Memcmp(const c_ByteRingBuffer_t* self, c_size_t offset, const uint8_t* buffer, c_size_t len);
/*
* Parses an unsigned long value from the ring buffer starting at a specific relative offset.
* @param self: The ring buffer instance.
* @param offset: The relative offset from the current head pointer to start parsing from.
* @param base: The number base system to parse (0 for auto-detection, 2-36).
* @param out_value: Destination pointer for the parsed unsigned long.
* @param out_end_offset: Optional destination pointer for the relative offset immediately following the parsed number.
* @return C_SUCCESS on clean execution, C_ERR_INVALID_PARAM, or C_ERR_OUT_OF_BOUNDS.
*/
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);
#endif /*INCLUDED_C_BYTERINGBUFFER_H*/
+327 -47
View File
@@ -2,61 +2,341 @@
#include <stdlib.h>
#include <stdio.h>
#define RUN_TEST_CASE(test_func) \
do { \
printf("[RUNNING] %-40s ... ", #test_func); \
fflush(stdout); \
test_func(); \
printf("[PASSED]\n"); \
} while (0)
void test_log(const char* test_name) {
printf("[PASS] %s\n", test_name);
void test_ring_buffer_behavior(void) {
c_ByteRingBuffer_t ring;
assert(c_ByteRingBuffer_Init(&ring, 5) == C_SUCCESS);
assert(c_ByteRingBuffer_IsEmpty(&ring) == C_TRUE);
// Fill to capacity bounds
assert(c_ByteRingBuffer_WriteByte(&ring, 0xAA) == C_SUCCESS);
assert(c_ByteRingBuffer_WriteByte(&ring, 0xBB) == C_SUCCESS);
assert(c_ByteRingBuffer_WriteByte(&ring, 0xCC) == C_SUCCESS);
assert(c_ByteRingBuffer_GetSize(&ring) == 3);
uint8_t batch_src[3] = {0x11, 0x22, 0x33};
// Should write only 2 bytes because total capacity limit is 5
assert(c_ByteRingBuffer_WriteBuffer(&ring, batch_src, 3) == 2);
assert(c_ByteRingBuffer_IsFull(&ring) == C_TRUE);
// Verify retrieval matches FIFO sequencing rules
uint8_t read_byte = 0;
assert(c_ByteRingBuffer_ReadByte(&ring, &read_byte) == C_SUCCESS);
assert(read_byte == 0xAA);
assert(c_ByteRingBuffer_IsFull(&ring) == C_FALSE);
// Bulk wrap-around extraction
uint8_t batch_dest[4];
assert(c_ByteRingBuffer_ReadBuffer(&ring, batch_dest, 4) == 4);
assert(batch_dest[0] == 0xBB);
assert(batch_dest[1] == 0xCC);
assert(batch_dest[2] == 0x11);
assert(batch_dest[3] == 0x22);
assert(c_ByteRingBuffer_IsEmpty(&ring) == C_TRUE);
c_ByteRingBuffer_Destroy(&ring);
}
static void test_overwrite_and_peek_mechanics(void) {
c_ByteRingBuffer_t ring;
assert(c_ByteRingBuffer_Init(&ring, 4) == C_SUCCESS); // Capacity = 4
// 1. Validate Single Overwrite
c_ByteRingBuffer_WriteByte(&ring, 0x01);
c_ByteRingBuffer_WriteByte(&ring, 0x02);
c_ByteRingBuffer_WriteByte(&ring, 0x03);
c_ByteRingBuffer_WriteByte(&ring, 0x04); // Buffer now full: [0x01, 0x02, 0x03, 0x04]
assert(c_ByteRingBuffer_IsFull(&ring) == C_TRUE);
c_ByteRingBuffer_WriteByteOverwrite(&ring, 0x05); // 0x01 gets evicted. Head shifts to 0x02
uint8_t peek_check = 0;
assert(c_ByteRingBuffer_PeekByte(&ring, &peek_check) == C_SUCCESS);
assert(peek_check == 0x02); // FIFO rules dictate oldest remaining byte is 0x02
// 2. Validate Bulk Overwrite Loops
uint8_t incoming_stream[3] = {0x06, 0x07, 0x08};
// Ring has 4 bytes capacity. Writing 3 bytes over an already full buffer evicts [0x02, 0x03, 0x04]
assert(c_ByteRingBuffer_WriteBufferOverwrite(&ring, incoming_stream, 3) == 3);
uint8_t verification_dump[4] = {0};
c_size_t read_out = c_ByteRingBuffer_PeekBuffer(&ring, verification_dump, 4);
assert(read_out == 4);
assert(verification_dump[0] == 0x05); // Preserved from previous transaction
assert(verification_dump[1] == 0x06);
assert(verification_dump[2] == 0x07);
assert(verification_dump[3] == 0x08);
// 3. Confirm Peek leaves data sequence entirely untouched
assert(c_ByteRingBuffer_GetSize(&ring) == 4);
c_ByteRingBuffer_Destroy(&ring);
}
static void test_dma_and_discard_mechanics(void) {
c_ByteRingBuffer_t ring;
assert(c_ByteRingBuffer_Init(&ring, 8) == C_SUCCESS); // Capacity = 8
// Force initialization of data that loops around the internal ring memory map
uint8_t payload[] = {0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
c_ByteRingBuffer_WriteBuffer(&ring, payload, 5);
// Discard oldest 2 items: drops 0xA1, 0xA2. Cursors shift.
assert(c_ByteRingBuffer_Discard(&ring, 2) == 2);
assert(c_ByteRingBuffer_GetSize(&ring) == 3);
// Write some more to force a wrap-around layout
uint8_t wrap_payload[] = {0xB1, 0xB2, 0xB3, 0xB4};
c_ByteRingBuffer_WriteBuffer(&ring, wrap_payload, 4); // Total size now = 7 bytes
// Validate GetReadPtr isolates Block Segment 1 cleanly
c_size_t read_chunk_len = 0;
const uint8_t* read_ptr = c_ByteRingBuffer_GetReadPtr(&ring, &read_chunk_len);
assert(read_ptr != NULL);
// Head was shifted to index 2. 8 - 2 = 6 available linearly up to memory array edge
assert(read_chunk_len == 6);
assert(read_ptr[0] == 0xA3); // First remaining item
// Clear those processed items via direct execution tracking
c_ByteRingBuffer_Discard(&ring, read_chunk_len);
// Call secondary pass to capture remaining wrapped bytes
read_ptr = c_ByteRingBuffer_GetReadPtr(&ring, &read_chunk_len);
assert(read_chunk_len == 1);
assert(read_ptr[0] == 0xB4); // Wrapped character check
c_ByteRingBuffer_Destroy(&ring);
}
static void test_ring_buffer_index_searching(void) {
c_ByteRingBuffer_t ring;
assert(c_ByteRingBuffer_Init(&ring, 8) == C_SUCCESS); // Capacity = 8
// Force a data write footprint that wraps around the buffer margins
uint8_t standard_fill[] = {0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44};
c_ByteRingBuffer_WriteBuffer(&ring, standard_fill, 8);
// Discard 4 elements to position head cursor at absolute index 4
c_ByteRingBuffer_Discard(&ring, 4);
// Append sequence to cross edge wrap boundaries cleanly
uint8_t wrap_fill[] = {0x55, 0x66, 0x77};
c_ByteRingBuffer_WriteBuffer(&ring, wrap_fill, 3);
// Dynamic ring content layout from head: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]
// 1. Single byte search lookup match
assert(c_ByteRingBuffer_IndexOfByte(&ring, 0x33) == 2); // Relative offset index 2 from head
assert(c_ByteRingBuffer_IndexOfByte(&ring, 0x99) == C_ERR_NOT_FOUND);
// 2. Pattern buffer sequence scan (testing across structural wrap-around edge boundaries)
uint8_t search_pattern[] = {0x44, 0x55, 0x66};
c_index_t match_offset = c_ByteRingBuffer_IndexOfBuffer(&ring, search_pattern, 3);
assert(match_offset == 3); // 0x44 sits exactly at relative offset position 3
// Application Workflow Integration: Cleanly discard up to token match prefix point
c_ByteRingBuffer_Discard(&ring, (c_size_t)match_offset);
uint8_t current_head_byte = 0;
c_ByteRingBuffer_PeekByte(&ring, &current_head_byte);
assert(current_head_byte == 0x44); // The buffer head has been successfully synchronized to the token location
c_ByteRingBuffer_Destroy(&ring);
}
static void test_reverse_search_and_token_stream(void) {
c_ByteRingBuffer_t ring;
assert(c_ByteRingBuffer_Init(&ring, 8) == C_SUCCESS); // Capacity = 8
uint8_t raw_payload[] = {0xAA, 0x11, 0x22, 0xBB, 0x11, 0x22, 0xCC, 0xDD};
c_ByteRingBuffer_WriteBuffer(&ring, raw_payload, 8);
uint8_t pattern[] = {0x11, 0x22};
// 1. Verify Reverse Pattern Detection Matches Latest Occurrence
assert(c_ByteRingBuffer_IndexOfBuffer(&ring, pattern, 2) == 1); // First pair starts at offset 1
assert(c_ByteRingBuffer_LastIndexOfBuffer(&ring, pattern, 2) == 4); // Latest pair starts at offset 4
// 2. Clear out buffer to run frame serialization test
c_ByteRingBuffer_Discard(&ring, 8);
uint8_t stream_data[] = {'P', 'a', 'c', 'k', 'e', 't', '\r', '\n'};
c_ByteRingBuffer_WriteBuffer(&ring, stream_data, 8);
uint8_t frame_terminator[] = {'\r', '\n'};
uint8_t output_staging[16] = {0};
// Fail Case: Pass a staging array that is structurally too cramped to hold the payload safely
assert(c_ByteRingBuffer_ReadUntilToken(&ring, frame_terminator, 2, output_staging, 5) == 0);
assert(c_ByteRingBuffer_GetSize(&ring) == 8); // Data remains locked safely inside the ring
// Success Case: Pass a valid container size to execute frame extraction
c_size_t read_bytes = c_ByteRingBuffer_ReadUntilToken(&ring, frame_terminator, 2, output_staging, 16);
assert(read_bytes == 8);
assert(memcmp(output_staging, "Packet\r\n", 8) == 0);
assert(c_ByteRingBuffer_IsEmpty(&ring) == C_TRUE); // Frame has been cleanly consumed from the queue
c_ByteRingBuffer_Destroy(&ring);
}
static void test_relative_random_access(void) {
c_ByteRingBuffer_t ring;
assert(c_ByteRingBuffer_Init(&ring, 4) == C_SUCCESS); // Capacity = 4
uint8_t seed_data[] = {0x10, 0x20, 0x30};
c_ByteRingBuffer_WriteBuffer(&ring, seed_data, 3);
// Shift the head pointer downstream via a single byte consumption
uint8_t discard_sink = 0;
c_ByteRingBuffer_ReadByte(&ring, &discard_sink); // 0x10 dropped. Head points to 0x20
// Append items to cross physical wrapping thresholds cleanly
c_ByteRingBuffer_WriteByte(&ring, 0x40);
c_ByteRingBuffer_WriteByte(&ring, 0x50); // Buffer contents: [0x20, 0x30, 0x40, 0x50]
uint8_t extracted_byte = 0;
// 1. Verify standard random read coordinates relative to the head position
assert(c_ByteRingBuffer_GetAtRelative(&ring, 0, &extracted_byte) == C_SUCCESS);
assert(extracted_byte == 0x20); // Relative 0 points directly to the current head
assert(c_ByteRingBuffer_GetAtRelative(&ring, 2, &extracted_byte) == C_SUCCESS);
assert(extracted_byte == 0x40); // Wrapped index check
assert(c_ByteRingBuffer_GetAtRelative(&ring, 3, &extracted_byte) == C_SUCCESS);
assert(extracted_byte == 0x50); // Newest unread byte check
// 2. Validate bounds-checking flags
assert(c_ByteRingBuffer_GetAtRelative(&ring, 4, &extracted_byte) == C_ERR_OUT_OF_BOUNDS);
assert(c_ByteRingBuffer_GetAtRelative(&ring, 99, &extracted_byte) == C_ERR_OUT_OF_BOUNDS);
assert(c_ByteRingBuffer_GetAtRelative(NULL, 0, &extracted_byte) == C_ERR_INVALID_PARAM);
c_ByteRingBuffer_Destroy(&ring);
}
static void test_conditional_is_validator(void) {
c_ByteRingBuffer_t ring;
assert(c_ByteRingBuffer_Init(&ring, 4) == C_SUCCESS);
uint8_t input_stream[] = {0xAA, 0xBB, 0xCC};
c_ByteRingBuffer_WriteBuffer(&ring, input_stream, 3);
// 1. Validate clean true/false matches relative to the head
assert(c_ByteRingBuffer_Is(&ring, 0, 0xAA) == C_TRUE); // Oldest byte at head matches
assert(c_ByteRingBuffer_Is(&ring, 1, 0xBB) == C_TRUE); // Next element matches
assert(c_ByteRingBuffer_Is(&ring, 1, 0x99) == C_FALSE); // Mismatch returns false
// Consume 1 byte to advance the head pointer and test wrapping boundaries
uint8_t sink = 0;
c_ByteRingBuffer_ReadByte(&ring, &sink); // Head now points to 0xBB
c_ByteRingBuffer_WriteByte(&ring, 0xDD); // Layout: [..., 0xBB, 0xCC, 0xDD]
// 2. Re-verify offsets after structural pointer wrap shifts
assert(c_ByteRingBuffer_Is(&ring, 0, 0xBB) == C_TRUE); // Head index position 0 is now 0xBB
assert(c_ByteRingBuffer_Is(&ring, 2, 0xDD) == C_TRUE); // Wrapped index element match
// 3. Confirm error/bounds conditions return false instead of blowing up memory layout boundaries
assert(c_ByteRingBuffer_Is(&ring, 3, 0x00) == C_FALSE); // Out of bounds index
assert(c_ByteRingBuffer_Is(&ring, -5, 0xBB) == C_FALSE); // Negative index handling protection
assert(c_ByteRingBuffer_Is(NULL, 0, 0xBB) == C_FALSE); // NULL safety check
c_ByteRingBuffer_Destroy(&ring);
}
static void test_ring_buffer_memcmp(void) {
c_ByteRingBuffer_t ring;
assert(c_ByteRingBuffer_Init(&ring, 6) == C_SUCCESS); // Capacity = 6
uint8_t payload[] = {0x00, 0x11, 0x22, 0x33};
c_ByteRingBuffer_WriteBuffer(&ring, payload, 4);
// Consume 2 bytes to step the head pointer forward to absolute index 2
uint8_t sink = 0;
c_ByteRingBuffer_ReadByte(&ring, &sink);
c_ByteRingBuffer_ReadByte(&ring, &sink); // Buffer active layout from head: [0x22, 0x33]
// Append data to trigger an explicit physical wrap-around edge split layout
uint8_t wrap_payload[] = {0x44, 0x55, 0x66};
c_ByteRingBuffer_WriteBuffer(&ring, wrap_payload, 3);
// Buffer dynamic content path tracking from head: [0x22, 0x33, 0x44, 0x55, 0x66]
// Physical layout behind indices inside array: [0x55, 0x66, 0x22, 0x33, 0x44, ...]
// 1. Validate contiguous segment match comparisons
uint8_t check_a[] = {0x22, 0x33};
assert(c_ByteRingBuffer_Memcmp(&ring, 0, check_a, 2) == 0); // Perfect contiguous match
// 2. Validate multi-segment wrap-around comparison mechanics
uint8_t check_b[] = {0x33, 0x44, 0x55, 0x66};
assert(c_ByteRingBuffer_Memcmp(&ring, 1, check_b, 4) == 0); // Perfect split wrap-around match
// 3. Mismatch checks
uint8_t check_mismatch[] = {0x33, 0x44, 0x99, 0x66};
assert(c_ByteRingBuffer_Memcmp(&ring, 1, check_mismatch, 4) != 0); // Identifies internal divergence
// 4. Bounds and parameter checks
assert(c_ByteRingBuffer_Memcmp(&ring, 0, check_b, 100) == C_ERR_OUT_OF_BOUNDS); // Request width overflows content
assert(c_ByteRingBuffer_Memcmp(&ring, 99, check_b, 1) == C_ERR_OUT_OF_BOUNDS); // Start pointer invalid
assert(c_ByteRingBuffer_Memcmp(NULL, 0, check_b, 1) == C_ERR_INVALID_PARAM);
c_ByteRingBuffer_Destroy(&ring);
}
static void test_ring_buffer_strtoul(void) {
c_ByteRingBuffer_t ring;
assert(c_ByteRingBuffer_Init(&ring, 8) == C_SUCCESS); // Capacity = 8
// 1. Standard Linear Base-10 Parsing
uint8_t input_a[] = {'1', '2', '3', '4', ' ', 'A', 'B', 'C'};
c_ByteRingBuffer_WriteBuffer(&ring, input_a, 8);
unsigned long parsed_val = 0;
c_size_t end_offset = 0;
assert(c_ByteRingBuffer_Strtoul(&ring, 0, 10, &parsed_val, &end_offset) == C_SUCCESS);
assert(parsed_val == 1234);
assert(end_offset == 4); // Points exactly to the trailing space character offset
// Reset buffer tracking lines
c_ByteRingBuffer_Discard(&ring, 8);
// 2. Fragmented Wrap Hex Parsing
// Pre-fill 5 elements to push cursor indices near wrapping layout boundaries
uint8_t pre_fill[] = {0, 0, 0, 0, 0};
c_ByteRingBuffer_WriteBuffer(&ring, pre_fill, 5);
c_ByteRingBuffer_Discard(&ring, 5); // Head pointer sits at physical index 5
// Write Hex parameter payload string ("0x2F") across memory boundaries
uint8_t input_hex[] = {'0', 'x', '2', 'F'};
c_ByteRingBuffer_WriteBuffer(&ring, input_hex, 4);
assert(c_ByteRingBuffer_Strtoul(&ring, 0, 16, &parsed_val, &end_offset) == C_SUCCESS);
assert(parsed_val == 47); // 0x2F translates to decimal 47
assert(end_offset == 4);
c_ByteRingBuffer_Destroy(&ring);
}
int main() {
printf("==================================================\n");
printf(" Starting c_ByteRingBuffer Unit Testing Suite\n");
printf("==================================================\n\n");
c_ByteRingBuffer_t rb;
// Set fixed byte size capacity to 4
c_err_t err = c_ByteRingBuffer_Init(&rb, 4);
assert(err == C_ERR_SUCCESS);
assert(c_ByteRingBuffer_IsEmpty(&rb) == C_TRUE);
test_log("1. Raw byte buffer initialization verified");
// ==========================================
// 2. Testing Single Byte Operations
// ==========================================
err = c_ByteRingBuffer_WriteByte(&rb, 0xAA); assert(err == C_ERR_SUCCESS);
err = c_ByteRingBuffer_WriteByte(&rb, 0xBB); assert(err == C_ERR_SUCCESS);
assert(c_ByteRingBuffer_GetSize(&rb) == 2);
uint8_t byte_out = 0;
err = c_ByteRingBuffer_ReadByte(&rb, &byte_out);
assert(err == C_ERR_SUCCESS);
assert(byte_out == 0xAA);
assert(c_ByteRingBuffer_GetSize(&rb) == 1);
test_log("2. Single byte discrete FIFO verified");
// Clear buffer out
c_ByteRingBuffer_ReadByte(&rb, &byte_out);
assert(c_ByteRingBuffer_IsEmpty(&rb) == C_TRUE);
// ==========================================
// 3. Testing Streaming Buffer Operations
// ==========================================
uint8_t stream_in[5] = {0x11, 0x22, 0x33, 0x44, 0x55};
// Total capacity is 4. Passing a length of 5 should stream up to max boundary
c_size_t written = c_ByteRingBuffer_WriteBuffer(&rb, stream_in, 5);
assert(written == 4);
assert(c_ByteRingBuffer_IsFull(&rb) == C_TRUE);
uint8_t stream_out[4] = {0};
c_size_t read = c_ByteRingBuffer_ReadBuffer(&rb, stream_out, 4);
assert(read == 4);
assert(stream_out[0] == 0x11);
assert(stream_out[3] == 0x44);
assert(c_ByteRingBuffer_IsEmpty(&rb) == C_TRUE);
test_log("3. Bulk string/buffer array streams chunk-read verified");
c_ByteRingBuffer_Destroy(&rb);
test_log("4. Clean resource teardown verified");
RUN_TEST_CASE(test_ring_buffer_behavior);
RUN_TEST_CASE(test_overwrite_and_peek_mechanics);
RUN_TEST_CASE(test_dma_and_discard_mechanics);
RUN_TEST_CASE(test_ring_buffer_index_searching);
RUN_TEST_CASE(test_reverse_search_and_token_stream);
RUN_TEST_CASE(test_relative_random_access);
RUN_TEST_CASE(test_conditional_is_validator);
RUN_TEST_CASE(test_ring_buffer_memcmp);
RUN_TEST_CASE(test_ring_buffer_strtoul);
printf("\n==================================================\n");
printf(" Success! Byte RingBuffer tests passed!\n");
+404 -9
View File
@@ -1,6 +1,11 @@
#include <c_FastByteRingBuffer.h>
#include <c_Memory.h>
#include <c_Alignment.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
C_STATIC_FORCE_INLINE
c_size_t round_up_to_pow2(c_size_t v) {
@@ -82,23 +87,46 @@ c_err_t c_FastByteRingBuffer_ReadByte(c_FastByteRingBuffer_t* self, uint8_t* out
return C_ERR_SUCCESS;
}
// 區塊寫入
c_size_t c_FastByteRingBuffer_WriteBuffer(c_FastByteRingBuffer_t* self, const uint8_t* src, c_size_t len) {
if (!self || !self->buffer || !src || len == 0) return 0;
if (self->is_full) 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->mask;
// Calculate free space directly using fast size validation tracking
c_size_t current_size = c_FastByteRingBuffer_GetSize(self);
c_size_t free_space = self->capacity - current_size;
// Clamp write operations to avoid overrunning existing readable elements
if (len > free_space) {
len = free_space;
}
if (len == 0) return 0;
// Segment 1: Write from tail index up to the physical end boundary of the backing array
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: Wrap around to index 0 using bitwise optimizations if a split block configuration is required
c_size_t second_chunk = len - first_chunk;
if (second_chunk > 0) {
memcpy(self->buffer, src + first_chunk, second_chunk);
self->tail = second_chunk; // The wrapped tail calculation reduces cleanly to second_chunk
} else {
// Fast tail stepping path utilizing the mask constant
self->tail = (self->tail + first_chunk) & self->mask;
}
// Set the full status flag if the cursors perfectly intersect
if (self->tail == self->head) {
self->is_full = C_TRUE;
}
bytes_written++;
}
return bytes_written;
return len;
}
// 區塊讀取
c_size_t c_FastByteRingBuffer_ReadBuffer(c_FastByteRingBuffer_t* self, uint8_t* dest, c_size_t len) {
if (!self || !self->buffer || !dest || len == 0) return 0;
@@ -133,3 +161,370 @@ c_bool_t c_FastByteRingBuffer_IsFull(const c_FastByteRingBuffer_t* self) {
if (!self) return C_FALSE;
return self->is_full;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
C_STATIC_FORCE_INLINE
uint8_t c_FastByteRingBuffer_GetAtRelativeInternal(const c_FastByteRingBuffer_t* self, c_size_t relative_offset) {
c_size_t absolute_index = (self->head + relative_offset) & self->mask;
return self->buffer[absolute_index];
}
/* ------------------------------------------------------------------------------------------------------------------ */
void c_FastByteRingBuffer_WriteByteOverwrite(c_FastByteRingBuffer_t* self, uint8_t byte) {
if (!self || !self->buffer) return;
if (self->is_full) {
// 環形陣列已滿時,強制將讀取指標向前推一格,拋棄最舊數據
self->head = (self->head + 1) & self->mask;
}
self->buffer[self->tail] = byte;
self->tail = (self->tail + 1) & self->mask;
if (self->tail == self->head) {
self->is_full = C_TRUE;
}
}
c_size_t c_FastByteRingBuffer_WriteBufferOverwrite(c_FastByteRingBuffer_t* self, const uint8_t* src, c_size_t len) {
if (!self || !self->buffer || !src || len == 0) return 0;
// 邊界極端優化:如果寫入長度超過總容量,只有最後符合容量大小的數據能存活
if (len >= self->capacity) {
src += (len - self->capacity);
len = self->capacity;
memcpy(self->buffer, src, len);
self->head = 0;
self->tail = 0;
self->is_full = C_TRUE;
return len;
}
c_size_t size = c_FastByteRingBuffer_GetSize(self);
c_size_t free_space = self->capacity - size;
// 若寫入長度大於剩餘空間,計算溢出量並自動同步前推 head 指標
if (len > free_space) {
c_size_t overwrite_count = len - free_space;
self->head = (self->head + overwrite_count) & self->mask;
}
// 分段一:從 tail 寫入到物理內存陣列末尾
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);
// 分段二:折返到內存陣列開頭寫入剩餘數據
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->mask;
}
if (self->tail == self->head) {
self->is_full = C_TRUE;
} else {
self->is_full = C_FALSE;
}
return len;
}
c_err_t c_FastByteRingBuffer_PeekByte(const c_FastByteRingBuffer_t* self, uint8_t* out_byte) {
if (!self || !self->buffer || !out_byte) return C_ERR_INVALID_PARAM;
if (c_FastByteRingBuffer_IsEmpty(self)) return C_ERR_OUT_OF_BOUNDS;
*out_byte = self->buffer[self->head];
return C_SUCCESS;
}
c_size_t c_FastByteRingBuffer_PeekBuffer(const c_FastByteRingBuffer_t* self, uint8_t* dest, c_size_t len) {
if (!self || !self->buffer || !dest || len == 0) return 0;
c_size_t available_bytes = c_FastByteRingBuffer_GetSize(self);
if (len > available_bytes) {
len = available_bytes;
}
if (len == 0) return 0;
// 複製標準讀取邏輯,但完全不變動真實核心 head 指標狀態
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_FastByteRingBuffer_Discard(c_FastByteRingBuffer_t* self, c_size_t len) {
if (!self || !self->buffer || len == 0) return 0;
c_size_t available_bytes = c_FastByteRingBuffer_GetSize(self);
if (len > available_bytes) {
len = available_bytes;
}
if (len == 0) return 0;
self->head = (self->head + len) & self->mask;
self->is_full = C_FALSE;
return len;
}
const uint8_t* c_FastByteRingBuffer_GetReadPtr(const c_FastByteRingBuffer_t* self, c_size_t* out_contiguous_len) {
if (!self || !self->buffer || !out_contiguous_len) return NULL;
*out_contiguous_len = 0;
if (c_FastByteRingBuffer_IsEmpty(self)) return NULL;
if (self->tail > self->head) {
*out_contiguous_len = self->tail - self->head;
} else {
*out_contiguous_len = self->capacity - self->head;
}
return self->buffer + self->head;
}
uint8_t* c_FastByteRingBuffer_GetWritePtr(const c_FastByteRingBuffer_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) {
*out_contiguous_len = self->capacity - self->tail;
} else {
*out_contiguous_len = self->head - self->tail;
}
return self->buffer + self->tail;
}
c_index_t c_FastByteRingBuffer_IndexOfByte(const c_FastByteRingBuffer_t* self, uint8_t target) {
if (!self || !self->buffer) return C_ERR_NOT_FOUND;
c_size_t total_size = c_FastByteRingBuffer_GetSize(self);
for (c_size_t offset = 0; offset < total_size; offset++) {
if (c_FastByteRingBuffer_GetAtRelativeInternal(self, offset) == target) {
return (c_index_t)offset;
}
}
return C_ERR_NOT_FOUND;
}
c_index_t c_FastByteRingBuffer_IndexOfBuffer(const c_FastByteRingBuffer_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_FastByteRingBuffer_GetSize(self);
if (pattern_len > total_size) return C_ERR_NOT_FOUND;
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;
for (c_size_t p_idx = 0; p_idx < pattern_len; p_idx++) {
if (c_FastByteRingBuffer_GetAtRelativeInternal(self, offset + p_idx) != pattern[p_idx]) {
match_found = C_FALSE;
break;
}
}
if (match_found) {
return (c_index_t)offset;
}
}
return C_ERR_NOT_FOUND;
}
c_index_t c_FastByteRingBuffer_LastIndexOfBuffer(const c_FastByteRingBuffer_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_FastByteRingBuffer_GetSize(self);
if (pattern_len > total_size) return C_ERR_NOT_FOUND;
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_FastByteRingBuffer_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;
}
return C_ERR_NOT_FOUND;
}
c_size_t c_FastByteRingBuffer_ReadUntilToken(c_FastByteRingBuffer_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;
c_index_t match_offset = c_FastByteRingBuffer_IndexOfBuffer(self, token, token_len);
if (match_offset == C_ERR_NOT_FOUND) {
return 0;
}
c_size_t aggregate_bytes = (c_size_t)match_offset + token_len;
if (aggregate_bytes > dest_max_len) {
return 0; // 避免目標緩衝區溢出
}
return c_FastByteRingBuffer_ReadBuffer(self, dest, aggregate_bytes);
}
c_err_t c_FastByteRingBuffer_GetAtRelative(const c_FastByteRingBuffer_t* self, c_size_t relative_offset, uint8_t* out_byte) {
if (!self || !self->buffer || !out_byte) return C_ERR_INVALID_PARAM;
c_size_t active_size = c_FastByteRingBuffer_GetSize(self);
if (relative_offset >= active_size) {
return C_ERR_OUT_OF_BOUNDS;
}
*out_byte = c_FastByteRingBuffer_GetAtRelativeInternal(self, relative_offset);
return C_SUCCESS;
}
c_bool_t c_FastByteRingBuffer_Is(const c_FastByteRingBuffer_t* self, c_index_t offset, uint8_t value) {
if (!self || !self->buffer || offset < 0) return C_FALSE;
c_size_t active_size = c_FastByteRingBuffer_GetSize(self);
if ((c_size_t)offset >= active_size) return C_FALSE;
return (c_FastByteRingBuffer_GetAtRelativeInternal(self, (c_size_t)offset) == value) ? C_TRUE : C_FALSE;
}
int c_FastByteRingBuffer_Memcmp(const c_FastByteRingBuffer_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;
c_size_t active_size = c_FastByteRingBuffer_GetSize(self);
if (offset >= active_size || (offset + len) > active_size) {
return C_ERR_OUT_OF_BOUNDS;
}
c_size_t absolute_start = (self->head + offset) & self->mask;
c_size_t bytes_to_end = self->capacity - absolute_start;
if (len <= bytes_to_end) {
return memcmp(self->buffer + absolute_start, buffer, len);
} else {
int first_segment_match = memcmp(self->buffer + absolute_start, buffer, bytes_to_end);
if (first_segment_match != 0) {
return first_segment_match;
}
return memcmp(self->buffer, buffer + bytes_to_end, len - bytes_to_end);
}
}
c_err_t c_FastByteRingBuffer_Strtoul(const c_FastByteRingBuffer_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_FastByteRingBuffer_GetSize(self);
if (offset >= total_size) return C_ERR_OUT_OF_BOUNDS;
// 1. Skip leading spaces safely using fast inline relative scans
c_size_t scan_idx = offset;
while (scan_idx < total_size) {
uint8_t byte = c_FastByteRingBuffer_GetAtRelativeInternal(self, scan_idx);
if (!isspace(byte)) break;
scan_idx++;
}
if (scan_idx == total_size) return C_ERR_INVALID_PARAM; // Contains only whitespace
// 2. Locate trailing delimiters to calculate numeric chunk span width
c_size_t start_numeric_offset = scan_idx;
c_size_t numeric_len = 0;
while (scan_idx < total_size) {
uint8_t byte = c_FastByteRingBuffer_GetAtRelativeInternal(self, scan_idx);
// Include numerical modifiers (+, -), hex identifiers (x, X) and alphanumeric bases
if (!isalnum(byte) && byte != '+' && byte != '-') {
break;
}
numeric_len++;
scan_idx++;
}
if (numeric_len == 0) return C_ERR_INVALID_PARAM;
// 3. Resolve the starting index using bitwise mask instead of slow % operators
c_size_t absolute_start = (self->head + start_numeric_offset) & self->mask;
c_size_t bytes_to_end = self->capacity - absolute_start;
unsigned long result = 0;
char* parse_end = NULL;
int current_errno = errno;
errno = 0;
// Fast-path: Segment runs contiguous without wrapping lines
if (numeric_len <= bytes_to_end) {
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 {
// Slow-path: Structural wrap configuration requires localized stack flattening
if (numeric_len >= 64) return C_ERR_OUT_OF_BOUNDS;
char stack_scratch[64];
for (c_size_t i = 0; i < numeric_len; i++) {
stack_scratch[i] = (char)c_FastByteRingBuffer_GetAtRelativeInternal(self, start_numeric_offset + i);
}
stack_scratch[numeric_len] = '\0'; // Guarantee absolute zero string 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; // Preserve runtime environment variables smoothly
return C_SUCCESS;
}
+32
View File
@@ -24,11 +24,43 @@ void c_FastByteRingBuffer_Destroy(c_FastByteRingBuffer_t* self);
c_err_t c_FastByteRingBuffer_WriteByte(c_FastByteRingBuffer_t* self, uint8_t byte);
c_err_t c_FastByteRingBuffer_ReadByte(c_FastByteRingBuffer_t* self, uint8_t* out_byte);
/*
* Writes a bulk buffer span into the fast ring buffer without overwriting existing data.
* @param self: The fast ring buffer instance.
* @param src: Source byte array pointer.
* @param len: Number of bytes to transfer.
* @return The actual number of bytes written into the buffer.
*/
c_size_t c_FastByteRingBuffer_WriteBuffer(c_FastByteRingBuffer_t* self, const uint8_t* src, c_size_t len);
c_size_t c_FastByteRingBuffer_ReadBuffer(c_FastByteRingBuffer_t* self, uint8_t* dest, c_size_t len);
c_size_t c_FastByteRingBuffer_GetSize(const c_FastByteRingBuffer_t* self);
c_bool_t c_FastByteRingBuffer_IsEmpty(const c_FastByteRingBuffer_t* self);
c_bool_t c_FastByteRingBuffer_IsFull(const c_FastByteRingBuffer_t* self);
/* 新增的進階控制接口 */
void c_FastByteRingBuffer_WriteByteOverwrite(c_FastByteRingBuffer_t* self, uint8_t byte);
c_size_t c_FastByteRingBuffer_WriteBufferOverwrite(c_FastByteRingBuffer_t* self, const uint8_t* src, c_size_t len);
c_err_t c_FastByteRingBuffer_PeekByte(const c_FastByteRingBuffer_t* self, uint8_t* out_byte);
c_size_t c_FastByteRingBuffer_PeekBuffer(const c_FastByteRingBuffer_t* self, uint8_t* dest, c_size_t len);
c_size_t c_FastByteRingBuffer_Discard(c_FastByteRingBuffer_t* self, c_size_t len);
const uint8_t* c_FastByteRingBuffer_GetReadPtr(const c_FastByteRingBuffer_t* self, c_size_t* out_contiguous_len);
uint8_t* c_FastByteRingBuffer_GetWritePtr(const c_FastByteRingBuffer_t* self, c_size_t* out_contiguous_len);
c_index_t c_FastByteRingBuffer_IndexOfByte(const c_FastByteRingBuffer_t* self, uint8_t target);
c_index_t c_FastByteRingBuffer_IndexOfBuffer(const c_FastByteRingBuffer_t* self, const uint8_t* pattern, c_size_t pattern_len);
c_index_t c_FastByteRingBuffer_LastIndexOfBuffer(const c_FastByteRingBuffer_t* self, const uint8_t* pattern, c_size_t pattern_len);
c_size_t c_FastByteRingBuffer_ReadUntilToken(c_FastByteRingBuffer_t* self, const uint8_t* token, c_size_t token_len, uint8_t* dest, c_size_t dest_max_len);
c_err_t c_FastByteRingBuffer_GetAtRelative(const c_FastByteRingBuffer_t* self, c_size_t relative_offset, uint8_t* out_byte);
c_bool_t c_FastByteRingBuffer_Is(const c_FastByteRingBuffer_t* self, c_index_t offset, uint8_t value);
int c_FastByteRingBuffer_Memcmp(const c_FastByteRingBuffer_t* self, c_size_t offset, const uint8_t* buffer, c_size_t len);
c_err_t c_FastByteRingBuffer_Strtoul(const c_FastByteRingBuffer_t* self, c_size_t offset, int base, unsigned long* out_value, c_size_t* out_end_offset);
#endif /*INCLUDED_C_FASTBYTERINGBUFFER_H*/
+321 -57
View File
@@ -2,72 +2,336 @@
#include <stdlib.h>
#include <stdio.h>
#define RUN_TEST_CASE(test_func) \
do { \
printf("[RUNNING] %-40s ... ", #test_func); \
fflush(stdout); \
test_func(); \
printf("[PASSED]\n"); \
} while (0)
void test_log(const char* test_name) {
printf("[PASS] %s\n", test_name);
void test_ring_buffer_behavior(void) {
c_FastByteRingBuffer_t ring;
// Capacity initialization must be a power of two
assert(c_FastByteRingBuffer_Init(&ring, 4) == C_SUCCESS);
uint8_t input_stream[] = {0x11, 0x22, 0x33};
// 1. Verify standard incremental writing actions
assert(c_FastByteRingBuffer_WriteBuffer(&ring, input_stream, 3) == 3);
assert(c_FastByteRingBuffer_GetSize(&ring) == 3);
// 2. Verify overflow rejection clamping protection
uint8_t flood_stream[] = {0x44, 0x55};
// Only 1 byte of free space remains out of total capacity 4
assert(c_FastByteRingBuffer_WriteBuffer(&ring, flood_stream, 2) == 1);
assert(c_FastByteRingBuffer_IsFull(&ring) == C_TRUE);
// Check data integrity via peek operations
uint8_t output_peek[4] = {0};
c_FastByteRingBuffer_PeekBuffer(&ring, output_peek, 4);
assert(output_peek[0] == 0x11);
assert(output_peek[3] == 0x44); // 0x44 filled the last slot; 0x55 was cleanly rejected
c_FastByteRingBuffer_Destroy(&ring);
}
static void test_overwrite_and_peek_mechanics(void) {
c_FastByteRingBuffer_t ring;
assert(c_FastByteRingBuffer_Init(&ring, 4) == C_SUCCESS); // Capacity = 4
// 1. Validate Single Overwrite
c_FastByteRingBuffer_WriteByte(&ring, 0x01);
c_FastByteRingBuffer_WriteByte(&ring, 0x02);
c_FastByteRingBuffer_WriteByte(&ring, 0x03);
c_FastByteRingBuffer_WriteByte(&ring, 0x04); // Buffer now full: [0x01, 0x02, 0x03, 0x04]
assert(c_FastByteRingBuffer_IsFull(&ring) == C_TRUE);
c_FastByteRingBuffer_WriteByteOverwrite(&ring, 0x05); // 0x01 gets evicted. Head shifts to 0x02
uint8_t peek_check = 0;
assert(c_FastByteRingBuffer_PeekByte(&ring, &peek_check) == C_SUCCESS);
assert(peek_check == 0x02); // FIFO rules dictate oldest remaining byte is 0x02
// 2. Validate Bulk Overwrite Loops
uint8_t incoming_stream[3] = {0x06, 0x07, 0x08};
// Ring has 4 bytes capacity. Writing 3 bytes over an already full buffer evicts [0x02, 0x03, 0x04]
assert(c_FastByteRingBuffer_WriteBufferOverwrite(&ring, incoming_stream, 3) == 3);
uint8_t verification_dump[4] = {0};
c_size_t read_out = c_FastByteRingBuffer_PeekBuffer(&ring, verification_dump, 4);
assert(read_out == 4);
assert(verification_dump[0] == 0x05); // Preserved from previous transaction
assert(verification_dump[1] == 0x06);
assert(verification_dump[2] == 0x07);
assert(verification_dump[3] == 0x08);
// 3. Confirm Peek leaves data sequence entirely untouched
assert(c_FastByteRingBuffer_GetSize(&ring) == 4);
c_FastByteRingBuffer_Destroy(&ring);
}
static void test_dma_and_discard_mechanics(void) {
c_FastByteRingBuffer_t ring;
assert(c_FastByteRingBuffer_Init(&ring, 8) == C_SUCCESS); // Capacity = 8
// Force initialization of data that loops around the internal ring memory map
uint8_t payload[] = {0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
c_FastByteRingBuffer_WriteBuffer(&ring, payload, 5);
// Discard oldest 2 items: drops 0xA1, 0xA2. Cursors shift.
assert(c_FastByteRingBuffer_Discard(&ring, 2) == 2);
assert(c_FastByteRingBuffer_GetSize(&ring) == 3);
// Write some more to force a wrap-around layout
uint8_t wrap_payload[] = {0xB1, 0xB2, 0xB3, 0xB4};
c_FastByteRingBuffer_WriteBuffer(&ring, wrap_payload, 4); // Total size now = 7 bytes
// Validate GetReadPtr isolates Block Segment 1 cleanly
c_size_t read_chunk_len = 0;
const uint8_t* read_ptr = c_FastByteRingBuffer_GetReadPtr(&ring, &read_chunk_len);
assert(read_ptr != NULL);
// Head was shifted to index 2. 8 - 2 = 6 available linearly up to memory array edge
assert(read_chunk_len == 6);
assert(read_ptr[0] == 0xA3); // First remaining item
// Clear those processed items via direct execution tracking
c_FastByteRingBuffer_Discard(&ring, read_chunk_len);
// Call secondary pass to capture remaining wrapped bytes
read_ptr = c_FastByteRingBuffer_GetReadPtr(&ring, &read_chunk_len);
assert(read_chunk_len == 1);
assert(read_ptr[0] == 0xB4); // Wrapped character check
c_FastByteRingBuffer_Destroy(&ring);
}
static void test_ring_buffer_index_searching(void) {
c_FastByteRingBuffer_t ring;
assert(c_FastByteRingBuffer_Init(&ring, 8) == C_SUCCESS); // Capacity = 8
// Force a data write footprint that wraps around the buffer margins
uint8_t standard_fill[] = {0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44};
c_FastByteRingBuffer_WriteBuffer(&ring, standard_fill, 8);
// Discard 4 elements to position head cursor at absolute index 4
c_FastByteRingBuffer_Discard(&ring, 4);
// Append sequence to cross edge wrap boundaries cleanly
uint8_t wrap_fill[] = {0x55, 0x66, 0x77};
c_FastByteRingBuffer_WriteBuffer(&ring, wrap_fill, 3);
// Dynamic ring content layout from head: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]
// 1. Single byte search lookup match
assert(c_FastByteRingBuffer_IndexOfByte(&ring, 0x33) == 2); // Relative offset index 2 from head
assert(c_FastByteRingBuffer_IndexOfByte(&ring, 0x99) == C_ERR_NOT_FOUND);
// 2. Pattern buffer sequence scan (testing across structural wrap-around edge boundaries)
uint8_t search_pattern[] = {0x44, 0x55, 0x66};
c_index_t match_offset = c_FastByteRingBuffer_IndexOfBuffer(&ring, search_pattern, 3);
assert(match_offset == 3); // 0x44 sits exactly at relative offset position 3
// Application Workflow Integration: Cleanly discard up to token match prefix point
c_FastByteRingBuffer_Discard(&ring, (c_size_t)match_offset);
uint8_t current_head_byte = 0;
c_FastByteRingBuffer_PeekByte(&ring, &current_head_byte);
assert(current_head_byte == 0x44); // The buffer head has been successfully synchronized to the token location
c_FastByteRingBuffer_Destroy(&ring);
}
static void test_reverse_search_and_token_stream(void) {
c_FastByteRingBuffer_t ring;
assert(c_FastByteRingBuffer_Init(&ring, 8) == C_SUCCESS); // Capacity = 8
uint8_t raw_payload[] = {0xAA, 0x11, 0x22, 0xBB, 0x11, 0x22, 0xCC, 0xDD};
c_FastByteRingBuffer_WriteBuffer(&ring, raw_payload, 8);
uint8_t pattern[] = {0x11, 0x22};
// 1. Verify Reverse Pattern Detection Matches Latest Occurrence
assert(c_FastByteRingBuffer_IndexOfBuffer(&ring, pattern, 2) == 1); // First pair starts at offset 1
assert(c_FastByteRingBuffer_LastIndexOfBuffer(&ring, pattern, 2) == 4); // Latest pair starts at offset 4
// 2. Clear out buffer to run frame serialization test
c_FastByteRingBuffer_Discard(&ring, 8);
uint8_t stream_data[] = {'P', 'a', 'c', 'k', 'e', 't', '\r', '\n'};
c_FastByteRingBuffer_WriteBuffer(&ring, stream_data, 8);
uint8_t frame_terminator[] = {'\r', '\n'};
uint8_t output_staging[16] = {0};
// Fail Case: Pass a staging array that is structurally too cramped to hold the payload safely
assert(c_FastByteRingBuffer_ReadUntilToken(&ring, frame_terminator, 2, output_staging, 5) == 0);
assert(c_FastByteRingBuffer_GetSize(&ring) == 8); // Data remains locked safely inside the ring
// Success Case: Pass a valid container size to execute frame extraction
c_size_t read_bytes = c_FastByteRingBuffer_ReadUntilToken(&ring, frame_terminator, 2, output_staging, 16);
assert(read_bytes == 8);
assert(memcmp(output_staging, "Packet\r\n", 8) == 0);
assert(c_FastByteRingBuffer_IsEmpty(&ring) == C_TRUE); // Frame has been cleanly consumed from the queue
c_FastByteRingBuffer_Destroy(&ring);
}
static void test_relative_random_access(void) {
c_FastByteRingBuffer_t ring;
assert(c_FastByteRingBuffer_Init(&ring, 4) == C_SUCCESS); // Capacity = 4
uint8_t seed_data[] = {0x10, 0x20, 0x30};
c_FastByteRingBuffer_WriteBuffer(&ring, seed_data, 3);
// Shift the head pointer downstream via a single byte consumption
uint8_t discard_sink = 0;
c_FastByteRingBuffer_ReadByte(&ring, &discard_sink); // 0x10 dropped. Head points to 0x20
// Append items to cross physical wrapping thresholds cleanly
c_FastByteRingBuffer_WriteByte(&ring, 0x40);
c_FastByteRingBuffer_WriteByte(&ring, 0x50); // Buffer contents: [0x20, 0x30, 0x40, 0x50]
uint8_t extracted_byte = 0;
// 1. Verify standard random read coordinates relative to the head position
assert(c_FastByteRingBuffer_GetAtRelative(&ring, 0, &extracted_byte) == C_SUCCESS);
assert(extracted_byte == 0x20); // Relative 0 points directly to the current head
assert(c_FastByteRingBuffer_GetAtRelative(&ring, 2, &extracted_byte) == C_SUCCESS);
assert(extracted_byte == 0x40); // Wrapped index check
assert(c_FastByteRingBuffer_GetAtRelative(&ring, 3, &extracted_byte) == C_SUCCESS);
assert(extracted_byte == 0x50); // Newest unread byte check
// 2. Validate bounds-checking flags
assert(c_FastByteRingBuffer_GetAtRelative(&ring, 4, &extracted_byte) == C_ERR_OUT_OF_BOUNDS);
assert(c_FastByteRingBuffer_GetAtRelative(&ring, 99, &extracted_byte) == C_ERR_OUT_OF_BOUNDS);
assert(c_FastByteRingBuffer_GetAtRelative(NULL, 0, &extracted_byte) == C_ERR_INVALID_PARAM);
c_FastByteRingBuffer_Destroy(&ring);
}
static void test_conditional_is_validator(void) {
c_FastByteRingBuffer_t ring;
assert(c_FastByteRingBuffer_Init(&ring, 4) == C_SUCCESS);
uint8_t input_stream[] = {0xAA, 0xBB, 0xCC};
c_FastByteRingBuffer_WriteBuffer(&ring, input_stream, 3);
// 1. Validate clean true/false matches relative to the head
assert(c_FastByteRingBuffer_Is(&ring, 0, 0xAA) == C_TRUE); // Oldest byte at head matches
assert(c_FastByteRingBuffer_Is(&ring, 1, 0xBB) == C_TRUE); // Next element matches
assert(c_FastByteRingBuffer_Is(&ring, 1, 0x99) == C_FALSE); // Mismatch returns false
// Consume 1 byte to advance the head pointer and test wrapping boundaries
uint8_t sink = 0;
c_FastByteRingBuffer_ReadByte(&ring, &sink); // Head now points to 0xBB
c_FastByteRingBuffer_WriteByte(&ring, 0xDD); // Layout: [..., 0xBB, 0xCC, 0xDD]
// 2. Re-verify offsets after structural pointer wrap shifts
assert(c_FastByteRingBuffer_Is(&ring, 0, 0xBB) == C_TRUE); // Head index position 0 is now 0xBB
assert(c_FastByteRingBuffer_Is(&ring, 2, 0xDD) == C_TRUE); // Wrapped index element match
// 3. Confirm error/bounds conditions return false instead of blowing up memory layout boundaries
assert(c_FastByteRingBuffer_Is(&ring, 3, 0x00) == C_FALSE); // Out of bounds index
assert(c_FastByteRingBuffer_Is(&ring, -5, 0xBB) == C_FALSE); // Negative index handling protection
assert(c_FastByteRingBuffer_Is(NULL, 0, 0xBB) == C_FALSE); // NULL safety check
c_FastByteRingBuffer_Destroy(&ring);
}
static void test_ring_buffer_memcmp(void) {
c_FastByteRingBuffer_t ring;
assert(c_FastByteRingBuffer_Init(&ring, 6) == C_SUCCESS); // Capacity = 6
uint8_t payload[] = {0x00, 0x11, 0x22, 0x33};
c_FastByteRingBuffer_WriteBuffer(&ring, payload, 4);
// Consume 2 bytes to step the head pointer forward to absolute index 2
uint8_t sink = 0;
c_FastByteRingBuffer_ReadByte(&ring, &sink);
c_FastByteRingBuffer_ReadByte(&ring, &sink); // Buffer active layout from head: [0x22, 0x33]
// Append data to trigger an explicit physical wrap-around edge split layout
uint8_t wrap_payload[] = {0x44, 0x55, 0x66};
c_FastByteRingBuffer_WriteBuffer(&ring, wrap_payload, 3);
// Buffer dynamic content path tracking from head: [0x22, 0x33, 0x44, 0x55, 0x66]
// Physical layout behind indices inside array: [0x55, 0x66, 0x22, 0x33, 0x44, ...]
// 1. Validate contiguous segment match comparisons
uint8_t check_a[] = {0x22, 0x33};
assert(c_FastByteRingBuffer_Memcmp(&ring, 0, check_a, 2) == 0); // Perfect contiguous match
// 2. Validate multi-segment wrap-around comparison mechanics
uint8_t check_b[] = {0x33, 0x44, 0x55, 0x66};
assert(c_FastByteRingBuffer_Memcmp(&ring, 1, check_b, 4) == 0); // Perfect split wrap-around match
// 3. Mismatch checks
uint8_t check_mismatch[] = {0x33, 0x44, 0x99, 0x66};
assert(c_FastByteRingBuffer_Memcmp(&ring, 1, check_mismatch, 4) != 0); // Identifies internal divergence
// 4. Bounds and parameter checks
assert(c_FastByteRingBuffer_Memcmp(&ring, 0, check_b, 100) == C_ERR_OUT_OF_BOUNDS); // Request width overflows content
assert(c_FastByteRingBuffer_Memcmp(&ring, 99, check_b, 1) == C_ERR_OUT_OF_BOUNDS); // Start pointer invalid
assert(c_FastByteRingBuffer_Memcmp(NULL, 0, check_b, 1) == C_ERR_INVALID_PARAM);
c_FastByteRingBuffer_Destroy(&ring);
}
static void test_ring_buffer_strtoul(void) {
c_FastByteRingBuffer_t ring;
assert(c_FastByteRingBuffer_Init(&ring, 8) == C_SUCCESS); // Capacity = 8
// 1. Standard Linear Base-10 Parsing
uint8_t input_a[] = {'1', '2', '3', '4', ' ', 'A', 'B', 'C'};
c_FastByteRingBuffer_WriteBuffer(&ring, input_a, 8);
unsigned long parsed_val = 0;
c_size_t end_offset = 0;
assert(c_FastByteRingBuffer_Strtoul(&ring, 0, 10, &parsed_val, &end_offset) == C_SUCCESS);
assert(parsed_val == 1234);
assert(end_offset == 4); // Points exactly to the trailing space character offset
// Reset buffer tracking lines
c_FastByteRingBuffer_Discard(&ring, 8);
// 2. Fragmented Wrap Hex Parsing
// Pre-fill 5 elements to push cursor indices near wrapping layout boundaries
uint8_t pre_fill[] = {0, 0, 0, 0, 0};
c_FastByteRingBuffer_WriteBuffer(&ring, pre_fill, 5);
c_FastByteRingBuffer_Discard(&ring, 5); // Head pointer sits at physical index 5
// Write Hex parameter payload string ("0x2F") across memory boundaries
uint8_t input_hex[] = {'0', 'x', '2', 'F'};
c_FastByteRingBuffer_WriteBuffer(&ring, input_hex, 4);
assert(c_FastByteRingBuffer_Strtoul(&ring, 0, 16, &parsed_val, &end_offset) == C_SUCCESS);
assert(parsed_val == 47); // 0x2F translates to decimal 47
assert(end_offset == 4);
c_FastByteRingBuffer_Destroy(&ring);
}
int main() {
printf("==================================================\n");
printf(" 開始執行 c_FastByteRingBuffer 最終最佳化版測試\n");
printf(" Starting c_FastByteRingBuffer Unit Testing Suite\n");
printf("==================================================\n\n");
c_FastByteRingBuffer_t q;
// 故意傳入 6,測試是否會自動向上對齊到 8 (2的3次方)
c_err_t err = c_FastByteRingBuffer_Init(&q, 6);
assert(err == C_ERR_SUCCESS);
// 核心斷言:容量必須被修正為 8,遮罩必須為 7 (二進位 0111)
assert(q.capacity == 8);
assert(q.mask == 7);
assert(c_FastByteRingBuffer_IsEmpty(&q) == C_TRUE);
test_log("1. 2的冪次方自動容量對齊與遮罩初始化成功");
// ==========================================
// 2. 測試單一 Byte 寫入與讀取
// ==========================================
err = c_FastByteRingBuffer_WriteByte(&q, 0x11); assert(err == C_ERR_SUCCESS);
err = c_FastByteRingBuffer_WriteByte(&q, 0x22); assert(err == C_ERR_SUCCESS);
assert(c_FastByteRingBuffer_GetSize(&q) == 2);
uint8_t out = 0;
err = c_FastByteRingBuffer_ReadByte(&q, &out);
assert(err == C_ERR_SUCCESS);
assert(out == 0x11);
assert(c_FastByteRingBuffer_GetSize(&q) == 1);
test_log("2. 單一 Byte 讀寫與位元環形遞增驗證成功");
// 清空
c_FastByteRingBuffer_ReadByte(&q, &out);
assert(c_FastByteRingBuffer_IsEmpty(&q) == C_TRUE);
// ==========================================
// 3. 測試大量區塊資料串流與滿載邊界
// ==========================================
uint8_t src_stream[10] = {0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA};
// 目前容量為 8。傳入長度 10 的陣列,預期只能成功寫入 8 個位元組
c_size_t written = c_FastByteRingBuffer_WriteBuffer(&q, src_stream, 10);
assert(written == 8);
assert(c_FastByteRingBuffer_IsFull(&q) == C_TRUE);
// 溢位防呆檢查
assert(c_FastByteRingBuffer_WriteByte(&q, 0xFF) == C_ERR_FULL);
// 大量讀出驗證
uint8_t dest_stream[8] = {0};
c_size_t read = c_FastByteRingBuffer_ReadBuffer(&q, dest_stream, 8);
assert(read == 8);
assert(dest_stream[0] == 0xA1);
assert(dest_stream[7] == 0xA8);
assert(c_FastByteRingBuffer_IsEmpty(&q) == C_TRUE);
test_log("3. 區塊串流 API 與滿載位元遮罩邊界防呆成功");
c_FastByteRingBuffer_Destroy(&q);
test_log("4. 資源安全銷毀成功");
RUN_TEST_CASE(test_ring_buffer_behavior);
RUN_TEST_CASE(test_overwrite_and_peek_mechanics);
RUN_TEST_CASE(test_dma_and_discard_mechanics);
RUN_TEST_CASE(test_ring_buffer_index_searching);
RUN_TEST_CASE(test_reverse_search_and_token_stream);
RUN_TEST_CASE(test_relative_random_access);
RUN_TEST_CASE(test_conditional_is_validator);
RUN_TEST_CASE(test_ring_buffer_memcmp);
RUN_TEST_CASE(test_ring_buffer_strtoul);
printf("\n==================================================\n");
printf(" 恭喜!快取位元最佳化版 RingBuffer 所有單元測試順利通過!\n");
printf(" Success! Byte RingBuffer tests passed!\n");
printf("==================================================\n");
return 0;
}