166 lines
5.7 KiB
C
166 lines
5.7 KiB
C
#ifndef INCLUDED_C_STRINGVIEW_H
|
|
#define INCLUDED_C_STRINGVIEW_H
|
|
|
|
#ifndef INCLUDED_C_TYPES_H
|
|
#include <c_Types.h>
|
|
#endif /*INCLUDED_C_TYPES_H*/
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
/* */
|
|
|
|
typedef struct {
|
|
const char* str;
|
|
c_size_t size;
|
|
}c_StringView_t;
|
|
|
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
/* */
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
c_StringView_t c_StringView_FromCStr(const char* str) {
|
|
c_StringView_t result;
|
|
result.str = str;
|
|
result.size = str?strlen(str):0;
|
|
return result;
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
c_StringView_t c_StringView_FromParts(const char* str, c_size_t len) {
|
|
c_StringView_t result;
|
|
result.str = str;
|
|
result.size = len;
|
|
return result;
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
c_StringView_t c_StringView_Sub(const c_StringView_t* self, c_size_t start, c_size_t len) {
|
|
c_StringView_t result={0};
|
|
if (start < self->size) {
|
|
result.str = self->str + start;
|
|
result.size = ((start + len) <= self->size)?len:(self->size - start);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
c_StringView_t c_StringView_RemovePrefix(const c_StringView_t* self, c_size_t n) {
|
|
if (n >= self->size) {
|
|
return (c_StringView_t){NULL, 0};
|
|
}
|
|
return (c_StringView_t){self->str + n, self->size - n};
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
c_StringView_t c_StringView_Trim(c_StringView_t* self) {
|
|
c_size_t start = 0;
|
|
c_size_t end = self->size;
|
|
|
|
while (start < end && isspace((unsigned char)self->str[start])) {
|
|
start++;
|
|
}
|
|
while (end > start && isspace((unsigned char)self->str[end - 1])) {
|
|
end--;
|
|
}
|
|
return (c_StringView_t){ self->str + start, end - start };
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
int c_StringView_Cmp(c_StringView_t* sv1, c_StringView_t* sv2) {
|
|
c_size_t min_len = (sv1->size < sv2->size) ? sv1->size : sv2->size;
|
|
int cmp = memcmp(sv1->str, sv2->str, min_len);
|
|
if (cmp != 0) return cmp;
|
|
|
|
if (sv1->size < sv2->size) return -1;
|
|
if (sv1->size > sv2->size) return 1;
|
|
return 0;
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
bool c_StringView_IsEq(c_StringView_t* sv1, c_StringView_t* sv2) {
|
|
if (sv1->size != sv2->size) return false;
|
|
if (sv1->str == sv2->str) return true;
|
|
return memcmp(sv1->str, sv2->str, sv1->size) == 0;
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
bool c_StringView_HasPrefix(c_StringView_t* self, c_StringView_t* prefix) {
|
|
if (self->size < prefix->size) return false;
|
|
return memcmp(self->str, prefix->str, prefix->size) == 0;
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
bool c_StringView_HasSuffix(c_StringView_t* self, c_StringView_t* suffix) {
|
|
if (self->size < suffix->size) return false;
|
|
const char *start = self->str + (self->size - suffix->size);
|
|
return memcmp(start, suffix->str, suffix->size) == 0;
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
c_index_t c_StringView_FindChar(const c_StringView_t* self, const c_size_t start_pos, const char c) {
|
|
if (start_pos >= self->size) return -1;
|
|
for (c_size_t i = start_pos; i < self->size; i++) {
|
|
if (self->str[i] == c) {
|
|
return (c_index_t)i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
C_STATIC_FORCE_INLINE
|
|
bool c_StringView_Split(c_StringView_t * self, const char delim, c_StringView_t* left, c_StringView_t* right) {
|
|
const c_index_t idx = c_StringView_FindChar(self, 0, delim);
|
|
if (idx == -1) {
|
|
if (left) *left = *self;
|
|
if (right) *right = (c_StringView_t){ NULL, 0 };
|
|
return false;
|
|
}
|
|
if (left) *left = c_StringView_Sub(self, 0, idx);
|
|
if (right) *right = c_StringView_Sub(self, idx + 1, self->size - idx - 1);
|
|
return true;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
/* */
|
|
|
|
/**
|
|
* @brief 将 StringView 转换为传统的以 \0 结尾的 C 字符串(输出到外部缓冲区)
|
|
*
|
|
* @param self 指向待转换的 StringView 的指针
|
|
* @param buf 用户提供的外部字符缓冲区指针
|
|
* @param buf_size 外部缓冲区的大小(字节数)
|
|
* @return char* 返回传入的 buf 指针。若输入参数非法或 buf_size 为 0,则返回 NULL
|
|
*/
|
|
char* c_StringView_ToCStr(const c_StringView_t* self, char* buf, c_size_t buf_size);
|
|
|
|
/**
|
|
* @brief 将 StringView 转换为 unsigned long 整数 (模仿 strtoul)
|
|
*
|
|
* @param self 指向待解析的 StringView 的指针
|
|
* @param endptr 可选参数。若不为 NULL,则用于存储解析终止处的字符指针
|
|
* @param base 进制基数 (0 或 2-36)。为 0 时自动识别 0x (16进制), 0 (8进制), 否则默认10进制
|
|
* @return unsigned long 解析出的无符号长整数。若发生溢出返回 ULONG_MAX
|
|
*/
|
|
unsigned long c_StringView_ToUL(const c_StringView_t* self, const char** endptr, int base);
|
|
|
|
/**
|
|
* @brief 将 StringView 转换为高精度双精度浮点数 (模仿 strtod)
|
|
*
|
|
* @param self 指向待解析的 StringView 的指针
|
|
* @param endptr 可选参数。若不为 NULL,则用于存储解析终止处的字符指针
|
|
* @return double 解析出的浮点数。如果发生严重溢出返回 HUGE_VAL 或 -HUGE_VAL
|
|
*/
|
|
double c_StringView_ToDouble(const c_StringView_t* self, const char** endptr);
|
|
|
|
/**
|
|
* @brief 在主 StringView 中查找指定子 StringView 首次出现的位置
|
|
*
|
|
* @param self 指向主 StringView 的指针
|
|
* @param start_pos 开始查找的起始下标位置
|
|
* @param needle 指向待查找子 StringView 的指针
|
|
* @return c_index_t 找到时返回匹配子串在主串中的起始下标(相对 self->str);未找到或参数非法时返回 -1
|
|
*/
|
|
c_index_t c_StringView_FindStr(const c_StringView_t* self, const c_size_t start_pos, const c_StringView_t* needle);
|
|
|
|
#endif /*INCLUDED_C_STRINGVIEW_H*/
|