From d6d38008305d0fea52b3b92b9133302799fd001c Mon Sep 17 00:00:00 2001 From: Chen Peng Date: Mon, 10 Aug 2026 11:55:59 +0800 Subject: [PATCH] =?UTF-8?q?ByteRingBuffer=20=E6=8E=A5=E5=8F=A3=E5=AE=8C?= =?UTF-8?q?=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cKit/Foundation/c_ByteRingBuffer.c | 436 ++++++++++++++++++++++- cKit/Foundation/c_ByteRingBuffer.h | 106 ++++++ cKit/Foundation/c_ByteRingBuffer.t.c | 374 ++++++++++++++++--- cKit/Foundation/c_FastByteRingBuffer.c | 417 +++++++++++++++++++++- cKit/Foundation/c_FastByteRingBuffer.h | 32 ++ cKit/Foundation/c_FastByteRingBuffer.t.c | 378 +++++++++++++++++--- 6 files changed, 1627 insertions(+), 116 deletions(-) diff --git a/cKit/Foundation/c_ByteRingBuffer.c b/cKit/Foundation/c_ByteRingBuffer.c index 5396ec2..ab15d27 100644 --- a/cKit/Foundation/c_ByteRingBuffer.c +++ b/cKit/Foundation/c_ByteRingBuffer.c @@ -1,6 +1,10 @@ #include #include #include +#include +#include +#include + 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; -} \ No newline at end of file +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +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; +} diff --git a/cKit/Foundation/c_ByteRingBuffer.h b/cKit/Foundation/c_ByteRingBuffer.h index 8cedf03..6c08275 100644 --- a/cKit/Foundation/c_ByteRingBuffer.h +++ b/cKit/Foundation/c_ByteRingBuffer.h @@ -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*/ diff --git a/cKit/Foundation/c_ByteRingBuffer.t.c b/cKit/Foundation/c_ByteRingBuffer.t.c index 5ef76db..cf0c99c 100644 --- a/cKit/Foundation/c_ByteRingBuffer.t.c +++ b/cKit/Foundation/c_ByteRingBuffer.t.c @@ -2,61 +2,341 @@ #include #include +#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, ¤t_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"); diff --git a/cKit/Foundation/c_FastByteRingBuffer.c b/cKit/Foundation/c_FastByteRingBuffer.c index 0098f28..e7dffe3 100644 --- a/cKit/Foundation/c_FastByteRingBuffer.c +++ b/cKit/Foundation/c_FastByteRingBuffer.c @@ -1,6 +1,11 @@ #include #include -#include +#include +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ 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; - if (self->tail == self->head) { - self->is_full = C_TRUE; - } - bytes_written++; + // Clamp write operations to avoid overrunning existing readable elements + if (len > free_space) { + len = free_space; } - return bytes_written; + + 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; + } + + 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; +} diff --git a/cKit/Foundation/c_FastByteRingBuffer.h b/cKit/Foundation/c_FastByteRingBuffer.h index a88e41f..2087164 100644 --- a/cKit/Foundation/c_FastByteRingBuffer.h +++ b/cKit/Foundation/c_FastByteRingBuffer.h @@ -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*/ diff --git a/cKit/Foundation/c_FastByteRingBuffer.t.c b/cKit/Foundation/c_FastByteRingBuffer.t.c index 887360d..63e0189 100644 --- a/cKit/Foundation/c_FastByteRingBuffer.t.c +++ b/cKit/Foundation/c_FastByteRingBuffer.t.c @@ -2,72 +2,336 @@ #include #include +#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, ¤t_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; } \ No newline at end of file