-
Notifications
You must be signed in to change notification settings - Fork 0
[update] 添加了 ek_str 模块,用于支持实现字符串处理
#7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| #ifndef EK_STR_H | ||
| #define EK_STR_H | ||
|
|
||
| #include "../../../ek_conf.h" | ||
|
|
||
| #if EK_STR_ENABLE == 1 | ||
|
|
||
| # include "ek_def.h" | ||
|
|
||
| typedef struct ek_str_t ek_str_t; | ||
|
|
||
| struct ek_str_t | ||
| { | ||
| char *buf; | ||
| uint32_t cap; | ||
| uint32_t len; | ||
| }; | ||
|
|
||
| ek_str_t *ek_str_create(const char *str); | ||
| void ek_str_free(ek_str_t *s); | ||
| void ek_str_clear(ek_str_t *s); | ||
|
|
||
| bool ek_str_append_len(ek_str_t *s, const char *str, uint32_t len); | ||
| bool ek_str_append(ek_str_t *s, const char *str); | ||
| bool ek_str_append_fmt(ek_str_t *s, const char *fmt, ...); | ||
|
|
||
| bool ek_str_cat(ek_str_t *dst, ek_str_t *src); | ||
| ek_str_t *ek_str_slice(const ek_str_t *s, int32_t start, int32_t end); | ||
| const char *ek_str_get_cstring(ek_str_t *s); | ||
|
|
||
| uint32_t ek_str_get_len(ek_str_t *s); | ||
| uint32_t ek_str_get_cap(ek_str_t *s); | ||
|
|
||
| int ek_str_cmp(ek_str_t *s1, ek_str_t *s2); | ||
| int ek_str_ncmp(ek_str_t *s1, ek_str_t *s2, size_t n); | ||
|
|
||
| #endif // EK_STR_ENABLE | ||
|
|
||
| #endif // EK_STR_H | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,211 @@ | ||||||||||||||||||||||||
| #include "../inc/ek_str.h" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| #if EK_STR_ENABLE == 1 | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # include "../inc/ek_io.h" | ||||||||||||||||||||||||
| # include "../inc/ek_mem.h" | ||||||||||||||||||||||||
| # include "../inc/ek_assert.h" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # define INDEX_CLAMP(idx, len) \ | ||||||||||||||||||||||||
| do \ | ||||||||||||||||||||||||
| { \ | ||||||||||||||||||||||||
| if ((idx) < 0) (idx) += (len); \ | ||||||||||||||||||||||||
| if ((idx) < 0) (idx) = 0; \ | ||||||||||||||||||||||||
| if ((idx) > (len)) (idx) = (len); \ | ||||||||||||||||||||||||
| } while (0) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| static bool _ek_str_ensure_cap(ek_str_t *s, uint32_t len) | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| ek_assert_tiny(s != NULL); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (s->cap >= len) return true; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| uint32_t new_cap = s->cap; | ||||||||||||||||||||||||
| do | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| if (new_cap == 0) new_cap = 2; | ||||||||||||||||||||||||
| new_cap += new_cap / 2; | ||||||||||||||||||||||||
| } while (new_cap < len); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| char *buf = NULL; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (s->buf == NULL) buf = ek_malloc(len); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| if (s->buf == NULL) buf = ek_malloc(len); | |
| if (s->buf == NULL) buf = ek_malloc(new_cap); |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ek_str_clear 只把 len 置 0,但如果 buf 已分配,ek_str_get_cstring 仍会返回旧内容(因为未写入 buf[0] = '\0')。建议 clear 时同步写入终止符,确保 C 字符串语义正确。
| s->len = 0; | |
| s->len = 0; | |
| if (s->buf != NULL) | |
| { | |
| s->buf[0] = '\0'; | |
| } |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ek_str_append_len 对 len == 0 做了断言(即使 str != NULL),会导致 ek_str_append(s, "") / ek_str_create("") 直接触发断言;同时 ek_str_slice 在 start == end 时调用 ek_str_create("") 也会因此崩溃。建议把 len == 0(无论 str 是否为 NULL)当作 no-op 并直接返回 true。
| // 如果传入追加内容为空,并且字符长度也为空 | |
| // 什么也不做 | |
| if (str == NULL && len == 0) return true; | |
| ek_assert_tiny(str != NULL); | |
| ek_assert_tiny(len != 0); | |
| // 长度为 0 时,无论 str 是否为 NULL,都视为 no-op | |
| if (len == 0) return true; | |
| // 只有在真正需要读取数据时才要求 str 非空 | |
| ek_assert_tiny(str != NULL); |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ek_str_append_fmt 里 lwvsnprintf 会写入 len + 1(包含 \0),但 _ek_str_ensure_cap 只按 s->len + len 确保容量,可能导致末尾 \0 写越界。建议确保容量至少为 s->len + len + 1。
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ek_str_append 直接对 str 调用 strlen(str);如果调用方传入 NULL 会产生未定义行为/崩溃。建议在这里对 str 做断言或按 ek_str_append_len 的约定将 NULL 视为 no-op。
| { | |
| { | |
| // 按照 ek_str_append_len 的约定:传入 NULL 视为 no-op | |
| if (str == NULL) | |
| { | |
| return true; | |
| } |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ek_str_slice 在 clamp 后没有处理 end < start 的情况;此时 (end - start) 为负数,强转为 uint32_t 会变成超大值,导致巨量分配/越界读取。建议在计算 new_len 前显式处理 end <= start(返回空串或交换 start/end)。
| if (start == end) return ek_str_create(""); | |
| if (end <= start) return ek_str_create(""); |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ek_str_slice 分配了 new_len + 1 字节并写入终止符,但把 new_s->cap 设为 new_len,与其它地方把 cap 当作“可用 buffer 字节数(含 \0)”的用法不一致,会导致后续扩容判断异常(至少会多一次不必要的 realloc)。建议让 cap 与实际分配保持一致。
| new_s->cap = new_len; | |
| new_s->cap = new_len + 1; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,7 @@ int main(void) | |
| stack_test(); | ||
| ringbuf_test(); | ||
| vec_test(); | ||
| str_test(); | ||
|
|
||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,56 @@ | ||||||
| #include "test.h" | ||||||
|
|
||||||
| EK_LOG_FILE_TAG("str_test.c"); | ||||||
|
|
||||||
| void str_test(void) | ||||||
| { | ||||||
| EK_LOG_INFO("str test start"); | ||||||
|
|
||||||
| EK_LOG_INFO("make an empty str"); | ||||||
| ek_str_t *s = ek_str_create(NULL); | ||||||
| EK_LOG_INFO("s:%s", ek_str_get_cstring(s)); | ||||||
| EK_LOG_INFO("before free:%zu used:%zu", ek_heap_unused(), ek_heap_used()); | ||||||
| ek_str_free(s); | ||||||
| EK_LOG_INFO("after free:%zu, used:%zu", ek_heap_unused(), ek_heap_used()); | ||||||
|
|
||||||
| const char *i_am_a_string = "i am a string"; | ||||||
| EK_LOG_INFO("make a str:%s", i_am_a_string); | ||||||
| s = ek_str_create(i_am_a_string); | ||||||
| EK_LOG_INFO("s:%s", ek_str_get_cstring(s)); | ||||||
| EK_LOG_INFO("before free:%zu used:%zu", ek_heap_unused(), ek_heap_used()); | ||||||
| ek_str_free(s); | ||||||
| EK_LOG_INFO("after free:%zu, used:%zu", ek_heap_unused(), ek_heap_used()); | ||||||
|
|
||||||
| const char *hello = "hello"; | ||||||
| EK_LOG_INFO("make a str:%s", hello); | ||||||
| s = ek_str_create(hello); | ||||||
| EK_LOG_INFO("s:%s", ek_str_get_cstring(s)); | ||||||
| EK_LOG_INFO("str append"); | ||||||
| ek_str_append(s, " world!"); | ||||||
| EK_LOG_INFO("s:%s", ek_str_get_cstring(s)); | ||||||
|
|
||||||
| const char *embeddekit = "EmbeddedKit"; | ||||||
| EK_LOG_INFO("before append:%s", ek_str_get_cstring(s)); | ||||||
| EK_LOG_INFO("append:%s", embeddekit); | ||||||
| ek_str_append_fmt(s, "%s", embeddekit); | ||||||
| EK_LOG_INFO("after append:%s", ek_str_get_cstring(s)); | ||||||
|
|
||||||
| EK_LOG_INFO("slice \"%s\",at [12:%u] ", ek_str_get_cstring(s), ek_str_get_len(s)); | ||||||
| ek_str_t *slice = ek_str_slice(s, 12, ek_str_get_len(s)); | ||||||
| EK_LOG_INFO("slice:%s", ek_str_get_cstring(slice)); | ||||||
| ek_str_free(slice); | ||||||
|
|
||||||
| const char *n1netynine99 = " N1netyNine99"; | ||||||
| EK_LOG_INFO("cat test"); | ||||||
| EK_LOG_INFO("make a new string:%s", n1netynine99); | ||||||
| ek_str_t *s1 = ek_str_create(n1netynine99); | ||||||
| EK_LOG_INFO("string:%s", ek_str_get_cstring(s)); | ||||||
|
||||||
| EK_LOG_INFO("string:%s", ek_str_get_cstring(s)); | |
| EK_LOG_INFO("string:%s", ek_str_get_cstring(s1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ek_str.h声明了对外函数接口,但没有像其它 utils API 头文件那样加#ifdef __cplusplus/extern "C"包裹,C++ 工程中包含该头会发生符号名改编导致链接失败。建议参照L2_Core/utils/inc/ek_mem.h:15-18、ek_stack.h:42-45的写法补上。