一些基础组件
This commit is contained in:
@@ -0,0 +1 @@
|
||||
#include <c_Atomic.h>
|
||||
@@ -0,0 +1,148 @@
|
||||
#ifndef INCLUDED_C_ATOMIC_H
|
||||
#define INCLUDED_C_ATOMIC_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <windows.h>
|
||||
|
||||
// Windows 平台原子型態定義
|
||||
typedef volatile char c_atomic_8_t;
|
||||
typedef volatile long c_atomic_32_t;
|
||||
typedef volatile __int64 c_atomic_64_t;
|
||||
typedef volatile int c_atomic_int_t;
|
||||
typedef void* volatile c_atomic_ptr_t;
|
||||
typedef volatile char c_atomic_bool_t;
|
||||
|
||||
// MSVC 8-Bit (uint8_t/bool) 原子加減法模擬 (利用 CAS 迴圈確保硬體原子性)
|
||||
static inline char _msvc_atomic_fetch_add_8(volatile char* p, char v) {
|
||||
char old_val;
|
||||
do {
|
||||
old_val = *p;
|
||||
} while (InterlockedCompareExchange8((volatile char*)p, (char)(old_val + v), old_val) != old_val);
|
||||
return old_val;
|
||||
}
|
||||
|
||||
static inline char _msvc_atomic_fetch_sub_8(volatile char* p, char v) {
|
||||
char old_val;
|
||||
do {
|
||||
old_val = *p;
|
||||
} while (InterlockedCompareExchange8((volatile char*)p, (char)(old_val - v), old_val) != old_val);
|
||||
return old_val;
|
||||
}
|
||||
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
// GCC/Clang 平台原子型態定義 (與標準 C 原始型態無縫相容)
|
||||
typedef int8_t c_atomic_8_t;
|
||||
typedef int32_t c_atomic_32_t;
|
||||
typedef int64_t c_atomic_64_t;
|
||||
typedef int c_atomic_int_t;
|
||||
typedef void* c_atomic_ptr_t;
|
||||
typedef bool c_atomic_bool_t;
|
||||
|
||||
// GCC/Clang 內建函數自帶全自動型態泛型
|
||||
#define _C_ATOMIC_LOAD_ANY(p) __atomic_load_n((p), __ATOMIC_SEQ_CST)
|
||||
#define _C_ATOMIC_ADD_ANY(p, v) __atomic_fetch_add((p), (v), __ATOMIC_SEQ_CST)
|
||||
#define _C_ATOMIC_SUB_ANY(p, v) __atomic_fetch_sub((p), (v), __ATOMIC_SEQ_CST)
|
||||
#define _C_ATOMIC_EXCH_ANY(p, v) __atomic_exchange_n((p), (v), __ATOMIC_SEQ_CST)
|
||||
|
||||
#else
|
||||
#error "當前編譯器環境不支援硬體級跨平台原子操作!"
|
||||
#endif
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* 2. 強型態的特化 API 提供 */
|
||||
|
||||
#define c_atomic_init_8(p, v) (*(p) = (v))
|
||||
#define c_atomic_init_32(p, v) (*(p) = (v))
|
||||
#define c_atomic_init_64(p, v) (*(p) = (v))
|
||||
#define c_atomic_init_int(p, v) (*(p) = (v))
|
||||
#define c_atomic_init_ptr(p, v) (*(p) = (v))
|
||||
#define c_atomic_init_bool(p, v) (*(p) = (v))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// ---- 8-Bit (uint8_t) ----
|
||||
#define c_atomic_load_8(p) InterlockedCompareExchange8((volatile char*)(p), 0, 0)
|
||||
#define c_atomic_fetch_add_8(p, v) _msvc_atomic_fetch_add_8((volatile char*)(p), (char)(v))
|
||||
#define c_atomic_fetch_sub_8(p, v) _msvc_atomic_fetch_sub_8((volatile char*)(p), (char)(v))
|
||||
// ---- 32-Bit ----
|
||||
#define c_atomic_load_32(p) InterlockedCompareExchange((volatile LONG*)(p), 0, 0)
|
||||
#define c_atomic_fetch_add_32(p, v) InterlockedExchangeAdd((volatile LONG*)(p), (LONG)(v))
|
||||
#define c_atomic_fetch_sub_32(p, v) InterlockedExchangeAdd((volatile LONG*)(p), -(LONG)(v))
|
||||
// ---- 64-Bit ----
|
||||
#define c_atomic_load_64(p) InterlockedCompareExchange64((volatile LONG64*)(p), 0, 0)
|
||||
#define c_atomic_fetch_add_64(p, v) InterlockedExchangeAdd64((volatile LONG64*)(p), (LONGLONG)(v))
|
||||
#define c_atomic_fetch_sub_64(p, v) InterlockedExchangeAdd64((volatile LONG64*)(p), -(LONGLONG)(v))
|
||||
// ---- Pointer (void*) ----
|
||||
#define c_atomic_load_ptr(p) InterlockedCompareExchangePointer((void* volatile*)(p), NULL, NULL)
|
||||
#define c_atomic_exchange_ptr(p, v) InterlockedExchangePointer((void* volatile*)(p), (void*)(v))
|
||||
// ---- Bool ----
|
||||
#define c_atomic_load_bool(p) (InterlockedCompareExchange8((volatile char*)(p), 0, 0) != 0)
|
||||
#define c_atomic_exchange_bool(p, v) (InterlockedExchange8((volatile char*)(p), (char)(v)) != 0)
|
||||
#else
|
||||
// GCC/Clang 特化映射(全型態直接套用系統內建泛型,編譯效率極高)
|
||||
#define c_atomic_load_8(p) _C_ATOMIC_LOAD_ANY(p)
|
||||
#define c_atomic_fetch_add_8(p, v) _C_ATOMIC_ADD_ANY(p, v)
|
||||
#define c_atomic_fetch_sub_8(p, v) _C_ATOMIC_SUB_ANY(p, v)
|
||||
|
||||
#define c_atomic_load_32(p) _C_ATOMIC_LOAD_ANY(p)
|
||||
#define c_atomic_fetch_add_32(p, v) _C_ATOMIC_ADD_ANY(p, v)
|
||||
#define c_atomic_fetch_sub_32(p, v) _C_ATOMIC_SUB_ANY(p, v)
|
||||
|
||||
#define c_atomic_load_64(p) _C_ATOMIC_LOAD_ANY(p)
|
||||
#define c_atomic_fetch_add_64(p, v) _C_ATOMIC_ADD_ANY(p, v)
|
||||
#define c_atomic_fetch_sub_64(p, v) _C_ATOMIC_SUB_ANY(p, v)
|
||||
|
||||
#define c_atomic_load_ptr(p) _C_ATOMIC_LOAD_ANY(p)
|
||||
#define c_atomic_exchange_ptr(p, v) _C_ATOMIC_EXCH_ANY(p, v)
|
||||
|
||||
#define c_atomic_load_bool(p) _C_ATOMIC_LOAD_ANY(p)
|
||||
#define c_atomic_exchange_bool(p, v) _C_ATOMIC_EXCH_ANY(p, v)
|
||||
#endif
|
||||
|
||||
// 原生 Int 動態配適映射
|
||||
#define c_atomic_load_int(p) (sizeof(int) == 8 ? (int)c_atomic_load_64((c_atomic_64_t*)(p)) : (int)c_atomic_load_32((c_atomic_32_t*)(p)))
|
||||
#define c_atomic_fetch_add_int(p, v) (sizeof(int) == 8 ? (int)c_atomic_fetch_add_64((c_atomic_64_t*)(p), v) : (int)c_atomic_fetch_add_32((c_atomic_32_t*)(p), v))
|
||||
#define c_atomic_fetch_sub_int(p, v) (sizeof(int) == 8 ? (int)c_atomic_fetch_sub_64((c_atomic_64_t*)(p), v) : (int)c_atomic_fetch_sub_32((c_atomic_32_t*)(p), v))
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* 3. 全型態 C11 _Generic 萬能泛型巨集分派 */
|
||||
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
#define C_ATOMIC_LOAD(p) _Generic(*(p), \
|
||||
bool: c_atomic_load_bool((c_atomic_bool_t*)(p)), \
|
||||
int8_t: c_atomic_load_8((c_atomic_8_t*)(p)), \
|
||||
uint8_t: c_atomic_load_8((c_atomic_8_t*)(p)), \
|
||||
int32_t: c_atomic_load_32((c_atomic_32_t*)(p)), \
|
||||
uint32_t: c_atomic_load_32((c_atomic_32_t*)(p)), \
|
||||
int64_t: c_atomic_load_64((c_atomic_64_t*)(p)), \
|
||||
uint64_t: c_atomic_load_64((c_atomic_64_t*)(p)), \
|
||||
void*: c_atomic_load_ptr((c_atomic_ptr_t*)(p)) \
|
||||
)
|
||||
|
||||
#define C_ATOMIC_FETCH_ADD(p, v) _Generic(*(p), \
|
||||
int8_t: c_atomic_fetch_add_8((c_atomic_8_t*)(p), (v)), \
|
||||
uint8_t: c_atomic_fetch_add_8((c_atomic_8_t*)(p), (v)), \
|
||||
int32_t: c_atomic_fetch_add_32((c_atomic_32_t*)(p), (v)), \
|
||||
uint32_t: c_atomic_fetch_add_32((c_atomic_32_t*)(p), (v)), \
|
||||
int64_t: c_atomic_fetch_add_64((c_atomic_64_t*)(p), (v)), \
|
||||
uint64_t: c_atomic_fetch_add_64((c_atomic_64_t*)(p), (v)) \
|
||||
)
|
||||
|
||||
#define C_ATOMIC_FETCH_SUB(p, v) _Generic(*(p), \
|
||||
int8_t: c_atomic_fetch_sub_8((c_atomic_8_t*)(p), (v)), \
|
||||
uint8_t: c_atomic_fetch_sub_8((c_atomic_8_t*)(p), (v)), \
|
||||
int32_t: c_atomic_fetch_sub_32((c_atomic_32_t*)(p), (v)), \
|
||||
uint32_t: c_atomic_fetch_sub_32((c_atomic_32_t*)(p), (v)), \
|
||||
int64_t: c_atomic_fetch_sub_64((c_atomic_64_t*)(p), (v)), \
|
||||
uint64_t: c_atomic_fetch_sub_64((c_atomic_64_t*)(p), (v)) \
|
||||
)
|
||||
#endif
|
||||
|
||||
#endif /*INCLUDED_C_ATOMIC_H*/
|
||||
@@ -0,0 +1,434 @@
|
||||
#include <ctype.h>
|
||||
#include <c_ByteRingBuffer.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
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_err_t c_ByteRingBuffer_Init(c_ByteRingBuffer_t* self, c_size_t capacity, c_Allocator_t* allocator) {
|
||||
if (!self || capacity == 0) return C_ERR_PARAM;
|
||||
|
||||
self->allocator = (allocator != NULL) ? *allocator : c_DefaultAllocator;
|
||||
self->capacity = capacity;
|
||||
self->head = 0;
|
||||
self->tail = 0;
|
||||
self->is_full = C_FALSE;
|
||||
|
||||
self->buffer = (uint8_t*)c_Allocator_Alloc(&self->allocator, self->capacity);
|
||||
if (!self->buffer) return C_ERR_NOMEM;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_ByteRingBuffer_Destroy(c_ByteRingBuffer_t* self) {
|
||||
if (!self) return;
|
||||
if (self->buffer) {
|
||||
c_Allocator_Free(&self->allocator, self->buffer);
|
||||
self->buffer = NULL;
|
||||
}
|
||||
self->capacity = 0;
|
||||
self->head = 0;
|
||||
self->tail = 0;
|
||||
self->is_full = C_FALSE;
|
||||
}
|
||||
|
||||
c_size_t c_ByteRingBuffer_GetSize(const c_ByteRingBuffer_t* self) {
|
||||
if (!self) return 0;
|
||||
if (self->is_full) return self->capacity;
|
||||
if (self->tail >= self->head) {
|
||||
return self->tail - self->head;
|
||||
}
|
||||
return self->capacity + self->tail - self->head;
|
||||
}
|
||||
|
||||
c_bool_t c_ByteRingBuffer_IsEmpty(const c_ByteRingBuffer_t* self) {
|
||||
if (!self) return C_TRUE;
|
||||
return (self->head == self->tail && !self->is_full) ? C_TRUE : C_FALSE;
|
||||
}
|
||||
|
||||
c_bool_t c_ByteRingBuffer_IsFull(const c_ByteRingBuffer_t* self) {
|
||||
if (!self) return C_FALSE;
|
||||
return self->is_full;
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 单字节单块基础读写
|
||||
// ==================================================================================================================
|
||||
|
||||
c_err_t c_ByteRingBuffer_WriteByte(c_ByteRingBuffer_t* self, uint8_t byte) {
|
||||
if (!self) return C_ERR_PARAM;
|
||||
if (self->is_full) return C_ERR_OUTOFBOUND;
|
||||
|
||||
self->buffer[self->tail] = byte;
|
||||
self->tail = (self->tail + 1) % self->capacity;
|
||||
|
||||
if (self->tail == self->head) {
|
||||
self->is_full = C_TRUE;
|
||||
}
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_ByteRingBuffer_ReadByte(c_ByteRingBuffer_t* self, uint8_t* out_byte) {
|
||||
if (!self || !out_byte) return C_ERR_PARAM;
|
||||
if (c_ByteRingBuffer_IsEmpty(self)) return C_ERR_OUTOFBOUND;
|
||||
|
||||
*out_byte = self->buffer[self->head];
|
||||
self->head = (self->head + 1) % self->capacity;
|
||||
self->is_full = C_FALSE; // 读取一个字节后,必然不再满
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 连续缓冲区块合并搬运 (分段 memcpy 核心提速)
|
||||
// ==================================================================================================================
|
||||
|
||||
c_size_t c_ByteRingBuffer_WriteBuffer(c_ByteRingBuffer_t* self, const uint8_t* src, c_size_t len) {
|
||||
if (!self || !src || len == 0 || self->is_full) return 0;
|
||||
|
||||
c_size_t current_size = c_ByteRingBuffer_GetSize(self);
|
||||
c_size_t free_space = self->capacity - current_size;
|
||||
if (len > free_space) {
|
||||
len = free_space; // 裁剪可写长度
|
||||
}
|
||||
|
||||
c_size_t first_part = self->capacity - self->tail;
|
||||
if (len <= first_part) {
|
||||
memcpy(self->buffer + self->tail, src, len);
|
||||
self->tail = (self->tail + len) % self->capacity;
|
||||
} else {
|
||||
memcpy(self->buffer + self->tail, src, first_part);
|
||||
memcpy(self->buffer, src + first_part, len - first_part);
|
||||
self->tail = len - first_part;
|
||||
}
|
||||
|
||||
if (self->tail == self->head && len > 0) {
|
||||
self->is_full = C_TRUE;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
c_size_t c_ByteRingBuffer_ReadBuffer(c_ByteRingBuffer_t* self, uint8_t* dest, c_size_t len) {
|
||||
if (!self || !dest || len == 0 || c_ByteRingBuffer_IsEmpty(self)) return 0;
|
||||
|
||||
c_size_t current_size = c_ByteRingBuffer_GetSize(self);
|
||||
if (len > current_size) {
|
||||
len = current_size; // 裁剪可读长度
|
||||
}
|
||||
|
||||
c_size_t first_part = self->capacity - self->head;
|
||||
if (len <= first_part) {
|
||||
memcpy(dest, self->buffer + self->head, len);
|
||||
self->head = (self->head + len) % self->capacity;
|
||||
} else {
|
||||
memcpy(dest, self->buffer + self->head, first_part);
|
||||
memcpy(dest + first_part, self->buffer, len - first_part);
|
||||
self->head = len - first_part;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
self->is_full = C_FALSE;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 强制覆盖写高级流控制 (无死锁流核心控制)
|
||||
// ==================================================================================================================
|
||||
|
||||
void c_ByteRingBuffer_WriteByteOverwrite(c_ByteRingBuffer_t* self, uint8_t byte) {
|
||||
if (!self) return;
|
||||
if (self->is_full) {
|
||||
// 如果满了,强行把最老的队头数据向后赶一格,腾出空间
|
||||
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 || !src || len == 0) return 0;
|
||||
|
||||
// 如果单次写入量直接超过了总容量,数据彻底发生覆盖,只需保留最后填满的内容
|
||||
if (len >= self->capacity) {
|
||||
c_size_t offset = len - self->capacity;
|
||||
memcpy(self->buffer, src + offset, self->capacity);
|
||||
self->head = 0;
|
||||
self->tail = 0;
|
||||
self->is_full = C_TRUE;
|
||||
return self->capacity;
|
||||
}
|
||||
|
||||
c_size_t current_size = c_ByteRingBuffer_GetSize(self);
|
||||
c_size_t free_space = self->capacity - current_size;
|
||||
|
||||
// 如果装不下,需要强行丢弃最前端对应的溢出老数据块
|
||||
if (len > free_space) {
|
||||
c_size_t overflow = len - free_space;
|
||||
self->head = (self->head + overflow) % self->capacity;
|
||||
}
|
||||
|
||||
c_size_t first_part = self->capacity - self->tail;
|
||||
if (len <= first_part) {
|
||||
memcpy(self->buffer + self->tail, src, len);
|
||||
self->tail = (self->tail + len) % self->capacity;
|
||||
} else {
|
||||
memcpy(self->buffer + self->tail, src, first_part);
|
||||
memcpy(self->buffer, src + first_part, len - first_part);
|
||||
self->tail = len - first_part;
|
||||
}
|
||||
|
||||
if (self->tail == self->head) {
|
||||
self->is_full = C_TRUE;
|
||||
} else {
|
||||
self->is_full = C_FALSE; // 可能因丢弃老数据后正好填满,或仍未满
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 只读窥探与静默丢弃 (零拷贝解析首选)
|
||||
// ==================================================================================================================
|
||||
|
||||
c_err_t c_ByteRingBuffer_PeekByte(const c_ByteRingBuffer_t* self, uint8_t* out_byte) {
|
||||
if (!self || !out_byte) return C_ERR_PARAM;
|
||||
if (c_ByteRingBuffer_IsEmpty(self)) return C_ERR_OUTOFBOUND;
|
||||
|
||||
*out_byte = self->buffer[self->head];
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_size_t c_ByteRingBuffer_PeekBuffer(const c_ByteRingBuffer_t* self, uint8_t* dest, c_size_t len) {
|
||||
if (!self || !dest || len == 0 || c_ByteRingBuffer_IsEmpty(self)) return 0;
|
||||
|
||||
c_size_t current_size = c_ByteRingBuffer_GetSize(self);
|
||||
if (len > current_size) len = current_size;
|
||||
|
||||
c_size_t first_part = self->capacity - self->head;
|
||||
if (len <= first_part) {
|
||||
memcpy(dest, self->buffer + self->head, len);
|
||||
} else {
|
||||
memcpy(dest, self->buffer + self->head, first_part);
|
||||
memcpy(dest + first_part, self->buffer, len - first_part);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
c_size_t c_ByteRingBuffer_Discard(c_ByteRingBuffer_t* self, c_size_t len) {
|
||||
if (!self || len == 0 || c_ByteRingBuffer_IsEmpty(self)) return 0;
|
||||
|
||||
c_size_t current_size = c_ByteRingBuffer_GetSize(self);
|
||||
if (len > current_size) len = current_size;
|
||||
|
||||
self->head = (self->head + len) % self->capacity;
|
||||
if (len > 0) {
|
||||
self->is_full = C_FALSE; // 只要丢弃了数据,必定不为满
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 物理地址直通映射 (DMA / 零拷贝内核直通核心)
|
||||
// ==================================================================================================================
|
||||
|
||||
const uint8_t* c_ByteRingBuffer_GetReadPtr(const c_ByteRingBuffer_t* self, c_size_t* out_contiguous_len) {
|
||||
if (!self || !out_contiguous_len || c_ByteRingBuffer_IsEmpty(self)) {
|
||||
if (out_contiguous_len) *out_contiguous_len = 0;
|
||||
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_ByteRingBuffer_GetWritePtr(const c_ByteRingBuffer_t* self, c_size_t* out_contiguous_len) {
|
||||
if (!self || !out_contiguous_len || self->is_full) {
|
||||
if (out_contiguous_len) *out_contiguous_len = 0;
|
||||
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_err_t c_ByteRingBuffer_GetAtRelative(const c_ByteRingBuffer_t* self, c_size_t relative_offset, uint8_t* out_byte) {
|
||||
if (!self || !out_byte) return C_ERR_PARAM;
|
||||
c_size_t size = c_ByteRingBuffer_GetSize(self);
|
||||
if (relative_offset >= size) return C_ERR_OUTOFBOUND;
|
||||
|
||||
c_size_t physical_idx = (self->head + relative_offset) % self->capacity;
|
||||
*out_byte = self->buffer[physical_idx];
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_bool_t c_ByteRingBuffer_Is(const c_ByteRingBuffer_t* self, c_index_t offset, uint8_t value) {
|
||||
// 1. 防御参数为空以及相对索引为负的高危情况
|
||||
if (!self || offset < 0) {
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
// 2. 联动实时大小,防止相对索引发生正向越界
|
||||
c_size_t size = c_ByteRingBuffer_GetSize(self);
|
||||
if ((c_size_t)offset >= size) {
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
// 3. 精准计算物理环形映射索引并实施判定
|
||||
c_size_t physical_idx = (self->head + (c_size_t)offset) % self->capacity;
|
||||
return (self->buffer[physical_idx] == value) ? C_TRUE : C_FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将环形缓冲区内的分段虚拟连续流与外部扁平内存块进行词典序比对
|
||||
* @return 0 完美匹配; <0 环形区较小; >0 环形区较大。
|
||||
* 若发生范围越界或无效参数违规,严格返回特定错误码 C_ERR_OUTOFBOUND 或 C_ERR_PARAM。
|
||||
*/
|
||||
int c_ByteRingBuffer_Memcmp(const c_ByteRingBuffer_t* self, c_size_t offset, const uint8_t* buffer, c_size_t len) {
|
||||
if (!self || !buffer) {
|
||||
return C_ERR_PARAM; // 严格返回参数错误
|
||||
}
|
||||
|
||||
c_size_t size = c_ByteRingBuffer_GetSize(self);
|
||||
|
||||
// 【溢出防御】:防御式检查 offset + len 是否发生加法整型回绕
|
||||
if (offset > size || len > (size - offset)) {
|
||||
return C_ERR_OUTOFBOUND; // 严格返回范围违规错误码
|
||||
}
|
||||
|
||||
// 逐字节进行环形映射解包比对
|
||||
for (c_size_t i = 0; i < len; i++) {
|
||||
c_size_t physical_idx = (self->head + offset + i) % self->capacity;
|
||||
if (self->buffer[physical_idx] != buffer[i]) {
|
||||
// 标准词典序符号返回:不能直接用减法防止 uint8_t 减法下溢回绕引发符号颠倒
|
||||
return (self->buffer[physical_idx] < buffer[i]) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 高级文本数字符号有限状态机解析器 (Strtoul 工业级原状重塑)
|
||||
* @note 强御级加固:完全依靠 c_ByteRingBuffer_GetAtRelative 在环形虚拟长度内做边界锁死,
|
||||
* 支持多进制智能嗅探(0x/0)与防符号下溢的 ULONG_MAX 饱和截断。
|
||||
*/
|
||||
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_PARAM;
|
||||
|
||||
c_size_t total_size = c_ByteRingBuffer_GetSize(self);
|
||||
if (offset >= total_size) return C_ERR_PARAM;
|
||||
|
||||
// 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_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_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_PARAM;
|
||||
}
|
||||
|
||||
if (errno == ERANGE) {
|
||||
*out_value = result;
|
||||
if (out_end_offset) {
|
||||
*out_end_offset = start_numeric_offset + parsed_bytes;
|
||||
}
|
||||
return C_ERR_OUTOFBOUND;
|
||||
}
|
||||
|
||||
*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_OUTOFBOUND; // 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_PARAM;
|
||||
}
|
||||
|
||||
if (errno == ERANGE) return C_ERR_OUTOFBOUND;
|
||||
|
||||
*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;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
#ifndef INCLUDED_C_BYTERINGBUFFER_H
|
||||
#define INCLUDED_C_BYTERINGBUFFER_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
uint8_t* buffer;
|
||||
c_size_t capacity;
|
||||
c_size_t head;
|
||||
c_size_t tail;
|
||||
c_bool_t is_full;
|
||||
c_Allocator_t allocator;
|
||||
}c_ByteRingBuffer_t;
|
||||
|
||||
c_err_t c_ByteRingBuffer_Init(c_ByteRingBuffer_t* self, c_size_t capacity, c_Allocator_t* allocator);
|
||||
void c_ByteRingBuffer_Destroy(c_ByteRingBuffer_t* self);
|
||||
|
||||
c_err_t c_ByteRingBuffer_WriteByte(c_ByteRingBuffer_t* self, uint8_t byte);
|
||||
c_err_t c_ByteRingBuffer_ReadByte(c_ByteRingBuffer_t* self, uint8_t* out_byte);
|
||||
|
||||
c_size_t c_ByteRingBuffer_WriteBuffer(c_ByteRingBuffer_t* self, const uint8_t* src, c_size_t len);
|
||||
c_size_t c_ByteRingBuffer_ReadBuffer(c_ByteRingBuffer_t* self, uint8_t* dest, c_size_t len);
|
||||
|
||||
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*/
|
||||
@@ -0,0 +1,150 @@
|
||||
#include "c_ByteRingBuffer.h"
|
||||
#include "c_Test.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
TEST_CASE(test_ring_buffer_is_mechanics) {
|
||||
c_ByteRingBuffer_t rb;
|
||||
// 初始化容量为 4 的环形缓冲区
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_ByteRingBuffer_Init(&rb, 4, &c_DefaultAllocator));
|
||||
|
||||
// 1. 基础空态与高危参数测试
|
||||
ASSERT_INT_EQ(C_FALSE, c_ByteRingBuffer_Is(NULL, 0, 'A')); // 实例为空
|
||||
ASSERT_INT_EQ(C_FALSE, c_ByteRingBuffer_Is(&rb, -1, 'A')); // 相对偏移为负
|
||||
ASSERT_INT_EQ(C_FALSE, c_ByteRingBuffer_Is(&rb, 0, 0x00)); // 空态下的读取
|
||||
|
||||
// 2. 写入数据:使缓冲区进入常规状态
|
||||
c_ByteRingBuffer_WriteByte(&rb, 0xAA);
|
||||
c_ByteRingBuffer_WriteByte(&rb, 0xBB);
|
||||
|
||||
ASSERT_INT_EQ(C_TRUE, c_ByteRingBuffer_Is(&rb, 0, 0xAA));
|
||||
ASSERT_INT_EQ(C_TRUE, c_ByteRingBuffer_Is(&rb, 1, 0xBB));
|
||||
ASSERT_INT_EQ(C_FALSE, c_ByteRingBuffer_Is(&rb, 0, 0xFF)); // 值不匹配
|
||||
ASSERT_INT_EQ(C_FALSE, c_ByteRingBuffer_Is(&rb, 2, 0x00)); // 索引越界
|
||||
|
||||
// 3. 核心:构造“回绕状态(Wrapped-around)”下的 Is 判定
|
||||
uint8_t dummy;
|
||||
c_ByteRingBuffer_ReadByte(&rb, &dummy); // 消费 0xAA,head 变为 1
|
||||
c_ByteRingBuffer_ReadByte(&rb, &dummy); // 消费 0xBB,head 变为 2
|
||||
|
||||
// 连续写入,迫使 tail 翻越物理末尾(容量4)回绕到索引 0
|
||||
c_ByteRingBuffer_WriteByte(&rb, 0x11); // 物理位置 2
|
||||
c_ByteRingBuffer_WriteByte(&rb, 0x22); // 物理位置 3
|
||||
c_ByteRingBuffer_WriteByte(&rb, 0x33); // 物理位置 0 (回绕)
|
||||
|
||||
// 此时队列相对 head(位置2) 的顺序为: 0x11, 0x22, 0x33
|
||||
ASSERT_INT_EQ(C_TRUE, c_ByteRingBuffer_Is(&rb, 0, 0x11));
|
||||
ASSERT_INT_EQ(C_TRUE, c_ByteRingBuffer_Is(&rb, 1, 0x22));
|
||||
ASSERT_INT_EQ(C_TRUE, c_ByteRingBuffer_Is(&rb, 2, 0x33)); // 物理跨越边界验证成功
|
||||
ASSERT_INT_EQ(C_FALSE, c_ByteRingBuffer_Is(&rb, 3, 0x00)); // 依然能精准卡住边界
|
||||
|
||||
c_ByteRingBuffer_Destroy(&rb);
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 测试二:c_ByteRingBuffer_Memcmp 词典序与加法回绕溢出验证
|
||||
// ==================================================================================================================
|
||||
TEST_CASE(test_ring_buffer_memcmp_lexicographical) {
|
||||
c_ByteRingBuffer_t rb;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_ByteRingBuffer_Init(&rb, 8, &c_DefaultAllocator));
|
||||
|
||||
uint8_t src[] = {'A', 'B', 'C', 'D'};
|
||||
c_ByteRingBuffer_WriteBuffer(&rb, src, 4);
|
||||
|
||||
uint8_t flat_match[] = {'B', 'C'};
|
||||
uint8_t flat_smaller[] = {'B', 'A'}; // 词典序较小
|
||||
uint8_t flat_larger[] = {'B', 'Z'}; // 词典序较大
|
||||
|
||||
// 1. 正常区间匹配
|
||||
ASSERT_INT_EQ(0, c_ByteRingBuffer_Memcmp(&rb, 1, flat_match, 2));
|
||||
|
||||
// 2. 词典序正负号健壮性验证(切断减法溢出隐患)
|
||||
ASSERT_INT_EQ(1, c_ByteRingBuffer_Memcmp(&rb, 1, flat_smaller, 2)); // 环形区 'C' > 外部 'A',返回 1
|
||||
ASSERT_INT_EQ(-1, c_ByteRingBuffer_Memcmp(&rb, 1, flat_larger, 2)); // 环形区 'C' < 外部 'Z',返回 -1
|
||||
|
||||
// 3. 【核心加固点】:高危整型加法回绕攻击防御检查
|
||||
// 在 32/64位系统下,传入接近最大值的 len,若内部直接执行 offset + len,会发生整型溢出回绕变成一个小数字,从而绕过越界判定
|
||||
c_size_t attack_len = (c_size_t)-1;
|
||||
int res = c_ByteRingBuffer_Memcmp(&rb, 1, flat_match, attack_len);
|
||||
ASSERT_INT_EQ(C_ERR_OUTOFBOUND, res); // 必须被溢出隔离墙强行拦截,安全返回错误码
|
||||
|
||||
c_ByteRingBuffer_Destroy(&rb);
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 测试三:c_ByteRingBuffer_Strtoul 状态机大数溢出与流式连续解析验证
|
||||
// ==================================================================================================================
|
||||
TEST_CASE(test_ring_buffer_strtoul_state_machine) {
|
||||
c_ByteRingBuffer_t rb;
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_ByteRingBuffer_Init(&rb, 64, NULL));
|
||||
|
||||
// 写入一段极其复杂的、模拟 AT 指令应答流的混合文本
|
||||
// 包含了:前导空白、十六进制前缀、正负号、八进制以及尾部垃圾包
|
||||
const char* stream = " \r\n -0x1A7F,0755,9999999999999999999999999999999999999999,END";
|
||||
c_ByteRingBuffer_WriteBuffer(&rb, (const uint8_t*)stream, strlen(stream));
|
||||
|
||||
unsigned long parsed_val = 0;
|
||||
c_size_t end_offset = 0;
|
||||
c_err_t err;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 阶段 A: 验证 16 进制智能嗅探、前导空白跳过、补码取负机制
|
||||
// -------------------------------------------------------------------------
|
||||
// 期望:跳过空白,识别出 '-' 和 '0x',解析出 1A7F,并正确强转取负。base 设为 0 (自动侦测)
|
||||
err = c_ByteRingBuffer_Strtoul(&rb, 0, 0, &parsed_val, &end_offset);
|
||||
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
// 0x1A7F = 6783。由于有负号,其无符号长整型等同于 (unsigned long)(-6783)
|
||||
ASSERT_TRUE(parsed_val == (unsigned long)(-6783));
|
||||
// 状态机应当精准停留在第一个非合法十六进制字符(也就是逗号 ',')的相对偏移位置
|
||||
ASSERT_INT_EQ(12, end_offset);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 阶段 B: 验证 8 进制自动探测与非阻塞流式“接力解析”能力
|
||||
// -------------------------------------------------------------------------
|
||||
// 利用上一轮抛出的 end_offset + 1 (跳过逗号),直接在原位向后接力解析下一个数字
|
||||
err = c_ByteRingBuffer_Strtoul(&rb, end_offset + 1, 0, &parsed_val, &end_offset);
|
||||
|
||||
ASSERT_INT_EQ(C_ERR_OK, err);
|
||||
// 0755 以 0 开头被自动嗅探为 8 进制:7*64 + 5*8 + 5 = 448 + 40 + 5 = 493
|
||||
ASSERT_INT_EQ(493, parsed_val);
|
||||
ASSERT_INT_EQ(17, end_offset); // 停留在第二个逗号处
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 阶段 C: 验证超级大数输入的 ULONG_MAX 防回绕饱和截断机制
|
||||
// -------------------------------------------------------------------------
|
||||
// 接力解析那串长达几十位的恐怖数字,测试乘法防溢出墙
|
||||
err = c_ByteRingBuffer_Strtoul(&rb, end_offset + 1, 10, &parsed_val, &end_offset);
|
||||
|
||||
ASSERT_INT_EQ(C_ERR_OUTOFBOUND, err);
|
||||
// 必须稳定截断输出 ULONG_MAX,绝对不能因为发生底层相乘回绕而变成一个小数字
|
||||
ASSERT_TRUE(parsed_val == ULONG_MAX);
|
||||
// 状态机遇到非 10 进制字符(逗号 ',')安全收敛退出
|
||||
ASSERT_INT_EQ(58, end_offset);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 阶段 D: 防御测试:向后探测完全没有数字的纯文本区
|
||||
// -------------------------------------------------------------------------
|
||||
// 从 57 + 1 处向后解析 "END",状态机应当识别出无数字可解析,优雅报错拒绝
|
||||
err = c_ByteRingBuffer_Strtoul(&rb, end_offset + 1, 10, &parsed_val, NULL);
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, err);
|
||||
|
||||
c_ByteRingBuffer_Destroy(&rb);
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 主测试入口
|
||||
// ==================================================================================================================
|
||||
int main(void) {
|
||||
TEST_START(C_ByteRingBuffer_Advanced_Extension_Tests);
|
||||
|
||||
// 运行加固后的全量测试集
|
||||
RUN_TEST(test_ring_buffer_is_mechanics);
|
||||
RUN_TEST(test_ring_buffer_memcmp_lexicographical);
|
||||
RUN_TEST(test_ring_buffer_strtoul_state_machine);
|
||||
|
||||
TEST_REPORT();
|
||||
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#include <c_Cond.h>
|
||||
|
||||
#if defined(PLATFORM_POSIX)
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
c_err_t c_Cond_Init(c_Cond_t* cond) {
|
||||
if (!cond) return C_ERR_PARAM;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
// Windows 的条件变量初始化只是一个清零操作,不会失败
|
||||
InitializeConditionVariable(&cond->handle);
|
||||
cond->is_initialized = C_TRUE;
|
||||
return C_ERR_OK;
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
if (pthread_cond_init(&cond->handle, NULL) == 0) {
|
||||
cond->is_initialized = C_TRUE;
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
return C_ERR_FAIL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void c_Cond_Destroy(c_Cond_t* cond) {
|
||||
if (!cond || !cond->is_initialized) return;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
// Windows 的 CONDITION_VARIABLE 不需要显式销毁(内核会自动回收)
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
pthread_cond_destroy(&cond->handle);
|
||||
#endif
|
||||
cond->is_initialized = C_FALSE;
|
||||
}
|
||||
|
||||
void c_Cond_Wait(c_Cond_t* cond, c_Mutex_t* mutex) {
|
||||
if (!cond || !cond->is_initialized || !mutex || !mutex->is_initialized) return;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
// SleepConditionVariableCS 会自动在内部释放传入的锁,并在唤醒时重新重新获取锁
|
||||
SleepConditionVariableCS(&cond->handle, &mutex->handle, INFINITE);
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
pthread_cond_wait(&cond->handle, &mutex->handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
c_bool_t c_Cond_TimedWait(c_Cond_t* cond, c_Mutex_t* mutex, c_uint_t timeout_ms) {
|
||||
if (!cond || !cond->is_initialized || !mutex || !mutex->is_initialized) return false;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
// 返回非 0 表示成功(收到信号),返回 0 表示超时
|
||||
return SleepConditionVariableCS(&cond->handle, &mutex->handle, timeout_ms) != 0;
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
struct timespec ts;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
// 计算绝对终止时间
|
||||
long long ns = (long long)tv.tv_usec * 1000 + (long long)timeout_ms * 1000000;
|
||||
ts.tv_sec = tv.tv_sec + ns / 1000000000LL;
|
||||
ts.tv_nsec = ns % 1000000000LL;
|
||||
|
||||
// 返回 0 表示成功收到信号
|
||||
return pthread_cond_timedwait(&cond->handle, &mutex->handle, &ts) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void c_Cond_Signal(c_Cond_t* cond) {
|
||||
if (!cond || !cond->is_initialized) return;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
WakeConditionVariable(&cond->handle);
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
pthread_cond_signal(&cond->handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
void c_Cond_Broadcast(c_Cond_t* cond) {
|
||||
if (!cond || !cond->is_initialized) return;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
WakeAllConditionVariable(&cond->handle);
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
pthread_cond_broadcast(&cond->handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef INCLUDED_C_COND_H
|
||||
#define INCLUDED_C_COND_H
|
||||
|
||||
#ifndef INCLUDED_C_MUTEX_H
|
||||
#include <c_Mutex.h>
|
||||
#endif /*INCLUDED_C_MUTEX_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
CONDITION_VARIABLE handle;
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
pthread_cond_t handle;
|
||||
#endif
|
||||
c_bool_t is_initialized;
|
||||
} c_Cond_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_Cond_Init(c_Cond_t* cond);
|
||||
void c_Cond_Destroy(c_Cond_t* cond);
|
||||
void c_Cond_Wait(c_Cond_t* cond, c_Mutex_t* mutex);
|
||||
c_bool_t c_Cond_TimedWait(c_Cond_t* cond, c_Mutex_t* mutex, c_uint_t timeout_ms);
|
||||
void c_Cond_Signal(c_Cond_t* cond);
|
||||
void c_Cond_Broadcast(c_Cond_t* cond);
|
||||
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_COND_H*/
|
||||
@@ -0,0 +1,571 @@
|
||||
#include <c_Console.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// 仅在 POSIX (Linux/macOS) 平台下使用的全局变量,用于恢复终端
|
||||
#if defined(_WIN32) && defined(_WIN64)
|
||||
// 备份用户进入程序前的原始代码页
|
||||
static UINT orig_in_cp = 0;
|
||||
static UINT orig_out_cp = 0;
|
||||
#else
|
||||
static struct termios orig_termios;
|
||||
static int is_terminal_initialized = 0;
|
||||
#endif
|
||||
|
||||
// 内部私有回调:用于程序退出时自动恢复终端属性
|
||||
static void c_Console_ResetOnExit(void) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// 恢复 Windows 用户原本的代码页环境,避免污染用户的 CMD/PowerShell 终端
|
||||
if (orig_in_cp != 0) SetConsoleCP(orig_in_cp);
|
||||
if (orig_out_cp != 0) SetConsoleOutputCP(orig_out_cp);
|
||||
#else
|
||||
// Linux 恢复原始 Raw Mode 设置(参考上一轮代码)
|
||||
extern void disable_raw_mode(void);
|
||||
disable_raw_mode();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 跨平台初始化控制台环境
|
||||
* Windows: 激活全局 ANSI 转义序列支持
|
||||
* Linux/macOS: 关闭行缓冲、关闭按键回显,并注册退出恢复钩子
|
||||
*/
|
||||
void c_Console_Init(void) {
|
||||
// 1. 全平台通用的 C 标准库本地化声明(促使 printf/scanf 内部行为适配 UTF-8)
|
||||
setlocale(LC_ALL, ".UTF-8");
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// ----------------------------------------------------
|
||||
// Windows 平台:激活 VT 模式并全面强制 UTF-8 (65001)
|
||||
// ----------------------------------------------------
|
||||
// 备份旧代码页
|
||||
orig_in_cp = GetConsoleCP();
|
||||
orig_out_cp = GetConsoleOutputCP();
|
||||
|
||||
// 强行设为 UTF-8 编码(65001 == CP_UTF8)
|
||||
SetConsoleCP(CP_UTF8); // 影响控制台输入(如 scanf/fgets)
|
||||
SetConsoleOutputCP(CP_UTF8); // 影响控制台输出(如 printf)
|
||||
|
||||
// 注册退出钩子,以便程序正常退出或 exit 时自动还原环境
|
||||
atexit(c_Console_ResetOnExit);
|
||||
|
||||
// 激活虚拟终端 (VT) 从而支持 ANSI 转义序列
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut != INVALID_HANDLE_VALUE) {
|
||||
DWORD dwMode = 0;
|
||||
if (GetConsoleMode(hOut, &dwMode)) {
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
SetConsoleMode(hOut, dwMode);
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (hIn!=INVALID_HANDLE_VALUE) {
|
||||
DWORD mode;
|
||||
// 1. 获取当前控制台的输入模式
|
||||
if (GetConsoleMode(hIn, &mode)) {
|
||||
// 2. 启用鼠标输入
|
||||
mode |= ENABLE_MOUSE_INPUT;
|
||||
|
||||
// 3. 禁用快速编辑模式(非常关键!如果不禁用,鼠标事件会被控制台自身拦截用于复制文本)
|
||||
mode &= ~ENABLE_QUICK_EDIT_MODE;
|
||||
|
||||
// 4. 启用扩展属性(如果要修改 QUICK_EDIT,必须同时带上这个标志)
|
||||
mode |= ENABLE_EXTENDED_FLAGS;
|
||||
|
||||
// 5. 应用新模式
|
||||
SetConsoleMode(hIn, mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
// ----------------------------------------------------
|
||||
// Linux/macOS 平台:开启 Raw Mode(沿用上一轮逻辑)
|
||||
// ----------------------------------------------------
|
||||
extern struct termios orig_termios;
|
||||
extern int is_terminal_initialized;
|
||||
|
||||
if (!isatty(STDIN_FILENO)) return;
|
||||
if (tcgetattr(STDIN_FILENO, &orig_termios) < 0) return;
|
||||
|
||||
atexit(c_Console_ResetOnExit);
|
||||
is_terminal_initialized = 1;
|
||||
|
||||
struct termios raw = orig_termios;
|
||||
raw.c_lflag &= ~(ICANON | ECHO); // 禁用回显和标准缓冲
|
||||
raw.c_cc[VMIN] = 1;
|
||||
raw.c_cc[VTIME] = 0;
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||
#endif
|
||||
|
||||
// 确保标准输出缓冲区立即刷新
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
int c_Console_kbhit(void) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return _kbhit();
|
||||
#else
|
||||
struct timeval tv = {0, 0};
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(STDIN_FILENO, &fds);
|
||||
return select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv) > 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int c_Console_getch(void) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return _getch();
|
||||
#else
|
||||
char ch = 0;
|
||||
if (read(STDIN_FILENO, &ch, 1) < 0) return 0;
|
||||
return ch;
|
||||
#endif
|
||||
}
|
||||
|
||||
void c_Console_Sleep(int milliseconds) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
Sleep(milliseconds);
|
||||
#else
|
||||
usleep(milliseconds * 1000);
|
||||
#endif
|
||||
}
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
void c_Console_BlankLine(int y) {
|
||||
// 1. 将光标移动到目标行的第 1 列
|
||||
// \033[%d;1H : 移动到第 y 行,第 1 列
|
||||
printf("\033[%d;1H", y);
|
||||
|
||||
// 2. 清除从光标位置到行尾的所有内容(现代终端推荐直接用 2K 清除整行)
|
||||
// \033[2K : 清除光标所在的整行内容,但光标位置保持不变
|
||||
printf("\033[2K");
|
||||
|
||||
// 3. 强制刷新输出缓冲区,确保屏幕立刻更新
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从指定坐标 (x, y) 开始清空到该行的末尾
|
||||
*/
|
||||
void c_Console_BlankLineFrom(int x, int y) {
|
||||
printf("\033[%d;%dH", y, x); // 移动到 (x, y)
|
||||
printf("\033[0K"); // 清除从当前光标到行尾
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 跨平台移动控制台光标到指定坐标
|
||||
* @param x 目标列坐标 (Column/Horizontal),从 1 开始计,自左向右递增
|
||||
* @param y 目标行坐标 (Row/Vertical),从 1 开始计,自上向下递增
|
||||
*/
|
||||
void c_Console_GotoXY(int x, int y) {
|
||||
// 防御性保护:ANSI 坐标必须从 1 开始
|
||||
if (x < 1) x = 1;
|
||||
if (y < 1) y = 1;
|
||||
|
||||
// \033[%d;%dH : 第一个参数是行(y),第二个参数是列(x)
|
||||
// 这是 ANSI X3.64 标准的固定顺序,切勿将 x 和 y 的位置颠倒
|
||||
printf("\033[%d;%dH", y, x);
|
||||
|
||||
// 强制刷新缓冲区,确保光标立即移动到位
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
void c_Console_GetSize(int *width, int *height) {
|
||||
if (!width || !height) return;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(hOut, &csbi)) {
|
||||
// srWindow 存储了当前可视窗口的矩形边界(0-based 坐标)
|
||||
*width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
*height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||
} else {
|
||||
*width = 80; // 失败时的兜底默认值
|
||||
*height = 25;
|
||||
}
|
||||
#else
|
||||
struct winsize w;
|
||||
// 使用 ioctl 的 TIOCGWINSZ 标志直接获取终端窗口大小
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) {
|
||||
*width = w.ws_col;
|
||||
*height = w.ws_row;
|
||||
} else {
|
||||
*width = 80; // 失败时的兜底默认值
|
||||
*height = 25;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
int c_Console_GetVisualWidth(const char *str) {
|
||||
if (!str) return 0;
|
||||
|
||||
int visual_width = 0;
|
||||
int i = 0;
|
||||
|
||||
while (str[i] != '\0') {
|
||||
unsigned char c = (unsigned char)str[i];
|
||||
|
||||
if (c < 0x80) {
|
||||
// ----------------------------------------------------
|
||||
// 1. ASCII 字符 (0x00 - 0x7F) -> 占 1 字节,视觉宽度 1
|
||||
// ----------------------------------------------------
|
||||
visual_width += 1;
|
||||
i += 1;
|
||||
}
|
||||
else if ((c & 0xE0) == 0xC0) {
|
||||
// ----------------------------------------------------
|
||||
// 2. 2字节 UTF-8 字符 (如部分拉丁文、希腊字母) -> 视觉宽度 1
|
||||
// ----------------------------------------------------
|
||||
visual_width += 1;
|
||||
i += 2;
|
||||
}
|
||||
else if ((c & 0xF0) == 0xE0) {
|
||||
// ----------------------------------------------------
|
||||
// 3. 3字节 UTF-8 字符 (绝大多数中日韩汉字、常用标点) -> 视觉宽度 2
|
||||
// ----------------------------------------------------
|
||||
visual_width += 2;
|
||||
i += 3;
|
||||
}
|
||||
else if ((c & 0xF8) == 0xF0) {
|
||||
// ----------------------------------------------------
|
||||
// 4. 4字节 UTF-8 字符 (如 Emoji 表情、生僻字) -> 视觉宽度 2
|
||||
// ----------------------------------------------------
|
||||
visual_width += 2;
|
||||
i += 4;
|
||||
}
|
||||
else {
|
||||
// 异常安全降级处理
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
return visual_width;
|
||||
}
|
||||
|
||||
void c_Console_PrintCenter(int y, int box_width, const char *str) {
|
||||
if (!str) return;
|
||||
|
||||
// 1. 计算文本的实际视觉宽度
|
||||
int text_width = c_Console_GetVisualWidth(str);
|
||||
|
||||
// 2. 计算居中所需的左侧起始 X 坐标 (1-based 坐标系)
|
||||
int start_x = (box_width - text_width) / 2 + 1;
|
||||
if (start_x < 1) start_x = 1; // 边界防御
|
||||
|
||||
// 3. 跨平台移动光标并打印
|
||||
// 注意:需要使用外部已实现的 c_Console_GotoXY,此处以标准 ANSI 语法演示
|
||||
printf("\033[%d;%dH%s", y, start_x, str);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void c_Console_PrintLeftAligned(const char *str, int align_len) {
|
||||
if (!str) return;
|
||||
|
||||
// 打印文本
|
||||
printf("%s", str);
|
||||
|
||||
// 计算实际占用的宽度,并动态补齐缺失的物理空格
|
||||
int text_width = c_Console_GetVisualWidth(str);
|
||||
int padding = align_len - text_width;
|
||||
|
||||
for (int i = 0; i < padding; i++) {
|
||||
putchar(' ');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int c_Console_WriteXY(int x, int y, const char *format, ...) {
|
||||
// 1. 防御性保护:确保坐标合法
|
||||
if (x < 1) x = 1;
|
||||
if (y < 1) y = 1;
|
||||
|
||||
// 2. 移动光标到目标坐标 (使用 ANSI CUP 序列)
|
||||
printf("\033[%d;%dH", y, x);
|
||||
|
||||
// 3. 处理 C 语言可变参数并安全输出
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int result = vprintf(format, args); // 核心:使用 vprintf 将参数包直接输出到控制台
|
||||
va_end(args);
|
||||
|
||||
// 4. 强制刷新输出缓冲区,确保文本和光标立即更新,防止画面滞后
|
||||
fflush(stdout);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int c_Console_WriteColorXY(int x, int y, c_ConsoleColor_t fg, c_ConsoleColor_t bg, const char *format, ...) {
|
||||
// 1. 防御性保护:确保坐标合法
|
||||
if (x < 1) x = 1;
|
||||
if (y < 1) y = 1;
|
||||
|
||||
// 2. 移动光标到指定位置
|
||||
printf("\033[%d;%dH", y, x);
|
||||
|
||||
// 3. 构建并发送 ANSI 颜色控制序列
|
||||
// 标准前景色: 30-37, 高亮前景色: 90-97
|
||||
// 标准背景色: 40-47, 高亮背景色: 100-107
|
||||
if (fg != C_COLOR_NONE) {
|
||||
if (fg < 8) {
|
||||
printf("\033[%dm", 30 + fg); // 标准前景色
|
||||
} else {
|
||||
printf("\033[%dm", 90 + (fg - 8)); // 高亮前景色
|
||||
}
|
||||
}
|
||||
if (bg != C_COLOR_NONE) {
|
||||
if (bg < 8) {
|
||||
printf("\033[%dm", 40 + bg); // 标准背景色
|
||||
} else {
|
||||
printf("\033[%dm", 100 + (bg - 8));// 高亮背景色
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 处理可变参数并安全输出文本
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int result = vprintf(format, args);
|
||||
va_end(args);
|
||||
|
||||
// 5. 关键:格式化文本打印结束后,**必须重置颜色**,否则会污染后续的所有打印
|
||||
printf("\033[0m");
|
||||
|
||||
// 6. 强制刷新输出缓冲区
|
||||
fflush(stdout);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
void c_Console_EnableMouse(void) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD dwMode = 0;
|
||||
GetConsoleMode(hIn, &dwMode);
|
||||
// 启用鼠标输入标志
|
||||
dwMode |= ENABLE_MOUSE_INPUT;
|
||||
// 如果启用了快速编辑模式(QuickEdit),会导致鼠标点击被系统拦截变成选中文字,必须关闭
|
||||
dwMode &= ~ENABLE_QUICK_EDIT_MODE;
|
||||
SetConsoleMode(hIn, dwMode);
|
||||
#else
|
||||
// Linux/macOS: 发送 ANSI 序列开启鼠标追踪
|
||||
// \033[?1003h: 追踪所有鼠标动作(包括移动、点击、释放)
|
||||
// \033[?1006h: 启用 SGR 鼠标编码模式(现代终端标配,坐标支持超过 255)
|
||||
printf("\033[?1003h\033[?1006h");
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void c_Console_DisableMouse(void) {
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
printf("\033[?1003l\033[?1006l"); // 关闭鼠标追踪
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 跨平台非阻塞读取鼠标事件
|
||||
* @param mouse_evt 用于接收转换后的通用鼠标事件结构体
|
||||
* @return int 如果成功捕获到鼠标事件返回 1,否则返回 0
|
||||
*/
|
||||
int c_Console_ReadMouse(c_ConsoleMouseEvent_t *mouse_evt) {
|
||||
if (!mouse_evt) return 0;
|
||||
mouse_evt->type = MOUSE_EVENT_NONE;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// ----------------------------------------------------
|
||||
// Windows 平台实现
|
||||
// ----------------------------------------------------
|
||||
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD numEvents = 0;
|
||||
|
||||
// 检查输入缓冲区中是否有事件
|
||||
GetNumberOfConsoleInputEvents(hIn, &numEvents);
|
||||
if (numEvents == 0) return 0;
|
||||
|
||||
INPUT_RECORD inRec;
|
||||
DWORD numRead = 0;
|
||||
// 窥视而不直接移出,先确认是不是鼠标事件
|
||||
PeekConsoleInput(hIn, &inRec, 1, &numRead);
|
||||
|
||||
if (numRead > 0 && inRec.EventType == MOUSE_EVENT) {
|
||||
// 确实是鼠标事件,正式读出
|
||||
ReadConsoleInput(hIn, &inRec, 1, &numRead);
|
||||
MOUSE_EVENT_RECORD mer = inRec.Event.MouseEvent;
|
||||
|
||||
// 转换坐标 (Windows 是 0-based,我们要统一转换为 1-based)
|
||||
mouse_evt->x = mer.dwMousePosition.X + 1;
|
||||
mouse_evt->y = mer.dwMousePosition.Y + 1;
|
||||
|
||||
// 判断事件类型
|
||||
if (mer.dwEventFlags == 0) {
|
||||
// 点击或释放
|
||||
if (mer.dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) {
|
||||
mouse_evt->type = MOUSE_EVENT_PRESS_LEFT;
|
||||
} else if (mer.dwButtonState & RIGHTMOST_BUTTON_PRESSED) {
|
||||
mouse_evt->type = MOUSE_EVENT_PRESS_RIGHT;
|
||||
} else if (mer.dwButtonState & FROM_LEFT_2ND_BUTTON_PRESSED) {
|
||||
mouse_evt->type = MOUSE_EVENT_PRESS_MIDDLE;
|
||||
} else {
|
||||
mouse_evt->type = MOUSE_EVENT_RELEASE;
|
||||
}
|
||||
} else if (mer.dwEventFlags == MOUSE_WHEELED) {
|
||||
// 滚轮滚动 (HIWORD 为正向上,为负向下)
|
||||
if ((short)HIWORD(mer.dwButtonState) > 0) {
|
||||
mouse_evt->type = MOUSE_EVENT_WHEEL_UP;
|
||||
} else {
|
||||
mouse_evt->type = MOUSE_EVENT_WHEEL_DOWN;
|
||||
}
|
||||
} else if (mer.dwEventFlags == MOUSE_MOVED) {
|
||||
mouse_evt->type = MOUSE_EVENT_MOVE;
|
||||
}
|
||||
return 1;
|
||||
} else if (numRead > 0) {
|
||||
// 如果不是鼠标事件(比如是键盘事件),则丢弃或由其他函数处理
|
||||
ReadConsoleInput(hIn, &inRec, 1, &numRead);
|
||||
}
|
||||
|
||||
#else
|
||||
// ----------------------------------------------------
|
||||
// Linux/macOS 平台实现 (解析 SGR 鼠标协议序列)
|
||||
// SGR 格式一般为: \033[<按钮代号>;X坐标;Y坐标M (或 m 代表释放)
|
||||
// ----------------------------------------------------
|
||||
|
||||
if (!c_Console_kbhit()) return 0;
|
||||
|
||||
int ch = c_Console_getch();
|
||||
if (ch == 27) { // 捕获到 ESC (\033)
|
||||
if (c_Console_getch() == '[') {
|
||||
if (c_Console_getch() == '<') {
|
||||
int btn = 0, x = 0, y = 0;
|
||||
char action = 0;
|
||||
|
||||
// 解析 SGR 密文参数,例如 "0;45;12M"
|
||||
// 现代终端标准,直接使用 scanf 变体或手动循环读取
|
||||
if (scanf("%d;%d;%d%c", &btn, &x, &y, &action) == 4) {
|
||||
mouse_evt->x = x;
|
||||
mouse_evt->y = y;
|
||||
|
||||
if (action == 'm') {
|
||||
mouse_evt->type = MOUSE_EVENT_RELEASE;
|
||||
} else if (action == 'M') {
|
||||
if (btn == 0) mouse_evt->type = MOUSE_EVENT_PRESS_LEFT;
|
||||
else if (btn == 1) mouse_evt->type = MOUSE_EVENT_PRESS_MIDDLE;
|
||||
else if (btn == 2) mouse_evt->type = MOUSE_EVENT_PRESS_RIGHT;
|
||||
else if (btn == 32) mouse_evt->type = MOUSE_EVENT_MOVE; // 伴随左键的移动
|
||||
else if (btn == 35) mouse_evt->type = MOUSE_EVENT_MOVE; // 纯移动
|
||||
else if (btn == 64) mouse_evt->type = MOUSE_EVENT_WHEEL_UP;
|
||||
else if (btn == 65) mouse_evt->type = MOUSE_EVENT_WHEEL_DOWN;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* @brief 跨平台精准读取并转换键盘按键(包含方向键、Esc、Enter)
|
||||
* @note 必须确保终端已通过 c_Console_Init() 切换到非阻塞/Raw 模式
|
||||
* @return int 返回转换后的统一 c_ConsoleKeyCode_t 键值,若为普通字符则原样返回其 ASCII 码
|
||||
*/
|
||||
int c_Console_ReadKey(void) {
|
||||
if (!c_Console_kbhit()) return C_KEY_UNKNOWN;
|
||||
|
||||
int ch = c_Console_getch();
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// ----------------------------------------------------
|
||||
// Windows 平台:处理扩展键双字节
|
||||
// ----------------------------------------------------
|
||||
if (ch == 0 || ch == 224) {
|
||||
// 遇到特殊前缀,必须紧接着读取第二个字节
|
||||
int sub_ch = _getch();
|
||||
switch (sub_ch) {
|
||||
case 72: return C_KEY_UP;
|
||||
case 80: return C_KEY_DOWN;
|
||||
case 75: return C_KEY_LEFT;
|
||||
case 77: return C_KEY_RIGHT;
|
||||
default: return C_KEY_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// Windows 的回车可能返回 '\r' (13),统一规范化
|
||||
if (ch == '\r' || ch == '\n') return C_KEY_ENTER;
|
||||
|
||||
return ch; // 普通 ASCII 字符 (如 'w', 'a', 's', 'd') 直接原样返回
|
||||
|
||||
#else
|
||||
// ----------------------------------------------------
|
||||
// Linux/macOS 平台:处理 ANSI 键盘转义序列流
|
||||
// ----------------------------------------------------
|
||||
if (ch == 27) { // 捕获到第一个字节是 ESC
|
||||
// 立即检查缓冲区,看后面有没有跟着字符。如果没有,说明用户真的只按了独立的 Esc 键
|
||||
if (!c_Console_kbhit()) {
|
||||
return C_KEY_ESC;
|
||||
}
|
||||
|
||||
int next1 = c_Console_getch();
|
||||
if (next1 == '[') {
|
||||
if (!c_Console_kbhit()) return C_KEY_UNKNOWN;
|
||||
int next2 = c_Console_getch();
|
||||
|
||||
// 标准方向键序列判定: ESC [ A/B/C/D
|
||||
switch (next2) {
|
||||
case 'A': return C_KEY_UP;
|
||||
case 'B': return C_KEY_DOWN;
|
||||
case 'C': return C_KEY_RIGHT;
|
||||
case 'D': return C_KEY_LEFT;
|
||||
default: return C_KEY_UNKNOWN;
|
||||
}
|
||||
}
|
||||
return C_KEY_UNKNOWN;
|
||||
}
|
||||
|
||||
// Linux 退格键适配
|
||||
if (ch == 127) return C_KEY_BACKSPACE;
|
||||
// 回车键统一
|
||||
if (ch == '\n' || ch == '\r') return C_KEY_ENTER;
|
||||
|
||||
return ch; // 普通 ASCII 字符原样返回
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
#ifndef INCLUDED_C_CONSOLE_H
|
||||
#define INCLUDED_C_CONSOLE_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_STDIO_H
|
||||
#define INCLUDED_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif /*INCLUDED_STDIO_H*/
|
||||
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <conio.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// ==========================================
|
||||
// 2. 终端控制 ANSI 转义宏 (全平台通用)
|
||||
// ==========================================
|
||||
#define CONSOLE_CLEAR() printf("\033[2J\033[H") // 清屏并将光标归位
|
||||
#define CONSOLE_GOTOXY(x, y) printf("\033[%d;%dH", (y), (x)) // 移动光标 (1-based)
|
||||
#define CONSOLE_HIDE_CURSOR() printf("\033[?25l") // 隐藏光标
|
||||
#define CONSOLE_SHOW_CURSOR() printf("\033[?25h") // 显示光标
|
||||
#define CONSOLE_COLOR_RESET() printf("\033[0m") // 重置属性
|
||||
#define CONSOLE_COLOR_RED() printf("\033[1;31m") // 高亮红
|
||||
#define CONSOLE_COLOR_GREEN() printf("\033[1;32m") // 高亮绿
|
||||
#define CONSOLE_COLOR_BLUE() printf("\033[1;34m") // 高亮蓝
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* @brief 控制台 16 色标准颜色枚举(同时适用于前景色和背景色计算)
|
||||
*/
|
||||
typedef enum {
|
||||
C_COLOR_BLACK = 0,
|
||||
C_COLOR_RED = 1,
|
||||
C_COLOR_GREEN = 2,
|
||||
C_COLOR_YELLOW = 3,
|
||||
C_COLOR_BLUE = 4,
|
||||
C_COLOR_MAGENTA = 5,
|
||||
C_COLOR_CYAN = 6,
|
||||
C_COLOR_WHITE = 7,
|
||||
|
||||
// 高亮色系列(加粗/明亮)
|
||||
C_COLOR_BRIGHT_BLACK = 8,
|
||||
C_COLOR_BRIGHT_RED = 9,
|
||||
C_COLOR_BRIGHT_GREEN = 10,
|
||||
C_COLOR_BRIGHT_YELLOW = 11,
|
||||
C_COLOR_BRIGHT_BLUE = 12,
|
||||
C_COLOR_BRIGHT_MAGENTA = 13,
|
||||
C_COLOR_BRIGHT_CYAN = 14,
|
||||
C_COLOR_BRIGHT_WHITE = 15,
|
||||
|
||||
C_COLOR_NONE = -1 // 不改变原有颜色(保持默认)
|
||||
} c_ConsoleColor_t;
|
||||
|
||||
// 鼠标事件类型
|
||||
typedef enum {
|
||||
MOUSE_EVENT_NONE = 0,
|
||||
MOUSE_EVENT_PRESS_LEFT, // 左键按下
|
||||
MOUSE_EVENT_PRESS_RIGHT, // 右键按下
|
||||
MOUSE_EVENT_PRESS_MIDDLE, // 中键按下
|
||||
MOUSE_EVENT_RELEASE, // 任意键释放
|
||||
MOUSE_EVENT_WHEEL_UP, // 滚轮向上滚动
|
||||
MOUSE_EVENT_WHEEL_DOWN, // 滚轮向下滚动
|
||||
MOUSE_EVENT_MOVE // 鼠标移动
|
||||
} c_ConsoleMouseEventType_t;
|
||||
|
||||
// 统一的鼠标事件结构体
|
||||
typedef struct {
|
||||
c_ConsoleMouseEventType_t type; // 事件类型
|
||||
int x; // 触发时的列坐标 (从 1 开始)
|
||||
int y; // 触发时的行坐标 (从 1 开始)
|
||||
} c_ConsoleMouseEvent_t;
|
||||
|
||||
typedef enum {
|
||||
// 基础控制键(单字节即可判定的按键)
|
||||
C_KEY_UNKNOWN = 0,
|
||||
C_KEY_ENTER = 13, // 统一回车键
|
||||
C_KEY_ESC = 27, // 统一 ESC 键
|
||||
C_KEY_SPACE = 32, // 空格键
|
||||
C_KEY_BACKSPACE = 127,// 退格键
|
||||
|
||||
// 特殊扩展按键(方向键)
|
||||
C_KEY_UP = 1001,
|
||||
C_KEY_DOWN = 1002,
|
||||
C_KEY_LEFT = 1003,
|
||||
C_KEY_RIGHT = 1004
|
||||
} c_ConsoleKeyCode_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* @brief 跨平台初始化控制台环境
|
||||
* Windows: 激活全局 ANSI 转义序列支持
|
||||
* Linux/macOS: 关闭行缓冲、关闭按键回显,并注册退出恢复钩子
|
||||
*/
|
||||
void c_Console_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 跨平台非阻塞检查是否有键盘输入
|
||||
* @return
|
||||
*/
|
||||
int c_Console_kbhit(void);
|
||||
|
||||
/**
|
||||
* @brief 跨平台直接读取单个按键字符
|
||||
* @return
|
||||
*/
|
||||
int c_Console_getch(void);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param milliseconds
|
||||
*/
|
||||
void c_Console_Sleep(int milliseconds);
|
||||
|
||||
/**
|
||||
* @brief 跨平台清除控制台中的指定行
|
||||
* @param y 目标行的纵坐标 (从 1 开始计)
|
||||
*/
|
||||
void c_Console_BlankLine(int y);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 从指定坐标 (x, y) 开始清空到该行的末尾
|
||||
*/
|
||||
void c_Console_BlankLineFrom(int x, int y);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 跨平台移动控制台光标到指定坐标
|
||||
* @param x 目标列坐标 (Column/Horizontal),从 1 开始计,自左向右递增
|
||||
* @param y 目标行坐标 (Row/Vertical),从 1 开始计,自上向下递增
|
||||
*/
|
||||
void c_Console_GotoXY(int x, int y);
|
||||
|
||||
/**
|
||||
* @brief 跨平台获取当前控制台窗口的大小(宽高)
|
||||
* @param width 用于接收总列数(X方向字符数)的指针
|
||||
* @param height 用于接收总行数(Y方向字符数)的指针
|
||||
*/
|
||||
void c_Console_GetSize(int *width, int *height);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 在控制台指定行的指定总宽度内,将 UTF-8 文本居中打印
|
||||
* @param y 目标行坐标 (从 1 开始)
|
||||
* @param box_width 容器的总宽度(例如整个控制台的宽度,或一个 UI 矩形框的宽度)
|
||||
* @param str 要打印的 UTF-8 字符串
|
||||
*/
|
||||
void c_Console_PrintCenter(int y, int box_width, const char *str);
|
||||
|
||||
/**
|
||||
* @brief 精准计算一个 UTF-8 字符串在控制台上的实际“光标显示宽度”
|
||||
* @param str 输入的 UTF-8 字符串
|
||||
* @return 屏幕实际占用的列数(英文字符算 1,中文/日文/韩文等全角字符算 2)
|
||||
*/
|
||||
int c_Console_GetVisualWidth(const char *str);
|
||||
|
||||
/**
|
||||
* @brief 格式化等宽对齐打印(常用于表格、菜单选项对齐)
|
||||
* @param str 要打印的文本
|
||||
* @param align_len 限定的视觉总宽度(若文本不足此宽度,自动在右侧补齐空格)
|
||||
*/
|
||||
void c_Console_PrintLeftAligned(const char *str, int align_len);
|
||||
|
||||
/**
|
||||
* @brief 跨平台在指定坐标处格式化输出 UTF-8 文本
|
||||
* @param x 目标列坐标 (从 1 开始计)
|
||||
* @param y 目标行坐标 (从 1 开始计)
|
||||
* @param format 格式化字符串 (与 printf 完全一致,如 "Score: %d")
|
||||
* @param ... 可变参数
|
||||
* @return 成功打印的物理字节数(若失败返回负数)
|
||||
*/
|
||||
int c_Console_WriteXY(int x, int y, const char *format, ...);
|
||||
|
||||
/**
|
||||
* @brief 跨平台在指定坐标处,以指定的颜色格式化输出文本
|
||||
* @param x 目标列坐标 (从 1 开始)
|
||||
* @param y 目标行坐标 (从 1 开始)
|
||||
* @param fg 前景色 (文字颜色),传入 C_COLOR_NONE 表示不修改
|
||||
* @param bg 背景色 (文字底色),传入 C_COLOR_NONE 表示不修改
|
||||
* @param format 格式化字符串
|
||||
* @param ... 可变参数
|
||||
* @return 成功打印的物理字节数
|
||||
*/
|
||||
int c_Console_WriteColorXY(int x, int y, c_ConsoleColor_t fg, c_ConsoleColor_t bg, const char *format, ...);
|
||||
|
||||
/**
|
||||
* @brief 开启鼠标事件追踪
|
||||
*/
|
||||
void c_Console_EnableMouse(void);
|
||||
|
||||
/**
|
||||
* @brief 关闭鼠标事件追踪(程序退出时必须调用,避免污染用户终端)
|
||||
*/
|
||||
void c_Console_DisableMouse(void);
|
||||
|
||||
/**
|
||||
* @brief 跨平台非阻塞读取鼠标事件
|
||||
* @param mouse_evt 用于接收转换后的通用鼠标事件结构体
|
||||
* @return int 如果成功捕获到鼠标事件返回 1,否则返回 0
|
||||
*/
|
||||
int c_Console_ReadMouse(c_ConsoleMouseEvent_t *mouse_evt);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 跨平台精准读取并转换键盘按键(包含方向键、Esc、Enter)
|
||||
* @note 必须确保终端已通过 c_Console_Init() 切换到非阻塞/Raw 模式
|
||||
* @return int 返回转换后的统一 c_ConsoleKeyCode_t 键值,若为普通字符则原样返回其 ASCII 码
|
||||
*/
|
||||
int c_Console_ReadKey(void);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_Console_HideCursor(void) {
|
||||
printf("\033[?25l");
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_Console_ShowCursor(void) {
|
||||
printf("\033[?25h");
|
||||
}
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_CONSOLE_H*/
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "c_Console.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
c_Console_Init();
|
||||
CONSOLE_CLEAR();
|
||||
CONSOLE_HIDE_CURSOR();
|
||||
|
||||
int x = 10, y = 5;
|
||||
int running = 1;
|
||||
|
||||
CONSOLE_GOTOXY(1, 1);
|
||||
CONSOLE_COLOR_GREEN();
|
||||
printf("=== 跨平台控制台框架示例 ===");
|
||||
CONSOLE_GOTOXY(1, 2);
|
||||
CONSOLE_COLOR_RESET();
|
||||
printf("控制键: W/A/S/D 移动,Q 退出\n");
|
||||
|
||||
while (running) {
|
||||
// 1. 处理输入
|
||||
if (c_Console_kbhit()) {
|
||||
int key = c_Console_getch();
|
||||
|
||||
// 擦除旧位置
|
||||
CONSOLE_GOTOXY(x, y);
|
||||
printf(" ");
|
||||
|
||||
switch (key) {
|
||||
case 'w': case 'W': y--; break;
|
||||
case 's': case 'S': y++; break;
|
||||
case 'a': case 'A': x--; break;
|
||||
case 'd': case 'D': x++; break;
|
||||
case 'q': case 'Q': running = 0; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// 边界约束
|
||||
if (x < 1) x = 1;
|
||||
if (y < 3) y = 3;
|
||||
}
|
||||
|
||||
// 2. 渲染画面
|
||||
CONSOLE_GOTOXY(x, y);
|
||||
CONSOLE_COLOR_RED();
|
||||
printf("@"); // 绘制玩家
|
||||
fflush(stdout); // 刷新标准输出缓冲区(防止字符滞留)
|
||||
|
||||
// 3. 控制帧率 (约 30 FPS)
|
||||
c_Console_Sleep(33);
|
||||
}
|
||||
|
||||
// 退出处理
|
||||
CONSOLE_CLEAR();
|
||||
CONSOLE_SHOW_CURSOR();
|
||||
CONSOLE_COLOR_RESET();
|
||||
printf("程序已安全退出。\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#include "c_Console.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
// 1. 全局初始化控制台环境(支持 UTF-8、启用鼠标追踪、隐藏光标、Raw模式)
|
||||
c_Console_Init();
|
||||
CONSOLE_CLEAR();
|
||||
CONSOLE_HIDE_CURSOR();
|
||||
|
||||
int screen_w = 80, screen_h = 25;
|
||||
c_Console_GetSize(&screen_w, &screen_h);
|
||||
|
||||
// 2. 绘制可视化测试台的固定 UI 边框与标题
|
||||
c_Console_WriteColorXY(1, 1, C_COLOR_BRIGHT_WHITE, C_COLOR_BLUE, "==== 跨平台 c_Console 自动化全功能可视化测试台 ====");
|
||||
c_Console_WriteColorXY(1, 2, C_COLOR_BRIGHT_BLACK, C_COLOR_NONE, "操作指南: 按 [方向键 ↑↓←→] 测试键盘,用 [鼠标点击/滚轮] 测试动态事件。按 [Esc] 键退出。");
|
||||
|
||||
// 3. 绘制 UTF-8 中英文混排对照区,验证等宽对齐算法
|
||||
c_Console_WriteColorXY(2, 5, C_COLOR_BRIGHT_CYAN, C_COLOR_NONE, "【1. UTF-8 宽度测试对齐基准线】:");
|
||||
c_Console_WriteColorXY(2, 6, C_COLOR_WHITE, C_COLOR_NONE, "字符串A: [C++ 2026]") ;
|
||||
c_Console_WriteColorXY(2, 7, C_COLOR_WHITE, C_COLOR_NONE, "字符串B: [C语言实现]") ;
|
||||
|
||||
// 动态计算并打印视觉宽度进行自检
|
||||
int lenA = c_Console_GetVisualWidth("[C++ 2026]");
|
||||
int lenB = c_Console_GetVisualWidth("[C语言实现]");
|
||||
c_Console_WriteColorXY(30, 6, C_COLOR_YELLOW, C_COLOR_NONE, "<- 判定宽度: %d (理论应为 10)", lenA);
|
||||
c_Console_WriteColorXY(30, 7, C_COLOR_YELLOW, C_COLOR_NONE, "<- 判定宽度: %d (理论应为 10)", lenB);
|
||||
|
||||
// 4. 绘制两个交互式虚拟测试按钮供鼠标点击
|
||||
c_Console_WriteColorXY(5, 10, C_COLOR_WHITE, C_COLOR_GREEN, " [ 按钮 A ] ");
|
||||
c_Console_WriteColorXY(20, 10, C_COLOR_WHITE, C_COLOR_MAGENTA, " [ 按钮 B ] ");
|
||||
|
||||
// 键盘移动小方块的起始坐标状态
|
||||
int k_x = 5, k_y = 15;
|
||||
c_Console_WriteColorXY(k_x, k_y, C_COLOR_BRIGHT_RED, C_COLOR_NONE, "■");
|
||||
|
||||
int running = 1;
|
||||
while (running) {
|
||||
// ----------------------------------------------------
|
||||
// 测试项 A: 键盘输入流测试
|
||||
// ----------------------------------------------------
|
||||
int key = c_Console_ReadKey();
|
||||
if (key != C_KEY_UNKNOWN) {
|
||||
// 清空第 12 行并更新键盘状态
|
||||
printf("\033[12;1H\033[2K");
|
||||
|
||||
if (key == C_KEY_ESC) {
|
||||
running = 0; // 按 Esc 键退出
|
||||
}
|
||||
else if (key == C_KEY_UP || key == C_KEY_DOWN || key == C_KEY_LEFT || key == C_KEY_RIGHT) {
|
||||
// 擦除旧位置
|
||||
c_Console_WriteColorXY(k_x, k_y, C_COLOR_NONE, C_COLOR_NONE, " ");
|
||||
// 响应方向键
|
||||
if (key == C_KEY_UP) k_y--;
|
||||
if (key == C_KEY_DOWN) k_y++;
|
||||
if (key == C_KEY_LEFT) k_x -= 2; // 方块占2个物理宽度
|
||||
if (key == C_KEY_RIGHT) k_x += 2;
|
||||
// 边界约束
|
||||
if (k_x < 1) k_x = 1; if (k_y < 13) k_y = 13;
|
||||
// 绘制新位置
|
||||
c_Console_WriteColorXY(k_x, k_y, C_COLOR_BRIGHT_RED, C_COLOR_NONE, "■");
|
||||
|
||||
c_Console_WriteColorXY(2, 12, C_COLOR_BRIGHT_GREEN, C_COLOR_NONE, "键盘响应: [方向键] 成功触发! 当前方块坐标: (%d, %d)", k_x, k_y);
|
||||
}
|
||||
else if (key == C_KEY_ENTER) {
|
||||
c_Console_WriteColorXY(2, 12, C_COLOR_BRIGHT_GREEN, C_COLOR_NONE, "键盘响应: [Enter 回车键] 成功触发!");
|
||||
}
|
||||
else {
|
||||
c_Console_WriteColorXY(2, 12, C_COLOR_BRIGHT_GREEN, C_COLOR_NONE, "键盘响应: 普通字符键 [%c] ASCII: %d", (char)key, key);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// 测试项 B: 鼠标与滚轮流测试
|
||||
// ----------------------------------------------------
|
||||
c_ConsoleMouseEvent_t mouse_evt;
|
||||
if (c_Console_ReadMouse(&mouse_evt)) {
|
||||
// 在第 18 行实时刷新鼠标动作
|
||||
printf("\033[18;1H\033[2K");
|
||||
|
||||
switch (mouse_evt.type) {
|
||||
case MOUSE_EVENT_PRESS_LEFT:
|
||||
c_Console_WriteColorXY(2, 18, C_COLOR_BRIGHT_YELLOW, C_COLOR_NONE, "鼠标响应: 【左键点击】 坐标: (%d, %d)", mouse_evt.x, mouse_evt.y);
|
||||
|
||||
// 按钮矩形区域碰撞检测
|
||||
if (mouse_evt.y == 10) {
|
||||
if (mouse_evt.x >= 5 && mouse_evt.x <= 15) {
|
||||
c_Console_WriteColorXY(5, 11, C_COLOR_BRIGHT_RED, C_COLOR_NONE, "🔥 触发 A !");
|
||||
} else if (mouse_evt.x >= 20 && mouse_evt.x <= 30) {
|
||||
c_Console_WriteColorXY(20, 11, C_COLOR_BRIGHT_RED, C_COLOR_NONE, "🔥 触发 B !");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOUSE_EVENT_PRESS_RIGHT:
|
||||
c_Console_WriteColorXY(2, 18, C_COLOR_BRIGHT_YELLOW, C_COLOR_NONE, "鼠标响应: 【右键点击】 坐标: (%d, %d)", mouse_evt.x, mouse_evt.y);
|
||||
break;
|
||||
case MOUSE_EVENT_WHEEL_UP:
|
||||
c_Console_WriteColorXY(2, 18, C_COLOR_BRIGHT_BLUE, C_COLOR_NONE, "滚轮响应: 【向上滚动 ↑】 坐标: (%d, %d)", mouse_evt.x, mouse_evt.y);
|
||||
break;
|
||||
case MOUSE_EVENT_WHEEL_DOWN:
|
||||
c_Console_WriteColorXY(2, 18, C_COLOR_BRIGHT_BLUE, C_COLOR_NONE, "滚轮响应: 【向下滚动 ↓】 坐标: (%d, %d)", mouse_evt.x, mouse_evt.y);
|
||||
break;
|
||||
case MOUSE_EVENT_MOVE:
|
||||
// 右下角高频显示当前准心坐标
|
||||
c_Console_WriteColorXY(screen_w - 20, 3, C_COLOR_BRIGHT_BLACK, C_COLOR_NONE, "鼠标悬停: (%03d, %03d)", mouse_evt.x, mouse_evt.y);
|
||||
break;
|
||||
case MOUSE_EVENT_RELEASE:
|
||||
// 释放鼠标时清理按钮下方的触发提示字样
|
||||
printf("\033[11;1H\033[2K");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 控制刷新帧率(约 50 FPS),避免 CPU 满载
|
||||
c_Console_Sleep(20);
|
||||
}
|
||||
|
||||
// 5. 退出处理:清屏并让 atexit 自动恢复终端原始设置
|
||||
printf("\033[2J\033[H");
|
||||
CONSOLE_CLEAR();
|
||||
CONSOLE_SHOW_CURSOR();
|
||||
CONSOLE_COLOR_RESET();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,395 @@
|
||||
#include <c_Fmt.h>
|
||||
#include <c_Memory.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
#define T c_Fmt_t
|
||||
|
||||
struct buf {
|
||||
char *buf;
|
||||
char *bp;
|
||||
int size;
|
||||
};
|
||||
|
||||
#define pad(n,c) do { int nn = (n); \
|
||||
while (nn-- > 0) \
|
||||
put((c), cl); } while (0)
|
||||
|
||||
static void cvt_s(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
char *str = va_arg(box->ap, char *);
|
||||
assert(str);
|
||||
c_Fmt_puts(str, (int)strlen(str), put, cl, flags,
|
||||
width, precision);
|
||||
}
|
||||
|
||||
static void cvt_d(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
int val = va_arg(box->ap, int);
|
||||
unsigned m;
|
||||
char buf[43];
|
||||
char *p = buf + sizeof buf;
|
||||
if (val == INT_MIN)
|
||||
m = INT_MAX + 1U;
|
||||
else if (val < 0)
|
||||
m = -val;
|
||||
else
|
||||
m = val;
|
||||
do
|
||||
*--p = m%10 + '0';
|
||||
while ((m /= 10) > 0);
|
||||
if (val < 0)
|
||||
*--p = '-';
|
||||
c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags,
|
||||
width, precision);
|
||||
}
|
||||
|
||||
static void cvt_u(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
unsigned m = va_arg(box->ap, unsigned);
|
||||
char buf[43];
|
||||
char *p = buf + sizeof buf;
|
||||
do
|
||||
*--p = m%10 + '0';
|
||||
while ((m /= 10) > 0);
|
||||
c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags,
|
||||
width, precision);
|
||||
}
|
||||
|
||||
static void cvt_o(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
unsigned m = va_arg(box->ap, unsigned);
|
||||
char buf[43];
|
||||
char *p = buf + sizeof buf;
|
||||
do
|
||||
*--p = (m&0x7) + '0';
|
||||
while ((m>>= 3) != 0);
|
||||
c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags,
|
||||
width, precision);
|
||||
}
|
||||
|
||||
static void cvt_x(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
unsigned m = va_arg(box->ap, unsigned);
|
||||
char buf[43];
|
||||
char *p = buf + sizeof buf;
|
||||
do
|
||||
*--p = "0123456789abcdef"[m&0xf];
|
||||
while ((m>>= 4) != 0);
|
||||
c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags,
|
||||
width, precision);
|
||||
}
|
||||
|
||||
static void cvt_p(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
c_uintptr_t m = (c_uintptr_t)va_arg(box->ap, void*);
|
||||
char buf[43];
|
||||
char *p = buf + sizeof buf;
|
||||
precision = INT_MIN;
|
||||
do {
|
||||
*--p = "0123456789abcdef"[m&0xf];
|
||||
}while ((m>>= 4) != 0);
|
||||
c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags,
|
||||
width, precision);
|
||||
}
|
||||
|
||||
static void cvt_c(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
if (width == INT_MIN)
|
||||
width = 0;
|
||||
if (width < 0) {
|
||||
flags['-'] = 1;
|
||||
width = -width;
|
||||
}
|
||||
if (!flags['-'])
|
||||
pad(width - 1, ' ');
|
||||
put((unsigned char)va_arg(box->ap, int), cl);
|
||||
if ( flags['-'])
|
||||
pad(width - 1, ' ');
|
||||
}
|
||||
|
||||
static void cvt_f(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
char buf[DBL_MAX_10_EXP+1+1+99+1];
|
||||
if (precision < 0)
|
||||
precision = 6;
|
||||
if (code == 'g' && precision == 0)
|
||||
precision = 1;
|
||||
{
|
||||
static char fmt[] = "%.dd?";
|
||||
assert(precision <= 99);
|
||||
fmt[4] = code;
|
||||
fmt[3] = precision%10 + '0';
|
||||
fmt[2] = (precision/10)%10 + '0';
|
||||
sprintf(buf, fmt, va_arg(box->ap, double));
|
||||
}
|
||||
c_Fmt_putd(buf, strlen(buf), put, cl, flags,
|
||||
width, precision);
|
||||
}
|
||||
|
||||
static T cvt[256] = {
|
||||
/* 0- 7 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 8- 15 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 16- 23 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 24- 31 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 32- 39 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 40- 47 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 48- 55 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 56- 63 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 64- 71 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 72- 79 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 80- 87 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 88- 95 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 96-103 */ 0, 0, 0, cvt_c, cvt_d, cvt_f, cvt_f, cvt_f,
|
||||
/* 104-111 */ 0, 0, 0, 0, 0, 0, 0, cvt_o,
|
||||
/* 112-119 */ cvt_p, 0, 0, cvt_s, 0, cvt_u, 0, 0,
|
||||
/* 120-127 */ cvt_x, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static char *c_Fmt_flags = "-+ 0";
|
||||
|
||||
static int outc(int c, void *cl) {
|
||||
FILE *f = cl;
|
||||
return putc(c, f);
|
||||
}
|
||||
|
||||
static int insert(int c, void *cl) {
|
||||
struct buf *p = cl;
|
||||
assert (p->bp < (p->buf + p->size));
|
||||
*p->bp++ = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
static int append(int c, void *cl) {
|
||||
struct buf *p = cl;
|
||||
if (p->bp >= p->buf + p->size) {
|
||||
C_RESIZE(p->buf, 2*p->size);
|
||||
p->bp = p->buf + p->size;
|
||||
p->size *= 2;
|
||||
}
|
||||
*p->bp++ = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
void c_Fmt_puts(const char *str, int len,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
assert(str);
|
||||
assert(len >= 0);
|
||||
assert(flags);
|
||||
if (width == INT_MIN)
|
||||
width = 0;
|
||||
if (width < 0) {
|
||||
flags['-'] = 1;
|
||||
width = -width;
|
||||
}
|
||||
if (precision >= 0)
|
||||
flags['0'] = 0;
|
||||
if (precision >= 0 && precision < len)
|
||||
len = precision;
|
||||
if (!flags['-'])
|
||||
pad(width - len, ' ');
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++)
|
||||
put((unsigned char)*str++, cl);
|
||||
}
|
||||
if ( flags['-'])
|
||||
pad(width - len, ' ');
|
||||
}
|
||||
|
||||
void c_Fmt_fmt(int put(int c, void *), void *cl,
|
||||
const char *fmt, ...) {
|
||||
va_list_box box;
|
||||
va_start(box.ap, fmt);
|
||||
c_Fmt_vfmt(put, cl, fmt, &box);
|
||||
va_end(box.ap);
|
||||
}
|
||||
|
||||
void c_Fmt_print(const char *fmt, ...) {
|
||||
va_list_box box;
|
||||
va_start(box.ap, fmt);
|
||||
c_Fmt_vfmt(outc, stdout, fmt, &box);
|
||||
va_end(box.ap);
|
||||
}
|
||||
void c_Fmt_fprint(FILE *stream, const char *fmt, ...) {
|
||||
va_list_box box;
|
||||
va_start(box.ap, fmt);
|
||||
c_Fmt_vfmt(outc, stream, fmt, &box);
|
||||
va_end(box.ap);
|
||||
}
|
||||
|
||||
int c_Fmt_sfmt(char *buf, int size, const char *fmt, ...) {
|
||||
int len;
|
||||
va_list_box box;
|
||||
va_start(box.ap, fmt);
|
||||
len = c_Fmt_vsfmt(buf, size, fmt, &box);
|
||||
va_end(box.ap);
|
||||
return len;
|
||||
}
|
||||
int c_Fmt_vsfmt(char *buf, int size, const char *fmt,
|
||||
va_list_box *box) {
|
||||
struct buf cl;
|
||||
assert(buf);
|
||||
assert(size > 0);
|
||||
assert(fmt);
|
||||
cl.buf = cl.bp = buf;
|
||||
cl.size = size;
|
||||
c_Fmt_vfmt(insert, &cl, fmt, box);
|
||||
insert(0, &cl);
|
||||
return cl.bp - cl.buf - 1;
|
||||
}
|
||||
|
||||
char *c_Fmt_string(const char *fmt, ...) {
|
||||
char *str;
|
||||
va_list_box box;
|
||||
assert(fmt);
|
||||
va_start(box.ap, fmt);
|
||||
str =c_Fmt_vstring(fmt, &box);
|
||||
va_end(box.ap);
|
||||
return str;
|
||||
}
|
||||
char *c_Fmt_vstring(const char *fmt, va_list_box *box) {
|
||||
struct buf cl;
|
||||
assert(fmt);
|
||||
cl.size = 256;
|
||||
cl.buf = cl.bp = C_ALLOC(cl.size);
|
||||
c_Fmt_vfmt(append, &cl, fmt, box);
|
||||
append(0, &cl);
|
||||
return C_RESIZE(cl.buf, cl.bp - cl.buf);
|
||||
}
|
||||
|
||||
void c_Fmt_vfmt(int put(int c, void *cl), void *cl,
|
||||
const char *fmt, va_list_box *box) {
|
||||
assert(put);
|
||||
assert(fmt);
|
||||
while (*fmt)
|
||||
if (*fmt != '%' || *++fmt == '%')
|
||||
put((unsigned char)*fmt++, cl);
|
||||
else
|
||||
{
|
||||
unsigned char c, flags[256];
|
||||
int width = INT_MIN, precision = INT_MIN;
|
||||
memset(flags, '\0', sizeof flags);
|
||||
if (c_Fmt_flags) {
|
||||
unsigned char c = *fmt;
|
||||
for ( ; c && strchr(c_Fmt_flags, c); c = *++fmt) {
|
||||
assert(flags[c] < 255);
|
||||
flags[c]++;
|
||||
}
|
||||
}
|
||||
if (*fmt == '*' || isdigit(*fmt)) {
|
||||
int n;
|
||||
if (*fmt == '*') {
|
||||
n = va_arg(box->ap, int);
|
||||
assert(n != INT_MIN);
|
||||
fmt++;
|
||||
} else
|
||||
for (n = 0; isdigit(*fmt); fmt++) {
|
||||
int d = *fmt - '0';
|
||||
assert(n <= (INT_MAX - d)/10);
|
||||
n = 10*n + d;
|
||||
}
|
||||
width = n;
|
||||
}
|
||||
if (*fmt == '.' && (*++fmt == '*' || isdigit(*fmt))) {
|
||||
int n;
|
||||
if (*fmt == '*') {
|
||||
n = va_arg(box->ap, int);
|
||||
assert(n != INT_MIN);
|
||||
fmt++;
|
||||
} else
|
||||
for (n = 0; isdigit(*fmt); fmt++) {
|
||||
int d = *fmt - '0';
|
||||
assert(n <= (INT_MAX - d)/10);
|
||||
n = 10*n + d;
|
||||
}
|
||||
precision = n;
|
||||
}
|
||||
c = *fmt++;
|
||||
assert(cvt[c]);
|
||||
(*cvt[c])(c, box, put, cl, flags, width, precision);
|
||||
}
|
||||
}
|
||||
|
||||
T c_Fmt_register(int code, T newcvt) {
|
||||
T old;
|
||||
assert(0 < code
|
||||
&& code < (int)(sizeof (cvt)/sizeof (cvt[0])));
|
||||
old = cvt[code];
|
||||
cvt[code] = newcvt;
|
||||
return old;
|
||||
}
|
||||
|
||||
void c_Fmt_putd(const char *str, int len,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
int sign;
|
||||
assert(str);
|
||||
assert(len >= 0);
|
||||
assert(flags);
|
||||
if (width == INT_MIN)
|
||||
width = 0;
|
||||
if (width < 0) {
|
||||
flags['-'] = 1;
|
||||
width = -width;
|
||||
}
|
||||
if (precision >= 0)
|
||||
flags['0'] = 0;
|
||||
if (len > 0 && (*str == '-' || *str == '+')) {
|
||||
sign = *str++;
|
||||
len--;
|
||||
} else if (flags['+'])
|
||||
sign = '+';
|
||||
else if (flags[' '])
|
||||
sign = ' ';
|
||||
else
|
||||
sign = 0;
|
||||
{ int n;
|
||||
if (precision < 0)
|
||||
precision = 1;
|
||||
if (len < precision)
|
||||
n = precision;
|
||||
else if (precision == 0 && len == 1 && str[0] == '0')
|
||||
n = 0;
|
||||
else
|
||||
n = len;
|
||||
if (sign)
|
||||
n++;
|
||||
if (flags['-']) {
|
||||
if (sign)
|
||||
put(sign, cl);
|
||||
} else if (flags['0']) {
|
||||
if (sign)
|
||||
put(sign, cl);
|
||||
pad(width - n, '0');
|
||||
} else {
|
||||
pad(width - n, ' ');
|
||||
if (sign)
|
||||
put(sign, cl);
|
||||
}
|
||||
pad(precision - len, '0');
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++)
|
||||
put((unsigned char)*str++, cl);
|
||||
}
|
||||
if (flags['-'])
|
||||
pad(width - n, ' '); }
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef INCLUDED_C_FMT_H
|
||||
#define INCLUDED_C_FMT_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_STDIO_H
|
||||
#define INCLUDED_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif /*INCLUDED_STDIO_H*/
|
||||
|
||||
#ifndef INCLUDED_STDARG_H
|
||||
#define INCLUDED_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif /*INCLUDED_STDARG_H*/
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct va_list_box {
|
||||
va_list ap;
|
||||
} va_list_box;
|
||||
|
||||
#ifdef T
|
||||
#undef T
|
||||
#endif
|
||||
|
||||
#define T c_Fmt_t
|
||||
|
||||
typedef void (*T)(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[256], int width, int precision);
|
||||
|
||||
extern void c_Fmt_fmt (int put(int c, void *cl), void *cl,
|
||||
const char *fmt, ...);
|
||||
|
||||
extern void c_Fmt_vfmt(int put(int c, void *cl), void *cl,
|
||||
const char *fmt, va_list_box *box);
|
||||
|
||||
extern void c_Fmt_print (const char *fmt, ...);
|
||||
|
||||
extern void c_Fmt_fprint(FILE *stream,
|
||||
const char *fmt, ...);
|
||||
|
||||
extern int c_Fmt_sfmt (char *buf, int size,
|
||||
const char *fmt, ...);
|
||||
|
||||
extern int c_Fmt_vsfmt(char *buf, int size,
|
||||
const char *fmt, va_list_box *box);
|
||||
|
||||
extern char *c_Fmt_string (const char *fmt, ...);
|
||||
|
||||
extern char *c_Fmt_vstring(const char *fmt, va_list_box *box);
|
||||
|
||||
extern T c_Fmt_register(int code, T cvt);
|
||||
|
||||
extern void c_Fmt_putd(const char *str, int len,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[256], int width, int precision);
|
||||
|
||||
extern void c_Fmt_puts(const char *str, int len,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[256], int width, int precision);
|
||||
|
||||
#undef T
|
||||
|
||||
#endif /*INCLUDED_C_FMT_H*/
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <c_Mutex.h>
|
||||
|
||||
c_err_t c_Mutex_Init(c_Mutex_t* mutex) {
|
||||
if (!mutex) return C_ERR_FAIL;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
// InitializeCriticalSection 不会失败,但 InitializeCriticalSectionAndSpinCount 可能会
|
||||
// 工业级推荐直接使用此 API,性能优秀
|
||||
InitializeCriticalSection(&mutex->handle);
|
||||
mutex->is_initialized = C_TRUE;
|
||||
return C_ERR_OK;
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
// POSIX 默认是 PTHREAD_MUTEX_DEFAULT (不可重入锁)
|
||||
if (pthread_mutex_init(&mutex->handle, NULL) == 0) {
|
||||
mutex->is_initialized = C_TRUE;
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
return C_ERR_FAIL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void c_Mutex_Destroy(c_Mutex_t* mutex) {
|
||||
if (!mutex || !mutex->is_initialized) return;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
DeleteCriticalSection(&mutex->handle);
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
pthread_mutex_destroy(&mutex->handle);
|
||||
#endif
|
||||
mutex->is_initialized = C_FALSE;
|
||||
}
|
||||
|
||||
|
||||
void c_Mutex_Lock(c_Mutex_t* mutex) {
|
||||
if (!mutex || !mutex->is_initialized) return;
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
EnterCriticalSection(&mutex->handle);
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
pthread_mutex_lock(&mutex->handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
c_bool_t c_Mutex_TryLock(c_Mutex_t* mutex) {
|
||||
if (!mutex || !mutex->is_initialized) return C_FALSE;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
// TryEnterCriticalSection 返回非 0 表示成功
|
||||
return (TryEnterCriticalSection(&mutex->handle) != 0)?C_TRUE:C_FALSE;
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
// pthread_mutex_trylock 返回 0 表示成功
|
||||
return (pthread_mutex_trylock(&mutex->handle) == 0)?C_TRUE:C_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void c_Mutex_UnLock(c_Mutex_t* mutex) {
|
||||
if (!mutex || !mutex->is_initialized) return;
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
LeaveCriticalSection(&mutex->handle);
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
pthread_mutex_unlock(&mutex->handle);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef INCLUDED_C_MUTEX_H
|
||||
#define INCLUDED_C_MUTEX_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define PLATFORM_WINDOWS 1
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define PLATFORM_POSIX 1
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
CRITICAL_SECTION handle;
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
pthread_mutex_t handle;
|
||||
#endif
|
||||
c_bool_t is_initialized;
|
||||
} c_Mutex_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_Mutex_Init(c_Mutex_t* mutex);
|
||||
void c_Mutex_Destroy(c_Mutex_t* mutex);
|
||||
void c_Mutex_Lock(c_Mutex_t* mutex);
|
||||
c_bool_t c_Mutex_TryLock(c_Mutex_t* mutex);
|
||||
void c_Mutex_UnLock(c_Mutex_t* mutex);
|
||||
|
||||
#endif /*INCLUDED_C_MUTEX_H*/
|
||||
@@ -0,0 +1,372 @@
|
||||
#include <c_RBTree.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
#define RB_NODE_DATA(node) ((void*)((char*)(node) + sizeof(c_RBNode_t)))
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
// ==================================================================================================================
|
||||
// 1. 基石级无哨兵旋转 (Rotations)
|
||||
// ==================================================================================================================
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBTree_LeftRotate(c_RBTree_t* self, c_RBNode_t* x) {
|
||||
c_RBNode_t* y = x->right;
|
||||
x->right = y->left;
|
||||
if (y->left != NULL) {
|
||||
y->left->parent = x;
|
||||
}
|
||||
y->parent = x->parent;
|
||||
if (x->parent == NULL) {
|
||||
self->root = y;
|
||||
} else if (x == x->parent->left) {
|
||||
x->parent->left = y;
|
||||
} else {
|
||||
x->parent->right = y;
|
||||
}
|
||||
y->left = x;
|
||||
x->parent = y;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBTree_RightRotate(c_RBTree_t* self, c_RBNode_t* y) {
|
||||
c_RBNode_t* x = y->left;
|
||||
y->left = x->right;
|
||||
if (x->right != NULL) {
|
||||
x->right->parent = y;
|
||||
}
|
||||
x->parent = y->parent;
|
||||
if (y->parent == NULL) {
|
||||
self->root = x;
|
||||
} else if (y == y->parent->right) {
|
||||
y->parent->right = x;
|
||||
} else {
|
||||
y->parent->left = x;
|
||||
}
|
||||
x->right = y;
|
||||
y->parent = x;
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 2. 无哨兵插入自修复 (Insert Fixup)
|
||||
// ==================================================================================================================
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBTree_InsertFixup(c_RBTree_t* self, c_RBNode_t* z) {
|
||||
// 只有当 z 不是根,且其父亲是红色时(红红冲突)才需要修复
|
||||
while (z != self->root && c_RBTree_GetColor(z->parent) == C_RB_RED) {
|
||||
if (z->parent == z->parent->parent->left) {
|
||||
c_RBNode_t* y = z->parent->parent->right; // 叔叔节点(可能为 NULL)
|
||||
|
||||
if (c_RBTree_GetColor(y) == C_RB_RED) {
|
||||
// 情况 1: 叔叔是红的 -> 变色,指针上移
|
||||
c_RBTree_SetColor(z->parent, C_RB_BLACK);
|
||||
c_RBTree_SetColor(y, C_RB_BLACK);
|
||||
c_RBTree_SetColor(z->parent->parent, C_RB_RED);
|
||||
z = z->parent->parent;
|
||||
} else {
|
||||
// 情况 2: 叔叔是黑的,且 z 是右孩子 -> 先左旋转化为情况 3
|
||||
if (z == z->parent->right) {
|
||||
z = z->parent;
|
||||
c_RBTree_LeftRotate(self, z);
|
||||
}
|
||||
// 情况 3: 叔叔是黑的,且 z 是左孩子 -> 变色并右旋
|
||||
c_RBTree_SetColor(z->parent, C_RB_BLACK);
|
||||
c_RBTree_SetColor(z->parent->parent, C_RB_RED);
|
||||
c_RBTree_RightRotate(self, z->parent->parent);
|
||||
}
|
||||
} else {
|
||||
// 镜像对称对称分支
|
||||
c_RBNode_t* y = z->parent->parent->left;
|
||||
if (c_RBTree_GetColor(y) == C_RB_RED) {
|
||||
c_RBTree_SetColor(z->parent, C_RB_BLACK);
|
||||
c_RBTree_SetColor(y, C_RB_BLACK);
|
||||
c_RBTree_SetColor(z->parent->parent, C_RB_RED);
|
||||
z = z->parent->parent;
|
||||
} else {
|
||||
if (z == z->parent->left) {
|
||||
z = z->parent;
|
||||
c_RBTree_RightRotate(self, z);
|
||||
}
|
||||
c_RBTree_SetColor(z->parent, C_RB_BLACK);
|
||||
c_RBTree_SetColor(z->parent->parent, C_RB_RED);
|
||||
c_RBTree_LeftRotate(self, z->parent->parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
c_RBTree_SetColor(self->root, C_RB_BLACK); // 根节点雷打不动强制为黑
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 【二级指针艺术】:将 u 子树用 v 子树就地顶替
|
||||
* @note 完美合并了“修改父节点孩子指针”与“修改根节点 self->root”的两套繁琐逻辑
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBTree_Transplant(c_RBTree_t* self, c_RBNode_t* u, c_RBNode_t* v) {
|
||||
c_RBNode_t** pp = (u->parent == NULL) ? &(self->root) : ((u == u->parent->left) ? &(u->parent->left) : &(u->parent->right));
|
||||
*pp = v; // 一行代码,顺着物理内存地址直接擦除并覆写
|
||||
if (v != NULL) {
|
||||
v->parent = u->parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 后序遍历释放树中所有节点的私有递归函数
|
||||
*/
|
||||
static void c_RBTree_ClearInternal(c_RBTree_t* self, c_RBNode_t* node) {
|
||||
if (node == NULL) return;
|
||||
|
||||
// 采用后序遍历(Post-order),自下而上打包火化,防止提前斩断前驱后驱通路
|
||||
c_RBTree_ClearInternal(self, node->left);
|
||||
c_RBTree_ClearInternal(self, node->right);
|
||||
|
||||
// 闭环通过内置的多态分配器,物理回收合并变长连续空间
|
||||
c_Allocator_Free(&self->allocator, node);
|
||||
}
|
||||
|
||||
|
||||
// ==================================================================================================================
|
||||
// 3. 终极奥义:基于二级指针引用的无哨兵删除自修复 (Remove & Fixup)
|
||||
// ==================================================================================================================
|
||||
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBTree_RemoveFixup(c_RBTree_t* self, c_RBNode_t* x, c_RBNode_t* x_parent) {
|
||||
// 由于 x 可能为 NULL(此时表示黑色叶子外部边缘),我们需要依靠显式传递的 x_parent 来逆向感知拓扑
|
||||
while (x != self->root && c_RBTree_GetColor(x) == C_RB_BLACK) {
|
||||
if (x == x_parent->left || (x == NULL && x_parent->left == NULL)) {
|
||||
c_RBNode_t* w = x_parent->right; // 兄弟节点
|
||||
|
||||
if (c_RBTree_GetColor(w) == C_RB_RED) {
|
||||
// 情况 1: 兄弟是红的
|
||||
c_RBTree_SetColor(w, C_RB_BLACK);
|
||||
c_RBTree_SetColor(x_parent, C_RB_RED);
|
||||
c_RBTree_LeftRotate(self, x_parent);
|
||||
w = x_parent->right;
|
||||
}
|
||||
|
||||
if (w != NULL && c_RBTree_GetColor(w->left) == C_RB_BLACK && c_RBTree_GetColor(w->right) == C_RB_BLACK) {
|
||||
// 情况 2: 兄弟的孩子全为黑
|
||||
c_RBTree_SetColor(w, C_RB_RED);
|
||||
x = x_parent;
|
||||
x_parent = x->parent; // 双指针同步上滑
|
||||
} else {
|
||||
// 情况 3: 兄弟的右孩子是黑色
|
||||
if (w != NULL && c_RBTree_GetColor(w->right) == C_RB_BLACK) {
|
||||
c_RBTree_SetColor(w->left, C_RB_BLACK);
|
||||
c_RBTree_SetColor(w, C_RB_RED);
|
||||
c_RBTree_RightRotate(self, w);
|
||||
w = x_parent->right;
|
||||
}
|
||||
// 情况 4: 复杂的终极平衡变色旋转
|
||||
if (w != NULL) {
|
||||
c_RBTree_SetColor(w, c_RBTree_GetColor(x_parent));
|
||||
c_RBTree_SetColor(w->right, C_RB_BLACK);
|
||||
}
|
||||
c_RBTree_SetColor(x_parent, C_RB_BLACK);
|
||||
c_RBTree_LeftRotate(self, x_parent);
|
||||
x = self->root; // 强行收敛跳出
|
||||
}
|
||||
} else {
|
||||
// 镜像对称对称分支
|
||||
c_RBNode_t* w = x_parent->left;
|
||||
if (c_RBTree_GetColor(w) == C_RB_RED) {
|
||||
c_RBTree_SetColor(w, C_RB_BLACK);
|
||||
c_RBTree_SetColor(x_parent, C_RB_RED);
|
||||
c_RBTree_RightRotate(self, x_parent);
|
||||
w = x_parent->left;
|
||||
}
|
||||
if (w != NULL && c_RBTree_GetColor(w->right) == C_RB_BLACK && c_RBTree_GetColor(w->left) == C_RB_BLACK) {
|
||||
c_RBTree_SetColor(w, C_RB_RED);
|
||||
x = x_parent;
|
||||
x_parent = x->parent;
|
||||
} else {
|
||||
if (w != NULL && c_RBTree_GetColor(w->left) == C_RB_BLACK) {
|
||||
c_RBTree_SetColor(w->right, C_RB_BLACK);
|
||||
c_RBTree_SetColor(w, C_RB_RED);
|
||||
c_RBTree_LeftRotate(self, w);
|
||||
w = x_parent->left;
|
||||
}
|
||||
if (w != NULL) {
|
||||
c_RBTree_SetColor(w, c_RBTree_GetColor(x_parent));
|
||||
c_RBTree_SetColor(w->left, C_RB_BLACK);
|
||||
}
|
||||
c_RBTree_SetColor(x_parent, C_RB_BLACK);
|
||||
c_RBTree_RightRotate(self, x_parent);
|
||||
x = self->root;
|
||||
}
|
||||
}
|
||||
}
|
||||
c_RBTree_SetColor(x, C_RB_BLACK);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
|
||||
// 原地初始化:控制头全清零,root 严格初始化为 NULL (0)
|
||||
c_err_t c_RBTree_Init(c_RBTree_t* self, c_size_t item_size, c_RBTree_CompareFn_t compare_fn, c_Allocator_t* allocator) {
|
||||
if (!self || item_size == 0 || !compare_fn) return C_ERR_PARAM;
|
||||
|
||||
self->allocator = (allocator != NULL) ? *allocator : c_DefaultAllocator;
|
||||
self->item_size = item_size;
|
||||
self->compare = compare_fn;
|
||||
self->size = 0;
|
||||
self->root = NULL; // 零开销初始空态
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
c_err_t c_RBTree_Insert(c_RBTree_t* self, const void* item) {
|
||||
if (!self || !item) return C_ERR_PARAM;
|
||||
|
||||
c_RBNode_t* y = NULL;
|
||||
c_RBNode_t* x = self->root;
|
||||
|
||||
while (x != NULL) {
|
||||
y = x;
|
||||
int cmp = self->compare(item, RB_NODE_DATA(x));
|
||||
if (cmp < 0) x = x->left;
|
||||
else if (cmp > 0) x = x->right;
|
||||
else return C_ERR_PARAM; // 去重拒绝
|
||||
}
|
||||
|
||||
// 分配变长一体化连续空间
|
||||
c_size_t total_bytes = sizeof(c_RBNode_t) + self->item_size;
|
||||
c_RBNode_t* z = (c_RBNode_t*)c_Allocator_Alloc(&self->allocator, total_bytes);
|
||||
if (!z) return C_ERR_NOMEM;
|
||||
|
||||
z->parent = y;
|
||||
z->left = NULL;
|
||||
z->right = NULL;
|
||||
z->color = C_RB_RED; // 新插入节点必为红色
|
||||
memcpy(RB_NODE_DATA(z), item, self->item_size);
|
||||
|
||||
if (y == NULL) {
|
||||
self->root = z;
|
||||
} else if (self->compare(RB_NODE_DATA(z), RB_NODE_DATA(y)) < 0) {
|
||||
y->left = z;
|
||||
} else {
|
||||
y->right = z;
|
||||
}
|
||||
|
||||
c_RBTree_InsertFixup(self, z);
|
||||
self->size++;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
c_err_t c_RBTree_Remove(c_RBTree_t* self, const void* key) {
|
||||
if (!self || !key || self->size == 0) return C_ERR_PARAM;
|
||||
|
||||
c_RBNode_t* z = self->root;
|
||||
while (z != NULL) {
|
||||
int cmp = self->compare(key, RB_NODE_DATA(z));
|
||||
if (cmp < 0) z = z->left;
|
||||
else if (cmp > 0) z = z->right;
|
||||
else break;
|
||||
}
|
||||
|
||||
if (z == NULL) return C_ERR_NOTFOUND; // 节点不存在
|
||||
|
||||
c_RBNode_t* x;
|
||||
c_RBNode_t* x_parent; // 【防跑飞核心】:显式声明局部变量缓存亲代变量
|
||||
c_RBNode_t* y = z;
|
||||
c_RBColor_t y_original_color = y->color;
|
||||
|
||||
if (z->left == NULL) {
|
||||
x = z->right;
|
||||
x_parent = z->parent;
|
||||
c_RBTree_Transplant(self, z, z->right);
|
||||
} else if (z->right == NULL) {
|
||||
x = z->left;
|
||||
x_parent = z->parent;
|
||||
c_RBTree_Transplant(self, z, z->left);
|
||||
} else {
|
||||
// 寻找右子树的最小后继
|
||||
y = z->right;
|
||||
while (y->left != NULL) {
|
||||
y = y->left;
|
||||
}
|
||||
y_original_color = y->color;
|
||||
x = y->right;
|
||||
|
||||
if (y->parent == z) {
|
||||
x_parent = y; // 后继直接在下面,新父亲变为后继本身
|
||||
} else {
|
||||
x_parent = y->parent;
|
||||
c_RBTree_Transplant(self, y, y->right);
|
||||
y->right = z->right;
|
||||
if (y->right != NULL) y->right->parent = y;
|
||||
}
|
||||
c_RBTree_Transplant(self, z, y);
|
||||
y->left = z->left;
|
||||
if (y->left != NULL) y->left->parent = y;
|
||||
y->color = z->color;
|
||||
}
|
||||
|
||||
// 物理熔断销毁控制外壳
|
||||
c_Allocator_Free(&self->allocator, z);
|
||||
|
||||
// 如果剥离的底色是黑色,黑高天平倾斜,驱动自平衡修复
|
||||
if (y_original_color == C_RB_BLACK) {
|
||||
c_RBTree_RemoveFixup(self, x, x_parent);
|
||||
}
|
||||
|
||||
self->size--;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
bool c_RBTree_Contains(const c_RBTree_t* self, const void* key) {
|
||||
if (!self || !key) return false;
|
||||
c_RBNode_t* x = self->root;
|
||||
while (x != NULL) {
|
||||
int cmp = self->compare(key, RB_NODE_DATA(x));
|
||||
if (cmp < 0) x = x->left;
|
||||
else if (cmp > 0) x = x->right;
|
||||
else return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void c_RBTree_Clear(c_RBTree_t* self) {
|
||||
// 强御级边界守卫:拦截一切非法或已经是空态的呼叫
|
||||
if (!self || self->root == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 驱动自下而上的递归解体火化流
|
||||
c_RBTree_ClearInternal(self, self->root);
|
||||
|
||||
// 状态完全与死节点脱钩,重新滑移复位到你指定的 0 纯净空树状态
|
||||
self->root = NULL;
|
||||
self->size = 0; // 账目同步清零
|
||||
}
|
||||
|
||||
void c_RBTree_Destroy(c_RBTree_t* self) {
|
||||
// 強御級防禦:若控制頭指標本身為空,或者這棵樹已經是純淨的 0 態空樹,則直接冪等返回
|
||||
if (!self || self->root == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 呼叫核心 Clear 接口:自下而上執行後序(Post-order)遞迴火化
|
||||
// 內部的 c_Allocator_Free 會將所有變長節點資源完整退還給綁定的專屬記憶體池
|
||||
c_RBTree_Clear(self);
|
||||
|
||||
// 2. 徹底清空控制頭中的核心狀態,將生命週期變數完美還原至 0
|
||||
self->root = NULL;
|
||||
self->size = 0;
|
||||
self->item_size = 0;
|
||||
self->compare = NULL;
|
||||
|
||||
// 注意:self->allocator 控制頭本身是由呼叫者在外部(例如棧或宿主結構體)宣告的物理記憶體,
|
||||
// 其銷毀工作(如 c_Allocator_Destroy)應遵循職責隔離規範,由外部的宿主模組繼續向下推動。
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifndef INCLUDED_C_RBTREE_H
|
||||
#define INCLUDED_C_RBTREE_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// 红黑树颜色枚举
|
||||
typedef enum {
|
||||
C_RB_RED = 0,
|
||||
C_RB_BLACK = 1
|
||||
} c_RBColor_t;
|
||||
|
||||
// 泛型值复制红黑树节点控制头
|
||||
typedef struct c_RBNode_t {
|
||||
struct c_RBNode_t* parent;
|
||||
struct c_RBNode_t* left;
|
||||
struct c_RBNode_t* right;
|
||||
c_RBColor_t color;
|
||||
// 后面会紧跟一整块大小为 item_size 的物理连续内存块,直接存放真实数据值
|
||||
} c_RBNode_t;
|
||||
|
||||
typedef int (*c_RBTree_CompareFn_t)(const void* a, const void* b);
|
||||
|
||||
typedef struct {
|
||||
c_RBNode_t* root; // 树的根节点
|
||||
c_size_t item_size; // 单个元素的字节大小
|
||||
c_size_t size; // 当前树内持有的有效节点个数
|
||||
c_RBTree_CompareFn_t compare; // 关联的具体类型比较算子
|
||||
c_Allocator_t allocator; // 内部绑定的自主内存管理器
|
||||
} c_RBTree_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_RBTree_Init(c_RBTree_t* self, c_size_t item_size, c_RBTree_CompareFn_t compare_fn, c_Allocator_t* allocator);
|
||||
void c_RBTree_Destroy(c_RBTree_t* self);
|
||||
|
||||
c_err_t c_RBTree_Insert(c_RBTree_t* self, const void* item);
|
||||
c_err_t c_RBTree_Remove(c_RBTree_t* self, const void* key);
|
||||
bool c_RBTree_Contains(const c_RBTree_t* self, const void* key);
|
||||
void c_RBTree_Clear(c_RBTree_t* self);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_size_t c_RBTree_Size(const c_RBTree_t* self) {
|
||||
if (!self) return 0;
|
||||
return self->size; // 直接返回内置计数器
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_RBColor_t c_RBTree_GetColor(const c_RBNode_t* node) {
|
||||
// 根据红黑树标准性质:所有外部叶子空节点(NULL)物理上皆默认为黑色
|
||||
return (node == NULL) ? C_RB_BLACK : node->color;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBTree_SetColor(c_RBNode_t* node, c_RBColor_t color) {
|
||||
if (node != NULL) {
|
||||
node->color = color; // 只有非空节点才允许修改颜色状态
|
||||
}
|
||||
}
|
||||
|
||||
#endif /*INCLUDED_C_RBTREE_H*/
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "c_RBTree.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "c_Test.h"
|
||||
|
||||
|
||||
// 专属的具体类型整型比较算子回调实现
|
||||
static int int_compare_operator(const void* a, const void* b) {
|
||||
return *(const int*)a - *(const int*)b;
|
||||
}
|
||||
|
||||
// 辅助函数:通过递归计算树的黑高(Black Height)并验证红黑树的关键平衡性质
|
||||
static int verify_rb_properties(const c_RBNode_t* node, int* error_flag) {
|
||||
if (node == NULL) {
|
||||
return 1; // 空节点(叶子)默认为黑色,黑高贡献为 1
|
||||
}
|
||||
|
||||
// 性质 4 校验:如果一个节点是红色的,则它的两个子节点必须是黑色的(红红不能相连)
|
||||
if (c_RBTree_GetColor(node) == C_RB_RED) {
|
||||
if (c_RBTree_GetColor(node->left) == C_RB_RED || c_RBTree_GetColor(node->right) == C_RB_RED) {
|
||||
*error_flag = 1; // 违反红黑树性质
|
||||
}
|
||||
}
|
||||
|
||||
// 递归计算左右子树的黑高
|
||||
int left_black_height = verify_rb_properties(node->left, error_flag);
|
||||
int right_black_height = verify_rb_properties(node->right, error_flag);
|
||||
|
||||
// 性质 5 校验:从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点(黑高必须绝对相等)
|
||||
if (left_black_height != right_black_height) {
|
||||
*error_flag = 2; // 违反黑高平衡性质
|
||||
}
|
||||
|
||||
// 返回当前节点的总黑高
|
||||
return left_black_height + (c_RBTree_GetColor(node) == C_RB_BLACK ? 1 : 0);
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 核心测试用例
|
||||
// ==================================================================================================================
|
||||
|
||||
#define ASSERT_FALSE(condition) \
|
||||
ASSERT_TRUE(!condition)
|
||||
|
||||
TEST_CASE(test_naked_rb_tree_ultimate_closure) {
|
||||
c_RBTree_t tree;
|
||||
|
||||
// 1. 初始化验证:必须满足极致零堆开销契约
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RBTree_Init(&tree, sizeof(int), int_compare_operator, NULL));
|
||||
ASSERT_INT_EQ(0, (int)c_RBTree_Size(&tree));
|
||||
ASSERT_TRUE(tree.root == NULL); // 空树状态下,根指针变量的值严格为 0 (NULL)
|
||||
|
||||
// 2. 【高密度顺序与交叉插入】:强迫其内部连续触发无哨兵旋转、亲代重组与颜色翻转
|
||||
// 故意采用会引发严重倾斜的序列,测试自平衡状态机的抗压极限
|
||||
int dataset[] = {40, 20, 60, 10, 30, 50, 70, 5, 15, 25, 35};
|
||||
for (int i = 0; i < 11; i++) {
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RBTree_Insert(&tree, &dataset[i]));
|
||||
}
|
||||
|
||||
// 实时读取 Size 契约验证
|
||||
ASSERT_INT_EQ(11, (int)c_RBTree_Size(&tree));
|
||||
|
||||
// 3. 验证查重机制与包含性检测 (Contains)
|
||||
int duplicate = 30;
|
||||
int fake_key = 999;
|
||||
ASSERT_INT_EQ(C_ERR_PARAM, c_RBTree_Insert(&tree, &duplicate)); // 重复键必须被去重拦截墙强力回绝
|
||||
ASSERT_TRUE(c_RBTree_Contains(&tree, &duplicate));
|
||||
ASSERT_FALSE(c_RBTree_Contains(&tree, &fake_key));
|
||||
|
||||
// 4. 数学及拓扑性质动态审计
|
||||
int balance_error_flag = 0;
|
||||
verify_rb_properties(tree.root, &balance_error_flag);
|
||||
ASSERT_INT_EQ_MSG(0, balance_error_flag, "Red-Black Tree invariant properties violated after insertions!");
|
||||
ASSERT_INT_EQ(C_RB_BLACK, c_RBTree_GetColor(tree.root)); // 性质 2:根节点必须保持绝对黑色
|
||||
|
||||
// 5. 【无哨兵极限删除考验】:连续定点抹杀节点资源,触发所有最复杂的黑高调整分支
|
||||
int kill_leaf = 35; // 分支 A: 移除一个普通的叶子项
|
||||
int kill_single_child = 5; // 分支 B: 移除一个带单子女的节点
|
||||
int kill_double_child = 20; // 分支 C: 终极考验,移除一个同时带有左右复杂子树的核心对称交叉点
|
||||
|
||||
// 定点抹杀第一击
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RBTree_Remove(&tree, &kill_leaf));
|
||||
ASSERT_INT_EQ(10, (int)c_RBTree_Size(&tree));
|
||||
ASSERT_FALSE(c_RBTree_Contains(&tree, &kill_leaf));
|
||||
|
||||
// 定点抹杀第二击
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RBTree_Remove(&tree, &kill_single_child));
|
||||
ASSERT_INT_EQ(9, (int)c_RBTree_Size(&tree));
|
||||
ASSERT_FALSE(c_RBTree_Contains(&tree, &kill_single_child));
|
||||
|
||||
// 定点抹杀第三击(双子树迁移)
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_RBTree_Remove(&tree, &kill_double_child));
|
||||
ASSERT_INT_EQ(8, (int)c_RBTree_Size(&tree));
|
||||
ASSERT_FALSE(c_RBTree_Contains(&tree, &kill_double_child));
|
||||
|
||||
// 6. 删除后的黑高平衡性二次严苛审计
|
||||
balance_error_flag = 0;
|
||||
verify_rb_properties(tree.root, &balance_error_flag);
|
||||
ASSERT_INT_EQ_MSG(0, balance_error_flag, "Red-Black Tree broke its black-height balance after dynamic removals!");
|
||||
ASSERT_INT_EQ(C_RB_BLACK, c_RBTree_GetColor(tree.root)); // 根节点依然稳健为黑
|
||||
|
||||
// 7. 验证快速清空机制与纯净还原 (Clear)
|
||||
c_RBTree_Clear(&tree);
|
||||
ASSERT_INT_EQ(0, (int)c_RBTree_Size(&tree));
|
||||
ASSERT_TRUE(tree.root == NULL); // 必须在物理堆中 0 滞留,控制头完美复位到 0 态
|
||||
|
||||
c_RBTree_Destroy(&tree);
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 主测试入口
|
||||
// ==================================================================================================================
|
||||
int main(void) {
|
||||
printf("\n");
|
||||
TEST_START(C_Naked_RedBlackTree_Integrated_Tests);
|
||||
|
||||
// 运行无哨兵加固后的红黑树核心功能及自修复流验证
|
||||
RUN_TEST(test_naked_rb_tree_ultimate_closure);
|
||||
|
||||
TEST_REPORT();
|
||||
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_SmartPtr.h>
|
||||
@@ -0,0 +1,115 @@
|
||||
#ifndef INCLUDED_C_SMARTPTR_H
|
||||
#define INCLUDED_C_SMARTPTR_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ATOMIC_H
|
||||
#include <c_Atomic.h>
|
||||
#endif /*INCLUDED_C_ATOMIC_H*/
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
// 釋放資源的函數指標類型
|
||||
typedef void (*c_SmartPtrFreeFn_t)(void* ptr, void* args);
|
||||
|
||||
// 智慧指標結構體
|
||||
typedef struct {
|
||||
void* ptr;
|
||||
c_SmartPtrFreeFn_t free_fn;
|
||||
void* args;
|
||||
c_atomic_int_t* ref_count;
|
||||
} c_SmartPtr_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_SmartPtr_t c_SmartPtr_Make(void* ptr, const c_SmartPtrFreeFn_t free_fn, void* args) {
|
||||
c_SmartPtr_t sptr = { .ptr = ptr, .free_fn = free_fn, .args = args, .ref_count = NULL };
|
||||
|
||||
if (ptr == NULL) return sptr;
|
||||
|
||||
C_NEW(sptr.ref_count);
|
||||
if (sptr.ref_count != NULL) {
|
||||
c_atomic_init_int(sptr.ref_count, 1);
|
||||
}
|
||||
return sptr;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_err_t c_SmartPtr_Init(c_SmartPtr_t* self, void* ptr, const c_SmartPtrFreeFn_t free_fn, void* args) {
|
||||
if (ptr == NULL) return C_ERR_PARAM;
|
||||
|
||||
self->ptr = ptr;
|
||||
self->free_fn = free_fn;
|
||||
self->args = args;
|
||||
self->ref_count = NULL;
|
||||
|
||||
C_NEW(self->ref_count);
|
||||
if (self->ref_count == NULL) {
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
c_atomic_init_int(self->ref_count, 1);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_SmartPtr_Destroy(c_SmartPtr_t* self) {
|
||||
if (!self || !self->ref_count) return;
|
||||
|
||||
if (C_ATOMIC_FETCH_SUB(self->ref_count, 1) == 1) {
|
||||
if (self->free_fn && self->ptr) {
|
||||
self->free_fn(self->ptr, self->args);
|
||||
}
|
||||
C_FREE(self->ref_count);
|
||||
}
|
||||
|
||||
memset(self, 0, sizeof(c_SmartPtr_t));
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_err_t c_SmartPtr_Copy(c_SmartPtr_t* dest, const c_SmartPtr_t* src) {
|
||||
if (!dest || !src || dest == src) return C_ERR_PARAM;
|
||||
if (!src->ptr || !src->ref_count) return C_ERR_PARAM;
|
||||
|
||||
c_SmartPtr_Destroy(dest);
|
||||
|
||||
dest->ptr = src->ptr;
|
||||
dest->free_fn = src->free_fn;
|
||||
dest->args = src->args;
|
||||
dest->ref_count = src->ref_count;
|
||||
|
||||
C_ATOMIC_FETCH_ADD(dest->ref_count, 1);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_err_t c_SmartPtr_Move(c_SmartPtr_t* dest, c_SmartPtr_t* src) {
|
||||
if (!dest || !src || dest == src) return C_ERR_PARAM;
|
||||
|
||||
c_SmartPtr_Destroy(dest);
|
||||
*dest = *src;
|
||||
memset(src, 0, sizeof(c_SmartPtr_t));
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
int c_SmartPtr_UseCount(const c_SmartPtr_t* self) {
|
||||
if (!self || !self->ref_count) return 0;
|
||||
return (int)C_ATOMIC_LOAD(self->ref_count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_SMARTPTR_H*/
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "c_SmartPtr.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// 自訂釋放函數:負責關閉檔案
|
||||
void close_file_callback(void* ptr, void* args) {
|
||||
FILE* fp = (FILE*)ptr;
|
||||
char* filename = (char*)args;
|
||||
|
||||
if (fp) {
|
||||
printf("[SmartPtr] 引用計數歸零,自動關閉檔案: %s\n", filename);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("--- 1. 建立資源 (Open File) ---\n");
|
||||
char* my_file = "test.txt";
|
||||
FILE* fp = fopen(my_file, "w");
|
||||
if (!fp) return 1;
|
||||
|
||||
// 寫入一些測試資料
|
||||
fprintf(fp, "Hello Smart Pointer in C!");
|
||||
fflush(fp); // 確保資料進硬碟,但先不關閉檔案
|
||||
|
||||
// 使用智慧指標接管檔案控制權
|
||||
c_SmartPtr_t sptr_a = c_SmartPtr_Make(fp, close_file_callback, my_file);
|
||||
printf("sptr_a 建立完成,目前引用計數: %d\n", c_SmartPtr_UseCount(&sptr_a));
|
||||
|
||||
// 建立另外兩個空白的智慧指標容器
|
||||
c_SmartPtr_t sptr_b = {0};
|
||||
c_SmartPtr_t sptr_c = {0};
|
||||
|
||||
printf("\n--- 2. 測試 Copy 語義 (共享檔案控制權) ---\n");
|
||||
c_SmartPtr_Copy(&sptr_b, &sptr_a);
|
||||
printf("A 的計數: %d, B 的計數: %d\n", c_SmartPtr_UseCount(&sptr_a), c_SmartPtr_UseCount(&sptr_b));
|
||||
|
||||
printf("\n--- 3. 測試 Move 語義 (B 所有權轉移給 C) ---\n");
|
||||
c_SmartPtr_Move(&sptr_c, &sptr_b);
|
||||
printf("Move 後 -> B 計數: %d (已空), C 計數: %d\n", c_SmartPtr_UseCount(&sptr_b), c_SmartPtr_UseCount(&sptr_c));
|
||||
|
||||
printf("\n--- 4. 開始依序銷毀指標物件 ---\n");
|
||||
printf("銷毀 sptr_a...\n");
|
||||
c_SmartPtr_Destroy(&sptr_a); // 計數 2 -> 1
|
||||
printf("sptr_a 銷毀後,C 的計數: %d\n", c_SmartPtr_UseCount(&sptr_c));
|
||||
|
||||
printf("銷毀 sptr_b (本身已空,無影響)...\n");
|
||||
c_SmartPtr_Destroy(&sptr_b);
|
||||
|
||||
printf("銷毀 sptr_c...\n");
|
||||
// 計數 1 -> 0,自動觸發 close_file_callback 關閉檔案!
|
||||
c_SmartPtr_Destroy(&sptr_c);
|
||||
|
||||
printf("\n程式結束,所有資源安全回收。\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
#include <c_SmartPtrVector.h>
|
||||
|
||||
#define DEFAULT_INITIAL_CAPACITY 4
|
||||
#define DEFAULT_GROW_FACTOR 2
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_SmartPtrVector_Init(c_SmartPtrVector_t* vector, c_size_t capacity, c_Allocator_t* allocator) {
|
||||
if (!vector) return C_ERR_PARAM;
|
||||
vector->allocator = (allocator != NULL) ? *allocator : c_DefaultAllocator;
|
||||
vector->size = 0;
|
||||
vector->capacity = (capacity > 0) ? capacity : DEFAULT_INITIAL_CAPACITY; // 預留初始 4 個槽位
|
||||
vector->array = (c_SmartPtr_t*)c_Allocator_Alloc(&vector->allocator, vector->capacity * sizeof(c_SmartPtr_t));
|
||||
if (!vector->array) {
|
||||
vector->capacity = 0;
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
// 嚴格原位抹零:確保初始狀態下所有槽位的物理指標與引用計數皆為純淨的 0 (NULL)
|
||||
memset((void*)vector->array, 0, vector->capacity * sizeof(c_SmartPtr_t));
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 銷毀智慧指標向量
|
||||
* @note 閉環契約:必須依序呼叫你定義的 c_SmartPtr_Destroy 遞减引用計數,
|
||||
* 若降為 0 則會自動驅使內部的 free_fn,最後再退還 Vector 控制底座記憶體給內置分配器。
|
||||
*/
|
||||
void c_SmartPtrVector_Destroy(c_SmartPtrVector_t* vector) {
|
||||
if (!vector) return;
|
||||
|
||||
if (vector->array) {
|
||||
// ① 依序安全銷毀當前所有有效槽位持有的引用
|
||||
for (c_size_t i = 0; i < vector->size; i++) {
|
||||
c_SmartPtr_Destroy(&vector->array[i]);
|
||||
}
|
||||
// ② 歸还底座記憶體給綁定的記憶體池
|
||||
c_Allocator_Free(&vector->allocator, vector->array);
|
||||
vector->array = NULL;
|
||||
}
|
||||
vector->size = 0;
|
||||
vector->capacity = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 內部私有自動動態翻倍擴容函數
|
||||
* @note 強異常安全性:採用移動語義平移控制權,即使分配失敗,老資料依舊原裝存活
|
||||
*/
|
||||
static bool c_SmartPtrVector_EnsureCapacity(c_SmartPtrVector_t* vector) {
|
||||
if (vector->size < vector->capacity) return true;
|
||||
|
||||
c_size_t new_capacity = vector->capacity * 2;
|
||||
|
||||
// 1. 利用內置分配器,在當前專屬記憶體池裡開闢全新的目標緩衝區
|
||||
c_SmartPtr_t* new_array = (c_SmartPtr_t*)c_Allocator_Alloc(&vector->allocator, new_capacity * sizeof(c_SmartPtr_t));
|
||||
if (!new_array) return false;
|
||||
|
||||
// 清零新緩衝區槽位
|
||||
memset((void*)new_array, 0, new_capacity * sizeof(c_SmartPtr_t));
|
||||
|
||||
// 2. 利用你的 c_SmartPtr_Move 將老槽位的所有權絕對安全地轉移過去
|
||||
// 你的 Move 內部執行了 *dest = *src 和對 src 的 memset,完美斬斷老位,且引用計數完美保持不變!
|
||||
for (c_size_t i = 0; i < vector->size; i++) {
|
||||
c_SmartPtr_Move(&new_array[i], &vector->array[i]);
|
||||
}
|
||||
|
||||
// 3. 歸还已經被完全掏空、所有指標皆為 NULL 的老緩衝區底座
|
||||
c_Allocator_Free(&vector->allocator, vector->array);
|
||||
|
||||
vector->array = new_array;
|
||||
vector->capacity = new_capacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ==================================================================================================================
|
||||
// 2. 核心元素增删控制操作 API
|
||||
// ==================================================================================================================
|
||||
|
||||
c_err_t c_SmartPtrVector_PushBack(c_SmartPtrVector_t* vector, const c_SmartPtr_t* ptr) {
|
||||
if (!vector || !ptr) return C_ERR_PARAM;
|
||||
if (!c_SmartPtrVector_EnsureCapacity(vector)) return C_ERR_NOMEM;
|
||||
|
||||
// 使用你的 c_SmartPtr_Copy 進行拷貝:內部會自動對 dest->ref_count 執行原子加一加
|
||||
c_err_t err = c_SmartPtr_Copy(&vector->array[vector->size], ptr);
|
||||
if (err == C_ERR_OK) {
|
||||
vector->size++;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_SmartPtrVector_PopBack(c_SmartPtrVector_t* vector, c_SmartPtr_t* ptr) {
|
||||
if (!vector || vector->size == 0) return C_ERR_OUTOFBOUND;
|
||||
|
||||
vector->size--;
|
||||
c_SmartPtr_t* slot = &vector->array[vector->size];
|
||||
|
||||
if (ptr) {
|
||||
// 利用你的 c_SmartPtr_Move 轉移所有權給外部接收器,省去不必要的加減計數硬件震盪开销
|
||||
c_SmartPtr_Move(ptr, slot);
|
||||
} else {
|
||||
// 外部若靜默彈出,直接驅動銷毀鏈銷毁
|
||||
c_SmartPtr_Destroy(slot);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_SmartPtrVector_Insert(c_SmartPtrVector_t* vector, c_size_t index, const c_SmartPtr_t* ptr) {
|
||||
if (!vector || !ptr || index > vector->size) return C_ERR_PARAM;
|
||||
if (!c_SmartPtrVector_EnsureCapacity(vector)) return C_ERR_NOMEM;
|
||||
|
||||
// 1. 自尾部向左推進,利用你的 c_SmartPtr_Move 將 index 右側的元素整體右推滑移一格
|
||||
for (c_size_t i = vector->size; i > index; i--) {
|
||||
c_SmartPtr_Move(&vector->array[i], &vector->array[i - 1]);
|
||||
}
|
||||
|
||||
// 2. 槽位騰空,使用你的 Copy 深度注入,計數自動原子閉合
|
||||
c_err_t err = c_SmartPtr_Copy(&vector->array[index], ptr);
|
||||
if (err == C_ERR_OK) {
|
||||
vector->size++;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_SmartPtrVector_RemoveAndTake(c_SmartPtrVector_t* self, c_size_t index, c_SmartPtr_t* ptr) {
|
||||
if (!self || index >= self->size) return C_ERR_PARAM;
|
||||
|
||||
c_SmartPtr_t* target_slot = &self->array[index];
|
||||
|
||||
if (ptr) {
|
||||
// 所有權剝离,交由外部接管
|
||||
c_SmartPtr_Move(ptr, target_slot);
|
||||
} else {
|
||||
// 靜默抹殺,遞减原子引用計數
|
||||
c_SmartPtr_Destroy(target_slot);
|
||||
}
|
||||
|
||||
// 3. 自左向右推進,利用你的 Move 將右側後續有效項依序前移填補拓扑空缺
|
||||
for (c_size_t i = index; i < self->size - 1; i++) {
|
||||
c_SmartPtr_Move(&self->array[i], &self->array[i + 1]);
|
||||
}
|
||||
|
||||
// 將滑移後的最後一項殘存記憶體空間強制做乾淨的原位抹零
|
||||
memset((void*)&self->array[self->size - 1], 0, sizeof(c_SmartPtr_t));
|
||||
|
||||
self->size--;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_SmartPtrVector_Remove(c_SmartPtrVector_t* self, c_size_t index) {
|
||||
return c_SmartPtrVector_RemoveAndTake(self, index, NULL);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef INCLUDED_C_SMARTPTRVECTOR_H
|
||||
#define INCLUDED_C_SMARTPTRVECTOR_H
|
||||
|
||||
#ifndef INCLUDED_C_SMARTPTR_H
|
||||
#include <c_SmartPtr.h>
|
||||
#endif /*INCLUDED_C_SMARTPTR_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
c_SmartPtr_t* array;
|
||||
c_size_t capacity;
|
||||
c_size_t size;
|
||||
c_Allocator_t allocator;
|
||||
}c_SmartPtrVector_t;
|
||||
|
||||
#define C_SMART_PTR_VECTOR_INITIALIZER {NULL, 0, 0}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_SmartPtrVector_t c_SmartPtrVector_Create(void) {
|
||||
return (c_SmartPtrVector_t)C_SMART_PTR_VECTOR_INITIALIZER;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
const c_SmartPtr_t* c_SmartPtrVector_Get(const c_SmartPtrVector_t* self, c_size_t index) {
|
||||
if (!self || index >= self->size) {
|
||||
return NULL;
|
||||
}
|
||||
return &self->array[index];
|
||||
}
|
||||
|
||||
c_err_t c_SmartPtrVector_Init(c_SmartPtrVector_t* vector, c_size_t capacity, c_Allocator_t* allocator);
|
||||
|
||||
void c_SmartPtrVector_Destroy(c_SmartPtrVector_t* vector);
|
||||
|
||||
c_err_t c_SmartPtrVector_PushBack(c_SmartPtrVector_t* vector, const c_SmartPtr_t* ptr);
|
||||
|
||||
c_err_t c_SmartPtrVector_PopBack(c_SmartPtrVector_t* vector, c_SmartPtr_t* ptr);
|
||||
|
||||
c_err_t c_SmartPtrVector_Remove(c_SmartPtrVector_t* self, c_size_t index);
|
||||
|
||||
c_err_t c_SmartPtrVector_Insert(c_SmartPtrVector_t* vector, c_size_t index, const c_SmartPtr_t* ptr);
|
||||
|
||||
c_err_t c_SmartPtrVector_RemoveAndTake(c_SmartPtrVector_t* self, c_size_t index, c_SmartPtr_t* ptr);
|
||||
|
||||
#endif /*INCLUDED_C_SMARTPTRVECTOR_H*/
|
||||
@@ -0,0 +1,126 @@
|
||||
#include "c_SmartPtrVector.h"
|
||||
#include "c_Test.h"
|
||||
|
||||
// 模擬業務層的實體資源
|
||||
typedef struct {
|
||||
int resource_id;
|
||||
int payload;
|
||||
} TestObject_t;
|
||||
|
||||
static int g_resource_destroy_count = 0; // 全局計數器:監控實體被真正 free 的次數
|
||||
|
||||
// 匹配你定義的 c_SmartPtrFreeFn_t 簽名
|
||||
static void test_resource_free(void* ptr, void* args) {
|
||||
if (ptr) {
|
||||
TestObject_t* obj = (TestObject_t*)ptr;
|
||||
int custom_flag = *(int*)args;
|
||||
printf("[FreeFn Triggered] Object ID: %d, Args Flag: %d\n", obj->resource_id, custom_flag);
|
||||
|
||||
free(obj); // 釋放真實業務對象
|
||||
g_resource_destroy_count++; // 被物理火化銷毀次數遞增
|
||||
}
|
||||
}
|
||||
|
||||
// 模擬分配器的樁實現(用於驅動 Vector 的內置多態分配鏈)
|
||||
static size_t g_tracker_active_allocs = 0;
|
||||
static void* mock_vector_alloc(size_t size, void* ctx) {
|
||||
(void)ctx; g_tracker_active_allocs++; return malloc(size);
|
||||
}
|
||||
static void mock_vector_free(void* ptr, void* ctx) {
|
||||
(void)ctx; if (ptr) g_tracker_active_allocs--; free(ptr);
|
||||
}
|
||||
|
||||
TEST_CASE(test_real_smart_ptr_vector_atomic_closure) {
|
||||
g_resource_destroy_count = 0;
|
||||
g_tracker_active_allocs = 0;
|
||||
|
||||
// 1. 初始化多態記憶體管理器底座
|
||||
c_Allocator_t pool_allocator = {
|
||||
.alloc = mock_vector_alloc,
|
||||
.realloc = NULL,
|
||||
.free = mock_vector_free,
|
||||
.dtor = NULL,
|
||||
.ud = NULL
|
||||
};
|
||||
|
||||
c_SmartPtrVector_t vector;
|
||||
// 初始化容量為 2 的向量,並綁定專屬分配器
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SmartPtrVector_Init(&vector, 2, &pool_allocator));
|
||||
ASSERT_INT_EQ(1, g_tracker_active_allocs); // 內置分配器成功申請了大底座
|
||||
|
||||
// 2. 建立一具真實的智慧指標(指向在堆上動態開闢的 TestObject_t)
|
||||
TestObject_t* raw_obj = (TestObject_t*)malloc(sizeof(TestObject_t));
|
||||
raw_obj->resource_id = 8899;
|
||||
raw_obj->payload = 100;
|
||||
|
||||
int free_args = 2026; // 自定義銷毀引數
|
||||
|
||||
// 使用你編寫的 c_SmartPtr_Make / Init 進行生命周期宣告
|
||||
c_SmartPtr_t master_sptr = c_SmartPtr_Make(raw_obj, test_resource_free, &free_args);
|
||||
|
||||
// 剛出生,使用計數必須精準等於 1
|
||||
ASSERT_INT_EQ(1, c_SmartPtr_UseCount(&master_sptr));
|
||||
|
||||
// 3. 測試 PushBack 自動引用計數增長
|
||||
// 將智慧指標塞入 Vector 槽位 0 中,內部呼叫 c_SmartPtr_Copy,計數應原子遞增至 2
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SmartPtrVector_PushBack(&vector, &master_sptr));
|
||||
ASSERT_INT_EQ(2, c_SmartPtr_UseCount(&master_sptr));
|
||||
ASSERT_INT_EQ(2, c_SmartPtr_UseCount(c_SmartPtrVector_Get(&vector, 0)));
|
||||
|
||||
// 4. 建立第二具獨立智慧指標,迫使 Vector 翻倍自動擴容(2 -> 4)
|
||||
TestObject_t* raw_obj2 = (TestObject_t*)malloc(sizeof(TestObject_t));
|
||||
raw_obj2->resource_id = 1122;
|
||||
c_SmartPtr_t master_sptr2 = c_SmartPtr_Make(raw_obj2, test_resource_free, &free_args);
|
||||
|
||||
c_SmartPtrVector_PushBack(&vector, &master_sptr2); // 槽位 1
|
||||
|
||||
// 核心觀察點:塞入第三個元素,強行觸展 EnsureCapacity 翻倍搬迁!
|
||||
// 搬遷時內部使用 c_SmartPtr_Move 做轉移,所有權安全平移,兩具老智慧指標的引用計數絕不應發生任何變化!
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SmartPtrVector_PushBack(&vector, &master_sptr2)); // 槽位 2 重複掛接 sptr2
|
||||
|
||||
ASSERT_INT_EQ(3, vector.size);
|
||||
ASSERT_INT_EQ(4, vector.capacity);
|
||||
ASSERT_INT_EQ(1, g_tracker_active_allocs); // 搬遷成功後,老緩衝區被內置分配器乾淨 Free 釋放
|
||||
|
||||
// 驗證搬遷後,sptr1 的計數依然穩健維持在 2,完全沒有受到內存重組的交叉干擾
|
||||
ASSERT_INT_EQ(2, c_SmartPtr_UseCount(&master_sptr));
|
||||
|
||||
// 5. 測試中段任意位置定点裁剪(Remove 靜默抹殺)
|
||||
// 移除槽位 0 的 sptr1,這會扣減其引用計數(2 降到 1)。此時資源依然活在外部持有的 master_sptr 中
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SmartPtrVector_Remove(&vector, 0));
|
||||
ASSERT_INT_EQ(1, c_SmartPtr_UseCount(&master_sptr));
|
||||
ASSERT_INT_EQ(0, g_resource_destroy_count); // 資源依然安全存活,尚未觸發真正的物理 free_fn
|
||||
|
||||
// 6. 測試 RemoveAndTake 控制權完全接管倒騰
|
||||
c_SmartPtr_t external_receiver = {0};
|
||||
// 將槽位 0 (因前移,此時存放的是 sptr2 的重複項) 自主剝离倒騰給 external_receiver
|
||||
// 由於是 Move 倒騰,sptr2 的計數和所有權無痛轉交,完全符合直覺
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_SmartPtrVector_RemoveAndTake(&vector, 0, &external_receiver));
|
||||
ASSERT_TRUE(external_receiver.ptr == raw_obj2);
|
||||
|
||||
// 7. 外部主動銷毀釋放所有本地變數,將計數往物理火化終點推進
|
||||
c_SmartPtr_Destroy(&external_receiver);
|
||||
c_SmartPtr_Destroy(&master_sptr2);
|
||||
|
||||
// 8. 終極解體:摧毀 Vector
|
||||
c_SmartPtrVector_Destroy(&vector);
|
||||
|
||||
// 槽位 0 之前剩下一具 sptr2。Vector 銷毀時會將其釋放,此時 raw_obj2 的引用計數徹底歸 0,自動觸發物理銷毀!
|
||||
ASSERT_INT_EQ(1, g_resource_destroy_count); // raw_obj2 已經在 Vector 解體時被閉環火化
|
||||
|
||||
// 9. 外部最後銷毀外部唯一的 master_sptr
|
||||
c_SmartPtr_Destroy(&master_sptr); // 引用計數歸 0,raw_obj1 自動物理火化!
|
||||
|
||||
// 10. 終極一致性雙向審判断言:
|
||||
// 所有變數與容器全部解體後,兩具堆實體資源必須無一遺漏、百分之百被自動物理銷毀歸還!
|
||||
ASSERT_INT_EQ_MSG(2, g_resource_destroy_count, "CRITICAL: Smart Pointer Vector Caused Reference Counting Mismatch or Memory Leak!");
|
||||
// 分配器活躍塊重歸於 0,物理堆記憶體 0 滯留
|
||||
ASSERT_INT_EQ_MSG(0, g_tracker_active_allocs, "Vector internal allocator leaked heap space!");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
TEST_START(C_Real_SmartPtrVector_System_Tests);
|
||||
RUN_TEST(test_real_smart_ptr_vector_atomic_closure);
|
||||
TEST_REPORT();
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
#include <c_Str.h>
|
||||
#include <c_Memory.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define idx(i, len) ((i) <= 0 ? (i) + (len) : (i) - 1)
|
||||
|
||||
#define convert(s, i, j) do { int _len; \
|
||||
assert(s); _len = (int)strlen(s); \
|
||||
i = idx(i, _len); j = idx(j, _len); \
|
||||
if (i > j) { int t = i; i = j; j = t; } \
|
||||
assert(i >= 0 && j <= _len); } while (0)
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
char *c_Str_sub(const char *s, int i, int j) {
|
||||
char *str, *p;
|
||||
convert(s, i, j);
|
||||
p = str = C_ALLOC(j - i + 1);
|
||||
while (i < j)
|
||||
*p++ = s[i++];
|
||||
*p = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
char *c_Str_dup(const char *s, int i, int j, int n) {
|
||||
int k;
|
||||
char *str, *p;
|
||||
assert(n >= 0);
|
||||
convert(s, i, j);
|
||||
p = str = C_ALLOC(n*(j - i) + 1);
|
||||
if (j - i > 0)
|
||||
while (n-- > 0)
|
||||
for (k = i; k < j; k++)
|
||||
*p++ = s[k];
|
||||
*p = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
char *c_Str_reverse(const char *s, int i, int j) {
|
||||
char *str, *p;
|
||||
convert(s, i, j);
|
||||
p = str = C_ALLOC(j - i + 1);
|
||||
while (j > i)
|
||||
*p++ = s[--j];
|
||||
*p = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
char *c_Str_cat(const char *s1, int i1, int j1,
|
||||
const char *s2, int i2, int j2) {
|
||||
char *str, *p;
|
||||
convert(s1, i1, j1);
|
||||
convert(s2, i2, j2);
|
||||
p = str = C_ALLOC(j1 - i1 + j2 - i2 + 1);
|
||||
while (i1 < j1)
|
||||
*p++ = s1[i1++];
|
||||
while (i2 < j2)
|
||||
*p++ = s2[i2++];
|
||||
*p = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
char *c_Str_catv(const char *s, ...) {
|
||||
char *str, *p;
|
||||
const char *save = s;
|
||||
int i, j, len = 0;
|
||||
va_list ap;
|
||||
va_start(ap, s);
|
||||
while (s) {
|
||||
i = va_arg(ap, int);
|
||||
j = va_arg(ap, int);
|
||||
convert(s, i, j);
|
||||
len += j - i;
|
||||
s = va_arg(ap, const char *);
|
||||
}
|
||||
va_end(ap);
|
||||
p = str = C_ALLOC(len + 1);
|
||||
s = save;
|
||||
va_start(ap, s);
|
||||
while (s) {
|
||||
i = va_arg(ap, int);
|
||||
j = va_arg(ap, int);
|
||||
convert(s, i, j);
|
||||
while (i < j)
|
||||
*p++ = s[i++];
|
||||
s = va_arg(ap, const char *);
|
||||
}
|
||||
va_end(ap);
|
||||
*p = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
char *c_Str_map(const char *s, int i, int j,
|
||||
const char *from, const char *to) {
|
||||
static char map[256] = { 0 };
|
||||
if (from && to) {
|
||||
unsigned c;
|
||||
for (c = 0; c < sizeof map; c++)
|
||||
map[c] = c;
|
||||
while (*from && *to)
|
||||
map[(unsigned char)*from++] = *to++;
|
||||
assert(*from == 0 && *to == 0);
|
||||
} else {
|
||||
assert(from == NULL && to == NULL && s);
|
||||
assert(map['a']);
|
||||
}
|
||||
if (s) {
|
||||
char *str, *p;
|
||||
convert(s, i, j);
|
||||
p = str = C_ALLOC(j - i + 1);
|
||||
while (i < j)
|
||||
*p++ = map[(unsigned char)s[i++]];
|
||||
*p = '\0';
|
||||
return str;
|
||||
} else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int c_Str_pos(const char *s, int i) {
|
||||
assert(s);
|
||||
const int len = (int) strlen(s);
|
||||
i = idx(i, len);
|
||||
assert(i >= 0 && i <= len);
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
int c_Str_len(const char *s, int i, int j) {
|
||||
convert(s, i, j);
|
||||
return j - i;
|
||||
}
|
||||
|
||||
int c_Str_cmp(const char *s1, int i1, int j1,
|
||||
const char *s2, int i2, int j2) {
|
||||
convert(s1, i1, j1);
|
||||
convert(s2, i2, j2);
|
||||
s1 += i1;
|
||||
s2 += i2;
|
||||
if (j1 - i1 < j2 - i2) {
|
||||
int cond = strncmp(s1, s2, j1 - i1);
|
||||
return cond == 0 ? -1 : cond;
|
||||
} else if (j1 - i1 > j2 - i2) {
|
||||
const int cond = strncmp(s1, s2, j2 - i2);
|
||||
return cond == 0 ? +1 : cond;
|
||||
} else
|
||||
return strncmp(s1, s2, j1 - i1);
|
||||
}
|
||||
|
||||
int c_Str_chr(const char *s, int i, int j, int c) {
|
||||
convert(s, i, j);
|
||||
for ( ; i < j; i++)
|
||||
if (s[i] == c)
|
||||
return i + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_Str_rchr(const char *s, int i, int j, int c) {
|
||||
convert(s, i, j);
|
||||
while (j > i)
|
||||
if (s[--j] == c)
|
||||
return j + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_Str_upto(const char *s, int i, int j,
|
||||
const char *set) {
|
||||
assert(set);
|
||||
convert(s, i, j);
|
||||
for ( ; i < j; i++)
|
||||
if (strchr(set, s[i]))
|
||||
return i + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_Str_rupto(const char *s, int i, int j,
|
||||
const char *set) {
|
||||
assert(set);
|
||||
convert(s, i, j);
|
||||
while (j > i)
|
||||
if (strchr(set, s[--j]))
|
||||
return j + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_Str_find(const char *s, int i, int j,
|
||||
const char *str) {
|
||||
convert(s, i, j);
|
||||
assert(str);
|
||||
const int len = (int)strlen(str);
|
||||
if (len == 0)
|
||||
return i + 1;
|
||||
else if (len == 1) {
|
||||
for ( ; i < j; i++)
|
||||
if (s[i] == *str)
|
||||
return i + 1;
|
||||
} else
|
||||
for ( ; i + len <= j; i++)
|
||||
if ((strncmp(&s[i], str, len) == 0))
|
||||
return i + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_Str_rfind(const char *s, int i, int j, const char *str) {
|
||||
convert(s, i, j);
|
||||
assert(str);
|
||||
const int len =(int)strlen(str);
|
||||
if (len == 0)
|
||||
return j + 1;
|
||||
else if (len == 1) {
|
||||
while (j > i)
|
||||
if (s[--j] == *str)
|
||||
return j + 1;
|
||||
} else
|
||||
for ( ; j - len >= i; j--)
|
||||
if (strncmp(&s[j-len], str, len) == 0)
|
||||
return j - len + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_Str_any(const char *s, int i, const char *set) {
|
||||
assert(s);
|
||||
assert(set);
|
||||
const int len =(int)strlen(s);
|
||||
i = idx(i, len);
|
||||
assert(i >= 0 && i <= len);
|
||||
if (i < len && strchr(set, s[i]))
|
||||
return i + 2;
|
||||
return 0;
|
||||
}
|
||||
int c_Str_many(const char *s, int i, int j,
|
||||
const char *set) {
|
||||
assert(set);
|
||||
convert(s, i, j);
|
||||
if (i < j && strchr(set, s[i])) {
|
||||
do
|
||||
i++;
|
||||
while (i < j && strchr(set, s[i]));
|
||||
return i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_Str_rmany(const char *s, int i, int j,
|
||||
const char *set) {
|
||||
assert(set);
|
||||
convert(s, i, j);
|
||||
if (j > i && strchr(set, s[j-1])) {
|
||||
do
|
||||
--j;
|
||||
while (j >= i && strchr(set, s[j]));
|
||||
return j + 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_Str_match(const char *s, int i, int j, const char *str) {
|
||||
convert(s, i, j);
|
||||
assert(str);
|
||||
const int len =(int)strlen(str);
|
||||
if (len == 0)
|
||||
return i + 1;
|
||||
else if (len == 1) {
|
||||
if (i < j && s[i] == *str)
|
||||
return i + 2;
|
||||
} else if (i + len <= j && (strncmp(&s[i], str, len) == 0))
|
||||
return i + len + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_Str_rmatch(const char *s, int i, int j, const char *str) {
|
||||
convert(s, i, j);
|
||||
assert(str);
|
||||
const int len =(int)strlen(str);
|
||||
if (len == 0)
|
||||
return j + 1;
|
||||
else if (len == 1) {
|
||||
if (j > i && s[j-1] == *str)
|
||||
return j;
|
||||
} else if (j - len >= i
|
||||
&& strncmp(&s[j-len], str, len) == 0)
|
||||
return j - len + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void c_Str_fmt(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision) {
|
||||
assert(box && flags);
|
||||
char *s = va_arg(box->ap, char *);
|
||||
int i = va_arg(box->ap, int);
|
||||
int j = va_arg(box->ap, int);
|
||||
convert(s, i, j);
|
||||
c_Fmt_puts(s + i, j - i, put, cl, flags,
|
||||
width, precision);
|
||||
}
|
||||
|
||||
void c_Str_free(char* s) {
|
||||
C_FREE(s);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef INCLUDED_C_STR_H
|
||||
#define INCLUDED_C_STR_H
|
||||
|
||||
#ifndef INCLUDED_C_FMT_H
|
||||
#include <c_Fmt.h>
|
||||
#endif /*INCLUDED_C_FMT_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
char *c_Str_sub(const char *s, int i, int j);
|
||||
char *c_Str_dup(const char *s, int i, int j, int n);
|
||||
char *c_Str_cat(const char *s1, int i1, int j1,
|
||||
const char *s2, int i2, int j2);
|
||||
char *c_Str_catv (const char *s, ...);
|
||||
char *c_Str_reverse(const char *s, int i, int j);
|
||||
char *c_Str_map (const char *s, int i, int j,
|
||||
const char *from, const char *to);
|
||||
void c_Str_free(char* s);
|
||||
int c_Str_pos(const char *s, int i);
|
||||
int c_Str_len(const char *s, int i, int j);
|
||||
int c_Str_cmp(const char *s1, int i1, int j1,
|
||||
const char *s2, int i2, int j2);
|
||||
int c_Str_chr (const char *s, int i, int j, int c);
|
||||
int c_Str_rchr (const char *s, int i, int j, int c);
|
||||
int c_Str_upto (const char *s, int i, int j,
|
||||
const char *set);
|
||||
int c_Str_rupto(const char *s, int i, int j,
|
||||
const char *set);
|
||||
int c_Str_find (const char *s, int i, int j,
|
||||
const char *str);
|
||||
int c_Str_rfind(const char *s, int i, int j,
|
||||
const char *str);
|
||||
int c_Str_any (const char *s, int i,
|
||||
const char *set);
|
||||
int c_Str_many (const char *s, int i, int j,
|
||||
const char *set);
|
||||
int c_Str_rmany (const char *s, int i, int j,
|
||||
const char *set);
|
||||
int c_Str_match (const char *s, int i, int j,
|
||||
const char *str);
|
||||
int c_Str_rmatch(const char *s, int i, int j,
|
||||
const char *str);
|
||||
void c_Str_fmt(int code, va_list_box *box,
|
||||
int put(int c, void *cl), void *cl,
|
||||
unsigned char flags[], int width, int precision);
|
||||
|
||||
#endif /*INCLUDED_C_STR_H*/
|
||||
@@ -0,0 +1,188 @@
|
||||
#include "c_Str.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
const char *test_str = "abcdefg"; // 長度為 7
|
||||
|
||||
char test_output_buffer[128];
|
||||
int buf_idx = 0;
|
||||
|
||||
int test_put(int c, void *cl) {
|
||||
if (buf_idx < 127) {
|
||||
test_output_buffer[buf_idx++] = (char)c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
// Global print wrapper to test formatting execution
|
||||
void test_printf(const char *fmt, ...) {
|
||||
va_list_box ap;
|
||||
va_start(ap.ap, fmt);
|
||||
c_Fmt_vfmt(test_put, NULL, fmt, &ap);
|
||||
va_end(ap.ap);
|
||||
}
|
||||
|
||||
void test_log(const char* name) {
|
||||
printf("[PASS] %s\n", name);
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
printf("==================================================\n");
|
||||
printf(" 開始執行 c_Str 核心功能組件 最終整合單元測試\n");
|
||||
printf("==================================================\n\n");
|
||||
|
||||
char *res_str = NULL;
|
||||
|
||||
// 1. 測試 c_Str_pos
|
||||
// test_str = "abcdefg" (len=7)
|
||||
// 1 -> index 0 -> 位置 1
|
||||
// 0 -> 尾端倒數 (0+7) = index 7 -> 位置 8
|
||||
// -1 -> 尾端倒數 (-1+7) = index 6 -> 位置 7
|
||||
assert(c_Str_pos(test_str, 1) == 1);
|
||||
assert(c_Str_pos(test_str, 0) == 8);
|
||||
assert(c_Str_pos(test_str, -1) == 7);
|
||||
test_log("c_Str_pos (1-based 位置計算驗證)");
|
||||
|
||||
// 2. 測試 c_Str_len
|
||||
// 從 1 (開頭) 到 0 (結尾) -> 全長 7
|
||||
// 從 2 ('b') 到 5 ('e' 後方) -> 5 - 1 = 4 ('bcde')
|
||||
assert(c_Str_len(test_str, 1, 0) == 7);
|
||||
assert(c_Str_len(test_str, 2, 5) == 3);
|
||||
test_log("c_Str_len (區間長度計算驗證)");
|
||||
|
||||
// 3. 測試 c_Str_sub
|
||||
res_str = c_Str_sub(test_str, 2, 5); // 提取 'bcd'
|
||||
assert(strcmp(res_str, "bcd") == 0);
|
||||
c_Str_free(res_str);
|
||||
test_log("c_Str_sub (子字串提取驗證)");
|
||||
|
||||
// 4. 測試 c_Str_dup
|
||||
res_str = c_Str_dup(test_str, 2, 4, 3); // 'bc' 重複 3 次
|
||||
assert(strcmp(res_str, "bcbcbc") == 0);
|
||||
c_Str_free(res_str);
|
||||
test_log("c_Str_dup (子字串重複複製驗證)");
|
||||
|
||||
// 5. 測試 c_Str_reverse
|
||||
res_str = c_Str_reverse(test_str, 2, 5); // 'bcd' 反轉 -> 'dcb'
|
||||
assert(strcmp(res_str, "dcb") == 0);
|
||||
c_Str_free(res_str);
|
||||
test_log("c_Str_reverse (子字串反轉驗證)");
|
||||
|
||||
// 6. 測試 c_Str_cat
|
||||
res_str = c_Str_cat("XYZ", 1, 0, "123", 1, 3); // "XYZ" + "12" -> "XYZ12"
|
||||
assert(strcmp(res_str, "XYZ12") == 0);
|
||||
c_Str_free(res_str);
|
||||
test_log("c_Str_cat (雙字串區間拼接驗證)");
|
||||
|
||||
// 7. 測試 c_Str_catv (多字串可變參數拼接,必須以 NULL 結尾)
|
||||
res_str = c_Str_catv("ABC", 1, 0, "XYZ", 2, 4, (char*)NULL); // "ABC" + "YZ"
|
||||
assert(strcmp(res_str, "ABCYZ") == 0);
|
||||
c_Str_free(res_str);
|
||||
test_log("c_Str_catv (多參數變長字串拼接驗證)");
|
||||
|
||||
// 8. 測試 c_Str_map (字元映射/置換)
|
||||
// 初始化對照表:將 'b' 換成 'X','d' 換成 'Y'
|
||||
c_Str_map(NULL, 0, 0, "bd", "XY");
|
||||
res_str = c_Str_map(test_str, 1, 0, NULL, NULL); // "abcdefg" -> "aXcYe f g"
|
||||
assert(strcmp(res_str, "aXcYefg") == 0);
|
||||
c_Str_free(res_str);
|
||||
test_log("c_Str_map (字元對照表置換驗證)");
|
||||
|
||||
// 9. 測試 c_Str_cmp
|
||||
// "abcdefg" 區間 [2,4] 是 "bc";"abc" 區間 [2,4] 是 "bc" -> 相等 (0)
|
||||
assert(c_Str_cmp(test_str, 2, 4, "abc", 2, 4) == 0);
|
||||
// "bc" 與 "bcd" 比對 -> 前者較短且完全匹配,預期回傳 -1
|
||||
assert(c_Str_cmp(test_str, 2, 4, "abcdefg", 2, 5) == -1);
|
||||
test_log("c_Str_cmp (區間字串深度比對驗證)");
|
||||
|
||||
// 10. 測試 c_Str_chr (正向尋找字元,回傳 1-based 位置)
|
||||
// 在 "abcdefg" 中找 'c' -> 位於 index 2 -> 回傳 3
|
||||
assert(c_Str_chr(test_str, 1, 0, 'c') == 3);
|
||||
assert(c_Str_chr(test_str, 1, 0, 'z') == 0); // 找不到
|
||||
test_log("c_Str_chr (正向字元查找位置驗證)");
|
||||
|
||||
// 11. 測試 c_Str_rchr (反向尋找字元)
|
||||
// 在 "abcdecd" 中找 'c'
|
||||
assert(c_Str_rchr("abcdecd", 1, 0, 'c') == 6);
|
||||
test_log("c_Str_rchr (反向字元查找位置驗證)");
|
||||
|
||||
// 12. 測試 c_Str_upto (正向尋找集合中任一字元首次出現位置)
|
||||
// "abcdefg" 中尋找 "xyz" 或 "d" -> 'd' 最先被匹配 (index 3) -> 回傳 4
|
||||
assert(c_Str_upto(test_str, 1, 0, "xyz d") == 4);
|
||||
test_log("c_Str_upto (字元集合正向切分點驗證)");
|
||||
|
||||
// 13. 測試 c_Str_rupto (反向尋找集合中任一字元首次出現位置)
|
||||
// "abcdefg" 中反向找 "ab" -> 'b' 最先被找到 (index 1) -> 回傳 2
|
||||
assert(c_Str_rupto(test_str, 1, 0, "ab") == 2);
|
||||
test_log("c_Str_rupto (字元集合反向切分點驗證)");
|
||||
|
||||
// 14. 測試 c_Str_find (正向尋找子字串)
|
||||
// "abcdefg" 中找 "cde" -> 開始於 index 2 -> 回傳 3
|
||||
assert(c_Str_find(test_str, 1, 0, "cde") == 3);
|
||||
test_log("c_Str_find (正向子字串搜尋匹配驗證)");
|
||||
|
||||
// 15. 測試 c_Str_rfind (反向尋找子字串)
|
||||
// "ababax" 中反向找 "aba" -> 應匹配到 index 2 開始的 "aba" -> 回傳 3
|
||||
assert(c_Str_rfind("ababax", 1, 0, "aba") == 3);
|
||||
test_log("c_Str_rfind (反向子字串搜尋匹配驗證)");
|
||||
|
||||
// 16. 測試 c_Str_any (檢查指定 index 的單一字元是否在集合中)
|
||||
// test_str="abcdefg", i=3 -> index 2 ('c')。'c' 有在 "cba" 之中 -> 回傳 index+2 = 4
|
||||
assert(c_Str_any(test_str, 3, "cba") == 4);
|
||||
assert(c_Str_any(test_str, 3, "xyz") == 0); // 'c' 不在 xyz 中
|
||||
test_log("c_Str_any (特定點字元集命中檢查驗證)");
|
||||
|
||||
// 17. 測試 c_Str_many (從指定起點向後匹配連續屬於集合的字元,直到不屬於為止)
|
||||
// "aaabX" 從 1 開始,連續符合 "abc" 的有 'a','a','a','b' (4個) -> 停止於 index 4 -> 回傳 4+1 = 5
|
||||
assert(c_Str_many("aaabX", 1, 0, "abc") == 5);
|
||||
test_log("c_Str_many (正向連續字元集跨越範圍驗證)");
|
||||
|
||||
// 18. 測試 c_Str_rmany (從指定終點向前匹配連續屬於集合的字元)
|
||||
// "Xbbba" 從 0(結尾) 向前,符合 "abc" 的有 'a','b','b','b' (4個) -> 停止於 index 0 ('X') -> 回傳 index+2 = 2
|
||||
assert(c_Str_rmany("Xbbba", 1, 0, "abc") == 2);
|
||||
test_log("c_Str_rmany (反向連續字元集跨越範圍驗證)");
|
||||
|
||||
// 19. 測試 c_Str_match (檢查指定起點是否「精準開頭匹配」該子字串)
|
||||
// "abcdefg" 在位置 3 (index 2, 'c') 是否精準匹配 "cde" -> 是,符合長度 3 -> 回傳 index+len+1 = 2+3+1 = 6
|
||||
assert(c_Str_match(test_str, 3, 0, "cde") == 6);
|
||||
assert(c_Str_match(test_str, 3, 0, "xyz") == 0);
|
||||
test_log("c_Str_match (指定起點子字串精準頭匹配驗證)");
|
||||
|
||||
// 20. 測試 c_Str_rmatch (檢查指定終點是否「精準結尾匹配」該子字串)
|
||||
// "abcde" 在 0(結尾, index 5) 向前看是否精準匹配 "cde" (len=3) -> index 5-3=2 開始是 "cde" -> 成功,回傳 2+1 = 3
|
||||
assert(c_Str_rmatch("abcde", 1, 0, "cde") == 3);
|
||||
test_log("c_Str_rmatch (指定終點子字串精準尾匹配驗證)");
|
||||
|
||||
// 21. 測試 c_Str_fmt (驗證自訂格式化回呼整合)
|
||||
#if 0
|
||||
unsigned char flags[256] = { 0 };
|
||||
mock_put_count = 0;
|
||||
// 傳入字串 "hello", 起點 2 ('e'), 終點 5 ('o' 後方) -> 預期提取出 "ell"
|
||||
run_str_fmt_test(0, flags, 0, 0, "hello", 2, 5);
|
||||
assert(mock_put_count == 3); // "ell" 長度為 3,應呼叫 3 次 put
|
||||
test_log("c_Str_fmt (格式化輸出器區間提取回呼驗證)");
|
||||
#endif
|
||||
|
||||
memset(test_output_buffer, 0, sizeof(test_output_buffer));
|
||||
buf_idx = 0;
|
||||
c_Fmt_t old_handler = c_Fmt_register('S', c_Str_fmt);
|
||||
assert(old_handler == NULL);
|
||||
printf("[PASS] c_Str_fmt successfully registered to specifier character 'S'.\n");
|
||||
|
||||
const char* sample_sentence = "the c-container-library platform";
|
||||
test_printf("Extracted Token: [%S]", sample_sentence, 5, 16);
|
||||
test_output_buffer[buf_idx] = '\0';
|
||||
|
||||
printf(" Rendered Output String: %s\n", test_output_buffer);
|
||||
assert(strcmp(test_output_buffer, "Extracted Token: [c-container]") == 0);
|
||||
printf("[PASS] Core Fmt engine correctly processed parameters via c_Str_fmt.\n");
|
||||
|
||||
printf("\n==================================================\n");
|
||||
printf(" 恭喜!c_Str 基礎工具類共計 22 個核心介面全數單元測試通過!\n");
|
||||
printf("==================================================\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_ArenaAllocator.h>
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef INCLUDED_C_ARENAALLOCATOR_H
|
||||
#define INCLUDED_C_ARENAALLOCATOR_H
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_ARENA_H
|
||||
#include <c_Arena.h>
|
||||
#endif /*INCLUDED_C_ARENA_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
c_Arena_t* arena;
|
||||
}c_ArenaAllocator_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_ArenaAllocator_Init(c_ArenaAllocator_t* self, c_Arena_t* arena) {
|
||||
self->arena = arena;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
C_ALLOCATOR_ALLOC_FN(c_ArenaAllocator_Alloc) {
|
||||
c_ArenaAllocator_t* self = (c_ArenaAllocator_t*)ud;
|
||||
if (!self || !self->arena) return NULL;
|
||||
return c_Arena_Alloc(self->arena, (int)nBytes);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
C_ALLOCATOR_FREE_FN(c_ArenaAllocator_Free) {
|
||||
c_ArenaAllocator_t* self = (c_ArenaAllocator_t*)ud;
|
||||
if (!self || !self->arena) return;
|
||||
c_Arena_Free(self->arena, ptr);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
C_ALLOCATOR_REALLOC_FN(c_ArenaAllocator_Realloc) {
|
||||
c_ArenaAllocator_t* self = (c_ArenaAllocator_t*)ud;
|
||||
if (!self || !self->arena) return NULL;
|
||||
|
||||
// 情况 1: 指针为空,直接分配新内存
|
||||
if (!ptr) {
|
||||
return c_Arena_Alloc(self->arena, (int)nNewSize);
|
||||
}
|
||||
|
||||
// 情况 2: 新大小为 0,直接释放内存
|
||||
if (nNewSize==0) {
|
||||
c_Arena_Free(self->arena, ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// 情况 3: 伙伴块大小不匹配(需要升级或降级阶数),申请新块
|
||||
return c_Arena_Resize(self->arena, ptr, nOldSize, nNewSize);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_Allocator_t c_ArenaAllocator_Build(c_ArenaAllocator_t* self, c_Arena_t* arena) {
|
||||
c_ArenaAllocator_Init(self, arena);
|
||||
c_Allocator_t allocator={0};
|
||||
allocator.ud = self;
|
||||
allocator.alloc = c_ArenaAllocator_Alloc;
|
||||
allocator.free = c_ArenaAllocator_Free;
|
||||
allocator.realloc = c_ArenaAllocator_Realloc;
|
||||
return allocator;
|
||||
}
|
||||
|
||||
#endif /*INCLUDED_C_ARENAALLOCATOR_H*/
|
||||
Reference in New Issue
Block a user