diff --git a/L2_Core/utils/inc/ek_str.h b/L2_Core/utils/inc/ek_str.h new file mode 100644 index 0000000..b6f120e --- /dev/null +++ b/L2_Core/utils/inc/ek_str.h @@ -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 diff --git a/L2_Core/utils/src/ek_str.c b/L2_Core/utils/src/ek_str.c new file mode 100644 index 0000000..18e71b7 --- /dev/null +++ b/L2_Core/utils/src/ek_str.c @@ -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); + else buf = ek_realloc(s->buf, new_cap); + + if (buf == NULL) return false; + + s->buf = buf; + s->cap = new_cap; + + return true; +} + +ek_str_t *ek_str_create(const char *str) +{ + ek_str_t *s = ek_malloc(sizeof(ek_str_t)); + if (s == NULL) return NULL; + + s->buf = NULL; + s->cap = 0; + s->len = 0; + + // 传入 NULL 则创建一个空的字符串 + if (str == NULL) return s; + + if (ek_str_append(s, str) == false) + { + ek_free(s); + return NULL; + } + + return s; +} + +void ek_str_free(ek_str_t *s) +{ + ek_assert_tiny(s != NULL); + + s->cap = 0; + s->len = 0; + ek_free(s->buf); + ek_free(s); +} + +void ek_str_clear(ek_str_t *s) +{ + ek_assert_tiny(s != NULL); + + s->len = 0; +} + +bool ek_str_append_len(ek_str_t *s, const char *str, uint32_t len) +{ + ek_assert_tiny(s != NULL); + + // 如果传入追加内容为空,并且字符长度也为空 + // 什么也不做 + if (str == NULL && len == 0) return true; + + ek_assert_tiny(str != NULL); + ek_assert_tiny(len != 0); + + if (_ek_str_ensure_cap(s, s->len + len + 1) == false) return false; + memcpy(s->buf + s->len, str, len); + + s->len += len; + s->buf[s->len] = '\0'; + + return true; +} + +bool ek_str_append_fmt(ek_str_t *s, const char *fmt, ...) +{ + ek_assert_tiny(s != NULL); + + // 获取格式化字符串的长度 + va_list args; + va_start(args, fmt); + int len = lwvsnprintf(NULL, 0, fmt, args); + va_end(args); + + if (len < 0) return false; + + if (_ek_str_ensure_cap(s, s->len + len) == false) return false; + + va_start(args, fmt); + // +1 给 \0 + lwvsnprintf(s->buf + s->len, len + 1, fmt, args); + va_end(args); + + s->len += len; + s->buf[s->len] = '\0'; + + return true; +} + +bool ek_str_append(ek_str_t *s, const char *str) +{ + return ek_str_append_len(s, str, strlen(str)); +} + +bool ek_str_cat(ek_str_t *dst, ek_str_t *src) +{ + return ek_str_append_len(dst, src->buf, src->len); +} + +ek_str_t *ek_str_slice(const ek_str_t *s, int32_t start, int32_t end) +{ + ek_assert_tiny(s != NULL); + + int32_t len = (int32_t)s->len; + + INDEX_CLAMP(start, len); + INDEX_CLAMP(end, len); + + if (start == end) return ek_str_create(""); + + uint32_t new_len = (uint32_t)(end - start); + + ek_str_t *new_s = ek_str_create(NULL); + if (new_s == NULL) return NULL; + + new_s->buf = ek_malloc(new_len + 1); + if (new_s->buf == NULL) + { + ek_free(new_s); + return NULL; + } + + memcpy(new_s->buf, s->buf + start, new_len); + new_s->len = new_len; + new_s->cap = new_len; + new_s->buf[new_s->len] = '\0'; + + return new_s; +} + +const char *ek_str_get_cstring(ek_str_t *s) +{ + ek_assert_tiny(s != NULL); + if (s->buf == NULL) return ""; + return s->buf; +} + +uint32_t ek_str_get_len(ek_str_t *s) +{ + ek_assert_tiny(s != NULL); + + return s->len; +} + +uint32_t ek_str_get_cap(ek_str_t *s) +{ + ek_assert_tiny(s != NULL); + + return s->cap; +} + +int ek_str_cmp(ek_str_t *s1, ek_str_t *s2) +{ + ek_assert_tiny(s1 != NULL); + ek_assert_tiny(s2 != NULL); + + return strcmp(ek_str_get_cstring(s1), ek_str_get_cstring(s2)); +} + +int ek_str_ncmp(ek_str_t *s1, ek_str_t *s2, size_t n) +{ + ek_assert_tiny(s1 != NULL); + ek_assert_tiny(s2 != NULL); + + uint32_t len1 = ek_str_get_len(s1); + uint32_t len2 = ek_str_get_len(s2); + + uint32_t shorter_len = (len1 <= len2) ? len1 : len2; + + if (n > shorter_len) n = shorter_len; + + return strncmp(ek_str_get_cstring(s1), ek_str_get_cstring(s2), n); +} + +#endif // EK_STR_ENABLE diff --git a/Test/main.c b/Test/main.c index 87e5b18..2fdfd85 100644 --- a/Test/main.c +++ b/Test/main.c @@ -31,6 +31,7 @@ int main(void) stack_test(); ringbuf_test(); vec_test(); + str_test(); return 0; } diff --git a/Test/str_test.c b/Test/str_test.c new file mode 100644 index 0000000..bbca161 --- /dev/null +++ b/Test/str_test.c @@ -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("cat start"); + ek_str_cat(s, s1); + EK_LOG_INFO("cat result:%s", ek_str_get_cstring(s)); + + ek_str_free(s); + ek_str_free(s1); + EK_LOG_INFO("string finished,unused heap:%zu", ek_heap_unused()); + EK_LOG_INFO("string finished,used heap:%zu", ek_heap_used()); +} diff --git a/Test/test.h b/Test/test.h index 43212ce..39d4c40 100644 --- a/Test/test.h +++ b/Test/test.h @@ -11,6 +11,7 @@ #include "../L2_Core/utils/inc/ek_ringbuf.h" #include "../L2_Core/utils/inc/ek_vec.h" #include "../L2_Core/utils/inc/ek_assert.h" +#include "../L2_Core/utils/inc/ek_str.h" #define PI (3.141592f) @@ -23,5 +24,6 @@ typedef struct void stack_test(void); void ringbuf_test(void); void vec_test(void); +void str_test(void); #endif diff --git a/ek_conf.h b/ek_conf.h index 9141212..2bc8a44 100644 --- a/ek_conf.h +++ b/ek_conf.h @@ -26,6 +26,7 @@ * 模块功能开关 * - EK_EXPORT_ENABLE: 使能自动初始化 * - EK_IO_ENABLE: 使能IO库(基于lwprintf) + * - EK_STR_ENABLE: 使能字符串处理模块 * - EK_LOG_ENABLE: 使能日志模块 * - EK_LIST_ENABLE: 使能链表模块 * - EK_VEC_ENABLE: 使能向量模块 @@ -35,6 +36,7 @@ * ======================================================================== */ #define EK_EXPORT_ENABLE (0) #define EK_IO_ENABLE (1) +#define EK_STR_ENABLE (1) #define EK_LOG_ENABLE (1) #define EK_LIST_ENABLE (1) #define EK_VEC_ENABLE (1)