diff --git a/README.md b/README.md index a4fd2ad..853d33b 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,14 @@ equeue ..> ringbuf : 依赖 | equeue | 队列 | 外部缓存 +## 性能对比 +|应用场合 |ringbuf |segarray | 说明 +|----------|---------|---------|-------------- +|不扩容 |内存优势 | 内存浪费| 嵌入式无堆内存 +|小对象扩容|尚可 | 优秀 | 开销低,后者无拷贝更丝滑 +|大对象扩容|性能较差 | 极致优秀| 前者整体搬移,后者分段扩容 + + ## 接口函数原型 ```c // -------------------- 初始化 -------------------- diff --git a/doc/log.md b/doc/log.md deleted file mode 100644 index fe47068..0000000 --- a/doc/log.md +++ /dev/null @@ -1,11 +0,0 @@ - -# 日志 - -### 2026-05-16 - -### 2026-05-15 -1. ringbuffer,resize扩容,截断的处理代码简化了。 - -### 2026-05-14 -1. darray模块,insert传参obj没有判断NULL,导致crash -2. darray模块,reseze没有处理当realloc之后,size > capacity 的情况 diff --git a/doc/notes.md b/doc/notes.md index 88172ae..cdfe5ae 100644 --- a/doc/notes.md +++ b/doc/notes.md @@ -33,3 +33,58 @@ $$ memory = 2 * mapcap * sizeof(pointer) + objs:capacity $$ ### 5. based on scheme 4, limited to 512 bytes > obj_size < 512 ? segsize=512 : segsize=1 + +## ringbuf + +对于嵌入式场景下(没有malloc,也即不扩容的场景),需要用到的estack和equeue,只选择ringbuf即可。而不使用segarray。 +```c +bool ringbuf_init(struct _ringbuf *self, size_t obj_size, size_t capacity, void *mem_base);| +``` + +### 不扩容场景 +ringbuf比segarray更节省内存。ringbuf只多一个obj_size的空间。 + +$$ + membase:size = objsize * (capacity + 1) +$$ + +而segarray是不确定的,所以segarray就得按照可能的最大空间分配。比如方案4的情况下: +seghead默认是放中间的,方便不需要库容的时候,push_back和push_front一开始不用分配内存。 +则为了保证push_back和push_front都能够用,至少需要三个空间。 + +$$ +mem_base_size = obj_size * (capacity * 3) +$$ + +当然了,若非要将segarray用于嵌入式场景。也可以将seghead初始放在首段开头,那么需要的总内存为: + +$$ +mem_base_size = obj_size * (capacity * 2) +$$ + +**因此,若嵌入式不扩容场景下,ringbuf更节省内存。** +这可能也是Rust的底层VecDeque选择使用ringbuf的原因。 + +### 小文件库容 + +对于小文件,ringbuf库容全内存搬移,还尚可以接受。 +segarray扩容,开新segment,然后指向新的segment,性能也不差。 + +### 大文件扩容 +但对于大文件,ringbuf库容全内存搬移,代价太大。 + +而segarray库容,不会触发大量数据搬移,而是新建一个segment,然后指向新的segment。 + +**因此,对于大文件扩容场景下,segarray远胜于ringbuf** + + + +## 经典问题 + +### 2026-05-15 +1. ringbuffer,resize扩容,截断的处理代码简化了。 + +### 2026-05-14 +> 这就是单元测试的魅力 +1. darray模块,insert传参obj没有判断NULL,导致crash +2. darray模块,resize没有处理当realloc之后,size > capacity 的情况 diff --git a/include/ringbuf.h b/include/ringbuf.h index b474223..31f1d1f 100644 --- a/include/ringbuf.h +++ b/include/ringbuf.h @@ -70,6 +70,7 @@ ringbuf_t ringbuf_new(size_t obj_size, size_t capacity); void ringbuf_free(ringbuf_t* ringbuf); #ifdef UNICSTL_STATIC_MEMORY +// mem_base_size = obj_size * (capacity + 1) bool ringbuf_init(struct _ringbuf *self, size_t obj_size, size_t capacity, void *mem_base); #endif diff --git a/include/segarray.h b/include/segarray.h index 1e6ccd7..6e4fb9b 100644 --- a/include/segarray.h +++ b/include/segarray.h @@ -33,8 +33,7 @@ struct _segarray size_t _segsize; ringbuf_t _map; - size_t _maphead; - size_t _maptail; + ringbuf_t _mapfree; size_t _seghead; size_t _segtail; @@ -50,6 +49,11 @@ struct _segarray bool (*back)(struct _segarray* self, void* obj); bool (*front)(struct _segarray* self, void* obj); + // -------------------- random access -------------------- + bool (*set)(struct _segarray *self, size_t index, const void *obj); // O(1) + bool (*get)(struct _segarray *self, size_t index, void *obj); // O(1) + const void* (*at)(struct _segarray *self, size_t index); // O(1) + // base bool (*resize)(struct _segarray *self, size_t capacity); size_t (*size)(struct _segarray* self); diff --git a/include/unicstl_config.h b/include/unicstl_config.h index 6ad7ebc..0950d66 100644 --- a/include/unicstl_config.h +++ b/include/unicstl_config.h @@ -1,15 +1,16 @@ /** * @file unicstl_config.h * @author wenjf (Orig5826@163.com) - * @brief + * @brief * @version 0.1 * @date 2025-04-22 - * + * * @copyright Copyright (c) 2025 - * + * */ #ifndef _UNICSTL_CONFIG_H_ #define _UNICSTL_CONFIG_H_ +// clang-format off // #define NDEBUG // release mode if define #ifndef NDEBUG @@ -84,4 +85,5 @@ #endif // UNICSTL_DEBUG -#endif // _UNICSTL_CONFIG_H_ +// clang-format on +#endif // _UNICSTL_CONFIG_H_ diff --git a/include/unicstl_internal.h b/include/unicstl_internal.h index a515ad7..6648f98 100644 --- a/include/unicstl_internal.h +++ b/include/unicstl_internal.h @@ -28,37 +28,40 @@ #include "iterator.h" #include "logger.h" -#define UNICSTL_VERSION_MAJOR 0 -#define UNICSTL_VERSION_MINOR 0 -#define UNICSTL_VERSION_MICRO 10 -#define UNICSTL_VERSION ((UNICSTL_VERSION_MAJOR << 16) | (UNICSTL_VERSION_MINOR << 8) | UNICSTL_VERSION_MICRO) - -#define UNICSTL_TOSTRING_(x) #x -#define UNICSTL_TOSTRING(x) UNICSTL_TOSTRING_(x) -#define UNICSTL_VERSION_STRING UNICSTL_TOSTRING(UNICSTL_VERSION_MAJOR) "." UNICSTL_TOSTRING(UNICSTL_VERSION_MINOR) "." UNICSTL_TOSTRING(UNICSTL_VERSION_MICRO) - -#define UNICSTL_UNUSED(x) (void)(x) +// clang-format off +#define UNICSTL_VERSION_MAJOR 0 +#define UNICSTL_VERSION_MINOR 0 +#define UNICSTL_VERSION_MICRO 10 +#define UNICSTL_VERSION ((UNICSTL_VERSION_MAJOR << 16) | (UNICSTL_VERSION_MINOR << 8) | UNICSTL_VERSION_MICRO) +#define UNICSTL_TOSTRING_(x) #x +#define UNICSTL_TOSTRING(x) UNICSTL_TOSTRING_(x) +#define UNICSTL_VERSION_STRING UNICSTL_TOSTRING(UNICSTL_VERSION_MAJOR) "." \ + UNICSTL_TOSTRING(UNICSTL_VERSION_MINOR) "." \ + UNICSTL_TOSTRING(UNICSTL_VERSION_MICRO) /** * @brief default capacity and ratio * */ #ifndef UNICSTL_CAPACITY_INIT -#define UNICSTL_CAPACITY_INIT 8 // 若capacity参数为0时,自动分配默认初始容量 +#define UNICSTL_CAPACITY_INIT 8 // 若capacity参数为0时,自动分配默认初始容量 #endif #ifndef UNICSTL_CAPACITY_MAX -#define UNICSTL_CAPACITY_MAX 8192 // 最大容量 +#define UNICSTL_CAPACITY_MAX 8192 // 最大容量 #endif #ifndef UNICSTL_OBJSIZE_MAX -#define UNICSTL_OBJSIZE_MAX 8192 // 最大对象大小 +#define UNICSTL_OBJSIZE_MAX 8192 // 最大对象大小 #endif #ifndef UNICSTL_STATIC_MEMORY #define UNICSTL_STATIC_MEMORY #endif +#define UNICSTL_UNUSED(x) (void)(x) +// clang-format on + /** * @brief assert function * @@ -78,7 +81,8 @@ extern void _unicstl_assert(const char *expr, const char *file, int line); #else #define unicstl_assert(expr) // assert(expr) -#endif // UNICSTL_ASSERT_ENABLE + +#endif // UNICSTL_ASSERT_ENABLE /** * @brief malloc and free function @@ -115,7 +119,7 @@ extern void unicstl_free(void *ptr); static inline void *unicstl_malloc(size_t size) { return NULL; } static inline void *unicstl_calloc(size_t num, size_t size) { return NULL; } static inline void *unicstl_realloc(void *ptr, size_t size) { return NULL; } -static inline void unicstl_free(void *ptr) { } +static inline void unicstl_free(void *ptr) {} #endif // UNICSTL_MALLOC_ENABLE static inline const void *obj_at(const void *objs, size_t index, size_t obj_size) diff --git a/src/segarray.c b/src/segarray.c index bfa2245..fbceef0 100644 --- a/src/segarray.c +++ b/src/segarray.c @@ -20,15 +20,6 @@ static inline size_t segarray_map_full(struct _segarray *self, size_t capacity) return 0; } -static inline void print_pos(struct _segarray *self) -{ - log_debug("head[%d][%d], tail[%d][%d]\n", - self->_maphead, - self->_seghead, - self->_maptail - 1, - self->_segtail); -} - static bool segarray_push_back(struct _segarray *self, const void *obj) { unicstl_assert(self != NULL); @@ -38,34 +29,47 @@ static bool segarray_push_back(struct _segarray *self, const void *obj) } ringbuf_t map = self->_map; + ringbuf_t mapfree = self->_mapfree; + if (self->_segtail == self->_segsize) { - if (map->full(map)) + if(self->_mapfree->empty(mapfree)) { - log_warn("map->full"); - return false; - // size_t new_capacity = unicstl_new_capacity(self->capacity(self)); - // if (!map->resize(map, new_capacity)) - // { - // log_error("map->resize(map, new_capacity) failed!"); - // return false; - // } + if (map->full(map)) + { + // need resize + log_error("next, you need to realize it"); + return false; + } + else + { + rawbuf_t seg = rawbuf_new(self->_obj_size, self->_capacity); + if (seg == NULL) + { + log_error("rawbuf_new failed!"); + return false; + } + map->push_back(map, &seg); + self->_segtail = 0; + } } - - rawbuf_t seg = rawbuf_new(self->_obj_size, self->_capacity); - if (seg == NULL) + else { - log_error("rawbuf_new failed!"); - return false; + rawbuf_t seg; + if (!mapfree->pop_back(mapfree, &seg)) + { + log_error("mapfree->pop_back failed!"); + return false; + } + if (!map->push_back(map, &seg)) + { + log_error("map->push_back failed!"); + return false; + } + self->_seghead = 0; } - map->push_back(map, &seg); - - self->_maptail = self->_maptail + 1; - self->_segtail = 0; } - print_pos(self); - rawbuf_t seg; if (!map->back(map, &seg)) { @@ -94,35 +98,47 @@ static bool segarray_push_front(struct _segarray *self, const void *obj) } ringbuf_t map = self->_map; + ringbuf_t mapfree = self->_mapfree; + if (self->_seghead == 0) { - log_debug("map->size = %d\n", map->size(map)); - if (map->full(map)) + if(self->_mapfree->empty(mapfree)) { - log_warn("map->full"); - return false; - // size_t new_capacity = unicstl_new_capacity(self->capacity(self)); - // if (!map->resize(map, new_capacity)) - // { - // log_error("map->resize(map, new_capacity) failed!"); - // return false; - // } + if (map->full(map)) + { + // need resize + log_error("next, you need to realize it"); + return false; + } + else + { + rawbuf_t seg = rawbuf_new(self->_obj_size, self->_capacity); + if (seg == NULL) + { + log_error("rawbuf_new failed!"); + return false; + } + map->push_front(map, &seg); + self->_seghead = self->_segsize; + } } - - rawbuf_t seg = rawbuf_new(self->_obj_size, self->_capacity); - if (seg == NULL) + else { - log_error("rawbuf_new failed!"); - return false; + rawbuf_t seg; + if (!mapfree->pop_back(mapfree, &seg)) + { + log_error("mapfree->pop_back failed!"); + return false; + } + if (!map->push_front(map, &seg)) + { + log_error("map->push_back failed!"); + return false; + } + self->_seghead = self->_segsize; } - map->push_front(map, &seg); - - self->_maphead = self->_maphead - 1; - self->_seghead = self->_segsize; } - print_pos(self); - rawbuf_t seg; if (!map->front(map, &seg)) { @@ -150,9 +166,8 @@ static bool segarray_pop_back(struct _segarray *self, void *obj) return false; } - print_pos(self); - ringbuf_t map = self->_map; + ringbuf_t mapfree = self->_mapfree; rawbuf_t seg; size_t index = self->_segtail - 1; @@ -173,10 +188,13 @@ static bool segarray_pop_back(struct _segarray *self, void *obj) if(self->_segtail == 0) { self->_segtail = self->_segsize; - self->_maptail = self->_maptail - 1; map->pop_back(map, &seg); - rawbuf_free(&seg); + if (!mapfree->push_back(mapfree, &seg)) + { + log_error("mapfree->push_back failed!"); + return false; + } } log_info("pop_back success!"); @@ -197,9 +215,9 @@ static bool segarray_pop_front(struct _segarray *self, void *obj) return false; } - print_pos(self); - ringbuf_t map = self->_map; + ringbuf_t mapfree = self->_mapfree; + size_t index = self->_seghead; if(obj != NULL) { @@ -219,11 +237,15 @@ static bool segarray_pop_front(struct _segarray *self, void *obj) if(self->_seghead == self->_segsize) { self->_seghead = 0; - self->_maphead = self->_maphead + 1; rawbuf_t seg; map->pop_front(map, &seg); - rawbuf_free(&seg); + + if (!mapfree->push_back(mapfree, &seg)) + { + log_error("mapfree->push_back failed!"); + return false; + } } log_info("pop_front success!"); @@ -238,15 +260,7 @@ static bool segarray_back(struct _segarray *self, void *obj) { return false; } - - ringbuf_t map = self->_map; - rawbuf_t seg; - if (!map->back(map, &seg)) - { - return false; - } - size_t index = self->_segtail - 1; - return seg->get(seg, index, obj); + return self->get(self, self->size(self) - 1, obj); } static bool segarray_front(struct _segarray *self, void *obj) @@ -256,15 +270,95 @@ static bool segarray_front(struct _segarray *self, void *obj) { return false; } + return self->get(self, 0, obj); +} +static bool segarray_calc_index(struct _segarray *self, size_t index, size_t *map_index, size_t *seg_index) +{ ringbuf_t map = self->_map; - rawbuf_t seg; - if (!map->front(map, &seg)) + rawbuf_t seg = NULL; + size_t map_idx; + size_t seg_idx; + + size_t seg1_left = self->_segsize - self->_seghead; + if(index < seg1_left) + { + map_idx = 0; + seg_idx = self->_seghead + index; + } + else + { + size_t index_left = index - seg1_left; + map_idx = index_left / self->_segsize + 1; + seg_idx = index_left % self->_segsize; + } + *map_index = map_idx; + *seg_index = seg_idx; + return true; +} + +static bool segarray_set(struct _segarray *self, size_t index, const void *obj) +{ + unicstl_assert(self != NULL); + if (index >= self->size(self) || obj == NULL) { return false; } - size_t index = self->_seghead; - return seg->get(seg, index, obj); + + ringbuf_t map = self->_map; + rawbuf_t seg = NULL; + size_t map_index; + size_t seg_index; + + segarray_calc_index(self, index, &map_index, &seg_index); + + if (!map->get(map, map_index, &seg)) + { + return false; + } + return seg->set(seg, seg_index, obj); +} + +static bool segarray_get(struct _segarray *self, size_t index, void *obj) +{ + unicstl_assert(self != NULL); + if (index >= self->size(self) || obj == NULL) + { + return false; + } + ringbuf_t map = self->_map; + rawbuf_t seg = NULL; + size_t map_index; + size_t seg_index; + + segarray_calc_index(self, index, &map_index, &seg_index); + + if (!map->get(map, map_index, &seg)) + { + return false; + } + return seg->get(seg, seg_index, obj); +} + +static const void* segarray_at(struct _segarray *self, size_t index) +{ + unicstl_assert(self != NULL); + if (index >= self->size(self)) + { + return false; + } + ringbuf_t map = self->_map; + rawbuf_t seg = NULL; + size_t map_index; + size_t seg_index; + + segarray_calc_index(self, index, &map_index, &seg_index); + + if (!map->get(map, map_index, &seg)) + { + return false; + } + return seg->at(seg, seg_index); } static bool segarray_resize(struct _segarray *self, size_t capacity) @@ -280,6 +374,7 @@ static bool segarray_resize(struct _segarray *self, size_t capacity) return false; } + log_error("you need to implement this function!"); return true; } @@ -305,9 +400,14 @@ static bool segarray_full(struct _segarray *self) { unicstl_assert(self != NULL); size_t map_size = self->_map->size(self->_map); - - return (self->_maphead == 0 && self->_seghead == 0) || - (self->_maptail == map_size - 1 && self->_segtail == self->_segsize - 1); + if(self->_map->full(self->_map)) + { + if(self->_seghead == 0 || self->_segtail == self->_segsize - 1) + { + return true; + } + } + return false; } static bool segarray_clear(struct _segarray *self) @@ -317,7 +417,6 @@ static bool segarray_clear(struct _segarray *self) self->_seghead = clac_start_index(self->_segsize); self->_segtail = self->_seghead; - self->_maptail = self->_maphead; return true; } @@ -342,10 +441,17 @@ static void segarray_destory(struct _segarray *self) static void segarray_print(struct _segarray *self) { unicstl_assert(self != NULL); + const void *obj = NULL; for (size_t i = 0; i < self->size(self); i++) { - self->print_obj(obj_at(self->obj, self->_obj_size, i)); + obj = self->at(self, i); + if(obj == NULL) + { + log_error("objs[%ld] is NULL", i); + return; + } + self->print_obj(obj); } } @@ -353,7 +459,6 @@ bool segarray_iter_hasnext(struct _iterator *iter) { unicstl_assert(iter != NULL); unicstl_assert(iter->_container != NULL); - segarray_t self = (segarray_t)iter->_container; if (iter->_order == SEGARRAY_FORWARD) @@ -377,52 +482,10 @@ const void *segarray_iter_next(struct _iterator *iter) { unicstl_assert(iter != NULL); unicstl_assert(iter->_container != NULL); - segarray_t self = (segarray_t)iter->_container; - const void *obj = NULL; - size_t index = iter->_index; - ringbuf_t map = self->_map; - rawbuf_t seg = NULL; - size_t map_index; - size_t seg_index; - - if (iter->_order == SEGARRAY_REVERSE) - { - log_debug("index:%ld", index); - index = self->size(self) + 1 - index; - log_debug("reverse-index:%ld", index); - } - - size_t seg1_left = self->_segsize - self->_seghead; - if(index < seg1_left) - { - map_index = 0; - seg_index = self->_seghead + index; - } - else - { - size_t index_left = index - seg1_left; - map_index = index_left / self->_segsize + 1; - seg_index = index_left % self->_segsize; - } - - log_debug("index:%ld, sethead:%ld", index, seg_index); - log_debug("map_index:%ld, seg_index:%ld", map_index, seg_index); - - if (map->get(map, map_index, &seg)) - { - obj = seg->at(seg, seg_index); - if(obj == NULL) - { - return NULL; - } - } - else - { - return NULL; - } + obj = self->at(self, iter->_index); if (iter->_order == SEGARRAY_FORWARD) { iter->_index++; @@ -443,14 +506,13 @@ iterator_t segarray_iter(struct _segarray *self, enum _segarray_order order) iter->_index = 0; iter->_order = order; - //.. if (iter->_order == SEGARRAY_FORWARD) { iter->_index = 0; } else { - iter->_index = self->_map->size(self->_map); + iter->_index = self->size(self) - 1; } iter->hasnext = segarray_iter_hasnext; @@ -494,10 +556,18 @@ bool segarray_init(struct _segarray *self, size_t obj_size, size_t capacity, voi return false; } + self->_mapfree = ringbuf_new(sizeof(rawbuf_t), 8); + if (self->_mapfree == NULL) + { + ringbuf_free(&self->_map); + return false; + } + rawbuf_t seg = rawbuf_new(obj_size, self->_segsize); if (seg == NULL) { ringbuf_free(&self->_map); + ringbuf_free(&self->_mapfree); return false; } // config first obj index in seg array @@ -505,8 +575,6 @@ bool segarray_init(struct _segarray *self, size_t obj_size, size_t capacity, voi self->_segtail = self->_seghead; // add first seg array to map - self->_maphead = 0; - self->_maptail = 1; self->_map->push_back(self->_map, &seg); } @@ -521,6 +589,11 @@ bool segarray_init(struct _segarray *self, size_t obj_size, size_t capacity, voi self->back = segarray_back; self->front = segarray_front; + // -------------------- random access -------------------- + self->set = segarray_set; + self->get = segarray_get; + self->at = segarray_at; + // base self->resize = segarray_resize; self->size = segarray_size; diff --git a/test/test_segarray.c b/test/test_segarray.c index 05b3db4..a0ad410 100644 --- a/test/test_segarray.c +++ b/test/test_segarray.c @@ -372,7 +372,6 @@ static void test_segarray_front_invalid(void) segarray_free(&segarray); } -#if 0 static void test_segarray_set(void) { int temp = 0; @@ -475,7 +474,6 @@ static void test_segarray_at(void) segarray_free(&segarray); } -#endif static void test_segarray_iter(void) { @@ -767,11 +765,12 @@ static void test_segarray_struct(void) TEST_ASSERT_FALSE(segarray->back(segarray, &temp)); } } + TEST_ASSERT_TRUE(segarray->empty(segarray)); for (i = 0; i < len; i++) { TEST_ASSERT_TRUE(segarray->push_front(segarray, &data[i])); - + TEST_ASSERT_TRUE(segarray->front(segarray, &temp)); TEST_ASSERT_EQUAL_INT(data[i].id, temp.id); TEST_ASSERT_EQUAL_STRING(data[i].name, temp.name); @@ -855,13 +854,13 @@ void test_segarray(void) RUN_TEST(test_segarray_front_invalid); // ---------- random access ---------- -// RUN_TEST(test_segarray_set); -// RUN_TEST(test_segarray_set_invalid); + RUN_TEST(test_segarray_set); + RUN_TEST(test_segarray_set_invalid); -// RUN_TEST(test_segarray_at); + RUN_TEST(test_segarray_at); // ---------- base ---------- - // RUN_TEST(test_segarray_iter); + RUN_TEST(test_segarray_iter); // RUN_TEST(test_segarray_resize); // RUN_TEST(test_segarray_resize_invalid); @@ -869,8 +868,8 @@ void test_segarray(void) // RUN_TEST(test_segarray_dynamic); - // RUN_TEST(test_segarray_status); + RUN_TEST(test_segarray_status); // ---------- ext ---------- - // RUN_TEST(test_segarray_struct); + RUN_TEST(test_segarray_struct); }