From fd76d9f34618415474a7a3e1311ea1733f1abc90 Mon Sep 17 00:00:00 2001 From: coffee Date: Wed, 25 Mar 2026 16:24:33 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=A2=9E=E5=8A=A0=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=86=85=E5=AD=98=E5=88=86=E9=85=8D=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 + include/HTlsf.h | 163 ++++++ src/HTlsf.c | 1338 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1519 insertions(+) create mode 100644 include/HTlsf.h create mode 100644 src/HTlsf.c diff --git a/README.md b/README.md index a20656f..832ec62 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,21 @@ ## 基础结构 该库使用字符数组实现不同结构设计, 方便用于单片机固定数组. + +## HTlsf 参数对比 + +可用 `tools/htlsf_config_matrix.sh` 快速比较不同 `HTLSF_CONFIG_SL_INDEX_COUNT_LOG2` / +`HTLSF_CONFIG_FL_INDEX_MAX` 组合下的: + +- 公开开销指标 +- 4KB/8KB/16KB 固定缓冲区可用容量 +- 16KB 池里 16/32/64/128/256 字节请求可连续分配次数 +- 主机目标体积, 以及可选 Cortex-M3 目标体积 +- 对应配置下 `HTlsf` 单元测试是否通过 + +示例: + +```sh +sh tools/htlsf_config_matrix.sh +CONFIGS="4:15 4:14 4:13" RUN_TESTS=0 sh tools/htlsf_config_matrix.sh +``` diff --git a/include/HTlsf.h b/include/HTlsf.h new file mode 100644 index 0000000..f56e53e --- /dev/null +++ b/include/HTlsf.h @@ -0,0 +1,163 @@ +/** + * 日期: 2026-03-22 + * 作者: coffee + * 描述: 参考 TLSF 算法实现的轻量内存池 + * + * 设计目标: + * 1. 不依赖系统堆, 仅管理外部传入的固定内存 + * 2. 保留 TLSF 双层索引和 O(1) 分配/释放特性, 适合高频小块分配 + * 3. 接口保持精简, 统计信息在运行期增量维护, 兼顾查询性能和实现可维护性 + * 4. 不直接依赖外部 tlsf.c/tlsf.h, 便于工程内裁剪和维护 + * + * 可选配置: + * - HTLSF_CONFIG_SL_INDEX_COUNT_LOG2: 二级索引数量对数, 默认4(即16组), 不建议低于4 + * - HTLSF_CONFIG_FL_INDEX_MAX: 最大一级索引位数, 默认15(支持32KB块) + * - HTLSF_DEBUG: 开启内部断言检查, 便于调试, 会增大代码体积 + */ + +#ifndef __H_TLSF_H__ +#define __H_TLSF_H__ + + +#include +#include + + +typedef struct HTlsf +{ + void *mem; ///< 对齐后的控制区起始地址 + size_t memSize; ///< 对齐后可用于 HTlsf 的大小 +} HTlsf; + +typedef void (*HTlsfWalk)(void *ptr, size_t size, uint8_t used, void *arg); + +/** + * @brief TLSF 对齐大小 + */ +size_t HTlsfAlignSize(void); + +/** + * @brief 每次分配的最小额外开销 + */ +size_t HTlsfAllocOverhead(void); + +/** + * @brief 池管理额外开销 + */ +size_t HTlsfPoolOverhead(void); + +/** + * @brief 初始化所需的最小原始内存大小 + * + * 说明: + * 返回值已包含最坏情况下的对齐填充, 可直接用于申请外部缓冲区大小 + */ +size_t HTlsfMinBufferSize(void); + +/** + * @brief 初始化 TLSF 内存池 + * @param htlsf 上下文 + * @param mem 外部缓冲区 + * @param size 缓冲区大小 + * @return 成功返回1, 失败返回0 + */ +uint8_t HTlsfInit(HTlsf *htlsf, void *mem, size_t size); + +/** + * @brief 判断是否已初始化完成 + */ +uint8_t HTlsfIsReady(const HTlsf *htlsf); + +/** + * @brief 重置内存池, 释放所有已分配块 + * + * 说明: + * 不释放外部缓冲区, 仅重新构建 TLSF 控制结构 + */ +uint8_t HTlsfReset(HTlsf *htlsf); + +/** + * @brief 获取当前池的可管理容量 + * + * 说明: + * 返回值为池内可参与分配的块大小总和, 不包含控制结构和池头开销 + */ +size_t HTlsfGetCapacity(const HTlsf *htlsf); + +/** + * @brief 申请内存 + */ +void *HTlsfMalloc(HTlsf *htlsf, size_t size); + +/** + * @brief 申请并清零内存 + */ +void *HTlsfCalloc(HTlsf *htlsf, size_t num, size_t size); + +/** + * @brief 对齐申请内存 + * @param align 对齐字节数, 必须是2的幂 + */ +void *HTlsfMemalign(HTlsf *htlsf, size_t align, size_t size); + +/** + * @brief 重分配内存 + * + * 说明: + * 与标准 realloc 行为一致: + * - ptr == NULL 等价 malloc + * - size == 0 等价 free 并返回NULL + */ +void *HTlsfRealloc(HTlsf *htlsf, void *ptr, size_t size); + +/** + * @brief 释放内存 + */ +void HTlsfFree(HTlsf *htlsf, void *ptr); + +/** + * @brief 获取块大小 + * + * 说明: + * 返回 TLSF 内部块大小, 不是原始请求大小 + */ +size_t HTlsfBlockSize(const void *ptr); + +/** + * @brief 获取已用块大小总和 + * + * 说明: + * 返回 TLSF 内部块大小总和, 用于观察碎片和负载 + * 该值由运行期缓存维护, 查询为 O(1) + */ +size_t HTlsfGetUsedSize(const HTlsf *htlsf); + +/** + * @brief 获取空闲块大小总和 + * + * 说明: + * 返回 TLSF 内部块大小总和 + * 该值由运行期缓存维护, 查询为 O(1) + */ +size_t HTlsfGetFreeSize(const HTlsf *htlsf); + +/** + * @brief 获取当前最大连续空闲块 + * + * 说明: + * 常见场景下为 O(1), 当最大空闲块刚被摘链时会按最高非空桶惰性刷新 + */ +size_t HTlsfGetMaxFreeBlock(const HTlsf *htlsf); + +/** + * @brief 遍历所有块 + */ +void HTlsfWalkPool(const HTlsf *htlsf, HTlsfWalk call, void *arg); + +/** + * @brief 检查 TLSF 内部一致性 + * @return 正常返回1, 异常返回0 + */ +uint8_t HTlsfCheck(const HTlsf *htlsf); + +#endif // __H_TLSF_H__ diff --git a/src/HTlsf.c b/src/HTlsf.c new file mode 100644 index 0000000..2ba679f --- /dev/null +++ b/src/HTlsf.c @@ -0,0 +1,1338 @@ +/* + * HTlsf: + * 参考 TLSF(Two-Level Segregated Fit) 思路实现的固定内存池分配器。 + * + * 核心思路: + * 1. 用“两级索引 + 位图”快速定位满足请求的空闲块 + * 2. 用物理相邻块合并降低外部碎片 + * 3. 控制区和内存池放在同一块外部缓冲区中, 降低额外 RAM 依赖 + * 4. 常用统计量在运行期增量维护, 避免每次查询都线性遍历整个池 + * + * 适用场景: + * - MCU/裸机/RTOS 中的固定内存池 + * - 高频小块分配 + * - 需要可控碎片和稳定时间复杂度的场景 + */ +#include "HTlsf.h" +#include +#include +#include +#include + + +#if defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) || defined(_WIN64) || defined(__LP64__) || defined(__LLP64__) +#define HTLSF_64BIT +#endif + +/* 调试版本打开内部断言, 方便排查块链表和位图不一致问题。 */ +#if defined(HTLSF_DEBUG) +#define HTLSF_ASSERT(exp) assert(exp) +#else +#define HTLSF_ASSERT(exp) ((void)0) +#endif + +/* 基础辅助宏, 统一内部表达方式, 减少重复样板代码。 */ +#define HTLSF_CAST(type, exp) ((type)(exp)) +#define HTLSF_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define HTLSF_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define HTLSF_GLUE2(x, y) x##y +#define HTLSF_GLUE(x, y) HTLSF_GLUE2(x, y) +/* 编译期静态断言, 用于约束位图和平台字长等不变量。 */ +#define HTLSF_STATIC_ASSERT(exp) typedef char HTLSF_GLUE(htlsf_static_assert_, __LINE__)[(exp) ? 1 : -1] + + +HTLSF_STATIC_ASSERT(sizeof(int) * CHAR_BIT == 32); +HTLSF_STATIC_ASSERT(sizeof(size_t) * CHAR_BIT >= 32); +HTLSF_STATIC_ASSERT(sizeof(size_t) * CHAR_BIT <= 64); + + +enum eHTlsfPublic +{ + /* + * 二级索引数量(log2)。 + * 值越大: + * - 分桶更细, 内部碎片更低 + * - 控制结构更大 + * + * 默认取 4, 即 16 个二级桶。 + * 这个值是 TLSF 更稳妥的默认档, 能避免大块区间边界舍入过粗导致的误跳桶。 + */ + kHTlsfSlIndexCountLog2 = +#ifdef HTLSF_CONFIG_SL_INDEX_COUNT_LOG2 + HTLSF_CONFIG_SL_INDEX_COUNT_LOG2, +#else + 4, +#endif +}; + +enum eHTlsfPrivate +{ +#if defined(HTLSF_64BIT) + /* 64 位平台按 8 字节对齐。 */ + kHTlsfAlignSizeLog2 = 3, +#else + /* 32 位平台按 4 字节对齐, 更符合 STM32 这类 MCU 的访问粒度。 */ + kHTlsfAlignSizeLog2 = 2, +#endif + kHTlsfAlignSize = (1 << kHTlsfAlignSizeLog2), + + /* + * 最大一级索引位数。 + * 决定单块最大支持的数量级, 同时也影响控制结构大小。 + * 默认 15 表示支持到 32KB 级别的单块。 + */ + kHTlsfFlIndexMax = +#ifdef HTLSF_CONFIG_FL_INDEX_MAX + HTLSF_CONFIG_FL_INDEX_MAX, +#else + 15, +#endif + kHTlsfSlIndexCount = (1 << kHTlsfSlIndexCountLog2), + /* 一级索引起始位 = 对齐粒度位数 + 二级索引位数。 */ + kHTlsfFlIndexShift = (kHTlsfSlIndexCountLog2 + kHTlsfAlignSizeLog2), + /* 一级桶总数。 */ + kHTlsfFlIndexCount = (kHTlsfFlIndexMax - kHTlsfFlIndexShift + 1), + /* 小块阈值以下直接走第 0 个一级桶。 */ + kHTlsfSmallBlockSize = (1 << kHTlsfFlIndexShift), +}; + +HTLSF_STATIC_ASSERT(kHTlsfSlIndexCount <= (int)(sizeof(unsigned int) * CHAR_BIT)); +HTLSF_STATIC_ASSERT(kHTlsfSlIndexCountLog2 >= 4); +HTLSF_STATIC_ASSERT(kHTlsfFlIndexMax > kHTlsfFlIndexShift); + + +typedef ptrdiff_t HTlsfPtr; + +/* + * 块头布局参考 TLSF: + * 1. size 字段低两位复用为块状态位, 避免额外状态字段 + * 2. 已分配块对外只暴露 size 之后的用户区 + * 3. prevPhysBlock 只在“前一个块为空闲”时有效, 用于 O(1) 向前合并 + */ +typedef struct HTlsfBlockHeader +{ + struct HTlsfBlockHeader *prevPhysBlock; + size_t size; + struct HTlsfBlockHeader *nextFree; + struct HTlsfBlockHeader *prevFree; +} HTlsfBlockHeader; + +typedef struct HTlsfControl +{ + /* 空链表哨兵节点, 所有空桶都指向它, 避免 NULL 特判。 */ + HTlsfBlockHeader blockNull; + /* 一级位图: 哪些一级桶非空。 */ + unsigned int flBitmap; + /* 二级位图: 每个一级桶下哪些二级桶非空。 */ + unsigned int slBitmap[kHTlsfFlIndexCount]; + /* 每个桶对应一条空闲块链表。 */ + HTlsfBlockHeader *blocks[kHTlsfFlIndexCount][kHTlsfSlIndexCount]; + /* 当前所有物理块载荷总和(used + free), split/merge 时增量维护。 */ + size_t totalPayloadSize; + /* 当前已分配块载荷总和。 */ + size_t usedSize; + /* 最大空闲块缓存, 删除最大块时标记 dirty, 查询时惰性刷新。 */ + size_t maxFreeBlock; + uint8_t maxFreeBlockDirty; +} HTlsfControl; + +typedef struct WalkStats +{ + size_t usedSize; + size_t freeSize; + size_t maxFreeBlock; +} WalkStats; + +typedef struct WalkProxy +{ + HTlsfWalk call; + void *arg; +} WalkProxy; + +typedef struct Integrity +{ + int prevStatus; + int status; +} Integrity; + + +/* size 低位状态标记。 */ +static const size_t sBlockHeaderFreeBit = 1 << 0; /* 当前块是否空闲 */ +static const size_t sBlockHeaderPrevFreeBit = 1 << 1; /* 前一个物理块是否空闲 */ +/* 对已分配块而言, 只额外暴露一个 size 字段。 */ +static const size_t sBlockHeaderOverhead = sizeof(size_t); +/* 用户指针从 size 字段后开始。 */ +static const size_t sBlockStartOffset = offsetof(HTlsfBlockHeader, size) + sizeof(size_t); +/* 空闲块至少要放得下 free 链表信息。 */ +static const size_t sBlockSizeMin = sizeof(HTlsfBlockHeader) - sizeof(HTlsfBlockHeader *); +/* 单块最大可管理载荷。 */ +static const size_t sBlockSizeMax = HTLSF_CAST(size_t, 1) << kHTlsfFlIndexMax; +/* 池额外开销: 首块头偏移 + 结尾哨兵块。 */ +static const size_t sPoolOverhead = 2 * sizeof(size_t); + + +static int HTlsfFfs(unsigned int word) +{ + /* 返回最低位 1 的索引, 用于从位图中 O(1) 找到第一个非空桶。 */ + return __builtin_ffs(word) - 1; +} + +static int HTlsfFls(unsigned int word) +{ + /* 返回最高位 1 的索引, 用于计算块大小属于哪个一级桶。 */ + const int bit = word ? 32 - __builtin_clz(word) : 0; + return bit - 1; +} + +static int HTlsfFlsSizeT(size_t size) +{ +#if defined(HTLSF_64BIT) + int high = (int)(size >> 32); + if (high != 0) { + return 32 + HTlsfFls((unsigned int)high); + } +#endif + return HTlsfFls((unsigned int)size); +} + +static size_t AlignUp(size_t value, size_t align) +{ + HTLSF_ASSERT((align & (align - 1)) == 0); + return (value + align - 1) & ~(align - 1); +} + +static size_t AlignDown(size_t value, size_t align) +{ + HTLSF_ASSERT((align & (align - 1)) == 0); + return value & ~(align - 1); +} + +static uintptr_t AlignPtrUp(uintptr_t value, size_t align) +{ + HTLSF_ASSERT((align & (align - 1)) == 0); + return (value + align - 1) & ~((uintptr_t)align - 1); +} + +static uint8_t IsPowerOf2(size_t value) +{ + return value != 0 && (value & (value - 1)) == 0; +} + +static uint8_t IsReady(const HTlsf *htlsf) +{ + return htlsf != NULL && htlsf->mem != NULL && htlsf->memSize != 0; +} + +static size_t BlockSize(const HTlsfBlockHeader *block) +{ + /* 屏蔽掉低两位状态位, 取纯载荷大小。 */ + return block->size & ~(sBlockHeaderFreeBit | sBlockHeaderPrevFreeBit); +} + +static void BlockSetSize(HTlsfBlockHeader *block, size_t size) +{ + const size_t oldSize = block->size; + block->size = size | (oldSize & (sBlockHeaderFreeBit | sBlockHeaderPrevFreeBit)); +} + +static int BlockIsLast(const HTlsfBlockHeader *block) +{ + return BlockSize(block) == 0; +} + +static int BlockIsFree(const HTlsfBlockHeader *block) +{ + return HTLSF_CAST(int, block->size & sBlockHeaderFreeBit); +} + +static void BlockSetFree(HTlsfBlockHeader *block) +{ + block->size |= sBlockHeaderFreeBit; +} + +static void BlockSetUsed(HTlsfBlockHeader *block) +{ + block->size &= ~sBlockHeaderFreeBit; +} + +static int BlockIsPrevFree(const HTlsfBlockHeader *block) +{ + return HTLSF_CAST(int, block->size & sBlockHeaderPrevFreeBit); +} + +static void BlockSetPrevFree(HTlsfBlockHeader *block) +{ + block->size |= sBlockHeaderPrevFreeBit; +} + +static void BlockSetPrevUsed(HTlsfBlockHeader *block) +{ + block->size &= ~sBlockHeaderPrevFreeBit; +} + +static HTlsfBlockHeader *BlockFromPtr(const void *ptr) +{ + return HTLSF_CAST(HTlsfBlockHeader *, HTLSF_CAST(unsigned char *, (void *)ptr) - sBlockStartOffset); +} + +static void *BlockToPtr(const HTlsfBlockHeader *block) +{ + return HTLSF_CAST(void *, HTLSF_CAST(unsigned char *, (void *)block) + sBlockStartOffset); +} + +static HTlsfBlockHeader *OffsetToBlock(const void *ptr, size_t size) +{ + return HTLSF_CAST(HTlsfBlockHeader *, HTLSF_CAST(HTlsfPtr, ptr) + size); +} + +static HTlsfBlockHeader *BlockPrev(const HTlsfBlockHeader *block) +{ + HTLSF_ASSERT(BlockIsPrevFree(block)); + return block->prevPhysBlock; +} + +static HTlsfBlockHeader *BlockNext(const HTlsfBlockHeader *block) +{ + /* + * 下一个物理块 = 当前块用户区起点 + 当前块载荷大小 - 头部对已分配块可见开销。 + * 这样无论块当前空闲还是已分配, 物理遍历公式都一致。 + */ + HTlsfBlockHeader *next = OffsetToBlock(BlockToPtr(block), BlockSize(block) - sBlockHeaderOverhead); + HTLSF_ASSERT(!BlockIsLast(block)); + return next; +} + +static HTlsfBlockHeader *BlockLinkNext(HTlsfBlockHeader *block) +{ + HTlsfBlockHeader *next = BlockNext(block); + next->prevPhysBlock = block; + return next; +} + +static void BlockMarkAsFree(HTlsfBlockHeader *block) +{ + HTlsfBlockHeader *next = BlockLinkNext(block); + BlockSetPrevFree(next); + BlockSetFree(block); +} + +static void BlockMarkAsUsed(HTlsfBlockHeader *block) +{ + HTlsfBlockHeader *next = BlockNext(block); + BlockSetPrevUsed(next); + BlockSetUsed(block); +} + +static size_t AdjustRequestSize(size_t size, size_t align) +{ + size_t adjust = 0; + if (size != 0) { + const size_t maxRequest = sBlockSizeMax - 1; + /* + * 先做上界和整数溢出保护, 再做对齐。 + * 否则极大请求在 align_up 前就可能发生回绕。 + */ + if (size > maxRequest || size > ((size_t)-1) - (align - 1)) { + return 0; + } + + const size_t aligned = AlignUp(size, align); + /* 请求大小至少要能容纳一个最小块, 否则后续无法继续切分。 */ + if (aligned <= maxRequest) { + adjust = HTLSF_MAX(aligned, sBlockSizeMin); + } + } + + return adjust; +} + +static void MappingInsert(size_t size, int *fli, int *sli) +{ + int fl; + int sl; + + if (size < kHTlsfSmallBlockSize) { + /* + * 小块直接线性映射到第 0 级, 降低小尺寸分配时的索引成本, + * 也是 TLSF 在 MCU 场景下保持高频小块性能的关键。 + */ + fl = 0; + sl = HTLSF_CAST(int, size) / (kHTlsfSmallBlockSize / kHTlsfSlIndexCount); + } else { + /* + * 大块按“最高位 + 次级分桶”映射: + * fl 负责数量级, sl 负责该数量级内更细的区间。 + * + * 例如同属于 256~511 区间的块, 会落到同一个 fl, + * 再由 sl 进一步拆成多个细桶。 + */ + fl = HTlsfFlsSizeT(size); + sl = HTLSF_CAST(int, size >> (fl - kHTlsfSlIndexCountLog2)) ^ (1 << kHTlsfSlIndexCountLog2); + fl -= (kHTlsfFlIndexShift - 1); + } + + *fli = fl; + *sli = sl; +} + +static void MappingSearch(size_t size, int *fli, int *sli) +{ + if (size >= kHTlsfSmallBlockSize) { + /* + * 查找时先把请求向上舍入到当前分桶可覆盖的上界, + * 这样拿到的空闲块一定不会比请求小。 + */ + const size_t round = (HTLSF_CAST(size_t, 1) << (HTlsfFlsSizeT(size) - kHTlsfSlIndexCountLog2)) - 1; + size += round; + } + + MappingInsert(size, fli, sli); +} + +static HTlsfBlockHeader *SearchSuitableBlock(HTlsfControl *control, int *fli, int *sli) +{ + int fl = *fli; + int sl = *sli; + /* + * 先在目标一级桶中找“当前 sl 及其之后”的空闲桶; + * 如果没有, 再跳到更大的一级桶。 + */ + unsigned int slMap = control->slBitmap[fl] & (~0U << sl); + if (slMap == 0) { + const unsigned int flMap = control->flBitmap & (~0U << (fl + 1)); + if (flMap == 0) { + return NULL; + } + + fl = HTlsfFfs(flMap); + *fli = fl; + slMap = control->slBitmap[fl]; + } + + HTLSF_ASSERT(slMap != 0); + sl = HTlsfFfs(slMap); + *sli = sl; + return control->blocks[fl][sl]; +} + +static void RemoveFreeBlock(HTlsfControl *control, HTlsfBlockHeader *block, int fl, int sl) +{ + HTlsfBlockHeader *prev = block->prevFree; + HTlsfBlockHeader *next = block->nextFree; + const size_t blockSize = BlockSize(block); + HTLSF_ASSERT(prev != NULL); + HTLSF_ASSERT(next != NULL); + next->prevFree = prev; + prev->nextFree = next; + + if (blockSize == control->maxFreeBlock) { + control->maxFreeBlockDirty = 1; + } + + if (control->blocks[fl][sl] == block) { + control->blocks[fl][sl] = next; + if (next == &control->blockNull) { + /* 当前桶空了, 需要同步清理位图, 保证后续查找正确跳桶。 */ + control->slBitmap[fl] &= ~(1U << sl); + if (control->slBitmap[fl] == 0) { + control->flBitmap &= ~(1U << fl); + } + } + } +} + +static void InsertFreeBlock(HTlsfControl *control, HTlsfBlockHeader *block, int fl, int sl) +{ + HTlsfBlockHeader *current = control->blocks[fl][sl]; + const size_t blockSize = BlockSize(block); + HTLSF_ASSERT(current != NULL); + HTLSF_ASSERT(block != NULL); + /* 头插法即可, 因为桶内不需要保持额外排序。 */ + block->nextFree = current; + block->prevFree = &control->blockNull; + current->prevFree = block; + control->blocks[fl][sl] = block; + control->flBitmap |= (1U << fl); + control->slBitmap[fl] |= (1U << sl); + if (control->maxFreeBlockDirty) { + if (blockSize >= control->maxFreeBlock) { + control->maxFreeBlock = blockSize; + control->maxFreeBlockDirty = 0; + } + } else if (blockSize > control->maxFreeBlock) { + control->maxFreeBlock = blockSize; + } +} + +static void BlockRemove(HTlsfControl *control, HTlsfBlockHeader *block) +{ + int fl; + int sl; + MappingInsert(BlockSize(block), &fl, &sl); + RemoveFreeBlock(control, block, fl, sl); +} + +static void BlockInsert(HTlsfControl *control, HTlsfBlockHeader *block) +{ + int fl; + int sl; + MappingInsert(BlockSize(block), &fl, &sl); + InsertFreeBlock(control, block, fl, sl); +} + +static int BlockCanSplit(HTlsfBlockHeader *block, size_t size) +{ + return BlockSize(block) >= sizeof(HTlsfBlockHeader) + size; +} + +static HTlsfBlockHeader *BlockSplit(HTlsfBlockHeader *block, size_t size) +{ + /* 从原空闲块尾部切出剩余块, 保留前半部分给当前请求。 */ + HTlsfBlockHeader *remaining = OffsetToBlock(BlockToPtr(block), size - sBlockHeaderOverhead); + const size_t remainSize = BlockSize(block) - (size + sBlockHeaderOverhead); + BlockSetSize(remaining, remainSize); + BlockSetSize(block, size); + BlockMarkAsFree(remaining); + return remaining; +} + +static HTlsfBlockHeader *BlockAbsorb(HTlsfBlockHeader *prev, HTlsfBlockHeader *block) +{ + /* 物理相邻块合并时只扩前块长度, 指针关系由 block_link_next 修正。 */ + prev->size += BlockSize(block) + sBlockHeaderOverhead; + BlockLinkNext(prev); + return prev; +} + +static HTlsfBlockHeader *BlockMergePrev(HTlsfControl *control, HTlsfBlockHeader *block) +{ + if (BlockIsPrevFree(block)) { + /* 前块若空闲, 先从空闲链表移除, 再做物理合并。 */ + HTlsfBlockHeader *prev = BlockPrev(block); + HTLSF_ASSERT(prev != NULL); + HTLSF_ASSERT(BlockIsFree(prev)); + BlockRemove(control, prev); + block = BlockAbsorb(prev, block); + control->totalPayloadSize += sBlockHeaderOverhead; + } + + return block; +} + +static HTlsfBlockHeader *BlockMergeNext(HTlsfControl *control, HTlsfBlockHeader *block) +{ + HTlsfBlockHeader *next = BlockNext(block); + HTLSF_ASSERT(next != NULL); + if (BlockIsFree(next)) { + /* 后块若空闲同样先摘链, 避免链表残留旧节点。 */ + BlockRemove(control, next); + block = BlockAbsorb(block, next); + control->totalPayloadSize += sBlockHeaderOverhead; + } + + return block; +} + +static void BlockTrimFree(HTlsfControl *control, HTlsfBlockHeader *block, size_t size) +{ + HTLSF_ASSERT(BlockIsFree(block)); + if (BlockCanSplit(block, size)) { + /* 空闲块比请求大时切分, 避免大块被整块吃掉造成浪费。 */ + HTlsfBlockHeader *remaining = BlockSplit(block, size); + control->totalPayloadSize -= sBlockHeaderOverhead; + BlockLinkNext(block); + BlockSetPrevFree(remaining); + BlockInsert(control, remaining); + } +} + +static void BlockTrimUsed(HTlsfControl *control, HTlsfBlockHeader *block, size_t size) +{ + HTLSF_ASSERT(!BlockIsFree(block)); + if (BlockCanSplit(block, size)) { + /* realloc 缩容后把尾部重新还回空闲链表。 */ + HTlsfBlockHeader *remaining = BlockSplit(block, size); + control->totalPayloadSize -= sBlockHeaderOverhead; + BlockSetPrevUsed(remaining); + remaining = BlockMergeNext(control, remaining); + BlockInsert(control, remaining); + } +} + +static HTlsfBlockHeader *BlockTrimFreeLeading(HTlsfControl *control, HTlsfBlockHeader *block, size_t size) +{ + HTlsfBlockHeader *remaining = block; + if (BlockCanSplit(block, size)) { + /* + * 对齐分配时可能需要丢掉前导空间。 + * 这里把前导空间切成一个新的空闲块,再返回后半段对齐后的块。 + */ + remaining = BlockSplit(block, size - sBlockHeaderOverhead); + control->totalPayloadSize -= sBlockHeaderOverhead; + BlockSetPrevFree(remaining); + BlockLinkNext(block); + BlockInsert(control, block); + } + + return remaining; +} + +static HTlsfBlockHeader *BlockLocateFree(HTlsfControl *control, size_t size) +{ + int fl = 0; + int sl = 0; + HTlsfBlockHeader *block = NULL; + + if (size != 0) { + MappingSearch(size, &fl, &sl); + if (fl < kHTlsfFlIndexCount) { + /* 先用位图锁定桶, 再 O(1) 取出空闲链表头。 */ + block = SearchSuitableBlock(control, &fl, &sl); + } + } + + if (block != NULL) { + HTLSF_ASSERT(BlockSize(block) >= size); + RemoveFreeBlock(control, block, fl, sl); + } + + return block; +} + +static void *BlockPrepareUsed(HTlsfControl *control, HTlsfBlockHeader *block, size_t size) +{ + void *ptr = NULL; + if (block != NULL) { + HTLSF_ASSERT(size != 0); + /* + * 1. 先把多余部分切回空闲链表 + * 2. 再标记当前块为已分配 + * 这样空闲链表始终只包含真正空闲的块 + */ + BlockTrimFree(control, block, size); + BlockMarkAsUsed(block); + control->usedSize += BlockSize(block); + ptr = BlockToPtr(block); + } + + return ptr; +} + +static void ControlConstruct(HTlsfControl *control) +{ + int i; + int j; + + /* 初始化时所有空桶统一指向哨兵节点, 避免后续判空时到处判断 NULL。 */ + control->blockNull.nextFree = &control->blockNull; + control->blockNull.prevFree = &control->blockNull; + control->flBitmap = 0; + control->totalPayloadSize = 0; + control->usedSize = 0; + control->maxFreeBlock = 0; + control->maxFreeBlockDirty = 0; + for (i = 0; i < kHTlsfFlIndexCount; ++i) { + control->slBitmap[i] = 0; + for (j = 0; j < kHTlsfSlIndexCount; ++j) { + control->blocks[i][j] = &control->blockNull; + } + } +} + +static size_t HTlsfControlSize(void) +{ + return sizeof(HTlsfControl); +} + +static size_t HTlsfPoolBytes(size_t bytes) +{ + if (bytes <= sPoolOverhead) { + return 0; + } + + return AlignDown(bytes - sPoolOverhead, kHTlsfAlignSize); +} + +static void *Create(void *mem) +{ + if (((HTlsfPtr)mem % kHTlsfAlignSize) != 0) { + return NULL; + } + + ControlConstruct((HTlsfControl *)mem); + return mem; +} + +static void *AddPool(void *tlsf, void *mem, size_t bytes) +{ + HTlsfBlockHeader *block; + HTlsfBlockHeader *next; + const size_t poolBytes = HTlsfPoolBytes(bytes); + HTlsfControl *control = (HTlsfControl *)tlsf; + + if (((HTlsfPtr)mem % kHTlsfAlignSize) != 0) { + return NULL; + } + + if (poolBytes < sBlockSizeMin || poolBytes > sBlockSizeMax) { + return NULL; + } + + /* + * pool 起始地址对应的是“首个用户区地址”。 + * 为了让块布局统一, 这里故意往前退一个 header_overhead, + * 让块头中的 prevPhysBlock 落在池外, 它永远不会被访问。 + */ + block = OffsetToBlock(mem, -(HTlsfPtr)sBlockHeaderOverhead); + BlockSetSize(block, poolBytes); + BlockSetFree(block); + BlockSetPrevUsed(block); + BlockInsert(control, block); + + /* 在池尾补一个 size 为 0 的哨兵块, 方便遍历和合并终止。 */ + next = BlockLinkNext(block); + BlockSetSize(next, 0); + BlockSetUsed(next); + BlockSetPrevFree(next); + control->totalPayloadSize = poolBytes; + control->usedSize = 0; + control->maxFreeBlock = poolBytes; + control->maxFreeBlockDirty = 0; + return mem; +} + +static void *CreateWithPool(void *mem, size_t bytes) +{ + void *tlsf = Create(mem); + if (tlsf == NULL) { + return NULL; + } + + /* 控制区和池放在同一块外部缓冲区里, 进一步减少额外 RAM 依赖。 */ + if (AddPool(tlsf, (char *)mem + HTlsfControlSize(), bytes - HTlsfControlSize()) == NULL) { + return NULL; + } + + return tlsf; +} + +static void *GetPool(void *tlsf) +{ + return HTLSF_CAST(void *, (char *)tlsf + HTlsfControlSize()); +} + +static HTlsfControl *GetControl(const HTlsf *htlsf) +{ + return (HTlsfControl *)htlsf->mem; +} + +static void *AllocFromControl(HTlsfControl *control, size_t size) +{ + const size_t adjust = AdjustRequestSize(size, kHTlsfAlignSize); + HTlsfBlockHeader *block; + + if (adjust == 0) { + return NULL; + } + + /* 先按尺寸定位空闲块, 再做必要切分。 */ + block = BlockLocateFree(control, adjust); + return BlockPrepareUsed(control, block, adjust); +} + +static void *HTlsfAllocRaw(HTlsf *htlsf, size_t size) +{ + if (!IsReady(htlsf)) { + return NULL; + } + + return AllocFromControl(GetControl(htlsf), size); +} + +static void HTlsfFreeRaw(HTlsf *htlsf, void *ptr) +{ + HTlsfControl *control; + HTlsfBlockHeader *block; + + if (!IsReady(htlsf) || ptr == NULL) { + return; + } + + control = (HTlsfControl *)htlsf->mem; + block = BlockFromPtr(ptr); + /* + * 发布版也主动拦截 double free。 + * 这样即使上层通信状态机异常重复释放, 也不会把空闲链表二次插入导致池结构损坏。 + */ + if (BlockIsFree(block)) { + return; + } + control->usedSize -= BlockSize(block); + /* + * free 的核心流程: + * 1. 当前块标记为空闲 + * 2. 尝试向前合并 + * 3. 尝试向后合并 + * 4. 合并完成后重新插回空闲桶 + */ + BlockMarkAsFree(block); + block = BlockMergePrev(control, block); + block = BlockMergeNext(control, block); + BlockInsert(control, block); +} + +static void *HTlsfMemalignRaw(HTlsf *htlsf, size_t align, size_t size) +{ + HTlsfControl *control; + size_t adjust; + size_t gapMinimum; + size_t sizeWithGap; + size_t alignedSize; + HTlsfBlockHeader *block; + + if (!IsReady(htlsf)) { + return NULL; + } + + control = (HTlsfControl *)htlsf->mem; + adjust = AdjustRequestSize(size, kHTlsfAlignSize); + if (adjust == 0) { + return NULL; + } + + gapMinimum = sizeof(HTlsfBlockHeader); + /* + * 对齐分配的关键点: + * 需要预留“前导间隙”空间, 否则即使找到足够大的块, + * 也可能因为前导碎片太小而无法安全拆分。 + */ + if (adjust > ((size_t)-1) - align - gapMinimum) { + return NULL; + } + sizeWithGap = AdjustRequestSize(adjust + align + gapMinimum, align); + alignedSize = (adjust != 0 && align > kHTlsfAlignSize) ? sizeWithGap : adjust; + if (alignedSize == 0) { + return NULL; + } + block = BlockLocateFree(control, alignedSize); + + if (block != NULL) { + void *ptr = BlockToPtr(block); + void *aligned = (void *)AlignPtrUp((uintptr_t)ptr, align); + size_t gap = (size_t)((uintptr_t)aligned - (uintptr_t)ptr); + + if (gap != 0 && gap < gapMinimum) { + /* 间隙太小时继续向后找下一个对齐点, 保证前导碎片能独立成块。 */ + const size_t gapRemain = gapMinimum - gap; + const size_t offset = HTLSF_MAX(gapRemain, align); + const void *nextAligned = HTLSF_CAST(void *, (uintptr_t)aligned + offset); + aligned = (void *)AlignPtrUp((uintptr_t)nextAligned, align); + gap = (size_t)((uintptr_t)aligned - (uintptr_t)ptr); + } + + if (gap != 0) { + HTLSF_ASSERT(gap >= gapMinimum); + block = BlockTrimFreeLeading(control, block, gap); + } + } + + return BlockPrepareUsed(control, block, adjust); +} + +static void *HTlsfReallocRaw(HTlsf *htlsf, void *ptr, size_t size) +{ + HTlsfControl *control; + void *newPtr = NULL; + + if (!IsReady(htlsf)) { + return NULL; + } + + control = (HTlsfControl *)htlsf->mem; + if (ptr != NULL && size == 0) { + HTlsfFreeRaw(htlsf, ptr); + return NULL; + } + + if (ptr == NULL) { + return HTlsfAllocRaw(htlsf, size); + } + + { + HTlsfBlockHeader *block = BlockFromPtr(ptr); + const size_t currSize = BlockSize(block); + const size_t adjust = AdjustRequestSize(size, kHTlsfAlignSize); + + /* + * 对已经释放的块执行 realloc 会破坏空闲链表关系。 + * 这里统一当作非法输入拒绝处理, 保持现有池状态不变。 + */ + if (BlockIsFree(block)) { + return NULL; + } + /* + * 若请求超出可表示范围, adjust 会变成 0。 + * 这里必须直接失败并保持原块不变, 语义上等价于标准 realloc 的“失败不释放旧块”。 + * 否则继续走到 trim 分支会把 0 当成合法块大小, 导致错误拆块。 + */ + if (adjust == 0) { + return NULL; + } + + /* 请求落在当前块尺寸上时无需任何操作, 直接返回可减少热路径开销。 */ + if (adjust == currSize) { + return ptr; + } + + /* + * 缩容只需要把尾部切回空闲链表, 不必先取 next 块并计算组合大小。 + * 这样能减少高频 jitter 场景下的热路径开销。 + */ + if (adjust < currSize) { + BlockTrimUsed(control, block, adjust); + control->usedSize -= currSize - BlockSize(block); + return ptr; + } + + /* + * 优先尝试原地扩容: + * 只有当后块不空闲或拼接后仍不够时, 才退化为“新分配 + 拷贝”。 + * 这样能显著减少碎片和数据搬移。 + */ + { + HTlsfBlockHeader *next = BlockNext(block); + const size_t combined = currSize + BlockSize(next) + sBlockHeaderOverhead; + + if (!BlockIsFree(next) || adjust > combined) { + newPtr = AllocFromControl(control, size); + if (newPtr != NULL) { + memcpy(newPtr, ptr, HTLSF_MIN(currSize, size)); + HTlsfFreeRaw(htlsf, ptr); + } + } else { + BlockMergeNext(control, block); + BlockMarkAsUsed(block); + BlockTrimUsed(control, block, adjust); + control->usedSize += BlockSize(block) - currSize; + newPtr = ptr; + } + } + } + + return newPtr; +} + +static void PoolWalk(void *pool, void (*walker)(void *ptr, size_t size, int used, void *user), void *user) +{ + HTlsfBlockHeader *block; + if (pool == NULL || walker == NULL) { + return; + } + + /* 从首块开始按物理顺序遍历, 直到遇到结尾哨兵块。 */ + block = OffsetToBlock(pool, -(int)sBlockHeaderOverhead); + while (block != NULL && !BlockIsLast(block)) { + walker(BlockToPtr(block), BlockSize(block), !BlockIsFree(block), user); + block = BlockNext(block); + } +} + +static void IntegrityWalker(void *ptr, size_t size, int used, void *user) +{ + HTlsfBlockHeader *block = BlockFromPtr(ptr); + Integrity *integ = (Integrity *)user; + const int thisPrevStatus = BlockIsPrevFree(block) ? 1 : 0; + const int thisStatus = BlockIsFree(block) ? 1 : 0; + int status = 0; + (void)used; + + if (integ->prevStatus != thisPrevStatus) { + --status; + } + if (size != BlockSize(block)) { + --status; + } + + integ->prevStatus = thisStatus; + integ->status += status; +} + +static int PoolCheck(void *pool) +{ + Integrity integ = {0, 0}; + PoolWalk(pool, IntegrityWalker, &integ); + return integ.status; +} + +static int ControlCheck(void *tlsf) +{ + int i; + int j; + HTlsfControl *control = (HTlsfControl *)tlsf; + int status = 0; + + for (i = 0; i < kHTlsfFlIndexCount; ++i) { + for (j = 0; j < kHTlsfSlIndexCount; ++j) { + /* 检查位图和链表头是否一致。 */ + const int flMap = control->flBitmap & (1U << i); + const int slList = control->slBitmap[i]; + const int slMap = slList & (1U << j); + const HTlsfBlockHeader *block = control->blocks[i][j]; + + if (flMap == 0 && slMap != 0) { + --status; + } + if (slMap == 0) { + if (block != &control->blockNull) { + --status; + } + continue; + } + + if (slList == 0 || block == &control->blockNull) { + --status; + continue; + } + + while (block != &control->blockNull) { + int fli; + int sli; + /* 检查空闲块是否被放进了正确的桶, 并且相邻空闲块已合并。 */ + if (!BlockIsFree(block)) { + --status; + } + if (BlockIsPrevFree(block)) { + --status; + } + if (BlockIsFree(BlockNext(block))) { + --status; + } + if (!BlockIsPrevFree(BlockNext(block))) { + --status; + } + if (BlockSize(block) < sBlockSizeMin) { + --status; + } + + MappingInsert(BlockSize(block), &fli, &sli); + if (fli != i || sli != j) { + --status; + } + block = block->nextFree; + } + } + } + + return status; +} + +static size_t ControlRecalculateMaxFreeBlock(const HTlsfControl *control) +{ + size_t maxFreeBlock = 0; + + if (control->flBitmap != 0) { + const int fl = HTlsfFls(control->flBitmap); + const unsigned int slMap = control->slBitmap[fl]; + const int sl = HTlsfFls(slMap); + const HTlsfBlockHeader *block = control->blocks[fl][sl]; + + while (block != &control->blockNull) { + const size_t blockSize = BlockSize(block); + if (blockSize > maxFreeBlock) { + maxFreeBlock = blockSize; + } + block = block->nextFree; + } + } + + return maxFreeBlock; +} + +static size_t ControlGetMaxFreeBlock(HTlsfControl *control) +{ + if (control->maxFreeBlockDirty) { + control->maxFreeBlock = ControlRecalculateMaxFreeBlock(control); + control->maxFreeBlockDirty = 0; + } + + return control->maxFreeBlock; +} + +static void StatsWalker(void *ptr, size_t size, int used, void *user) +{ + WalkStats *stats = (WalkStats *)user; + (void)ptr; + if (used) { + stats->usedSize += size; + return; + } + + stats->freeSize += size; + if (size > stats->maxFreeBlock) { + stats->maxFreeBlock = size; + } +} + +static void ProxyWalker(void *ptr, size_t size, int used, void *user) +{ + WalkProxy *proxy = (WalkProxy *)user; + if (proxy == NULL || proxy->call == NULL) { + return; + } + + proxy->call(ptr, size, used ? 1 : 0, proxy->arg); +} + +static void CollectStatsFromPool(const HTlsf *htlsf, WalkStats *stats) +{ + if (!IsReady(htlsf) || stats == NULL) { + return; + } + + memset(stats, 0, sizeof(*stats)); + PoolWalk(GetPool(htlsf->mem), StatsWalker, stats); +} + +static size_t GetUsableBytes(size_t bytes) +{ + const size_t need = HTlsfControlSize() + sPoolOverhead; + if (bytes <= need) { + return 0; + } + + return AlignDown(bytes - need, kHTlsfAlignSize); +} + +static uint8_t BuildTlsf(HTlsf *htlsf) +{ + if (htlsf == NULL || htlsf->mem == NULL) { + return 0; + } + + /* 至少要容纳: 控制区 + 池尾哨兵/头开销 + 一个最小可分配块。 */ + if (htlsf->memSize < HTlsfControlSize() + sPoolOverhead + sBlockSizeMin) { + return 0; + } + + if (CreateWithPool(htlsf->mem, htlsf->memSize) == NULL) { + /* 初始化失败时清空上下文, 避免外部误判为可用。 */ + htlsf->mem = NULL; + htlsf->memSize = 0; + return 0; + } + + return 1; +} + +size_t HTlsfAlignSize(void) +{ + return kHTlsfAlignSize; +} + +size_t HTlsfAllocOverhead(void) +{ + return sBlockHeaderOverhead; +} + +size_t HTlsfPoolOverhead(void) +{ + return sPoolOverhead; +} + +size_t HTlsfMinBufferSize(void) +{ + return HTlsfControlSize() + sPoolOverhead + sBlockSizeMin + kHTlsfAlignSize - 1; +} + +uint8_t HTlsfInit(HTlsf *htlsf, void *mem, size_t size) +{ + uintptr_t raw; + uintptr_t aligned; + size_t padding; + + if (htlsf == NULL || mem == NULL) { + return 0; + } + + memset(htlsf, 0, sizeof(*htlsf)); + raw = (uintptr_t)mem; + aligned = AlignPtrUp(raw, kHTlsfAlignSize); + padding = (size_t)(aligned - raw); + if (size <= padding) { + return 0; + } + + htlsf->mem = (void *)aligned; + htlsf->memSize = size - padding; + return BuildTlsf(htlsf); +} + +uint8_t HTlsfIsReady(const HTlsf *htlsf) +{ + return IsReady(htlsf); +} + +uint8_t HTlsfReset(HTlsf *htlsf) +{ + if (!IsReady(htlsf)) { + return 0; + } + + return BuildTlsf(htlsf); +} + +size_t HTlsfGetCapacity(const HTlsf *htlsf) +{ + if (!IsReady(htlsf)) { + return 0; + } + + return GetUsableBytes(htlsf->memSize); +} + +void *HTlsfMalloc(HTlsf *htlsf, size_t size) +{ + return HTlsfAllocRaw(htlsf, size); +} + +void *HTlsfCalloc(HTlsf *htlsf, size_t num, size_t size) +{ + size_t total; + void *ptr; + + if (!IsReady(htlsf) || num == 0 || size == 0) { + return NULL; + } + + if (num > ((size_t)-1) / size) { + return NULL; + } + + total = num * size; + ptr = HTlsfAllocRaw(htlsf, total); + if (ptr != NULL) { + memset(ptr, 0, total); + } + + return ptr; +} + +void *HTlsfMemalign(HTlsf *htlsf, size_t align, size_t size) +{ + if (!IsReady(htlsf) || size == 0) { + return NULL; + } + + if (!IsPowerOf2(align)) { + return NULL; + } + + if (align < kHTlsfAlignSize) { + align = kHTlsfAlignSize; + } + + /* + * 默认对齐已经满足时直接走普通分配路径。 + * 这样能少做一次对齐间隙计算, 也能减少不必要的块切分机会。 + */ + if (align == kHTlsfAlignSize) { + return HTlsfAllocRaw(htlsf, size); + } + + return HTlsfMemalignRaw(htlsf, align, size); +} + +void *HTlsfRealloc(HTlsf *htlsf, void *ptr, size_t size) +{ + return HTlsfReallocRaw(htlsf, ptr, size); +} + +void HTlsfFree(HTlsf *htlsf, void *ptr) +{ + HTlsfFreeRaw(htlsf, ptr); +} + +size_t HTlsfBlockSize(const void *ptr) +{ + if (ptr == NULL) { + return 0; + } + + return BlockSize(BlockFromPtr(ptr)); +} + +size_t HTlsfGetUsedSize(const HTlsf *htlsf) +{ + if (!IsReady(htlsf)) { + return 0; + } + + return GetControl(htlsf)->usedSize; +} + +size_t HTlsfGetFreeSize(const HTlsf *htlsf) +{ + HTlsfControl *control; + + if (!IsReady(htlsf)) { + return 0; + } + + control = GetControl(htlsf); + HTLSF_ASSERT(control->totalPayloadSize >= control->usedSize); + if (control->totalPayloadSize < control->usedSize) { + return 0; + } + return control->totalPayloadSize - control->usedSize; +} + +size_t HTlsfGetMaxFreeBlock(const HTlsf *htlsf) +{ + if (!IsReady(htlsf)) { + return 0; + } + + return ControlGetMaxFreeBlock(GetControl(htlsf)); +} + +void HTlsfWalkPool(const HTlsf *htlsf, HTlsfWalk call, void *arg) +{ + WalkProxy proxy; + if (!IsReady(htlsf) || call == NULL) { + return; + } + + proxy.call = call; + proxy.arg = arg; + PoolWalk(GetPool(htlsf->mem), ProxyWalker, &proxy); +} + +uint8_t HTlsfCheck(const HTlsf *htlsf) +{ + HTlsfControl *control; + WalkStats stats; + + if (!IsReady(htlsf)) { + return 0; + } + + control = GetControl(htlsf); + CollectStatsFromPool(htlsf, &stats); + if (control->totalPayloadSize < control->usedSize) { + return 0; + } + if (control->usedSize != stats.usedSize) { + return 0; + } + if ((control->totalPayloadSize - control->usedSize) != stats.freeSize) { + return 0; + } + if (control->totalPayloadSize != stats.usedSize + stats.freeSize) { + return 0; + } + if (ControlGetMaxFreeBlock(control) != stats.maxFreeBlock) { + return 0; + } + + return ControlCheck(htlsf->mem) == 0 && PoolCheck(GetPool(htlsf->mem)) == 0; +}