2026-08-30 12:59:42 +08:00
|
|
|
|
#include <c_StringView.h>
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
|
|
char* c_StringView_ToCStr(const c_StringView_t* self, char* buf, c_size_t buf_size) {
|
|
|
|
|
|
// 边界防御:缓冲区必须有效且大小至少为 1(以便容纳 \0)
|
|
|
|
|
|
if (!buf || buf_size == 0) {
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 边界防御:如果 self 为空或底层无数据,直接赋予空字符串并返回
|
|
|
|
|
|
if (!self || self->size == 0 || !self->str) {
|
|
|
|
|
|
buf[0] = '\0';
|
|
|
|
|
|
return buf;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算实际能够安全拷贝的数据字节数(预留 1 字节给 '\0')
|
|
|
|
|
|
c_size_t max_copy = buf_size - 1;
|
|
|
|
|
|
c_size_t actual_copy = (self->size < max_copy) ? self->size : max_copy;
|
|
|
|
|
|
|
|
|
|
|
|
// 执行内存拷贝(允许中间包含 \0 字符的特殊视图)
|
|
|
|
|
|
if (actual_copy > 0) {
|
|
|
|
|
|
memcpy(buf, self->str, actual_copy);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在缓冲区末尾强行补上标准传统 C 字符串的结束符
|
|
|
|
|
|
buf[actual_copy] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned long c_StringView_ToUL(const c_StringView_t* self, const char** endptr, int base) {
|
|
|
|
|
|
if (!self || self->size == 0 || !self->str) {
|
|
|
|
|
|
if (endptr) *endptr = (self ? self->str : NULL);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* start = self->str;
|
|
|
|
|
|
const char* end = self->str + self->size;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 跳过前导空白字符
|
|
|
|
|
|
while (start < end && isspace((unsigned char)*start)) {
|
|
|
|
|
|
start++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果全是空格,直接返回 0
|
|
|
|
|
|
if (start >= end) {
|
|
|
|
|
|
if (endptr) *endptr = self->str;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 处理正负号 (虽然是无符号转码,标准 strtoul 仍允许 '-' 号并对其求补码)
|
|
|
|
|
|
int negate = 0;
|
|
|
|
|
|
if (*start == '+') {
|
|
|
|
|
|
start++;
|
|
|
|
|
|
} else if (*start == '-') {
|
|
|
|
|
|
negate = 1;
|
|
|
|
|
|
start++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 自动识别进制 (Base == 0) 或校验 16 进制前缀
|
|
|
|
|
|
if (base == 0) {
|
2026-08-30 22:24:45 +08:00
|
|
|
|
if ((c_size_t)(end - start) >= 2 && *start == '0' && (start[1] == 'x' || start[1] == 'X')) {
|
2026-08-30 12:59:42 +08:00
|
|
|
|
base = 16;
|
|
|
|
|
|
start += 2;
|
|
|
|
|
|
} else if (start < end && *start == '0') {
|
|
|
|
|
|
base = 8;
|
|
|
|
|
|
start++;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
base = 10;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (base == 16) {
|
|
|
|
|
|
// 如果显式指定了16进制,允许略过 0x/0X 前缀
|
2026-08-30 22:24:45 +08:00
|
|
|
|
if ((c_size_t)(end - start) >= 2 && *start == '0' && (start[1] == 'x' || start[1] == 'X')) {
|
2026-08-30 12:59:42 +08:00
|
|
|
|
start += 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned long result = 0;
|
|
|
|
|
|
unsigned long cutoff = ULONG_MAX / (unsigned long)base;
|
|
|
|
|
|
int cutlim = ULONG_MAX % (unsigned long)base;
|
|
|
|
|
|
int any_digits = 0;
|
|
|
|
|
|
int overflow = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 核心解析循环,严格受限于底层 View 的 End 边界
|
|
|
|
|
|
while (start < end) {
|
|
|
|
|
|
unsigned char c = (unsigned char)*start;
|
|
|
|
|
|
int digit;
|
|
|
|
|
|
|
|
|
|
|
|
if (isdigit(c)) {
|
|
|
|
|
|
digit = c - '0';
|
|
|
|
|
|
} else if (isalpha(c)) {
|
|
|
|
|
|
digit = tolower(c) - 'a' + 10;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
break; // 遇到非法字符,退出解析
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (digit >= base) {
|
|
|
|
|
|
break; // 超过当前进制最大范围,退出解析
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
any_digits = 1;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查溢出
|
|
|
|
|
|
if (overflow || result > cutoff || (result == cutoff && digit > cutlim)) {
|
|
|
|
|
|
overflow = 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
result = result * (unsigned long)base + (unsigned long)digit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
start++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 组装返回值与写回结束指针
|
|
|
|
|
|
if (endptr) {
|
|
|
|
|
|
*endptr = any_digits ? start : self->str;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (overflow) {
|
|
|
|
|
|
return ULONG_MAX;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return negate ? (unsigned long)(-(long)result) : result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double c_StringView_ToDouble(const c_StringView_t* self, const char** endptr) {
|
|
|
|
|
|
if (!self || self->size == 0 || !self->str) {
|
|
|
|
|
|
if (endptr) *endptr = (self ? self->str : NULL);
|
|
|
|
|
|
return 0.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 跳过前导空白字符
|
|
|
|
|
|
const char* start = self->str;
|
|
|
|
|
|
const char* end = self->str + self->size;
|
|
|
|
|
|
while (start < end && isspace((unsigned char)*start)) {
|
|
|
|
|
|
start++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算实际可能包含有效浮点数据的长度
|
|
|
|
|
|
size_t active_len = end - start;
|
|
|
|
|
|
if (active_len == 0) {
|
|
|
|
|
|
if (endptr) *endptr = self->str;
|
|
|
|
|
|
return 0.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 核心避坑:由于 strtod 需要 \0 结尾,但 StringView 并没有。
|
|
|
|
|
|
// 我们在栈上开辟一个小缓冲区(256字节足够容纳任何有效的 IEEE 754 浮点数字符串,包括极其冗长的科学计数法)。
|
|
|
|
|
|
// 这避免了 malloc 带来的堆内存分配开销,同时保证了线程安全。
|
|
|
|
|
|
char local_buf[256];
|
|
|
|
|
|
size_t copy_len = (active_len < sizeof(local_buf) - 1) ? active_len : (sizeof(local_buf) - 1);
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(local_buf, start, copy_len);
|
|
|
|
|
|
local_buf[copy_len] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 调用标准库进行高精度解析(能完美处理 NaN、Inf 以及复杂的科学计数法扩展)
|
|
|
|
|
|
char* local_endptr = NULL;
|
|
|
|
|
|
double result = strtod(local_buf, &local_endptr);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 将本地缓冲区的相对终止偏移量映射回原始的 StringView 真实指针
|
|
|
|
|
|
if (endptr) {
|
|
|
|
|
|
if (local_endptr == local_buf) {
|
|
|
|
|
|
// 如果 strtod 完全未能识别出任何数字
|
|
|
|
|
|
*endptr = self->str;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ptrdiff_t parsed_offset = local_endptr - local_buf;
|
|
|
|
|
|
*endptr = start + parsed_offset;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
c_index_t c_StringView_FindStr(const c_StringView_t* self, const c_size_t start_pos, const c_StringView_t* needle) {
|
|
|
|
|
|
// 1. 边界与有效性防御
|
|
|
|
|
|
if (!self || !self->str || !needle || !needle->str) {
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 子串为空时,根据标准 C/C++ 行为,默认在有效起点处直接匹配成功
|
|
|
|
|
|
if (needle->size == 0) {
|
|
|
|
|
|
return (start_pos <= self->size) ? (c_index_t)start_pos : -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 起始位置越界,或者子串长度已经超过了可供查找的剩余主串长度
|
|
|
|
|
|
if (start_pos >= self->size || (self->size - start_pos) < needle->size) {
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 核心搜索算法:在主串死边界内进行滑窗 memcmp 匹配
|
|
|
|
|
|
c_size_t max_search_idx = self->size - needle->size;
|
|
|
|
|
|
c_size_t needle_len = needle->size;
|
|
|
|
|
|
const char* haystack = self->str;
|
|
|
|
|
|
|
|
|
|
|
|
for (c_size_t i = start_pos; i <= max_search_idx; i++) {
|
|
|
|
|
|
// 第一步快筛:首字符匹配时再执行深度 memcmp,极大提升非匹配时滑窗的执行效率
|
|
|
|
|
|
if (haystack[i] == needle->str[0]) {
|
|
|
|
|
|
if (memcmp(haystack + i, needle->str, needle_len) == 0) {
|
|
|
|
|
|
return (c_index_t)i; // 找到匹配,返回主串对应下标
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return -1; // 遍历完毕,未找到匹配项
|
|
|
|
|
|
}
|
|
|
|
|
|
|