diff --git a/.clang-format-ignore b/.clang-format-ignore index d34dec8..5b65603 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -1,2 +1,3 @@ # 忽略外部包 -externalModule/* \ No newline at end of file +/test/externalModule/cJSON/** +/test/externalModule/yyjson/** \ No newline at end of file diff --git a/.clang-tidy b/.clang-tidy index e240bd8..4876392 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -27,8 +27,9 @@ # Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-const-correctness,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,-misc-no-recursion,-misc-use-anonymous-namespace,readability-identifier-naming,-misc-include-cleaner' Checks: - - "-*,clang-diagnostic-*,clang-analyzer-*,llvm-*,concurrency-*,performance-*,cert-*" + - "-*,clang-diagnostic-*,clang-analyzer-*,concurrency-*,performance-*,cert-*" # 静默的窄化转换交给编译器来判断? + - 'llvm-*,-llvm-include-order' - 'bugprone-*,-bugprone-easily-swappable-parameters' - 'readability-identifier-naming' - 'misc-*,-misc-const-correctness,-misc-no-recursion,-misc-include-cleaner' diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..1d57af3 --- /dev/null +++ b/.clangd @@ -0,0 +1,10 @@ +Index: + Background: Build # 开启后台索引 + +# 如果你的 compile_commands.json 不在根目录,需要在这里显式指定 +CompileFlags: + CompilationDatabase: ".vscode" + +Diagnostics: + UnusedIncludes: None # 关键:这会关闭“未使用头文件”的提示 + MissingIncludes: None # 可选:关闭“缺失头文件”的提示 diff --git a/.gitignore b/.gitignore index e140c66..592d274 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,5 @@ build default.profdata default.profraw test/fuzzer/corpus -docs fuzz-* \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index d6a342b..4c8a900 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,31 @@ { - "clangd.enable": false, - "C_Cpp.intelliSenseEngine": "default", - "C_Cpp.codeAnalysis.clangTidy.enabled": true, - "c-cpp-flylint.enable": true, + "Lua.codeLens.enable": false, + "Lua.hint.enable": false, + "Lua.completion.enable": false, + "Lua.format.enable": false, + "Lua.hover.enable": false, + "Lua.diagnostics.enable": false, + "Lua.semantic.enable": false, + "Lua.addonManager.enable": false, + "Lua.signatureHelp.enable": false, + "clangd.enable": true, + "clangd.arguments": [], + "liveServer.settings.file": "${workspaceFolder}/coverage/**", + "liveServer.settings.ignoreFiles": [ + "**", // 第一步:先忽略所有文件(简单粗暴) + "!coverage/**" // 第二步:用感叹号 ! 把 coverage 目录“救”回来 + ], + "C_Cpp.intelliSenseEngine": "disabled", + "C_Cpp.errorSquiggles": "disabled", // 关闭微软的波浪线 + "C_Cpp.autocomplete": "disabled", // 关闭微软的自动补全 + "C_Cpp.default.compileCommands": "${workspaceFolder}/.vscode/compile_commands.json", + "C_Cpp.codeAnalysis.clangTidy.enabled": false, + "C_Cpp.codeAnalysis.clangTidy.args": [ + // "--extra-arg=-ferror-limit=1" + // "--extra-arg=-m32", + "--extra-arg=--target=arm-none-eabi-gcc" + ], + "c-cpp-flylint.enable": false, "c-cpp-flylint.cppcheck.severityLevels": { "error": "Error", "warning": "Warning", @@ -12,49 +35,15 @@ "information": "Information" }, "c-cpp-flylint.cppcheck.extraArgs": [ - // "--suppress=constParameterPointer", - // "--suppress=constParameterCallback", + "--suppress=constParameterPointer", + "--suppress=constParameterCallback", "--check-level=exhaustive", - // "--suppress=variableScope", - // "--suppress=unreadVariable", - // "--suppress=constVariablePointer", - // "--suppress=constParameter", + "--suppress=variableScope", + "--suppress=unreadVariable", + "--suppress=constVariablePointer", + "--suppress=constParameter", + "--suppress=unusedStructMember", ], - "files.watcherExclude": { - "**/test/fuzzer/corpus/**": true, - "./docs": true, - "./build": true, - "./.xmake": true, - }, - "files.exclude": { - "**/test/fuzzer/corpus/**": true, - "./docs": true, - "./build": true, - "./.xmake": true, - }, - "files.associations": { - "*.c": "c", - "inttypes.h": "c", - "float.h": "c", - "stdlib.h": "c", - "limits.h": "c", - "stdio.h": "c", - "stdint.h": "c", - "ryanjsontest.h": "c", - "dirent.h": "c", - "valloc.h": "c", - "initializer_list": "c", - "array": "c", - "string_view": "c", - "utility": "c", - "math.h": "c", - "compare": "c", - "type_traits": "c", - "cjson.h": "c", - "ryanjson.h": "c", - "string.h": "c", - "stdarg.h": "c", - "cstdlib": "c", - "ryanjsonconfig.h": "c" - }, + "makefile.configureOnOpen": false, + "liveServer.settings.port": 5501 } \ No newline at end of file diff --git a/Makefile b/Makefile index d13e7be..ce8eaba 100644 --- a/Makefile +++ b/Makefile @@ -1,28 +1,45 @@ -CFLAGS_INC = -I RyanJson -CFLAGS_INC += -I cJSON -CFLAGS_INC += -I yyjson -CFLAGS_INC += -I RyanJsonExample/valloc -CFLAGS_INC += -I RyanJsonExample +# 编译器设置 +CC = gcc +C_FLAGS = -std=gnu99 -O2 -Wall -Wextra -Wno-unused-parameter +# 头文件包含目录 +CFLAGS_INC = -I ./RyanJson +CFLAGS_INC += -I ./example +CFLAGS_INC += -I ./test +CFLAGS_INC += -I ./test/baseTest +CFLAGS_INC += -I ./test/externalModule/valloc +CFLAGS_INC += -I ./test/externalModule/tlsf +CFLAGS_INC += -I ./test/externalModule/cJSON +CFLAGS_INC += -I ./test/externalModule/yyjson +# 源文件扫描 (排除 fuzzer) src = $(wildcard ./RyanJson/*.c) -src += $(wildcard ./cJSON/*.c) -src += $(wildcard ./yyjson/*.c) -src += $(wildcard ./RyanJsonExample/valloc/*.c) -src += $(wildcard ./RyanJsonExample/*.c) +src += $(wildcard ./example/*.c) +src += $(wildcard ./test/*.c) +src += $(wildcard ./test/baseTest/*.c) +src += $(wildcard ./test/externalModule/valloc/*.c) +src += $(wildcard ./test/externalModule/tlsf/*.c) +src += $(wildcard ./test/externalModule/cJSON/*.c) +src += $(wildcard ./test/externalModule/yyjson/*.c) -obj = $(patsubst %.c, %.o, $(src)) -target = app.o -CC = gcc -C_FLAGS = -Wall -Wextra -Wno-unused-parameter -Wformat=2 +# 中间对象 +obj = $(src:.c=.o) + +# 目标程序 - 修改名字避免与源码文件夹 RyanJson 重名 +target = app + +# 默认规则 +all: $(target) $(target): $(obj) - $(CC) $(CFLAGS_INC) $(obj) $(C_FLAGS) -o $(target) -lm + $(CC) $(obj) $(C_FLAGS) -o $(target) -lm +# 编译模式规则 %.o: %.c - $(CC) $(CFLAGS_INC) $(C_FLAGS) -c $< -o $@ -lm + $(CC) $(CFLAGS_INC) $(C_FLAGS) -c $< -o $@ +# 清理规则 .PHONY: clean clean: - rm -rf $(obj) $(target) + rm -f $(obj) $(target) diff --git a/README.md b/README.md index 795eb5f..d38385a 100644 --- a/README.md +++ b/README.md @@ -9,26 +9,24 @@ ### 1、介绍 -**RyanJson** 是一个小巧的 C 语言 JSON 解析器,支持 JSON 文本的解析与生成,针对嵌入式平台的 **低内存占用** 进行了深度优化。 +**RyanJson** 是一个针对嵌入式平台深度优化的 C 语言 JSON 解析器。它在保持代码健壮性的同时,实现了极致的内存控制,旨在解决 cJSON 等传统库在复杂场景下内存占用过高的问题。 *初衷:项目重构后 JSON 结构复杂度提升,cJSON 内存占用过高,无法满足嵌入式场景需求。* #### ✅ 特性亮点 -- 💡 **极致内存优化**:在资源受限设备上实现 **40-70% 内存节省**(对比 cJSON),在 RT-Thread 平台 malloc 头部空间为 12 字节时节省约 40%,无 malloc 头部空间可接近 70%。同时保持工业级健壮性,运行速度与 cJSON 基本持平。 -- 🔍 **模糊测试保障**:基于[LLVM Fuzzer](https://llvm.org/docs/LibFuzzer.html) ,上亿级数据输入分支覆盖率 **99.9%**,确保在各种非法输入和极端场景下依旧安全。**[点击在线查看覆盖率信息](https://ryan-cw-code.github.io/RyanJson/)** -- 🛡️ **运行时安全分析验证**,使用 **[Sanitizer](https://clang.llvm.org/docs/index.html#sanitizers)** 系列工具,捕获内存越界、Use-after-free、数据竞争、未定义行为、内存泄漏等问题,提升代码健壮性与安全性 -- 📐**高质量代码保障** , 引入 **[clang-tidy](https://clang.llvm.org/extra/clang-tidy/#clang-tidy)** 与 **[Cppcheck](https://cppcheck.sourceforge.io/)** 进行静态分析,实现接近语法级的"**零缺陷**",显著提升可维护性与可读性 -- 🤖 **AI 辅助开发与审查**,结合 **[coderabbitai](https://www.coderabbit.ai)** 、 **[Copilot](https://github.com/features/copilot)** 、**[Gemini Code Assist](https://codeassist.google/)**,在编码与代码审查阶段持续优化代码质量,构建多层安全防线 -- 🧪 **9 大类专项测试用例**,覆盖广泛场景,全链路内存泄漏检测,强化稳定性与可靠性 -- ⚙️ **低内存占用**:动态内存扩展方案,内存空间利用率高。 -- 👩‍💻 **开发者友好**:轻松集成,类 cJSON API,迁移成本低。 -- 📜 **严格但不严苛**:符合 RFC 8259 绝大部分JSON标准,支持无限的Json嵌套级别(需注意堆栈空间)、灵活的配置修改项 -- 🔧 **可扩展性**:允许注释(需调用mini函数清除注释后再解析)、尾随逗号等无效字符(parse时可配置是否允许)等 +- 💡 **极致内存优化:** 通过动态内存扩展与紧凑结构设计,相比 cJSON 减少 **50% 左右的内存占用**。 +- 🔍 **模糊测试保障:** 基于[LLVM Fuzzer](https://llvm.org/docs/LibFuzzer.html) 生成上亿级测试用例,**分支覆盖率 100%**,确保非法输入和极端场景下依旧安全。**[点击在线查看覆盖率信息](https://ryan-cw-code.github.io/RyanJson/)** +- 🧪 **9 大类专项测试用例:** 覆盖广泛场景,全链路内存泄漏检测,强化稳定性与可靠性 +- 🛡️ **运行时安全分析验证:** 使用 **[Sanitizer](https://clang.llvm.org/docs/index.html#sanitizers)** 系列工具,捕获内存越界、Use-after-free、数据竞争、未定义行为、内存泄漏等问题,提升代码健壮性与安全性 +- 📐**高质量代码保障:** 引入 **[clang-tidy](https://clang.llvm.org/extra/clang-tidy/#clang-tidy)** 与 **[Cppcheck](https://cppcheck.sourceforge.io/)** 进行静态分析,代码质量接近语法级的"**零缺陷**" +- 🤖 **AI 辅助开发与审查:** 结合 **[Gemini Code Assist](https://codeassist.google/)** 、**[coderabbitai](https://www.coderabbit.ai)** 、 **[Copilot](https://github.com/features/copilot)** ,在编码与代码审查阶段持续优化代码质量,构建多层安全防线 +- 👩‍💻 **开发者友好:** 类 cJSON 接口设计,迁移成本低 +- 📜 **严格但不严苛:** 符合 **[RFC 8259](https://github.com/nst/JSONTestSuite)** 绝大部分标准,支持无限嵌套(受限于栈空间),支持注释与尾随逗号(可配置) ### 2、设计 -**RyanJson设计时大量借鉴了 [json](https://api.gitee.com/Lamdonn/json) 和 [cJSON](https://github.com/DaveGamble/cJSON) ! ** +**RyanJson设计时借鉴了 [json](https://api.gitee.com/Lamdonn/json) 和 [cJSON](https://github.com/DaveGamble/cJSON) !** Json语法是**JavaScript**对象语法的子集,可通过下面两个连接学习json语法。 @@ -36,7 +34,7 @@ Json语法是**JavaScript**对象语法的子集,可通过下面两个连接 [Parsing JSON is a Minefield 建议看看](https://seriot.ch/projects/parsing_json.html) -在RyanJson中,**结构体表示最小存储单元(键值对)**,通过单链表组织,结构如下: +RyanJson 的核心在于对内存布局的精细控制,**结构体表示最小存储单元(键值对)**,通过单链表组织数据,结构如下: ```c // Json 的最基础节点,所有 Json 元素都由该节点表示。 @@ -46,13 +44,20 @@ struct RyanJsonNode { struct RyanJsonNode *next; // 单链表节点指针 - /* - * 在 next 后紧跟一个字节的 flag,用于描述节点的核心信息: + /** + * @brief RyanJson 节点结构体 + * 每个节点由链表连接,包含元数据标识 (Flag) 与动态载荷存储区。 * - * 位分布如下: + * 内存布局: + * [ next指针 | flag(1字节) | padding/指针空间 | 动态载荷区 ] + * + * @brief 节点元数据标识 (Flag) + * 紧跟 next 指针后,利用 1 字节位域描述节点类型及存储状态。 + * + * flag 位分布定义: * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 * ----------------------------------------------------- - * 保留 KeyLen KeyLen HasKey NumExt Type2 Type1 Type0 + * strMode KeyLen KeyLen HasKey NumExt Type2 Type1 Type0 * * 各位含义: * - bit0-2 : 节点类型 @@ -61,192 +66,233 @@ struct RyanJsonNode * * - bit3 : 扩展位 * Bool 类型:0=false, 1=true - * Number 类型:0=int, 1=double + * Number 类型:0=int(4字节), 1=double(8字节) * * - bit4 : 是否包含 Key * 0=无 Key(数组元素) * 1=有 Key(对象成员) * * - bit5-6 : Key 长度字段字节数 - * 00=1字节 (≤255) - * 01=2字节 (≤65535) - * 10=3字节 (≤16M) + * 00=1字节 (≤UINT8_MAX) + * 01=2字节 (≤UINT16_MAX) + * 10=3字节 (≤UINT24_MAX) * 11=4字节 (≤UINT32_MAX) * - * - bit7 : 保留位(未来可用于压缩标记、特殊类型等) - */ - - /* - * flag 后若节点包含 key / strValue,则跟随一个指针, - * 指向存储区:[ keyLen | key | stringValue ] - * 其中 keyLen 的大小由 flag 中的长度信息决定(最多 4 字节)。 - * - * 在指针之后,根据节点类型存储具体数据: - * - null / bool : 由 flag 表示 - * - string : 由上述指针指向 - * - number : 根据 flag 决定存储 int(4字节) 或 double(8字节) - * - object : 动态分配空间存储子节点,链表结构如下: - * - * { - * "name": "RyanJson", - * next ( - * "version": "xxx", - * next ( - * "repository": "https://github.com/Ryan-CW-Code/RyanJson", - * next ( - * "keywords": ["json", "streamlined", "parser"], - * next ( - * "others": { ... } - * ))) - * } - */ - - /* - * 设计特点: - * - 一个 Json 节点最多 malloc 两次(一次节点本身,一次可选的 key/stringValue), - * 对嵌入式系统非常友好,减少 malloc 头部开销, 尽可能的减少内存碎片。 + * - bit7 : 表示key / strValue 存储模式 + * 0:inline 模式, 1=ptr 模式 * - * - key 和 stringValue 必须通过指针管理: - * * 如果直接放在节点里,虽然只需一次 malloc, - * 但修改场景会遇到替换/释放困难。 - * * 用户可能传递的 Json 对象不是指针,无法直接替换节点, - * 要求应用层传递指针会增加侵入性,不符合“应用层无需修改”的目标。 + * @brief 动态载荷存储区 + * 目的: + * - 在保持 API 易用性和稳定性的同时,最大限度减少 malloc 调用次数。 + * - 尤其在嵌入式平台,malloc 代价高昂:不仅有堆头部空间浪费,还会产生内存碎片。 + * - 通过利用结构体内的对齐填充 (Padding) 和指针空间,形成一个灵活的缓冲区。 + * + * 存储策略: + * 利用结构体内存对齐产生的 Padding(如 Flag 后的空隙)以及原本用于存储指针的空间,形成一个缓冲区 + * 若节点包含 key / strValue,则可能有两种方案: + * 1. inline 模式 (小数据优化) + * - 当 (KeyLen + Key + Value) 的总长度 ≤ 阈值时,直接存储在结构体内部。 + * - 阈值计算公式: + * 阈值 = Padding + sizeof(void*) + (malloc头部空间的一半),再向上对齐到字节边界。 + * 举例: + * - 内存对齐:4字节 + * - malloc头部空间:8字节 + * - 可用空间 = 3 (flag后padding) + 4 (指针空间) + 4 (malloc头部一半) + * - 向上对齐后得到阈值12字节 + * - 存储布局: + * [ KeyLen | Key | Value ] + * 起始地址即为 flag 之后,数据紧凑排列,无需额外 malloc。 + * + * 2. ptr 模式 (大数据) + * - 当数据长度 > 阈值时,结构体存储一个指针,指向独立的堆区。 + * - 存储布局: + * [ KeyLen | *ptr ] -> (ptr指向) [ Key | Value ] + * - KeyLen 的大小由 flag 中的长度字段决定 (最多 4 字节)。 + * - 这样保证大数据不会撑爆结构体,同时保持 API 一致性。 + + * 其他类型的存储: + * - null / bool : 由 flag 位直接表示,无需额外空间。 + * - number : 根据 flag 扩展位决定存储 int(4字节) 或 double(8字节)。 + * - object : 动态分配空间存储子节点,采用链表结构。 + * + * 设计考量: + * - malloc 在嵌入式平台的开销: + * * RTT 最小内存管理算法中,malloc 头部约 12 字节(可以考虑tlsf算法头部空间仅4字节,内存碎片也控制的很好,适合物联网应用)。 + * * 一个 RyanJson 节点本身可能只有个位数字节,头部空间就让内存占用翻倍。 + * - 因此: + * * 小数据尽量 inline 存储,避免二次 malloc。 + * * 大数据 fallback 到 ptr 模式,保证灵活性。 + * - 修改场景: + * * 理想情况:节点结构体后面直接跟 key/strValue,修改时释放并重新申请节点。 + * * 但这样 changKey/changStrValue 接口改动太大,用户层需要修改指针,代价高。 + * * 实际策略:提供就地修改接口。 + * - 若新值长度 ≤ 原有 inline 缓冲区,直接覆盖。 + * - 若超过阈值,自动切换到 ptr 模式,用户层无需关心。 * - * - 因此采用指针方式,保证灵活性和低侵入性。 + * 链表结构示例: + * { + * "name": "RyanJson", + * next ( + * "version": "xxx", + * next ( + * "repository": "https://github.com/Ryan-CW-Code/RyanJson", + * next ( + * "keywords": [ + * "json", + * next ( + * "streamlined", + * next ( + * "parser" + * )) + * ], + * next ( + * "others": { ... } + * } */ }; typedef struct RyanJsonNode *RyanJson_t; ``` -### 3、测试体系 +### 3、测试与质量保障 -**LLVM模糊测试**(核心亮点),模糊测试是 RyanJson 的 **核心稳定性保障**。 +#### 🧪 专项基础功能测试 + +| 测试类别 | 测试目标 | +| ------------------------ | ------------------------------------------------------- | +| **修改 Json 节点测试** | 验证字段动态更新及存储模式(Inline/Ptr)的自动切换逻辑 | +| **比较 Json 节点测试** | 验证节点及其属性的递归深度一致性比较与逻辑等价性 | +| **创建 Json 节点树测试** | 验证全类型节点的构造初始化及深层嵌套结构的正确性 | +| **删除 Json 节点测试** | 验证节点及其子项的递归内存回收机制,防止内存泄漏 | +| **分离 Json 节点测试** | 验证节点的分离操作、所属权转移及引用关系的生命周期管理 | +| **复制 Json 节点测试** | 验证对象深拷贝 (Deep Copy) 的数据完整性与拓扑结构一致性 | +| **Json 循环测试** | 验证数组/对象迭代器在不同数据规模下的稳定性与边界行为 | +| **文本解析 Json 测试** | 验证复杂 JSON 文本解析的健壮性及内存映射的准确性 | +| **替换 Json 节点测试** | 验证成员节点就地替换时的内存复用策略与逻辑一致性 | + +#### 🔍**LLVM Fuzzer 模糊测试**(核心亮点) + +LLVM Fuzzing 模糊测试是 RyanJson 的 **核心稳定性保障**。 **[点击在线查看覆盖率信息](https://ryan-cw-code.github.io/RyanJson/)** -- **千万级测试样本**:[LLVM Fuzzer](https://llvm.org/docs/LibFuzzer.html) 自动生成并执行上亿级随机与非法 JSON 输入。 -- **覆盖率极高**:分支覆盖率 **99.9%**,无崩溃、无泄漏。 -- **鲁棒性验证**:内存申请失败、扩容失败、非法转义字符、尾随逗号、嵌套过深、随机类型切换。 -- **内存安全验证**:结合 Sanitizer 工具链,确保无泄漏、无悬空指针、无越界。 - -| 测试类别 | 测试目标 | -| ------------------------------------- | ------------------------ | -| 内存故障测试 | 验证内存不足时的健壮性 | -| 解析 Json 节点测试 | 随机非法/合法输入解析 | -| 循环遍历删除节点测试 | 确保链表删除安全性 | -| 循环遍历分离节点测试 | 验证节点分离逻辑正确性 | -| Json 压缩去除转义测试 | 检查转义字符处理健壮性 | -| Json 打印和解析测试 | 序列化与反序列化一致性 | -| 循环遍历获取 Value 测试 | 确保随机访问稳定性 | -| 循环遍历随机复制 Json 节点测试 | 验证深拷贝与浅拷贝安全性 | -| 循环遍历随机修改节点类型/创建节点测试 | 动态扩展与类型切换能力 | - -#### 📊 手写的专项基础测试用例 - -| 测试类别 | 测试目标 | -| -------------------- | --------------------------- | -| 文本解析 Json 测试 | 基础解析与加载能力验证 | -| 创建 Json 节点树测试 | 节点生成与结构正确性检查 | -| 修改 Json 节点测试 | 字段修改的条件覆盖与正确性 | -| 删除 Json 节点测试 | 各类删除场景与边界条件验证 | -| 分离 Json 节点测试 | 节点分离与引用关系稳定性 | -| 替换 Json 节点测试 | 节点替换行为与一致性验证 | -| 比较 Json 节点测试 | 节点比较与等价性一致性 | -| 复制 Json 节点测试 | 深拷贝/浅拷贝的语义与完整性 | -| Json 循环测试 | 遍历性能与迭代稳定性 | - -### 4、代码质量与规范 - -#### ✅ 工具链全面集成 +- **上亿级测试样本**:[LLVM Fuzzer](https://llvm.org/docs/LibFuzzer.html) 自动生成并执行上亿级随机与非法 JSON 输入 +- **覆盖率极高**:**分支覆盖率 100%**(希望以后也能保持),无崩溃、无泄漏 +- **鲁棒性验证**:内存申请失败、扩容失败、非法转义字符、尾随逗号、嵌套过深、随机类型切换 +- **内存安全验证**:结合 **[Sanitizer](https://clang.llvm.org/docs/index.html#sanitizers)** 工具链,确保无泄漏、无悬空指针、无越界 + +| 测试类别 | 测试目标 | +| -------------------------- | --------------------------------------------------------- | +| **内存故障模拟测试** | 验证在堆内存耗尽 (OOM) 场景下的异常回滚及系统稳定性 | +| **随机解析鲁棒性测试** | 验证对非法语法、畸形字符及极端输入的容错与边界防御能力 | +| **循环遍历删除安全性测试** | 验证链表迭代过程中动态删除节点的双向一致性与指针安全性 | +| **循环遍历分离安全性测试** | 验证在高频率迭代中分离节点后的拓扑重构与内存权属逻辑 | +| **转义序列极致压缩测试** | 验证复杂及异常转义字符在高效压缩过程中的解析完整性 | +| **序列化回环一致性测试** | 验证 JSON 对象经过“解析-打印-解析”链路后数据的不失真性 | +| **高频迭代值读取测试** | 验证在不同层级结构下随机访问 Value 字段的寻址效率与稳定性 | +| **随机压力复制安全测试** | 验证大规模深拷贝过程中内存池的利用效率与拓扑结构安全性 | +| **动态类型切换压力测试** | 验证节点在运行期进行类型强制转换与动态扩展时的内存安全性 | + +#### 🛡️ 工具链全面集成 | 工具 | 用途 | | ------------------------------------------------------------ | ------------------------------------------------------------ | -| **[Sanitizer](https://clang.llvm.org/docs/index.html#sanitizers)** | 运行时捕获内存与线程安全问题 | +| **[Sanitizer](https://clang.llvm.org/docs/index.html#sanitizers)** | 运行时检测,捕获内存泄漏、越界、数据竞争。杜绝泄漏、越界、悬空指针 | | **[clang-tidy](https://clang.llvm.org/extra/clang-tidy/#clang-tidy)** | 静态分析潜在缺陷(空指针、资源泄漏等) | | **[Cppcheck](https://cppcheck.sourceforge.io/)** | 深度扫描内存与资源问题 | | **[ClangFormat](https://clang.llvm.org/docs/ClangFormat.html)** | 统一代码风格 | +| AI 审查 | **[Gemini Code Assist](https://codeassist.google/)** 、**[coderabbitai](https://www.coderabbit.ai)** 、 **[Copilot](https://github.com/features/copilot)** 辅助优化逻辑,构建多层安全防线 | | **编译器警告** | `-Wall -Wextra`(默认)、`-Weffc++`/`-Weverything`(Clang 可选,CI 强化时开启) | -#### ✅ 检查重点覆盖 - -- 内存安全:杜绝泄漏、越界、悬空指针 -- 性能优化:减少冗余拷贝与低效算法 -- 可读性:命名规范、注释完整、逻辑清晰 - -> ✅ **成果**:实现接近语法级"**零缺陷**",长期维护成本大幅降低 - - +### 4、基准测试 -### 5、示例 - -*测试代码和示例代码可在本项目根目录`RyanJsonExample`文件夹查看。* +*测试代码和示例代码可在本项目根目录 `test` 和 `RyanJsonExample` 文件夹查看。* #### 性能测试 -**请移步文末的 RyanDocs 文档中心查看,是基于 [yyjson_benchmark](https://github.com/ibireme/yyjson_benchmark) 的测试结果** +**请移步文末的 RyanDocs 文档中心查看,是基于 [yyjson_benchmark](https://github.com/ibireme/yyjson_benchmark) 的测试结果**。(已经过时,仅供参考) #### 内存占用测试 -*(20251222 linux平台,**不考虑malloc头部空间)**测试代码可在本项目根目录`RyanJsonExample`文件夹查看。新版本内存占用更低!* +(20251222 **malloc头部空间4字节,内存对齐4字节**)测试代码可在本项目根目录`RyanJsonExample`文件夹查看。 ``` ***************************************************************************** *************************** RyanJson / cJSON / yyjson 内存对比程序 ************************** ***************************************************************************** - ---------------------------- 混合类型json数据测试 -------------------------- -json原始文本长度为 2265, 序列化后RyanJson内存占用: 3613, cJSON内存占用: 9160, yyjson内存占用: 8692 -比cJSON节省: 60.56% 内存占用, 比yyjson节省: 58.43% 内存占用 - ---------------------------- 对象占多json数据测试 -------------------------- -json原始文本长度为 3991, 序列化后RyanJson内存占用: 5436, cJSON内存占用: 11633, yyjson内存占用: 12640 -比cJSON节省: 53.27% 内存占用, 比yyjson节省: 56.99% 内存占用 - ---------------------------- 数组占多json数据测试 -------------------------- -json原始文本长度为 1205, 序列化后RyanJson内存占用: 2365, cJSON内存占用: 7340, yyjson内存占用: 5028 -比cJSON节省: 67.78% 内存占用, 比yyjson节省: 52.96% 内存占用 - ---------------------------- 小对象json 混合类型内存占用测试 -------------------------- -json原始文本长度为 90, 序列化后RyanJson内存占用: 131, cJSON内存占用: 309, yyjson内存占用: 636 -比cJSON节省: 57.61% 内存占用, 比yyjson节省: 79.40% 内存占用 - ---------------------------- 小对象json 纯字符串内存占用测试 -------------------------- -json原始文本长度为 100, 序列化后RyanJson内存占用: 144, cJSON内存占用: 339, yyjson内存占用: 636 -比cJSON节省: 57.52% 内存占用, 比yyjson节省: 77.36% 内存占用 +┌── [TEST 1 | test/RyanJsonMemoryFootprintTest.c:294] 开始执行: testMixedJsonMemory() +json原始文本长度为 2265, 序列化后RyanJson内存占用: 4912, cJSON内存占用: 11336, yyjson内存占用: 8784 +比cJSON节省: 56.67% 内存占用, 比yyjson节省: 44.08% 内存占用 +└── [TEST 1 | test/RyanJsonMemoryFootprintTest.c:294] 结束执行: 结果 ✅ | 耗时: 0 ms + +┌── [TEST 2 | test/RyanJsonMemoryFootprintTest.c:295] 开始执行: testObjectJsonMemory() +json原始文本长度为 3991, 序列化后RyanJson内存占用: 7944, cJSON内存占用: 16020, yyjson内存占用: 12884 +比cJSON节省: 50.41% 内存占用, 比yyjson节省: 38.34% 内存占用 +└── [TEST 2 | test/RyanJsonMemoryFootprintTest.c:295] 结束执行: 结果 ✅ | 耗时: 1 ms + +┌── [TEST 3 | test/RyanJsonMemoryFootprintTest.c:296] 开始执行: testArrayJsonMemory() +json原始文本长度为 1205, 序列化后RyanJson内存占用: 3696, cJSON内存占用: 8680, yyjson内存占用: 5068 +比cJSON节省: 57.42% 内存占用, 比yyjson节省: 27.07% 内存占用 +└── [TEST 3 | test/RyanJsonMemoryFootprintTest.c:296] 结束执行: 结果 ✅ | 耗时: 0 ms + +┌── [TEST 4 | test/RyanJsonMemoryFootprintTest.c:297] 开始执行: testSmallMixedJsonMemory() +json原始文本长度为 90, 序列化后RyanJson内存占用: 168, cJSON内存占用: 392, yyjson内存占用: 648 +比cJSON节省: 57.14% 内存占用, 比yyjson节省: 74.07% 内存占用 +└── [TEST 4 | test/RyanJsonMemoryFootprintTest.c:297] 结束执行: 结果 ✅ | 耗时: 0 ms + +┌── [TEST 5 | test/RyanJsonMemoryFootprintTest.c:298] 开始执行: testSmallStringJsonMemory() +json原始文本长度为 100, 序列化后RyanJson内存占用: 216, cJSON内存占用: 472, yyjson内存占用: 648 +比cJSON节省: 54.24% 内存占用, 比yyjson节省: 66.67% 内存占用 +└── [TEST 5 | test/RyanJsonMemoryFootprintTest.c:298] 结束执行: 结果 ✅ | 耗时: 0 ms + +┌── [TEST 6 | test/RyanJsonMemoryFootprintTest.c:299] 开始执行: testCompressedBusinessJsonMemory() +json原始文本长度为 551, 序列化后RyanJson内存占用: 1184, cJSON内存占用: 3788, yyjson内存占用: 3020 +比cJSON节省: 68.74% 内存占用, 比yyjson节省: 60.79% 内存占用 +└── [TEST 6 | test/RyanJsonMemoryFootprintTest.c:299] 结束执行: 结果 ✅ | 耗时: 0 ms ``` -RT-Thread平台考虑malloc**头部空间12字节**情况下,嵌入式平台下占用最高的反而是malloc的内存头开销,所以建议用户优先选择malloc头部空间小的heap管理算法 +RT-Thread平台使用最小内存算法默认 **malloc头部空间12字节,内存对齐8字节**测试代码可在本项目根目录`RyanJsonExample`文件夹查看 ``` ***************************************************************************** *************************** RyanJson / cJSON / yyjson 内存对比程序 ************************** ***************************************************************************** - ---------------------------- 混合类型json数据测试 -------------------------- -json原始文本长度为 2265, 序列化后RyanJson内存占用: 7993, cJSON内存占用: 13732, yyjson内存占用: 8752 -比cJSON节省: 41.79% 内存占用, 比yyjson节省: 8.67% 内存占用 - ---------------------------- 对象占多json数据测试 -------------------------- -json原始文本长度为 3991, 序列化后RyanJson内存占用: 10668, cJSON内存占用: 19109, yyjson内存占用: 12712 -比cJSON节省: 44.17% 内存占用, 比yyjson节省: 16.08% 内存占用 - ---------------------------- 数组占多json数据测试 -------------------------- -json原始文本长度为 1205, 序列化后RyanJson内存占用: 5449, cJSON内存占用: 10424, yyjson内存占用: 5076 -比cJSON节省: 47.73% 内存占用, 比yyjson节省: -7.35% 内存占用 - ---------------------------- 小对象json 混合类型内存占用测试 -------------------------- -json原始文本长度为 90, 序列化后RyanJson内存占用: 287, cJSON内存占用: 477, yyjson内存占用: 672 -比cJSON节省: 39.83% 内存占用, 比yyjson节省: 57.29% 内存占用 - ---------------------------- 小对象json 纯字符串内存占用测试 -------------------------- -json原始文本长度为 100, 序列化后RyanJson内存占用: 300, cJSON内存占用: 567, yyjson内存占用: 672 -比cJSON节省: 47.09% 内存占用, 比yyjson节省: 55.36% 内存占用 +┌── [TEST 1 | test/RyanJsonMemoryFootprintTest.c:294] 开始执行: testMixedJsonMemory() +json原始文本长度为 2265, 序列化后RyanJson内存占用: 7292, cJSON内存占用: 14948, yyjson内存占用: 8852 +比cJSON节省: 51.22% 内存占用, 比yyjson节省: 17.62% 内存占用 +└── [TEST 1 | test/RyanJsonMemoryFootprintTest.c:294] 结束执行: 结果 ✅ | 耗时: 0 ms + +┌── [TEST 2 | test/RyanJsonMemoryFootprintTest.c:295] 开始执行: testObjectJsonMemory() +json原始文本长度为 3991, 序列化后RyanJson内存占用: 11308, cJSON内存占用: 21068, yyjson内存占用: 13016 +比cJSON节省: 46.33% 内存占用, 比yyjson节省: 13.12% 内存占用 +└── [TEST 2 | test/RyanJsonMemoryFootprintTest.c:295] 结束执行: 结果 ✅ | 耗时: 0 ms + +┌── [TEST 3 | test/RyanJsonMemoryFootprintTest.c:296] 开始执行: testArrayJsonMemory() +json原始文本长度为 1205, 序列化后RyanJson内存占用: 5644, cJSON内存占用: 11284, yyjson内存占用: 5104 +比cJSON节省: 49.98% 内存占用, 比yyjson节省: -10.58% 内存占用 +└── [TEST 3 | test/RyanJsonMemoryFootprintTest.c:296] 结束执行: 结果 ✅ | 耗时: 0 ms + +┌── [TEST 4 | test/RyanJsonMemoryFootprintTest.c:297] 开始执行: testSmallMixedJsonMemory() +json原始文本长度为 90, 序列化后RyanJson内存占用: 252, cJSON内存占用: 520, yyjson内存占用: 676 +比cJSON节省: 51.54% 内存占用, 比yyjson节省: 62.72% 内存占用 +└── [TEST 4 | test/RyanJsonMemoryFootprintTest.c:297] 结束执行: 结果 ✅ | 耗时: 0 ms + +┌── [TEST 5 | test/RyanJsonMemoryFootprintTest.c:298] 开始执行: testSmallStringJsonMemory() +json原始文本长度为 100, 序列化后RyanJson内存占用: 272, cJSON内存占用: 620, yyjson内存占用: 676 +比cJSON节省: 56.13% 内存占用, 比yyjson节省: 59.76% 内存占用 +└── [TEST 5 | test/RyanJsonMemoryFootprintTest.c:298] 结束执行: 结果 ✅ | 耗时: 0 ms + +┌── [TEST 6 | test/RyanJsonMemoryFootprintTest.c:299] 开始执行: testCompressedBusinessJsonMemory() +json原始文本长度为 551, 序列化后RyanJson内存占用: 2032, cJSON内存占用: 4872, yyjson内存占用: 3056 +比cJSON节省: 58.29% 内存占用, 比yyjson节省: 33.51% 内存占用 +└── [TEST 6 | test/RyanJsonMemoryFootprintTest.c:299] 结束执行: 结果 ✅ | 耗时: 0 ms ``` -RFC 8259 标准测试,大部分嵌入式场景不会出现极为特殊的Unicode字符集 +#### [RFC 8259](https://github.com/nst/JSONTestSuite) 标准符合性测试 + +**RyanJson使用Double存储浮点数,超大数字会丢失精度** ***如果项目需要完全兼容Unicode字符集,可以考虑yyjson / json-c*** @@ -254,84 +300,73 @@ RFC 8259 标准测试,大部分嵌入式场景不会出现极为特殊的Unico ***************************************************************************** *************************** RyanJson / cJSON / yyjson RFC8259标准测试 ************************** ***************************************************************************** -开始 RFC 8259 JSON 测试 ---------------------------- RFC8259 RyanJson -------------------------- -1 数据不完全一致 -- 原始: {"min":-1.0e+28,"max":1.0e+28} -- 序列化: {"min":-9999999999999999583119736832.0,"max":9999999999999999583119736832.0} -2 数据不完全一致 -- 原始: [123e65] -- 序列化: [12300000.0] -应该失败,但是成功: 123, len: 4 -3 数据不完全一致 -- 原始: [-123123e100000] -- 序列化: [-123123.0] -4 数据不完全一致 -- 原始: {"foo\u0000bar":42} -- 序列化: {"foo":42} -5 数据不完全一致 -- 原始: [1E22] -- 序列化: [100.0] -6 数据不完全一致 -- 原始: [1eE2] -- 序列化: [100.0] -应该失败,但是成功: [1eE2], len: 6 -应该成功,但是失败: [20e1], len: 6 -7 数据不完全一致 -- 原始: [123e45] -- 序列化: [12300000.0] -8 数据不完全一致 -- 原始: ["\u0000"] -- 序列化: [""] -9 数据不完全一致 -- 原始: [123123e100000] -- 序列化: [123123.0] -应该成功,但是失败: [0e1], len: 5 -10 数据不完全一致 -- 原始: [123.456e78] -- 序列化: [12345600000.0] -11 数据不完全一致 -- 原始: [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] -- 序列化: [-1.000000e-78] -12 数据不完全一致 -- 原始: {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430"} -- 序列化: {"title":"Полтора Землекопа"} -RFC 8259 JSON: (318/322) - ---------------------------- RFC8259 cJSON -------------------------- -应该失败,但是成功: [1.], len: 4 -1 数据不完全一致 -- 原始: {"min":-1.0e+28,"max":1.0e+28} -- 序列化: {"min":-1e+28,"max":1e+28} -2 数据不完全一致 -- 原始: [ - ] -- 序列化: [] -应该失败,但是成功: [ - ], len: 3 -3 数据不完全一致 -- 原始: ["\uqqqq"] -- 序列化: [""] -应该失败,但是成功: ["\uqqqq"], len: 10 -应该失败,但是成功: [2.e-3], len: 7 -应该失败,但是成功: [-2.], len: 5 -应该失败,但是成功: [-.123], len: 7 -应该失败,但是成功: 123, len: 4 -4 数据不完全一致 -- 原始: [-123123e100000] -- 序列化: [null] -5 数据不完全一致 -- 原始: [-1e+9999] -- 序列化: [null] -6 数据不完全一致 -- 原始: {"foo\u0000bar":42} -- 序列化: {"foo":42} +┌── [TEST 1 | test/RFC8259Test/RyanJsonRFC8259JsonTest.c:282] 开始执行: testRFC8259RyanJson() +1 数据不完全一致 -- 原始: [-1e+9999] -- 序列化: [null] +2 数据不完全一致 -- 原始: [123123e100000] -- 序列化: [null] +3 数据不完全一致 -- 原始: [-123123e100000] -- 序列化: [null] +4 数据不完全一致 -- 原始: [0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] -- 序列化: [null] +5 数据不完全一致 -- 原始: [1.5e+9999] -- 序列化: [null] +RFC 8259 JSON: (322/322) +└── [TEST 1 | test/RFC8259Test/RyanJsonRFC8259JsonTest.c:282] 结束执行: 结果 ✅ | 耗时: 69 ms + +┌── [TEST 2 | test/RFC8259Test/RyanJsonRFC8259JsonTest.c:283] 开始执行: testRFC8259yyjson() +RFC 8259 JSON: (322/322) +└── [TEST 2 | test/RFC8259Test/RyanJsonRFC8259JsonTest.c:283] 结束执行: 结果 ✅ | 耗时: 8 ms + +┌── [TEST 3 | test/RFC8259Test/RyanJsonRFC8259JsonTest.c:284] 开始执行: testRFC8259cJSON() +应该失败,但是成功: [012], len: 5 应该失败,但是成功: [2.e+3], len: 7 +1 数据不完全一致 -- 原始: [0e1] -- 序列化: [0] +应该失败,但是成功: 123, len: 4 应该失败,但是成功: [0.e1], len: 6 -7 数据不完全一致 -- 原始: -- 序列化: [""] -8 数据不完全一致 -- 原始: [0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] -- 序列化: [null] -9 数据不完全一致 -- 原始: ["\u0000"] -- 序列化: [""] -10 数据不完全一致 -- 原始: {} -- 序列化: {} -11 数据不完全一致 -- 原始: ["a -- 序列化: ["a"] +2 数据不完全一致 -- 原始: [-1e+9999] -- 序列化: [null] +3 数据不完全一致 -- 原始: [123123e100000] -- 序列化: [null] +4 数据不完全一致 -- 原始: [ -- 序列化: [""] +应该失败,但是成功: ["new +line"], len: 12 +5 数据不完全一致 -- 原始: [-123123e100000] -- 序列化: [null] +6 数据不完全一致 -- 原始: [1E+2] -- 序列化: [100] +应该失败,但是成功: [1.], len: 4 +7 数据不完全一致 -- 原始: [0e+1] -- 序列化: [0] +8 数据不完全一致 -- 原始: ["a -- 序列化: ["a"] 应该失败,但是成功: ["a, len: 7 应该失败,但是成功: [-012], len: 6 +9 数据不完全一致 -- 原始: [0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] -- 序列化: [null] +10 数据不完全一致 -- 原始: ["\uqqqq"] -- 序列化: [""] +应该失败,但是成功: ["\uqqqq"], len: 10 +11 数据不完全一致 -- 原始: [ + ] -- 序列化: [] +应该失败,但是成功: [ + ], len: 3 12 数据不完全一致 -- 原始: [ -- 序列化: [] 应该失败,但是成功: [, len: 3 -应该失败,但是成功: [012], len: 5 -应该失败,但是成功: ["new -line"], len: 12 -13 数据不完全一致 -- 原始: [ -- 序列化: [""] +13 数据不完全一致 -- 原始: [1.5e+9999] -- 序列化: [null] +14 数据不完全一致 -- 原始: -- 序列化: [""] +应该失败,但是成功: [2.e-3], len: 7 +应该失败,但是成功: [2.e3], len: 6 应该失败,但是成功: [" "], len: 5 -14 数据不完全一致 -- 原始: [1.5e+9999] -- 序列化: [null] +15 数据不完全一致 -- 原始: [1e+2] -- 序列化: [100] 应该失败,但是成功: [-01], len: 5 -15 数据不完全一致 -- 原始: [123123e100000] -- 序列化: [null] -16 数据不完全一致 -- 原始: [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] -- 序列化: [-1e-78] -17 数据不完全一致 -- 原始: {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430"} -- 序列化: {"title":"Полтора Землекопа"} -应该失败,但是成功: [2.e3], len: 6 +应该失败,但是成功: [-.123], len: 7 +16 数据不完全一致 -- 原始: {} -- 序列化: {} +17 数据不完全一致 -- 原始: [20e1] -- 序列化: [200] +18 数据不完全一致 -- 原始: [123.456e-789] -- 序列化: [0] +19 数据不完全一致 -- 原始: [123e-10000000] -- 序列化: [0] +应该失败,但是成功: [-2.], len: 5 RFC 8259 JSON: (305/322) - ---------------------------- RFC8259 yyjson -------------------------- -1 数据不完全一致 -- 原始: {"min":-1.0e+28,"max":1.0e+28} -- 序列化: {"min":-1e28,"max":1e28} -2 数据不完全一致 -- 原始: [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] -- 序列化: [-1e-78] -3 数据不完全一致 -- 原始: {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430"} -- 序列化: {"title":"Полтора Землекопа"} -RFC 8259 JSON: (322/322) -|||----------->>> area = 0, size = 0 +└── [TEST 3 | test/RFC8259Test/RyanJsonRFC8259JsonTest.c:284] 结束执行: 结果 ✅ | 耗时: 7 ms ``` -### 6、局限性 +### 5、局限性与注意事项 -- 使用`int / double`表示 JSON number 类型,**可能存在精度丢失**。建议 64 位数值用字符串表示。 -- **对象中允许有重复的key**,RyanJson库采用**单向链表**,链表结构会返回第一个匹配项。 +- **数值精度**:内部使用 `int` / `double` 存储 Number。对于超过 double 精度的 64 位整数或高精度浮点数,double内部使用 snprintf 打印,如果你的平台不支持科学计数法,建议使用 String 类型存储以避免精度丢失。 +- **重复 Key**:RyanJson 允许对象中存在重复 Key(解析时不报错),但在查找时只会返回链表中第一个匹配项。 -### 7、文档 +### 6、文档 📂 示例代码:`RyanJsonExample` 文件夹 📖 文档中心:RyanDocs -📧 联系方式:1831931681@qq.com +📧 联系与支持:如有任何疑问或商业合作需求,请联系:`1831931681@qq.com` diff --git a/RyanJson/RyanJson.c b/RyanJson/RyanJson.c index f8689db..e1ca559 100644 --- a/RyanJson/RyanJson.c +++ b/RyanJson/RyanJson.c @@ -1,8 +1,8 @@ #include "RyanJson.h" -#ifdef isEnableFuzzer +#ifdef RyanJsonLinuxTestEnv #undef RyanJsonNestingLimit -#define RyanJsonNestingLimit 350 +#define RyanJsonNestingLimit 350U #undef RyanJsonSnprintf #include @@ -10,33 +10,27 @@ static uint32_t RyanJsonRandRange(uint32_t min, uint32_t max) { - // int32_t seedp = (int32_t)time(NULL); - // return min + rand_r(&seedp) % (max - min + 1); - - static uint64_t state = 0; - // 初始化一次种子 - if (state == 0) { state = (uint64_t)time(NULL); } + // Xorshift32 算法,运行时种子(每次运行不同) + static uint32_t state = 0; + if (0 == state) { state = (uint32_t)time(NULL) | 1; } + // Xorshift32 算法 + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; - // Xorshift64* 算法 - state ^= state >> 12; - state ^= state << 25; - state ^= state >> 27; - uint64_t result = state * 2685821657736338717ULL; - - return min + (uint32_t)(result % (max - min + 1)); + return min + (state % (max - min + 1)); } static int32_t RyanJsonSnprintf(char *buf, size_t size, const char *fmt, ...) { - static uint32_t jsonsnprintCount = 1; - jsonsnprintCount++; - if (jsonsnprintCount % RyanJsonRandRange(10, 500) == 0) { return 0; }; +#ifdef isEnableFuzzer + // Fuzzer 模式:随机触发失败,测试错误处理路径 + if (0 == RyanJsonRandRange(0, 500)) { return 0; } +#endif va_list args; - va_start(args, fmt); // 每 500 次随机触发一次“失败” - + va_start(args, fmt); int32_t ret = vsnprintf(buf, size, fmt, args); - va_end(args); return ret; } @@ -44,31 +38,36 @@ static int32_t RyanJsonSnprintf(char *buf, size_t size, const char *fmt, ...) typedef struct { + const uint8_t *currentPtr; // 待解析字符串地址 uint32_t remainSize; // 待解析字符串剩余长度 uint32_t depth; // How deeply nested (in arrays/objects) is the input at the current offset. - const uint8_t *currentPtr; // 待解析字符串地址 } RyanJsonParseBuffer; typedef struct { - RyanJsonBool_e isNoAlloc; // 是否动态申请内存 - uint32_t cursor; // 解析到那个buf位置上了 - uint32_t size; // 待解析字符串剩余长度, 不动态申请内存时,到达此size大小将返回失败 uint8_t *bufAddress; // 反序列化后的字符串地址 + uint32_t cursor; // 解析到那个buf位置上了 + uint32_t size; // buf的总长度, 不动态申请内存时,到达此size大小将返回失败 + RyanJsonBool_e isNoAlloc; // 是否动态申请内存 } RyanJsonPrintBuffer; // !这部分跟 struct RyanJsonNode 要保持一致 typedef struct { + const char *key; + const char *strValue; + RyanjsonType_e type; RyanJsonBool_e boolIsTrueFlag; RyanJsonBool_e numberIsDoubleFlag; - - const char *key; - const char *strValue; } RyanJsonNodeInfo_t; -#define _checkType(info, type) ((info) == (type)) +#define RyanJsonFlagSize sizeof(uint8_t) +#define RyanJsonKeyFeidLenMaxSize sizeof(uint32_t) +#define RyanJsonAlign(size, align) (((size) + (align) - 1) & ~((align) - 1)) +#define RyanJsonAlignDown(size, align) ((size) & ~((align) - 1)) +#define _checkType(info, type) ((info) == (type)) +#define RyanJsonUnused(x) (void)(x) /** * @brief printBuf相关宏 @@ -76,12 +75,13 @@ typedef struct */ #define printBufPutChar(printfBuf, char) \ do { ((printfBuf)->bufAddress[(printfBuf)->cursor++] = (char)); } while (0) -#define printBufPutString(printfBuf, string, len) \ +#define printBufPutString(printfBuf, putStr, putStrLen) \ do \ { \ - for (uint32_t i = 0; i < (uint32_t)(len); i++) printBufPutChar(printfBuf, (string)[i]); \ + for (uint32_t i = 0; i < (uint32_t)(putStrLen); i++) printBufPutChar(printfBuf, (putStr)[i]); \ } while (0) -#define printBufCurrentPtr(printfBuf) (&((printfBuf)->bufAddress[(printfBuf)->cursor])) +#define printBufCurrentPtr(printfBuf) (&((printfBuf)->bufAddress[(printfBuf)->cursor])) +#define printBufRemainBytes(printfBuf) ((printfBuf)->size - (printfBuf)->cursor) /** * @brief parseBuf相关宏 @@ -107,14 +107,12 @@ typedef struct * @param bytesToAdvance * @return RyanJsonBool_e */ -static RyanJsonBool_e parseBufTyrAdvanceCurrentPrt(RyanJsonParseBuffer *parseBuf, uint32_t bytesToAdvance) +static inline RyanJsonBool_e parseBufTyrAdvanceCurrentPrt(RyanJsonParseBuffer *parseBuf, uint32_t bytesToAdvance) { RyanJsonCheckAssert(NULL != parseBuf); #ifdef isEnableFuzzer - static uint32_t count = 0; - count++; - if (0 == count % RyanJsonRandRange(10, 2000)) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(0 != RyanJsonRandRange(0, 1500)); #endif if (parseBufHasRemainBytes(parseBuf, bytesToAdvance)) @@ -137,17 +135,20 @@ static RyanJsonBool_e parseBufSkipWhitespace(RyanJsonParseBuffer *parseBuf) RyanJsonCheckAssert(NULL != parseBuf); #ifdef isEnableFuzzer - static uint32_t count = 0; - count++; - if (0 == count % RyanJsonRandRange(10, 2000)) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(0 != RyanJsonRandRange(0, 1500)); #endif - const uint8_t *cursor = parseBuf->currentPtr; - while (parseBufHasRemain(parseBuf) && *cursor && (' ' == *cursor || '\n' == *cursor || '\r' == *cursor)) + while (parseBufHasRemain(parseBuf)) { - RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); - // 更新本地指针以反映 buf->address 的变化(若 parseBufTyrAdvanceCurrentPrt 移动 address) - cursor = parseBuf->currentPtr; + uint8_t cursor = *parseBuf->currentPtr; + if (' ' == cursor || '\n' == cursor || '\r' == cursor) + { + RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); + } + else + { + break; + } } return RyanJsonTrue; @@ -163,18 +164,42 @@ static RyanJsonBool_e RyanJsonPrintValue(RyanJson_t pJson, RyanJsonPrintBuffer * static RyanJson_t RyanJsonCreateObjectAndKey(const char *key); static RyanJson_t RyanJsonCreateArrayAndKey(const char *key); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-align" -static uint8_t *RyanJsonGetHiddePrt(RyanJson_t pJson) +/** + * @brief 获取内联字符串的大小 + * 用函数可读性更强一点,编译器会优化的 + * @return uint32_t + */ +#define RyanJsonGetInlineStringSize \ + (RyanJsonAlign((sizeof(void *) - RyanJsonFlagSize + sizeof(void *) + (RyanJsonMallocHeaderSize / 2) + RyanJsonFlagSize), \ + RyanJsonMallocAlign) - \ + RyanJsonFlagSize) +// static uint32_t RyanJsonGetInlineStringSize(void) +// { +// uint32_t baseSize = sizeof(void *) - RyanJsonFlagSize; // flag的偏移字节量 +// baseSize += sizeof(void *) + RyanJsonMallocHeaderSize / 2; // 存储指针变量的字节量 +// return (uint32_t)(RyanJsonAlign(baseSize + RyanJsonFlagSize, RyanJsonMallocAlign) - RyanJsonFlagSize); +// } + +static inline uint8_t *RyanJsonGetHiddePrt(RyanJson_t pJson) { RyanJsonCheckAssert(NULL != pJson); - return *(uint8_t **)(RyanJsonGetPayloadPtr(pJson) + RyanJsonAlign); + + // 用memcpy规避非对其警告 + void *tmpPtr = NULL; + RyanJsonMemcpy((void *)&tmpPtr, (RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize), sizeof(void *)); + return (uint8_t *)tmpPtr; } -static void RyanJsonSetHiddePrt(RyanJson_t pJson, uint8_t *hiddePrt) + +static inline void RyanJsonSetHiddePrt(RyanJson_t pJson, uint8_t *hiddePrt) { RyanJsonCheckAssert(NULL != pJson); RyanJsonCheckAssert(NULL != hiddePrt); - *(uint8_t **)(RyanJsonGetPayloadPtr(pJson) + RyanJsonAlign) = hiddePrt; + + // 用memcpy规避非对其警告 + void *tmpPtr = hiddePrt; + // uint8_t是flag,uint32_t是记录key的长度空间 + RyanJsonMemcpy((RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize), (const void *)&tmpPtr, + sizeof(void *)); } /** @@ -184,38 +209,55 @@ static void RyanJsonSetHiddePrt(RyanJson_t pJson, uint8_t *hiddePrt) * @param index * @return uint8_t* */ -static uint8_t *RyanJsonGetHiddenPtrAt(RyanJson_t pJson, uint32_t index) +static inline uint8_t *RyanJsonGetHiddenPtrAt(RyanJson_t pJson, uint32_t index) { RyanJsonCheckAssert(NULL != pJson); return (uint8_t *)(RyanJsonGetHiddePrt(pJson) + (index)); } -#pragma GCC diagnostic pop -union RyanJsonUint32Bytes { - uint32_t value; - uint8_t buf[4]; -}; -static void RyanJsonSetLenKey(RyanJson_t pJson, uint32_t value) +static inline void RyanJsonSetKeyLen(RyanJson_t pJson, uint32_t value) { RyanJsonCheckAssert(NULL != pJson); - uint8_t *buf = RyanJsonGetHiddenPtrAt(pJson, 0); - uint8_t len = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson); - RyanJsonCheckAssert(len <= 4); + uint8_t *buf = RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize; + uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson); + RyanJsonCheckAssert(keyFieldLen <= RyanJsonKeyFeidLenMaxSize); - union RyanJsonUint32Bytes tmpLenField = {.value = value}; - for (uint8_t i = 0; i < len; i++) { buf[i] = tmpLenField.buf[i]; } + // 使用大小端无关的方式写入 + for (uint8_t i = 0; i < keyFieldLen; i++) + { + buf[i] = (uint8_t)(value & 0xFF); + value >>= 8; + } +} + +static inline uint32_t RyanJsonGetKeyLen(RyanJson_t pJson) +{ + RyanJsonCheckAssert(NULL != pJson); + uint8_t *buf = RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize; + uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson); + RyanJsonCheckAssert(keyFieldLen <= RyanJsonKeyFeidLenMaxSize); + + // 使用大小端无关的方式读取 + uint32_t value = 0; + for (uint8_t i = 0; i < keyFieldLen; i++) { value |= ((uint32_t)buf[i]) << (i * 8); } + return value; } -static uint32_t RyanJsonGetLenKey(RyanJson_t pJson) +static inline void *RyanJsonGetValue(RyanJson_t pJson) { RyanJsonCheckAssert(NULL != pJson); - uint8_t *buf = RyanJsonGetHiddenPtrAt(pJson, 0); - uint8_t len = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson); - RyanJsonCheckAssert(len <= 4); - union RyanJsonUint32Bytes tmpLenField = {0}; - for (uint8_t i = 0; i < len; i++) { tmpLenField.buf[i] = buf[i]; } - return tmpLenField.value; + uint32_t len = RyanJsonFlagSize; + //? 目前的场景不会发生 RyanJsonIsKey(pJson) 为false的情况 + // if (RyanJsonIsKey(pJson) || RyanJsonIsString(pJson)) + if (RyanJsonIsKey(pJson)) + { + len += RyanJsonGetInlineStringSize; + // jsonLog(" keyLen: %d, keyLenField: %d, \r\n", RyanJsonGetKeyLen(pJson), + // RyanJsonGetPayloadEncodeKeyLenByFlag(pJson)); + } + + return RyanJsonGetPayloadPtr(pJson) + len; } /** @@ -228,17 +270,15 @@ static uint32_t RyanJsonGetLenKey(RyanJson_t pJson) */ static void *RyanJsonExpandRealloc(void *block, uint32_t oldSize, uint32_t newSize) { + // 不考虑 block 为空的情况 RyanJsonCheckAssert(NULL != block); if (NULL != jsonRealloc) { return jsonRealloc(block, newSize); } void *newBlock = jsonMalloc(newSize); RyanJsonCheckReturnNull(NULL != newBlock); - if (NULL != block) - { - RyanJsonMemcpy(newBlock, block, oldSize); - jsonFree(block); - } + RyanJsonMemcpy(newBlock, block, oldSize); + jsonFree(block); return newBlock; } @@ -248,7 +288,7 @@ static void *RyanJsonExpandRealloc(void *block, uint32_t oldSize, uint32_t newSi * @param len * @return uint8_t */ -static uint8_t RyanJsonCalcLenBytes(uint32_t len) +static inline uint8_t RyanJsonCalcLenBytes(uint32_t len) { if (len < 0xff) { return 0; } if (len < 0xffff) { return 1; } @@ -256,24 +296,6 @@ static uint8_t RyanJsonCalcLenBytes(uint32_t len) return 3; } -/** - * @brief 提供内存钩子函数 - * - * @param userMalloc - * @param userFree - * @param userRealloc 可以为NULL - * @return RyanJsonBool_e - */ -RyanJsonBool_e RyanJsonInitHooks(RyanJsonMalloc_t userMalloc, RyanJsonFree_t userFree, RyanJsonRealloc_t userRealloc) -{ - RyanJsonCheckReturnFalse(NULL != userMalloc && NULL != userFree); - - jsonMalloc = userMalloc; - jsonFree = userFree; - jsonRealloc = userRealloc; - return RyanJsonTrue; -} - /** * @brief 安全的浮点数比较 * @@ -281,7 +303,7 @@ RyanJsonBool_e RyanJsonInitHooks(RyanJsonMalloc_t userMalloc, RyanJsonFree_t use * @param b * @return RyanJsonBool_e */ -static RyanJsonBool_e compare_double(double a, double b) +RyanJsonBool_e RyanJsonCompareDouble(double a, double b) { double diff = fabs(a - b); double absA = fabs(a); @@ -290,15 +312,15 @@ static RyanJsonBool_e compare_double(double a, double b) // 允许的容差:相对误差 + 绝对误差 double epsilon = DBL_EPSILON * maxVal; - double minTolerance = 1e-12; // 可调的绝对容差 + double absTolerance = RyanJsonAbsTolerance; // 绝对容差 - return diff <= (epsilon > minTolerance ? epsilon : minTolerance); + return diff <= (epsilon > absTolerance ? epsilon : absTolerance); } /** * @brief 申请buf容量, 决定是否进行扩容 * - * @param buf + * @param printfBuf * @param needed * @return RyanJsonBool_e */ @@ -323,34 +345,32 @@ static RyanJsonBool_e printBufAppend(RyanJsonPrintBuffer *printfBuf, uint32_t ne return RyanJsonTrue; } -void *RyanJsonGetValue(RyanJson_t pJson) +/** + * @brief 替换json对象节点 + * + * @param prev + * @param oldItem + * @param newItem + * @return RyanJsonBool_e + */ +static RyanJsonBool_e RyanJsonReplaceNode(RyanJson_t prev, RyanJson_t oldItem, RyanJson_t newItem) { - RyanJsonCheckReturnNull(NULL != pJson); + RyanJsonCheckAssert(NULL != oldItem && NULL != newItem); - uint32_t len = RyanJsonAlign; - if (RyanJsonIsKey(pJson) || RyanJsonIsString(pJson)) - { - len += sizeof(void *); - // jsonLog(" keyLen: %d, keyLenField: %d, \r\n", RyanJsonGetLenKey(pJson), RyanJsonGetPayloadEncodeKeyLenByFlag(pJson)); - } + // 链接前驱和新节点 + if (NULL != prev) { prev->next = newItem; } - return RyanJsonGetPayloadPtr(pJson) + len; -} + // 链接后继和新节点 + if (NULL != oldItem->next) { newItem->next = oldItem->next; } -char *RyanJsonGetKey(RyanJson_t pJson) -{ - RyanJsonCheckReturnNull(NULL != pJson); - return (char *)RyanJsonGetHiddenPtrAt(pJson, RyanJsonGetPayloadEncodeKeyLenByFlag(pJson)); + oldItem->next = NULL; + return RyanJsonTrue; } -char *RyanJsonGetStringValue(RyanJson_t pJson) +static inline RyanJsonBool_e RyanJsonChangeObjectValue(RyanJson_t pJson, RyanJson_t objValue) { - RyanJsonCheckReturnNull(NULL != pJson); - - uint32_t len = 0; - if (RyanJsonIsKey(pJson)) { len = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson) + RyanJsonGetLenKey(pJson) + 1U; } - - return (char *)RyanJsonGetHiddenPtrAt(pJson, len); + RyanJsonMemcpy(RyanJsonGetValue(pJson), (void *)&objValue, sizeof(void *)); + return RyanJsonTrue; } static RyanJsonBool_e RyanJsonChangeString(RyanJson_t pJson, RyanJsonBool_e isNew, const char *key, const char *strValue) @@ -368,7 +388,7 @@ static RyanJsonBool_e RyanJsonChangeString(RyanJson_t pJson, RyanJsonBool_e isNe { keyLen = RyanJsonStrlen(key); keyLenField = RyanJsonCalcLenBytes(keyLen); - mallocSize += keyLen + keyLenField + 1 + 1; + mallocSize += keyLen + 1; #ifdef isEnableFuzzer { @@ -387,29 +407,66 @@ static RyanJsonBool_e RyanJsonChangeString(RyanJson_t pJson, RyanJsonBool_e isNe } if (0 == mallocSize) { return RyanJsonTrue; } - // 申请新的空间 - uint8_t *newPtr = (uint8_t *)jsonMalloc(mallocSize); - if (NULL == newPtr) { return RyanJsonFalse; } - // 释放旧的内存 uint8_t *oldPrt = NULL; if (RyanJsonFalse == isNew) { - RyanJsonCheckAssert(RyanJsonIsKey(pJson) || RyanJsonIsString(pJson)); - oldPrt = RyanJsonGetHiddePrt(pJson); + if (RyanJsonTrue == RyanJsonGetPayloadStrIsPtrByFlag(pJson)) + { + RyanJsonCheckAssert(RyanJsonIsKey(pJson) || RyanJsonIsString(pJson)); + oldPrt = RyanJsonGetHiddePrt(pJson); + } + } + + char arr[RyanJsonGetInlineStringSize] = {0}; + // keyLenField(0-3) + 1 为key的长度 + if ((mallocSize + keyLenField + 1) <= RyanJsonGetInlineStringSize) + { + RyanJsonSetPayloadStrIsPtrByFlag(pJson, RyanJsonFalse); + RyanJsonMemcpy(arr, key, keyLen); + RyanJsonMemcpy(arr + keyLen, strValue, strValueLen); + } + else + { + // 申请新的空间 + uint8_t *newPtr = (uint8_t *)jsonMalloc(mallocSize); + RyanJsonCheckReturnFalse(NULL != newPtr); + + // 先赋值,因为 RyanJsonSetHiddePrt 可能会覆盖key和string的空间 + if (NULL != key) + { + if (0 != keyLen) { RyanJsonMemcpy(newPtr, key, keyLen); } + newPtr[keyLen] = '\0'; + } + + if (NULL != strValue) + { + uint8_t *strValueBuf = newPtr; + if (NULL != key) { strValueBuf = newPtr + keyLen + 1; } + + if (0 != strValueLen) { RyanJsonMemcpy(strValueBuf, strValue, strValueLen); } + strValueBuf[strValueLen] = '\0'; + } + + RyanJsonSetHiddePrt(pJson, newPtr); + RyanJsonSetPayloadStrIsPtrByFlag(pJson, RyanJsonTrue); } - RyanJsonSetHiddePrt(pJson, newPtr); // 设置key if (NULL != key) { RyanJsonSetPayloadWhiteKeyByFlag(pJson, RyanJsonTrue); RyanJsonSetPayloadEncodeKeyLenByFlag(pJson, keyLenField); - RyanJsonSetLenKey(pJson, keyLen); + RyanJsonSetKeyLen(pJson, keyLen); + + jsonLog(" keyLen: %d, keyLenField: %d, \r\n", RyanJsonGetKeyLen(pJson), RyanJsonGetPayloadEncodeKeyLenByFlag(pJson)); - jsonLog(" keyLen: %d, keyLenField: %d, \r\n", RyanJsonGetLenKey(pJson), RyanJsonGetPayloadEncodeKeyLenByFlag(pJson)); - if (0 != keyLen) { RyanJsonMemcpy(RyanJsonGetKey(pJson), key, keyLen); } - RyanJsonGetKey(pJson)[keyLen] = '\0'; + if (RyanJsonFalse == RyanJsonGetPayloadStrIsPtrByFlag(pJson)) + { + char *keyBuf = RyanJsonGetKey(pJson); + if (0 != keyLen) { RyanJsonMemcpy(keyBuf, arr, keyLen); } + keyBuf[keyLen] = '\0'; + } } else { @@ -421,8 +478,12 @@ static RyanJsonBool_e RyanJsonChangeString(RyanJson_t pJson, RyanJsonBool_e isNe if (NULL != strValue) { jsonLog("stLen: %d, strValue: %s \r\n", strValueLen, strValue); - if (0 != strValueLen) { RyanJsonMemcpy(RyanJsonGetStringValue(pJson), strValue, strValueLen); } - RyanJsonGetStringValue(pJson)[strValueLen] = '\0'; + if (RyanJsonFalse == RyanJsonGetPayloadStrIsPtrByFlag(pJson)) + { + char *strValueBuf = RyanJsonGetStringValue(pJson); + if (0 != strValueLen) { RyanJsonMemcpy(strValueBuf, arr + keyLen, strValueLen); } + strValueBuf[strValueLen] = '\0'; + } } if (oldPrt) { jsonFree(oldPrt); } @@ -434,7 +495,7 @@ static RyanJson_t RyanJsonNewNode(RyanJsonNodeInfo_t *info) RyanJsonCheckAssert(NULL != info); // 加1是flag的空间 - uint32_t size = sizeof(struct RyanJsonNode) + RyanJsonAlign; + uint32_t size = sizeof(struct RyanJsonNode) + RyanJsonFlagSize; if (_checkType(info->type, RyanJsonTypeNumber)) { @@ -446,31 +507,145 @@ static RyanJson_t RyanJsonNewNode(RyanJsonNodeInfo_t *info) } else if (_checkType(info->type, RyanJsonTypeArray) || _checkType(info->type, RyanJsonTypeObject)) { size += sizeof(RyanJson_t); } - if (NULL != info->key || _checkType(info->type, RyanJsonTypeString)) { size += sizeof(void *); } + if (NULL != info->key || _checkType(info->type, RyanJsonTypeString)) { size += RyanJsonGetInlineStringSize; } RyanJson_t pJson = (RyanJson_t)jsonMalloc((size_t)size); - if (NULL != pJson) + RyanJsonCheckReturnNull(NULL != pJson); + + // 只清空结构体就行了 + RyanJsonMemset(pJson, 0, size); // 这个size很小,没有优化的必要,直接memset吧 + + RyanJsonSetType(pJson, info->type); + + RyanJsonCheckCode(RyanJsonTrue == RyanJsonChangeString(pJson, RyanJsonTrue, info->key, info->strValue), { + jsonFree(pJson); + return NULL; + }); + + if (_checkType(info->type, RyanJsonTypeBool)) { RyanJsonSetPayloadBoolValueByFlag(pJson, info->boolIsTrueFlag); } + else if (_checkType(info->type, RyanJsonTypeNumber)) { RyanJsonSetPayloadNumberIsDoubleByFlag(pJson, info->numberIsDoubleFlag); } + + return pJson; +} + +/** + * @brief 创建一个item对象 + * 带有key的对象才可以方便的通过replace替换, + * !此接口不推荐用户调用 + * + * @param key + * @param item + * @return RyanJson_t + */ +static RyanJson_t RyanJsonCreateItem(const char *key, RyanJson_t item) +{ + RyanJsonCheckReturnNull(NULL != item); + + RyanJsonNodeInfo_t nodeInfo = { + .type = _checkType(RyanJsonGetType(item), RyanJsonTypeArray) ? RyanJsonTypeArray : RyanJsonTypeObject, + .key = key, + }; + + RyanJson_t newItem = RyanJsonNewNode(&nodeInfo); + + RyanJsonCheckReturnNull(NULL != newItem); + + if (_checkType(RyanJsonGetType(item), RyanJsonTypeArray) || _checkType(RyanJsonGetType(item), RyanJsonTypeObject)) { - // 只清空结构体就行了 - RyanJsonMemset(pJson, 0, size); // 这个size很小,没有优化的必要,直接memset吧 + RyanJsonChangeObjectValue(newItem, RyanJsonGetObjectValue(item)); - RyanJsonSetType(pJson, info->type); + if (RyanJsonTrue == RyanJsonGetPayloadStrIsPtrByFlag(item)) { jsonFree(RyanJsonGetHiddePrt(item)); } + jsonFree(item); + } + else + { + RyanJsonChangeObjectValue(newItem, item); + } - RyanJsonCheckCode(RyanJsonTrue == RyanJsonChangeString(pJson, RyanJsonTrue, info->key, info->strValue), { - jsonFree(pJson); - return NULL; - }); + return newItem; +} - if (_checkType(info->type, RyanJsonTypeBool)) { RyanJsonSetPayloadBoolValueByFlag(pJson, info->boolIsTrueFlag); } - else if (_checkType(info->type, RyanJsonTypeNumber)) - { - RyanJsonSetPayloadNumberIsDoubleByFlag(pJson, info->numberIsDoubleFlag); - } +/** + * @brief 提供内存钩子函数 + * + * @param userMalloc + * @param userFree + * @param userRealloc 可以为NULL + * @return RyanJsonBool_e + */ +RyanJsonBool_e RyanJsonInitHooks(RyanJsonMalloc_t userMalloc, RyanJsonFree_t userFree, RyanJsonRealloc_t userRealloc) +{ + RyanJsonCheckReturnFalse(NULL != userMalloc && NULL != userFree); + + jsonMalloc = userMalloc; + jsonFree = userFree; + jsonRealloc = userRealloc; + return RyanJsonTrue; +} + +char *RyanJsonGetKey(RyanJson_t pJson) +{ + RyanJsonCheckReturnNull(NULL != pJson); + if (RyanJsonFalse == RyanJsonGetPayloadStrIsPtrByFlag(pJson)) + { + uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson); + RyanJsonCheckAssert(keyFieldLen <= RyanJsonKeyFeidLenMaxSize); + return (char *)(RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + keyFieldLen); } - return pJson; + return (char *)RyanJsonGetHiddenPtrAt(pJson, 0); +} + +char *RyanJsonGetStringValue(RyanJson_t pJson) +{ + RyanJsonCheckReturnNull(NULL != pJson); + + uint32_t len = 0; + + if (RyanJsonFalse == RyanJsonGetPayloadStrIsPtrByFlag(pJson)) + { + uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson); + RyanJsonCheckAssert(keyFieldLen <= RyanJsonKeyFeidLenMaxSize); + + len += keyFieldLen; + if (RyanJsonIsKey(pJson)) { len += RyanJsonGetKeyLen(pJson) + 1U; } + return (char *)(RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + len); + } + + if (RyanJsonIsKey(pJson)) { len = RyanJsonGetKeyLen(pJson) + 1U; } + + return (char *)RyanJsonGetHiddenPtrAt(pJson, len); +} + +int32_t RyanJsonGetIntValue(RyanJson_t pJson) +{ + RyanJsonCheckCodeNoReturn(NULL != pJson, { return 0; }); + + int32_t intValue; + RyanJsonMemcpy(&intValue, RyanJsonGetValue(pJson), sizeof(intValue)); + return intValue; } +double RyanJsonGetDoubleValue(RyanJson_t pJson) +{ + RyanJsonCheckCodeNoReturn(NULL != pJson, { return 0; }); + + double doubleValue; + RyanJsonMemcpy(&doubleValue, RyanJsonGetValue(pJson), sizeof(doubleValue)); + return doubleValue; +} + +RyanJson_t RyanJsonGetObjectValue(RyanJson_t pJson) +{ + RyanJsonCheckCodeNoReturn(NULL != pJson, { return 0; }); + + RyanJson_t objValue; + RyanJsonMemcpy((void *)&objValue, RyanJsonGetValue(pJson), sizeof(void *)); + return objValue; +} + +RyanJson_t RyanJsonGetArrayValue(RyanJson_t pJson) { return RyanJsonGetObjectValue(pJson); } + /** * @brief 删除json及其子项 * @@ -490,7 +665,7 @@ void RyanJsonDelete(RyanJson_t pJson) RyanJsonDelete(RyanJsonGetObjectValue(pJson)); // 递归删除子对象 } - if (RyanJsonIsKey(pJson) || RyanJsonIsString(pJson)) { jsonFree(RyanJsonGetHiddePrt(pJson)); } + if (RyanJsonTrue == RyanJsonGetPayloadStrIsPtrByFlag(pJson)) { jsonFree(RyanJsonGetHiddePrt(pJson)); } jsonFree(pJson); @@ -503,10 +678,7 @@ void RyanJsonDelete(RyanJson_t pJson) * * @param block */ -void RyanJsonFree(void *block) -{ - if (block) { jsonFree(block); } -} +void RyanJsonFree(void *block) { jsonFree(block); } /** * @brief 从字符串中获取十六进制值 @@ -555,40 +727,49 @@ static RyanJsonBool_e RyanJsonParseHex(const uint8_t *text, uint32_t *value) } /** - * @brief 解析文本中的数字,添加到json节点中 + * @brief 解析数字字符串 + * 替代 strtod 以提高嵌入式平台兼容性 * - * @param buf 解析缓冲区 - * @param key 对应的key - * @param out 用于接收解析后的pJson对象的地址 - * @return RyanJsonBool_e 成功或失败 + * @param parseBuf + * @param numberValuePtr 解析后的值 + * @param isIntPtr 解析后的值 + * @return RyanJsonBool_e */ -static RyanJsonBool_e RyanJsonParseNumber(RyanJsonParseBuffer *parseBuf, char *key, RyanJson_t *out) +static RyanJsonBool_e RyanJsonInternalParseDouble(RyanJsonParseBuffer *parseBuf, double *numberValuePtr, RyanJsonBool_e *isIntPtr) { - RyanJsonCheckAssert(NULL != parseBuf && NULL != out); + RyanJsonCheckAssert(NULL != parseBuf && NULL != numberValuePtr && NULL != isIntPtr); - double number = 0.0; - int32_t sign = 1; + double number = 0; int32_t scale = 0; int32_t e_sign = 1; int32_t e_scale = 0; - RyanJsonBool_e isint = RyanJsonTrue; + RyanJsonBool_e isNegative = RyanJsonFalse; + RyanJsonBool_e isInt = RyanJsonTrue; // 处理符号 - if (parseBufHasRemain(parseBuf) && '-' == *parseBuf->currentPtr) + if ('-' == *parseBuf->currentPtr) { - sign = -1; - RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); + isNegative = RyanJsonTrue; + // 这个不会失败因为进来前已经判断过 parseBufHasRemain(parseBuf) + RyanJsonCheckNeverNoAssert(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9'); } - // 跳过前导零 - while (parseBufHasRemain(parseBuf) && '0' == *parseBuf->currentPtr) + // 前导0是非法的 + while ('0' == *parseBuf->currentPtr) { RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); - // 前导0后面不允许跟数组,比如"0123" + // 前导0后面不允许跟数据,比如"0123" RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf) && (*parseBuf->currentPtr < '0' || *parseBuf->currentPtr > '9')); } + // 允许多个前导零 + // while ('0' == *parseBuf->currentPtr) + // { + // RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); + // RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf)); + // } + // 整数部分 while (parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9') { @@ -608,7 +789,7 @@ static RyanJsonBool_e RyanJsonParseNumber(RyanJsonParseBuffer *parseBuf, char *k scale--; // 每读一位小数,scale减一 RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); } - isint = RyanJsonFalse; + isInt = RyanJsonFalse; } // 指数部分 @@ -616,8 +797,14 @@ static RyanJsonBool_e RyanJsonParseNumber(RyanJsonParseBuffer *parseBuf, char *k { RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf)); - if ('+' == *parseBuf->currentPtr || '-' == *parseBuf->currentPtr) { e_sign = ('-' == *parseBuf->currentPtr) ? -1 : 1; } - RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); + + // 只有遇到 +/- 符号时才跳过 + if ('+' == *parseBuf->currentPtr || '-' == *parseBuf->currentPtr) + { + e_sign = ('-' == *parseBuf->currentPtr) ? -1 : 1; + RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); + } + RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9'); while (parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9') @@ -625,20 +812,47 @@ static RyanJsonBool_e RyanJsonParseNumber(RyanJsonParseBuffer *parseBuf, char *k e_scale = e_scale * 10 + (*parseBuf->currentPtr - '0'); RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); } - isint = RyanJsonFalse; + isInt = RyanJsonFalse; } - // 创建 JSON 节点 - RyanJson_t newItem = NULL; - if (RyanJsonTrue == isint && number >= INT32_MIN && number <= INT32_MAX) + // 判断符号 + if (RyanJsonTrue == isNegative) { number = -number; } + + // 浮点数还需要处理 + if (RyanJsonFalse == isInt) { - newItem = RyanJsonCreateInt(key, (int32_t)(sign * number)); + // 避免 pow 调用过多,直接计算指数 + double expFactor = pow(10.0, scale + e_sign * e_scale); + number *= expFactor; } + + *numberValuePtr = number; + *isIntPtr = isInt; + return RyanJsonTrue; +} + +/** + * @brief 解析文本中的数字,添加到json节点中 + * + * @param parseBuf 解析缓冲区 + * @param key 对应的key + * @param out 用于接收解析后的pJson对象的地址 + * @return RyanJsonBool_e 成功或失败 + */ +static RyanJsonBool_e RyanJsonParseNumber(RyanJsonParseBuffer *parseBuf, char *key, RyanJson_t *out) +{ + RyanJsonCheckAssert(NULL != parseBuf && NULL != out); + + double number = 0; + RyanJsonBool_e isInt = RyanJsonTrue; + RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonInternalParseDouble(parseBuf, &number, &isInt)); + + // 创建 JSON 节点 + RyanJson_t newItem = NULL; + if (RyanJsonTrue == isInt && number >= INT32_MIN && number <= INT32_MAX) { newItem = RyanJsonCreateInt(key, (int32_t)number); } else { - // 避免 pow 调用过多,直接计算指数 - double expFactor = pow(10.0, scale + e_sign * e_scale); - newItem = RyanJsonCreateDouble(key, sign * number * expFactor); + newItem = RyanJsonCreateDouble(key, number); } RyanJsonCheckReturnFalse(NULL != newItem); @@ -650,8 +864,8 @@ static RyanJsonBool_e RyanJsonParseNumber(RyanJsonParseBuffer *parseBuf, char *k /** * @brief 解析文本中的字符串,添加到json节点中 * - * @param text 带有jsonString的文本 - * @param buf 接收解析后的字符串指针的地址 + * @param parseBuf 带有jsonString的文本 + * @param buffer 接收解析后的字符串指针的地址 * @return RyanJsonBool_e */ static RyanJsonBool_e RyanJsonParseStringBuffer(RyanJsonParseBuffer *parseBuf, char **buffer) @@ -691,18 +905,19 @@ static RyanJsonBool_e RyanJsonParseStringBuffer(RyanJsonParseBuffer *parseBuf, c RyanJsonCheckReturnFalse(NULL != outBuffer); uint8_t *outCurrentPtr = outBuffer; - while (parseBufHasRemain(parseBuf) && '\"' != *parseBuf->currentPtr) + // ?获取字符串长度的时候已经确保 '\"' != *parseBuf->currentPtr 一定有结尾了 + while ('\"' != *parseBuf->currentPtr) { // 普通字符 if ('\\' != *parseBuf->currentPtr) { *outCurrentPtr++ = *parseBuf->currentPtr; - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto error__; }); continue; } // 转义字符 - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto error__; }); switch (*parseBuf->currentPtr) { @@ -716,107 +931,101 @@ static RyanJsonBool_e RyanJsonParseStringBuffer(RyanJsonParseBuffer *parseBuf, c case '/': *outCurrentPtr++ = *parseBuf->currentPtr; break; case 'u': { - // 获取 Unicode 字符 uint64_t codepoint = 0; - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 4), { goto __error; }); - uint32_t first_code = 0; - RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseHex(parseBuf->currentPtr - 3, &first_code), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 4), { goto error__; }); + uint32_t firstCode = 0; + RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseHex(parseBuf->currentPtr - 3, &firstCode), { goto error__; }); // 检查是否有效 - if (first_code >= 0xDC00 && first_code <= 0xDFFF) { goto __error; } + RyanJsonCheckCode(firstCode < 0xDC00 || firstCode > 0xDFFF, { goto error__; }); - if (first_code >= 0xD800 && first_code <= 0xDBFF) // UTF16 代理对 + if (firstCode >= 0xD800 && firstCode <= 0xDBFF) // UTF16 代理对 { - if (!parseBufHasRemainAtIndex(parseBuf, 2)) { goto __error; } + RyanJsonCheckCode(parseBufHasRemainAtIndex(parseBuf, 2), { goto error__; }); - if (parseBuf->currentPtr[1] != '\\' || parseBuf->currentPtr[2] != 'u') - { - goto __error; // 缺少代理的后半部分 - } + RyanJsonCheckCode('\\' == parseBuf->currentPtr[1] && 'u' == parseBuf->currentPtr[2], { + goto error__; // 缺少代理的后半部分 + }); - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 6), { goto __error; }); - uint32_t second_code = 0; - RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseHex(parseBuf->currentPtr - 3, &second_code), - { goto __error; }); - if (0 == first_code || second_code < 0xDC00 || second_code > 0xDFFF) - { - goto __error; // 无效的代理后半部分 - } + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 6), { goto error__; }); + uint32_t secondCode = 0; + // 上面已经判断过,这个不应该失败 + RyanJsonCheckNeverNoAssert(RyanJsonTrue == RyanJsonParseHex(parseBuf->currentPtr - 3, &secondCode)); + RyanJsonCheckCode(secondCode >= 0xDC00 && secondCode <= 0xDFFF, { + goto error__; // 无效的代理后半部分 + }); - codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + codepoint = 0x10000 + (((firstCode & 0x3FF) << 10) | (secondCode & 0x3FF)); } else { - codepoint = first_code; + codepoint = firstCode; } /* encode as UTF-8 * takes at maximum 4 bytes to encode: * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - uint8_t utf8_length; - uint8_t first_byte_mark; + uint8_t utf8Length; + uint8_t firstByteMark; if (codepoint < 0x80) { - utf8_length = 1; // normal ascii, encoding 0xxxxxxx - first_byte_mark = 0; + utf8Length = 1; // normal ascii, encoding 0xxxxxxx + firstByteMark = 0; } else if (codepoint < 0x800) { - utf8_length = 2; // two bytes, encoding 110xxxxx 10xxxxxx - first_byte_mark = 0xC0; // 11000000 + utf8Length = 2; // two bytes, encoding 110xxxxx 10xxxxxx + firstByteMark = 0xC0; // 11000000 } else if (codepoint < 0x10000) { - utf8_length = 3; // three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx - first_byte_mark = 0xE0; // 11100000 + utf8Length = 3; // three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx + firstByteMark = 0xE0; // 11100000 } else { - utf8_length = 4; // four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx - first_byte_mark = 0xF0; // 11110000 + utf8Length = 4; // four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx + firstByteMark = 0xF0; // 11110000 } // 不太可能发生 // else // { - // goto __error; // 无效的 unicode 代码点 + // goto error__; // 无效的 unicode 代码点 // } // encode as utf8 - for (uint8_t utf8_position = (uint8_t)(utf8_length - 1); utf8_position > 0; utf8_position--) + for (uint8_t utf8Position = (uint8_t)(utf8Length - 1); utf8Position > 0; utf8Position--) { - outCurrentPtr[utf8_position] = (uint8_t)((codepoint | 0x80) & 0xBF); // 10xxxxxx + outCurrentPtr[utf8Position] = (uint8_t)((codepoint | 0x80) & 0xBF); // 10xxxxxx codepoint >>= 6; } // encode first byte - if (utf8_length > 1) { outCurrentPtr[0] = (uint8_t)((codepoint | first_byte_mark) & 0xFF); } + if (utf8Length > 1) { outCurrentPtr[0] = (uint8_t)((codepoint | firstByteMark) & 0xFF); } else { outCurrentPtr[0] = (uint8_t)(codepoint & 0x7F); } - outCurrentPtr += utf8_length; + outCurrentPtr += utf8Length; break; } default: // *outCurrentPtr++ = *buf->currentPtr; - goto __error; + goto error__; } - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto error__; }); } *outCurrentPtr = '\0'; - // todo 不等于的话是不是应该报错? - if (parseBufHasRemain(parseBuf) && *parseBuf->currentPtr == '\"') - { - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); - } + RyanJsonCheckNeverNoAssert('\"' == *parseBuf->currentPtr); + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto error__; }); *buffer = (char *)outBuffer; return RyanJsonTrue; -__error: +error__: jsonFree(outBuffer); *buffer = NULL; return RyanJsonFalse; @@ -863,42 +1072,42 @@ static RyanJsonBool_e RyanJsonParseArray(RyanJsonParseBuffer *parseBuf, char *ke RyanJson_t newItem = RyanJsonCreateArrayAndKey(key); RyanJsonCheckReturnFalse(NULL != newItem); - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); - RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto __error; }); + RyanJson_t prev = NULL, item; + RyanJsonCheckNeverNoAssert(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); + RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto error__; }); // 空数组 - RyanJsonCheckCode(parseBufHasRemain(parseBuf), { goto __error; }); - if (*parseBuf->currentPtr == ']') { goto __next; } + RyanJsonCheckCode(parseBufHasRemain(parseBuf), { goto error__; }); + if (*parseBuf->currentPtr == ']') { goto next__; } - RyanJson_t prev = NULL, item; do { // 跳过 ',' if (NULL != prev) { - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto error__; }); } - RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto error__; }); - if (RyanJsonFalse == RyanJsonParseValue(parseBuf, NULL, &item)) { goto __error; } + RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseValue(parseBuf, NULL, &item), { goto error__; }); - RyanJsonCheckAssert(RyanJsonTrue == RyanJsonInsert(newItem, UINT32_MAX, item)); + RyanJsonCheckNeverNoAssert(RyanJsonTrue == RyanJsonInsert(newItem, UINT32_MAX, item)); prev = item; } while (parseBufHasRemain(parseBuf) && *parseBuf->currentPtr == ','); - RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto __error; }); - RyanJsonCheckCode(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr == ']', { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto error__; }); + RyanJsonCheckCode(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr == ']', { goto error__; }); -__next: - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); +next__: + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto error__; }); *out = newItem; return RyanJsonTrue; -__error: +error__: RyanJsonDelete(newItem); *out = NULL; return RyanJsonFalse; @@ -920,56 +1129,55 @@ static RyanJsonBool_e RyanJsonParseObject(RyanJsonParseBuffer *parseBuf, char *k RyanJson_t newItem = RyanJsonCreateObjectAndKey(key); RyanJsonCheckReturnFalse(NULL != newItem); - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); - RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto __error; }); - - RyanJsonCheckCode(parseBufHasRemain(parseBuf), { goto __error; }); - if (*parseBuf->currentPtr == '}') { goto __next; } RyanJson_t prev = NULL, item; + RyanJsonCheckNeverNoAssert(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1)); + RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto error__; }); + + RyanJsonCheckCode(parseBufHasRemain(parseBuf), { goto error__; }); + if (*parseBuf->currentPtr == '}') { goto next__; } + do { if (NULL != prev) { - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); // 跳过 ',' + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto error__; }); // 跳过 ',' } - RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto error__; }); - if (RyanJsonFalse == RyanJsonParseStringBuffer(parseBuf, &objKey)) { goto __error; } + RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseStringBuffer(parseBuf, &objKey), { goto error__; }); + RyanJsonCheckNeverNoAssert(NULL != objKey); - RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto error__; }); // 解析指示符 ':' - RyanJsonCheckCode(parseBufHasRemain(parseBuf) && ':' == *parseBuf->currentPtr, { goto __error; }); + RyanJsonCheckCode(parseBufHasRemain(parseBuf) && ':' == *parseBuf->currentPtr, { goto error__; }); - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); - RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto error__; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto error__; }); - if (RyanJsonFalse == RyanJsonParseValue(parseBuf, objKey, &item)) { goto __error; } - if (objKey) - { - jsonFree(objKey); - objKey = NULL; - } + RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseValue(parseBuf, objKey, &item), { goto error__; }); + jsonFree(objKey); + objKey = NULL; - RyanJsonCheckAssert(RyanJsonTrue == RyanJsonInsert(newItem, UINT32_MAX, item)); + RyanJsonCheckNeverNoAssert(RyanJsonTrue == RyanJsonInsert(newItem, UINT32_MAX, item)); prev = item; } while (parseBufHasRemain(parseBuf) && *parseBuf->currentPtr == ','); - RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto __error; }); + RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), { goto error__; }); RyanJsonCheckCode(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr == '}', { objKey = NULL; // 由上层进行删除 - goto __error; + goto error__; }); -__next: - RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto __error; }); +next__: + RyanJsonCheckCode(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1), { goto error__; }); *out = newItem; return RyanJsonTrue; -__error: +error__: if (objKey) { jsonFree(objKey); } RyanJsonDelete(newItem); *out = NULL; @@ -1045,17 +1253,11 @@ static RyanJsonBool_e RyanJsonParseCheckNullTerminator(RyanJsonParseBuffer *pars if (requireNullTerminator) { - // 故意不检查 - RyanJsonCheckCode(RyanJsonTrue == parseBufSkipWhitespace(parseBuf), {}); - - // 后面还有数据非空字符 - RyanJsonCheckReturnFalse(!(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr)); - - // // 后面还有数据 - // RyanJsonCheckReturnFalse(!parseBufHasRemainBytes(parseBuf, 1)); + // 故意不检查,允许空白 + (void)parseBufSkipWhitespace(parseBuf); - // // 非空字符 - // RyanJsonCheckReturnFalse(!(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr)); + // 上面已经去掉空白,如果后面还有数据,则失败 + RyanJsonCheckReturnFalse(!parseBufHasRemain(parseBuf)); } return RyanJsonTrue; @@ -1070,6 +1272,7 @@ RyanJson_t RyanJsonParseOptions(const char *text, uint32_t size, RyanJsonBool_e RyanJsonCheckReturnNull(RyanJsonTrue == parseBufSkipWhitespace(&parseBuf)); RyanJsonCheckReturnNull(RyanJsonTrue == RyanJsonParseValue(&parseBuf, NULL, &pJson)); + // 检查解析后的文本后面是否有无意义的字符 RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseCheckNullTerminator(&parseBuf, requireNullTerminator), { RyanJsonDelete(pJson); @@ -1081,63 +1284,166 @@ RyanJson_t RyanJsonParseOptions(const char *text, uint32_t size, RyanJsonBool_e return pJson; } +/** + * @brief 规范化浮点数输出:删除尾部无效的0(非科学计数法时) + * + * @param printfBuf 打印缓冲区 + * @param len 当前输出长度 + * @return int32_t 处理后的长度 + */ +static int32_t RyanJsonTrimDoubleTrailingZeros(RyanJsonPrintBuffer *printfBuf, int32_t len) +{ + // ?测试平台输出偶尔输出大写 "E",测试用 +#ifdef RyanJsonLinuxTestEnv + int32_t eIndex = INT32_MIN; + if (0 == RyanJsonRandRange(0, 20)) + { + for (int32_t i = 0; i < len; i++) + { + if ('e' == printBufCurrentPtr(printfBuf)[i]) + { + printBufCurrentPtr(printfBuf)[i] = 'E'; + eIndex = i; + break; + } + } + } +#endif + + // 检查是不是科学计数法 + RyanJsonBool_e isScientificNotation = RyanJsonFalse; + for (int32_t i = 0; i < len; i++) + { + // 有些平台会输出'E' + if ('e' == printBufCurrentPtr(printfBuf)[i] || 'E' == printBufCurrentPtr(printfBuf)[i]) + { + isScientificNotation = RyanJsonTrue; + break; + } + } + + // ?恢复测试平台输出的大写"E" +#ifdef RyanJsonLinuxTestEnv + if (INT32_MIN != eIndex) { printBufCurrentPtr(printfBuf)[eIndex] = 'e'; } +#endif + + if (RyanJsonFalse == isScientificNotation) + { + // 删除小数部分中无效的 0 + // 最小也要为"0.0" + while (len > 3) + { + if ('0' != printBufCurrentPtr(printfBuf)[len - 1]) { break; } + if ('.' == printBufCurrentPtr(printfBuf)[len - 2]) { break; } + len--; + printBufCurrentPtr(printfBuf)[len] = '\0'; + } + } + + return len; +} /** * @brief 反序列化数字 * * @param pJson - * @param buf + * @param printfBuf * @return RyanJsonBool_e */ static RyanJsonBool_e RyanJsonPrintNumber(RyanJson_t pJson, RyanJsonPrintBuffer *printfBuf) { RyanJsonCheckAssert(NULL != pJson && NULL != printfBuf); - double numberValue; int32_t len; // RyanJsonNumber 类型是一个整数 if (RyanJsonFalse == RyanJsonGetPayloadNumberIsDoubleByFlag(pJson)) { - // RyanJsonCheckReturnFalse(printBufAppend(buf, 21)); // 64 位整数最多包含 20 个数字字符、1 符号 - RyanJsonCheckReturnFalse(printBufAppend(printfBuf, 11)); // 32 位整数最多包含 10 个数字字符、1 符号 + // INT32_MIN = -2147483648 (11 chars) + RyanJsonCheckReturnFalse(printBufAppend(printfBuf, 11)); - len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printfBuf->size, "%" PRId32, RyanJsonGetIntValue(pJson)); - RyanJsonCheckReturnFalse(len > 0); // snprintf 失败 + len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printBufRemainBytes(printfBuf), "%" PRId32, + RyanJsonGetIntValue(pJson)); + RyanJsonCheckReturnFalse(len > 0); printfBuf->cursor += (uint32_t)len; + + return RyanJsonTrue; } - else // RyanJsonNumber 的类型是浮点型 + + // RyanJsonNumber 的类型是浮点型 + RyanJsonCheckReturnFalse(printBufAppend(printfBuf, RyanJsonDoubleBufferSize)); + double doubleValue = RyanJsonGetDoubleValue(pJson); + + // 处理特殊值:无穷大和 NaN 输出为 null(RFC 8259 不支持 Infinity/NaN) + if (isinf(doubleValue) || isnan(doubleValue)) { - RyanJsonCheckReturnFalse(printBufAppend(printfBuf, 64)); // 浮点数用64可以适应大部分情况 - numberValue = RyanJsonGetDoubleValue(pJson); + printBufPutString(printfBuf, (uint8_t *)"null", 4); + return RyanJsonTrue; + } - // use full transformation within bounded space - if (fabs(floor(numberValue) - numberValue) <= DBL_EPSILON && fabs(numberValue) < 1.0e60) - { - len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printfBuf->size, "%.1lf", numberValue); - RyanJsonCheckReturnFalse(len > 0); // snprintf 失败 - } + double absDoubleValue = fabs(doubleValue); - // use exponential form conversion beyond the limited range - else if (fabs(numberValue) < 1.0e-6 || fabs(numberValue) > 1.0e9) - { - len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printfBuf->size, "%e", numberValue); - RyanJsonCheckReturnFalse(len > 0); // snprintf 失败 - } + // 判断是否为整数(在合理范围内),保留一位小数 (例如 5.0, 0.0) + // 注意:0 也需要特殊处理,否则会进入科学记数法分支 + // 在有界空间内使用完全变换 + if ((absDoubleValue < DBL_EPSILON || (absDoubleValue < 1.0e15 && absDoubleValue >= 1.0e-6)) && + fabs(floor(doubleValue) - doubleValue) <= DBL_EPSILON) + { + len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printBufRemainBytes(printfBuf), "%.1lf", doubleValue); + // 有外层限制 1e-6 ~ 1e15, 所以肯定不会越界 + RyanJsonCheckReturnFalse(len > 0); - // default conversion - else + // 嵌入式平台为了保险还是加上吧,用户可能设置的bufSize会比较小 +#ifndef RyanJsonLinuxTestEnv + RyanJsonCheckReturnFalse(len < (int32_t)printBufRemainBytes(printfBuf)); +#endif + } + else + { + +// ?测试平台轮流使用 "%.15g" 和 "%lf" 让下面去0的逻辑也可以执行 +#ifdef RyanJsonLinuxTestEnv +#undef RyanJsonSnprintfSupportScientific + // 基于 double 值本身选择格式(确定性),保证同一个值总是用相同格式 + // %lf 在 [1e-6, 1e6] 范围内输出安全 + // 极端值(>1e6 或 <1e-6)必须使用科学记数法,否则可能超出缓冲区 + RyanJsonBool_e RyanJsonSnprintfSupportScientific = doubleValue > 1.0 ? RyanJsonTrue : RyanJsonFalse; + +#endif + + // 极大/极小数或普通浮点数 + // 不使用 %.15g 是因为很多嵌入式平台 %.15g 效果和 %.17g效果一样 + // 可能的效果是 0.2 被序列化成 0.200000003000000 ,就算去掉尾部0也不美观 + // ?用%lf当前是有缺点的,给double留的64字节空间可能被撑爆,但是大部分嵌入式平台可以放心 + len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printBufRemainBytes(printfBuf), + RyanJsonSnprintfSupportScientific ? "%.15g" : "%lf", doubleValue); +#ifdef RyanJsonLinuxTestEnv + // 测试环境:偶尔模拟溢出以触发防御性检查分支 + if (0 == RyanJsonRandRange(0, 1000) && len > 0) { len = (int32_t)printBufRemainBytes(printfBuf) + 1; } +#endif + RyanJsonCheckReturnFalse(len > 0 && len < (int32_t)printBufRemainBytes(printfBuf)); + + // 往返检查:在去0之前进行,确保原始精度足够 + // 如果精度不够,改用 %.17g + double number = 0; + RyanJsonBool_e isInt = RyanJsonTrue; + RyanJsonParseBuffer parseBuf = {.currentPtr = printBufCurrentPtr(printfBuf), .remainSize = (uint32_t)len}; + RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonInternalParseDouble(&parseBuf, &number, &isInt)); + if (RyanJsonFalse == RyanJsonCompareDouble(number, doubleValue)) { - len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printfBuf->size, "%lf", numberValue); - RyanJsonCheckReturnFalse(len > 0); // snprintf 失败 - while (len > 0 && printBufCurrentPtr(printfBuf)[len - 1] == '0' && - printBufCurrentPtr(printfBuf)[len - 2] != '.') // 删除小数部分中无效的 0 - { - len--; - } + len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printBufRemainBytes(printfBuf), "%.17g", doubleValue); + RyanJsonCheckReturnFalse(len > 0); + +#ifndef RyanJsonLinuxTestEnv + // "%.17g"也判断是因为不可以相信嵌入式平台真的会输出科学计数法格式 + RyanJsonCheckReturnFalse(len < (int32_t)printBufRemainBytes(printfBuf)); +#endif } - printfBuf->cursor += (uint32_t)len; + + // 进行去0处理,理论上只有lf需要,但是保险可以都去 + len = RyanJsonTrimDoubleTrailingZeros(printfBuf, len); } + printfBuf->cursor += (uint32_t)len; return RyanJsonTrue; } @@ -1145,7 +1451,7 @@ static RyanJsonBool_e RyanJsonPrintNumber(RyanJson_t pJson, RyanJsonPrintBuffer * @brief 反序列化字符串 * * @param strValue - * @param buf + * @param printfBuf * @return RyanJsonBool_e */ static RyanJsonBool_e RyanJsonPrintStringBuffer(const uint8_t *strValue, RyanJsonPrintBuffer *printfBuf) @@ -1210,8 +1516,8 @@ static RyanJsonBool_e RyanJsonPrintStringBuffer(const uint8_t *strValue, RyanJso default: { // 可以不加p有效性的判断是因为,这个RyanJson生成的字符串,RyanJson可以确保p一定是有效的 // jsonLog("hexasdf:\\u%04X\n", codepoint); - RyanJsonCheckReturnFalse( - 5 == RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printfBuf->size, "u%04X", *strCurrentPtr)); + RyanJsonCheckReturnFalse(5 == RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), + printBufRemainBytes(printfBuf), "u%04X", *strCurrentPtr)); printfBuf->cursor += 5; // utf break; } @@ -1235,7 +1541,7 @@ static RyanJsonBool_e RyanJsonPrintString(RyanJson_t pJson, RyanJsonPrintBuffer * @brief 反序列化数组 * * @param pJson - * @param buf + * @param printfBuf * @param depth * @param format * @return RyanJsonBool_e @@ -1281,7 +1587,6 @@ static RyanJsonBool_e RyanJsonPrintArray(RyanJson_t pJson, RyanJsonPrintBuffer * if (format && count) { RyanJsonCheckReturnFalse(printBufAppend(printfBuf, depth + 1U)); - for (uint32_t i = 0; i <= depth; i++) { printBufPutChar(printfBuf, '\t'); } } @@ -1323,7 +1628,7 @@ static RyanJsonBool_e RyanJsonPrintArray(RyanJson_t pJson, RyanJsonPrintBuffer * * @brief 反序列化对象 * * @param pJson - * @param buf + * @param printfBuf * @param depth * @param format * @return RyanJsonBool_e @@ -1554,12 +1859,14 @@ RyanJson_t RyanJsonGetObjectByKey(RyanJson_t pJson, const char *key) RyanJsonCheckReturnNull(_checkType(RyanJsonGetType(pJson), RyanJsonTypeObject)); RyanJson_t nextItem = RyanJsonGetObjectValue(pJson); - RyanJsonCheckReturnNull(NULL != nextItem && RyanJsonIsKey(nextItem)); + RyanJsonCheckReturnNull(NULL != nextItem); + RyanJsonCheckNeverNoAssert(RyanJsonIsKey(nextItem)); while (0 != RyanJsonStrcmp(RyanJsonGetKey(nextItem), key)) { nextItem = nextItem->next; - RyanJsonCheckReturnNull(NULL != nextItem && RyanJsonIsKey(nextItem)); + RyanJsonCheckReturnNull(NULL != nextItem); + RyanJsonCheckNeverNoAssert(RyanJsonIsKey(nextItem)); } return nextItem; @@ -1594,7 +1901,7 @@ RyanJson_t RyanJsonDetachByIndex(RyanJson_t pJson, uint32_t index) if (NULL != prev) { prev->next = nextItem->next; } else { - RyanJsonGetObjectValue(pJson) = nextItem->next; + RyanJsonChangeObjectValue(pJson, nextItem->next); } nextItem->next = NULL; @@ -1615,20 +1922,22 @@ RyanJson_t RyanJsonDetachByKey(RyanJson_t pJson, const char *key) RyanJsonCheckReturnNull(_checkType(RyanJsonGetType(pJson), RyanJsonTypeObject)); RyanJson_t nextItem = RyanJsonGetObjectValue(pJson); - RyanJsonCheckReturnNull(NULL != nextItem && RyanJsonIsKey(nextItem)); + RyanJsonCheckReturnNull(NULL != nextItem); + RyanJsonCheckNeverNoAssert(RyanJsonIsKey(nextItem)); RyanJson_t prev = NULL; while (0 != RyanJsonStrcmp(RyanJsonGetKey(nextItem), key)) { prev = nextItem; nextItem = nextItem->next; - RyanJsonCheckReturnNull(NULL != nextItem && RyanJsonIsKey(nextItem)); + RyanJsonCheckReturnNull(NULL != nextItem); + RyanJsonCheckNeverNoAssert(RyanJsonIsKey(nextItem)); } if (NULL != prev) { prev->next = nextItem->next; } else // 更改的可能是第一个节点 { - RyanJsonGetObjectValue(pJson) = nextItem->next; + RyanJsonChangeObjectValue(pJson, nextItem->next); } nextItem->next = NULL; @@ -1682,19 +1991,20 @@ RyanJsonBool_e RyanJsonDeleteByKey(RyanJson_t pJson, const char *key) */ RyanJsonBool_e RyanJsonInsert(RyanJson_t pJson, uint32_t index, RyanJson_t item) { + RyanJson_t nextItem = NULL; + RyanJson_t prev = NULL; + RyanJsonCheckReturnFalse(NULL != item); - RyanJsonCheckCode(NULL != pJson, { goto __exit; }); + RyanJsonCheckCode(NULL != pJson, { goto error__; }); RyanJsonCheckCode(_checkType(RyanJsonGetType(pJson), RyanJsonTypeArray) || (_checkType(RyanJsonGetType(pJson), RyanJsonTypeObject) && RyanJsonIsKey(item)), { - jsonLog("__error 不是正确类型 %d\r\n", index); - goto __exit; + jsonLog("error__ 不是正确类型 %d\r\n", index); + goto error__; }); - RyanJson_t nextItem = RyanJsonGetObjectValue(pJson); - RyanJson_t prev = NULL; - + nextItem = RyanJsonGetObjectValue(pJson); while (nextItem && index > 0) { prev = nextItem; @@ -1705,7 +2015,7 @@ RyanJsonBool_e RyanJsonInsert(RyanJson_t pJson, uint32_t index, RyanJson_t item) if (NULL != prev) { prev->next = item; } else { - RyanJsonGetObjectValue(pJson) = item; + RyanJsonChangeObjectValue(pJson, item); } // nextItem为NULL时这样赋值也是可以的 @@ -1713,7 +2023,7 @@ RyanJsonBool_e RyanJsonInsert(RyanJson_t pJson, uint32_t index, RyanJson_t item) return RyanJsonTrue; -__exit: +error__: RyanJsonDelete(item); return RyanJsonFalse; } @@ -1731,28 +2041,6 @@ RyanJsonBool_e RyanJsonAddItemToObject(RyanJson_t pJson, const char *key, RyanJs return RyanJsonInsert(pJson, UINT32_MAX, pItem); } -/** - * @brief 替换json对象节点 - * - * @param prev - * @param oldItem - * @param newItem - * @return RyanJsonBool_e - */ -static RyanJsonBool_e RyanJsonReplaceNode(RyanJson_t prev, RyanJson_t oldItem, RyanJson_t newItem) -{ - RyanJsonCheckAssert(NULL != oldItem && NULL != newItem); - - // 链接前驱和新节点 - if (NULL != prev) { prev->next = newItem; } - - // 链接后继和新节点 - if (NULL != oldItem->next) { newItem->next = oldItem->next; } - - oldItem->next = NULL; - return RyanJsonTrue; -} - /** * @brief 通过 索引 替换json对象的子项 * @@ -1782,7 +2070,7 @@ RyanJsonBool_e RyanJsonReplaceByIndex(RyanJson_t pJson, uint32_t index, RyanJson } RyanJsonReplaceNode(prev, nextItem, item); - if (NULL == prev) { RyanJsonGetObjectValue(pJson) = item; } + if (NULL == prev) { RyanJsonChangeObjectValue(pJson, item); } RyanJsonDelete(nextItem); return RyanJsonTrue; @@ -1803,16 +2091,17 @@ RyanJsonBool_e RyanJsonReplaceByKey(RyanJson_t pJson, const char *key, RyanJson_ RyanJsonCheckReturnFalse(_checkType(RyanJsonGetType(pJson), RyanJsonTypeObject)); RyanJson_t prev = NULL; - // todo 增加nextItem没有key的测试 RyanJson_t nextItem = RyanJsonGetObjectValue(pJson); - RyanJsonCheckReturnFalse(NULL != nextItem && RyanJsonIsKey(nextItem)); + RyanJsonCheckReturnFalse(NULL != nextItem); + RyanJsonCheckNeverNoAssert(RyanJsonIsKey(nextItem)); // 找到要修改的节点 while (0 != RyanJsonStrcmp(RyanJsonGetKey(nextItem), key)) { prev = nextItem; nextItem = nextItem->next; - RyanJsonCheckReturnFalse(NULL != nextItem && RyanJsonIsKey(nextItem)); + RyanJsonCheckReturnFalse(NULL != nextItem); + RyanJsonCheckNeverNoAssert(RyanJsonIsKey(nextItem)); } // 没有key的对象 申请一个带key的对象 @@ -1827,7 +2116,7 @@ RyanJsonBool_e RyanJsonReplaceByKey(RyanJson_t pJson, const char *key, RyanJson_ } RyanJsonReplaceNode(prev, nextItem, item); - if (NULL == prev) { RyanJsonGetObjectValue(pJson) = item; } + if (NULL == prev) { RyanJsonChangeObjectValue(pJson, item); } RyanJsonDelete(nextItem); @@ -1848,6 +2137,20 @@ RyanJsonBool_e RyanJsonChangeStringValue(RyanJson_t pJson, const char *strValue) return RyanJsonChangeString(pJson, RyanJsonFalse, RyanJsonIsKey(pJson) ? RyanJsonGetKey(pJson) : NULL, strValue); } +RyanJsonBool_e RyanJsonChangeIntValue(RyanJson_t pJson, int32_t number) +{ + RyanJsonCheckReturnFalse(NULL != pJson); + RyanJsonMemcpy(RyanJsonGetValue(pJson), &number, sizeof(number)); + return RyanJsonTrue; +} + +RyanJsonBool_e RyanJsonChangeDoubleValue(RyanJson_t pJson, double number) +{ + RyanJsonCheckReturnFalse(NULL != pJson); + RyanJsonMemcpy(RyanJsonGetValue(pJson), &number, sizeof(number)); + return RyanJsonTrue; +} + /** * @brief 创建一个 NULL 类型的json对象 * @@ -1885,7 +2188,7 @@ RyanJson_t RyanJsonCreateInt(const char *key, int32_t number) RyanJson_t item = RyanJsonNewNode(&nodeInfo); RyanJsonCheckReturnNull(NULL != item); - RyanJsonGetIntValue(item) = number; + RyanJsonChangeIntValue(item, number); return item; } @@ -1902,7 +2205,7 @@ RyanJson_t RyanJsonCreateDouble(const char *key, double number) RyanJson_t item = RyanJsonNewNode(&nodeInfo); RyanJsonCheckReturnNull(NULL != item); - RyanJsonGetDoubleValue(item) = number; + RyanJsonChangeDoubleValue(item, number); return item; } @@ -1946,57 +2249,30 @@ static RyanJson_t RyanJsonCreateArrayAndKey(const char *key) } RyanJson_t RyanJsonCreateArray(void) { return RyanJsonCreateArrayAndKey(NULL); } -/** - * @brief 创建一个item对象 - * 带有key的对象才可以方便的通过replace替换, - * !此接口不推荐用户调用 - * - * @param key - * @param item - * @return RyanJson_t - */ -RyanJson_t RyanJsonCreateItem(const char *key, RyanJson_t item) -{ - RyanJsonCheckReturnNull(NULL != item); - - RyanJsonNodeInfo_t nodeInfo = { - .type = _checkType(RyanJsonGetType(item), RyanJsonTypeArray) ? RyanJsonTypeArray : RyanJsonTypeObject, - .key = key, - }; - - RyanJson_t newItem = RyanJsonNewNode(&nodeInfo); - - RyanJsonCheckReturnNull(NULL != newItem); - - if (_checkType(RyanJsonGetType(item), RyanJsonTypeArray) || _checkType(RyanJsonGetType(item), RyanJsonTypeObject)) - { - RyanJsonGetObjectValue(newItem) = RyanJsonGetObjectValue(item); - - if (RyanJsonIsKey(item) || RyanJsonIsString(item)) { jsonFree(RyanJsonGetHiddePrt(item)); } - jsonFree(item); - } - else - { - RyanJsonGetObjectValue(newItem) = item; - } - - return newItem; -} - RyanJsonBool_e RyanJsonIsKey(RyanJson_t pJson) { return RyanJsonMakeBool(NULL != pJson && RyanJsonGetPayloadWhiteKeyByFlag(pJson)); } RyanJsonBool_e RyanJsonIsNull(RyanJson_t pJson) { return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeNull == RyanJsonGetType(pJson)); } RyanJsonBool_e RyanJsonIsBool(RyanJson_t pJson) { return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeBool == RyanJsonGetType(pJson)); } RyanJsonBool_e RyanJsonIsNumber(RyanJson_t pJson) -{ return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeNumber == RyanJsonGetType(pJson)); } +{ + return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeNumber == RyanJsonGetType(pJson)); +} RyanJsonBool_e RyanJsonIsString(RyanJson_t pJson) -{ return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeString == RyanJsonGetType(pJson)); } +{ + return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeString == RyanJsonGetType(pJson)); +} RyanJsonBool_e RyanJsonIsArray(RyanJson_t pJson) { return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeArray == RyanJsonGetType(pJson)); } RyanJsonBool_e RyanJsonIsObject(RyanJson_t pJson) -{ return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeObject == RyanJsonGetType(pJson)); } +{ + return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeObject == RyanJsonGetType(pJson)); +} RyanJsonBool_e RyanJsonIsInt(RyanJson_t pJson) -{ return RyanJsonMakeBool(RyanJsonIsNumber(pJson) && (RyanJsonFalse == RyanJsonGetPayloadNumberIsDoubleByFlag(pJson))); } +{ + return RyanJsonMakeBool(RyanJsonIsNumber(pJson) && (RyanJsonFalse == RyanJsonGetPayloadNumberIsDoubleByFlag(pJson))); +} RyanJsonBool_e RyanJsonIsDouble(RyanJson_t pJson) -{ return RyanJsonMakeBool(RyanJsonIsNumber(pJson) && (RyanJsonTrue == RyanJsonGetPayloadNumberIsDoubleByFlag(pJson))); } +{ + return RyanJsonMakeBool(RyanJsonIsNumber(pJson) && (RyanJsonTrue == RyanJsonGetPayloadNumberIsDoubleByFlag(pJson))); +} /** * @brief 深拷贝一份json对象 @@ -2020,9 +2296,13 @@ RyanJson_t RyanJsonDuplicate(RyanJson_t pJson) break; case RyanJsonTypeNumber: - // 不可能出现另外一个条件了 if (RyanJsonIsInt(pJson)) { newItem = RyanJsonCreateInt(key, RyanJsonGetIntValue(pJson)); } - else if (RyanJsonIsDouble(pJson)) { newItem = RyanJsonCreateDouble(key, RyanJsonGetDoubleValue(pJson)); } + else + { + // 不可能出现另外一个条件了 + RyanJsonCheckNeverNoAssert(RyanJsonIsDouble(pJson)); + newItem = RyanJsonCreateDouble(key, RyanJsonGetDoubleValue(pJson)); + } break; case RyanJsonTypeString: newItem = RyanJsonCreateString(key, RyanJsonGetStringValue(pJson)); break; @@ -2035,16 +2315,16 @@ RyanJson_t RyanJsonDuplicate(RyanJson_t pJson) newItem = RyanJsonCreateObjectAndKey(key); } - RyanJsonCheckCode(NULL != newItem, { goto err; }); + RyanJsonCheckCode(NULL != newItem, { goto error__; }); RyanJson_t item, prev = NULL; RyanJson_t temp = RyanJsonGetObjectValue(pJson); while (temp) { item = RyanJsonDuplicate(temp); - RyanJsonCheckCode(NULL != item, { goto err; }); + RyanJsonCheckCode(NULL != item, { goto error__; }); - // RyanJsonCheckAssert(RyanJsonTrue == RyanJsonInsert(newItem, UINT32_MAX, item)); + // RyanJsonCheckNeverNoAssert(RyanJsonTrue == RyanJsonInsert(newItem, UINT32_MAX, item)); if (NULL != prev) { @@ -2053,7 +2333,7 @@ RyanJson_t RyanJsonDuplicate(RyanJson_t pJson) } else { - RyanJsonGetObjectValue(newItem) = item; + RyanJsonChangeObjectValue(newItem, item); prev = item; } @@ -2062,12 +2342,12 @@ RyanJson_t RyanJsonDuplicate(RyanJson_t pJson) break; } - default: goto err; + default: goto error__; } return newItem; -err: +error__: RyanJsonDelete(newItem); return NULL; } @@ -2076,13 +2356,7 @@ RyanJson_t RyanJsonDuplicate(RyanJson_t pJson) * @brief 通过删除无效字符、注释等, 减少json文本大小 * * @param text 文本指针 - */ - -/** - * @brief 通过删除无效字符、注释等, 减少json文本大小 - * - * @param text 文本指针 - * @param textLen 文本长度,使用int32_t是方式用户隐士转换不好发现bug + * @param textLen 文本长度,不使用uint32_t是方式用户传递的参数隐式转换不容易发现bug * @return uint32_t */ uint32_t RyanJsonMinify(char *text, int32_t textLen) @@ -2146,18 +2420,18 @@ uint32_t RyanJsonMinify(char *text, int32_t textLen) */ RyanJsonBool_e RyanJsonCompare(RyanJson_t leftJson, RyanJson_t rightJson) { - if (NULL == leftJson || NULL == rightJson) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(NULL != leftJson && NULL != rightJson); // 相同的对象相等 if (leftJson == rightJson) { return RyanJsonTrue; } - if (RyanJsonGetType(leftJson) != RyanJsonGetType(rightJson)) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(RyanJsonGetType(leftJson) == RyanJsonGetType(rightJson)); switch (RyanJsonGetType(leftJson)) { case RyanJsonTypeNull: return RyanJsonTrue; - case RyanJsonTypeBool: return RyanJsonGetBoolValue(leftJson) == RyanJsonGetBoolValue(rightJson) ? RyanJsonTrue : RyanJsonFalse; + case RyanJsonTypeBool: return (RyanJsonGetBoolValue(leftJson) == RyanJsonGetBoolValue(rightJson)) ? RyanJsonTrue : RyanJsonFalse; case RyanJsonTypeNumber: { @@ -2168,7 +2442,7 @@ RyanJsonBool_e RyanJsonCompare(RyanJson_t leftJson, RyanJson_t rightJson) if (RyanJsonTrue == RyanJsonIsDouble(leftJson) && RyanJsonTrue == RyanJsonIsDouble(rightJson)) { - return compare_double(RyanJsonGetDoubleValue(leftJson), RyanJsonGetDoubleValue(rightJson)); + return RyanJsonCompareDouble(RyanJsonGetDoubleValue(leftJson), RyanJsonGetDoubleValue(rightJson)); } return RyanJsonFalse; @@ -2179,13 +2453,13 @@ RyanJsonBool_e RyanJsonCompare(RyanJson_t leftJson, RyanJson_t rightJson) : RyanJsonFalse; case RyanJsonTypeArray: { - if (RyanJsonGetSize(leftJson) != RyanJsonGetSize(rightJson)) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(RyanJsonGetSize(leftJson) == RyanJsonGetSize(rightJson)); RyanJson_t item; uint32_t itemIndex = 0; RyanJsonArrayForEach(leftJson, item) { - if (RyanJsonTrue != RyanJsonCompare(item, RyanJsonGetObjectByIndex(rightJson, itemIndex))) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonCompare(item, RyanJsonGetObjectByIndex(rightJson, itemIndex))); itemIndex++; } @@ -2193,15 +2467,13 @@ RyanJsonBool_e RyanJsonCompare(RyanJson_t leftJson, RyanJson_t rightJson) } case RyanJsonTypeObject: { - if (RyanJsonGetSize(leftJson) != RyanJsonGetSize(rightJson)) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(RyanJsonGetSize(leftJson) == RyanJsonGetSize(rightJson)); RyanJson_t item; RyanJsonObjectForEach(leftJson, item) { - if (RyanJsonTrue != RyanJsonCompare(item, RyanJsonGetObjectByKey(rightJson, RyanJsonGetKey(item)))) - { - return RyanJsonFalse; - } + RyanJsonCheckReturnFalse(RyanJsonTrue == + RyanJsonCompare(item, RyanJsonGetObjectByKey(rightJson, RyanJsonGetKey(item)))); } return RyanJsonTrue; diff --git a/RyanJson/RyanJson.h b/RyanJson/RyanJson.h index 3c07030..11b2116 100644 --- a/RyanJson/RyanJson.h +++ b/RyanJson/RyanJson.h @@ -1,5 +1,5 @@ -#ifndef __RyanJson__ -#define __RyanJson__ +#ifndef RyanJson +#define RyanJson #ifdef __cplusplus extern "C" { @@ -22,10 +22,18 @@ extern "C" { #define RyanJsonCheckReturnFalse(EX) RyanJsonCheckCode(EX, return RyanJsonFalse;) #define RyanJsonCheckReturnNull(EX) RyanJsonCheckCode(EX, return NULL;) + +// !没有使能assert时RyanJsonCheckAssert不执行的 #ifdef RyanJsonEnableAssert -#define RyanJsonCheckAssert(EX) RyanJsonCheckCode(EX, RyanJsonAssert(NULL && "RyanJsonCheckAssert");) +#define RyanJsonCheckAssert(EX) RyanJsonCheckCode(EX, RyanJsonAssert(NULL &&#EX);) +#define RyanJsonCheckNeverNoAssert(EX) \ + do \ + { \ + if (!(EX)) RyanJsonAssert(NULL && #EX); \ + } while (0) #else -#define RyanJsonCheckAssert(EX) (void)(EX) +#define RyanJsonCheckAssert(EX) ((void)0) +#define RyanJsonCheckNeverNoAssert(EX) (void)(EX) #endif // Json 的最基础节点,所有 Json 元素都由该节点表示。 @@ -35,13 +43,20 @@ struct RyanJsonNode { struct RyanJsonNode *next; // 单链表节点指针 - /* - * 在 next 后紧跟一个字节的 flag,用于描述节点的核心信息: + /** + * @brief RyanJson 节点结构体 + * 每个节点由链表连接,包含元数据标识 (Flag) 与动态载荷存储区。 + * + * 内存布局: + * [ next指针 | flag(1字节) | padding/指针空间 | 动态载荷区 ] * - * 位分布如下: + * @brief 节点元数据标识 (Flag) + * 紧跟 next 指针后,利用 1 字节位域描述节点类型及存储状态。 + * + * flag 位分布定义: * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 * ----------------------------------------------------- - * 保留 KeyLen KeyLen HasKey NumExt Type2 Type1 Type0 + * strMode KeyLen KeyLen HasKey NumExt Type2 Type1 Type0 * * 各位含义: * - bit0-2 : 节点类型 @@ -50,58 +65,88 @@ struct RyanJsonNode * * - bit3 : 扩展位 * Bool 类型:0=false, 1=true - * Number 类型:0=int, 1=double + * Number 类型:0=int(4字节), 1=double(8字节) * * - bit4 : 是否包含 Key * 0=无 Key(数组元素) * 1=有 Key(对象成员) * * - bit5-6 : Key 长度字段字节数 - * 00=1字节 (≤255) - * 01=2字节 (≤65535) - * 10=3字节 (≤16M) + * 00=1字节 (≤UINT8_MAX) + * 01=2字节 (≤UINT16_MAX) + * 10=3字节 (≤UINT24_MAX) * 11=4字节 (≤UINT32_MAX) * - * - bit7 : 保留位(未来可用于压缩标记、特殊类型等) - */ - - /* - * flag 后若节点包含 key / strValue,则跟随一个指针, - * 指向存储区:[ keyLen | key | stringValue ] - * 其中 keyLen 的大小由 flag 中的长度信息决定(最多 4 字节)。 - * - * 在指针之后,根据节点类型存储具体数据: - * - null / bool : 由 flag 表示 - * - string : 由上述指针指向 - * - number : 根据 flag 决定存储 int(4字节) 或 double(8字节) - * - object : 动态分配空间存储子节点,链表结构如下: - * - * { - * "name": "RyanJson", - * next ( - * "version": "xxx", - * next ( - * "repository": "https://github.com/Ryan-CW-Code/RyanJson", - * next ( - * "keywords": ["json", "streamlined", "parser"], - * next ( - * "others": { ... } - * ))) - * } - */ - - /* - * 设计特点: - * - 一个 Json 节点最多 malloc 两次(一次节点本身,一次可选的 key/stringValue), - * 对嵌入式系统非常友好,减少 malloc 头部开销, 尽可能的减少内存碎片。 + * - bit7 : 表示key / strValue 存储模式 + * 0:inline 模式, 1=ptr 模式 * - * - key 和 stringValue 必须通过指针管理: - * * 如果直接放在节点里,虽然只需一次 malloc, - * 但修改场景会遇到替换/释放困难。 - * * 用户可能传递的 Json 对象不是指针,无法直接替换节点, - * 要求应用层传递指针会增加侵入性,不符合“应用层无需修改”的目标。 + * @brief 动态载荷存储区 + * 目的: + * - 在保持 API 易用性和稳定性的同时,最大限度减少 malloc 调用次数。 + * - 尤其在嵌入式平台,malloc 代价高昂:不仅有堆头部空间浪费,还会产生内存碎片。 + * - 通过利用结构体内的对齐填充 (Padding) 和指针空间,形成一个灵活的缓冲区。 + * + * 存储策略: + * 利用结构体内存对齐产生的 Padding(如 Flag 后的空隙)以及原本用于存储指针的空间,形成一个缓冲区 + * 若节点包含 key / strValue,则可能有两种方案: + * 1. inline 模式 (小数据优化) + * - 当 (KeyLen + Key + Value) 的总长度 ≤ 阈值时,直接存储在结构体内部。 + * - 阈值计算公式: + * 阈值 = Padding + sizeof(void*) + (malloc头部空间的一半),再向上对齐到字节边界。 + * 举例: + * - 内存对齐:4字节 + * - malloc头部空间:8字节 + * - 可用空间 = 3 (flag后padding) + 4 (指针空间) + 4 (malloc头部一半) + * - 向上对齐后得到阈值12字节 + * - 存储布局: + * [ KeyLen | Key | Value ] + * 起始地址即为 flag 之后,数据紧凑排列,无需额外 malloc。 + * + * 2. ptr 模式 (大数据) + * - 当数据长度 > 阈值时,结构体存储一个指针,指向独立的堆区。 + * - 存储布局: + * [ KeyLen | *ptr ] -> (ptr指向) [ Key | Value ] + * - KeyLen 的大小由 flag 中的长度字段决定 (最多 4 字节)。 + * - 这样保证大数据不会撑爆结构体,同时保持 API 一致性。 + + * 其他类型的存储: + * - null / bool : 由 flag 位直接表示,无需额外空间。 + * - number : 根据 flag 扩展位决定存储 int(4字节) 或 double(8字节)。 + * - object : 动态分配空间存储子节点,采用链表结构。 + * + * 设计考量: + * - malloc 在嵌入式平台的开销: + * * RTT 最小内存管理算法中,malloc 头部约 12 字节(可以考虑tlsf算法头部空间仅4字节,内存碎片也控制的很好,适合物联网应用)。 + * * 一个 RyanJson 节点本身可能只有个位数字节,头部空间就让内存占用翻倍。 + * - 因此: + * * 小数据尽量 inline 存储,避免二次 malloc。 + * * 大数据 fallback 到 ptr 模式,保证灵活性。 + * - 修改场景: + * * 理想情况:节点结构体后面直接跟 key/strValue,修改时释放并重新申请节点。 + * * 但这样 changKey/changStrValue 接口改动太大,用户层需要修改指针,代价高。 + * * 实际策略:提供就地修改接口。 + * - 若新值长度 ≤ 原有 inline 缓冲区,直接覆盖。 + * - 若超过阈值,自动切换到 ptr 模式,用户层无需关心。 * - * - 因此采用指针方式,保证灵活性和低侵入性。 + * 链表结构示例: + * { + * "name": "RyanJson", + * next ( + * "version": "xxx", + * next ( + * "repository": "https://github.com/Ryan-CW-Code/RyanJson", + * next ( + * "keywords": [ + * "json", + * next ( + * "streamlined", + * next ( + * "parser" + * )) + * ], + * next ( + * "others": { ... } + * } */ }; @@ -157,9 +202,11 @@ typedef void *(*RyanJsonRealloc_t)(void *block, size_t size); // flag空间不够的时候可以把这个字段弃用,用redis的listpack方法将key和keyLen一起表示,内存占用也挺好,但是复杂度高,有空间就保持现在这样 #define RyanJsonGetPayloadEncodeKeyLenByFlag(pJson) ((uint8_t)RyanJsonGetPayloadFlagField((pJson), 5, RyanJsonGetMask(2)) + 1) #define RyanJsonSetPayloadEncodeKeyLenByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 5, RyanJsonGetMask(2), (value)) + +#define RyanJsonGetPayloadStrIsPtrByFlag(pJson) RyanJsonGetPayloadFlagField((pJson), 7, RyanJsonGetMask(1)) +#define RyanJsonSetPayloadStrIsPtrByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 7, RyanJsonGetMask(1), (value)) + extern RyanJsonBool_e RyanJsonInsert(RyanJson_t pJson, uint32_t index, RyanJson_t item); -extern void *RyanJsonGetValue(RyanJson_t pJson); -extern RyanJson_t RyanJsonCreateItem(const char *key, RyanJson_t item); // 需用户释放内存 /** * !!!上面的接口不推荐使用 * @@ -168,7 +215,7 @@ extern RyanJson_t RyanJsonCreateItem(const char *key, RyanJson_t item); // 需 /** * @brief json对象函数 */ -extern RyanJsonBool_e RyanJsonInitHooks(RyanJsonMalloc_t _malloc, RyanJsonFree_t _free, RyanJsonRealloc_t _realloc); +extern RyanJsonBool_e RyanJsonInitHooks(RyanJsonMalloc_t userMalloc, RyanJsonFree_t userFree, RyanJsonRealloc_t userRealloc); extern RyanJson_t RyanJsonParseOptions(const char *text, uint32_t size, RyanJsonBool_e requireNullTerminator, const char **parseEndPtr); // 需用户释放内存 #define RyanJsonParse(text) RyanJsonParseOptions((text), (uint32_t)RyanJsonStrlen(text), RyanJsonFalse, NULL) // 需用户释放内存 @@ -187,6 +234,7 @@ extern char *RyanJsonPrintPreallocated(RyanJson_t pJson, char *buffer, uint32_t extern RyanJson_t RyanJsonDuplicate(RyanJson_t pJson); // 需用户释放内存 extern uint32_t RyanJsonMinify(char *text, int32_t textLen); extern RyanJsonBool_e RyanJsonCompare(RyanJson_t leftJson, RyanJson_t rightJson); +extern RyanJsonBool_e RyanJsonCompareDouble(double a, double b); extern uint32_t RyanJsonGetSize(RyanJson_t pJson); #define RyanJsonGetArraySize(pJson) RyanJsonGetSize(pJson) @@ -247,11 +295,11 @@ extern RyanJsonBool_e RyanJsonIsDouble(RyanJson_t pJson); */ extern char *RyanJsonGetKey(RyanJson_t pJson); extern char *RyanJsonGetStringValue(RyanJson_t pJson); -#define RyanJsonGetBoolValue(pJson) RyanJsonGetPayloadBoolValueByFlag(pJson) -#define RyanJsonGetIntValue(pJson) (*(int32_t *)RyanJsonGetValue(pJson)) -#define RyanJsonGetDoubleValue(pJson) (*(double *)RyanJsonGetValue(pJson)) -#define RyanJsonGetArrayValue(pJson) (*(RyanJson_t *)RyanJsonGetValue(pJson)) -#define RyanJsonGetObjectValue(pJson) (*(RyanJson_t *)RyanJsonGetValue(pJson)) +extern int32_t RyanJsonGetIntValue(RyanJson_t pJson); +extern double RyanJsonGetDoubleValue(RyanJson_t pJson); +extern RyanJson_t RyanJsonGetObjectValue(RyanJson_t pJson); +extern RyanJson_t RyanJsonGetArrayValue(RyanJson_t pJson); +#define RyanJsonGetBoolValue(pJson) RyanJsonGetPayloadBoolValueByFlag(pJson) /** * @brief 添加相关函数 @@ -284,9 +332,9 @@ extern RyanJsonBool_e RyanJsonAddItemToObject(RyanJson_t pJson, const char *key, */ extern RyanJsonBool_e RyanJsonChangeKey(RyanJson_t pJson, const char *key); extern RyanJsonBool_e RyanJsonChangeStringValue(RyanJson_t pJson, const char *strValue); -#define RyanJsonChangeBoolValue(pJson, boolean) RyanJsonSetPayloadBoolValueByFlag(pJson, boolean) -#define RyanJsonChangeIntValue(pJson, number) (RyanJsonGetIntValue(pJson) = (number)) -#define RyanJsonChangeDoubleValue(pJson, number) (RyanJsonGetDoubleValue(pJson) = (number)) +extern RyanJsonBool_e RyanJsonChangeIntValue(RyanJson_t pJson, int32_t number); +extern RyanJsonBool_e RyanJsonChangeDoubleValue(RyanJson_t pJson, double number); +#define RyanJsonChangeBoolValue(pJson, boolean) RyanJsonSetPayloadBoolValueByFlag(pJson, boolean) // 这是change方法的补充,当需要修改value类型时,使用此函数 // 请参考 changeJsonTest 示例,严格按照规则来使用 diff --git a/RyanJson/RyanJsonConfig.h b/RyanJson/RyanJsonConfig.h index 4d53a39..ad73095 100644 --- a/RyanJson/RyanJsonConfig.h +++ b/RyanJson/RyanJsonConfig.h @@ -1,5 +1,5 @@ -#ifndef __RyanJsonConfig__ -#define __RyanJsonConfig__ +#ifndef RyanJsonConfig +#define RyanJsonConfig #ifdef __cplusplus extern "C" { @@ -23,6 +23,8 @@ extern "C" { #define RyanJsonStrcmp rt_strcmp #define RyanJsonSnprintf rt_snprintf #define RyanJsonPlatformAssert(EX) RT_ASSERT(EX) +#define RyanJsonMallocHeaderSize 12U +#define RyanJsonMallocAlign RT_ALIGN_SIZE #else #include #define RyanJsonMemset memset @@ -31,25 +33,27 @@ extern "C" { #define RyanJsonStrcmp strcmp #define RyanJsonSnprintf snprintf #define RyanJsonPlatformAssert(EX) assert(EX) +#define RyanJsonMallocHeaderSize 8U +#define RyanJsonMallocAlign 4U #endif // 是否启用assert // #define RyanJsonEnableAssert -// 是否支持未对齐访问,未定义时会根据平台选择, -// 一般不用管,如果你明白你的需求就自己定义 -// true 表示支持未对齐访问 -// false 表示不支持未对齐访问 -// UINT8_MAX 标识让RyanJson自动判断,但可能会漏掉支持对齐访问的平台 -#ifndef RyanJsonUnalignedAccessSupported -#define RyanJsonUnalignedAccessSupported UINT8_MAX +#ifndef RyanJsonMallocAlign +#define RyanJsonMallocAlign 8U +#endif + +// +#ifndef RyanJsonMallocHeaderSize +#define RyanJsonMallocHeaderSize 8U #endif // 限制解析数组/对象中嵌套的深度 // RyanJson使用递归 序列化/反序列化 json // 请根据单片机资源合理设置以防止堆栈溢出。 #ifndef RyanJsonNestingLimit -#define RyanJsonNestingLimit 3000 +#define RyanJsonNestingLimit 300U #endif // 当 RyanJsonPrint 剩余缓冲空间不足时申请的空间大小 @@ -57,6 +61,31 @@ extern "C" { #define RyanJsonPrintfPreAlloSize (64U) #endif +// 浮点数比较可调的绝对容差,一般场景 1e-8 足够了. +// 可以根据自己需求进行调整 +// 容差必须大于 0.0,同时小于 1.0 +#ifndef RyanJsonAbsTolerance +#define RyanJsonAbsTolerance 1e-8 +#endif + +// 用户需声明目标平台的 snprintf 是否支持科学计数法输出, (库内部是否使用 %g/%e) +#ifndef RyanJsonSnprintfSupportScientific +#define RyanJsonSnprintfSupportScientific false +#endif + +// 用户需声明 double 序列化时的缓冲区大小 +// 如果 snprintf 支持科学计数法,建议值 ≥ 32 +// 如果不支持科学计数法,建议值 ≥ 330 +#ifndef RyanJsonDoubleBufferSize +#if true == RyanJsonSnprintfSupportScientific +#define RyanJsonDoubleBufferSize 32 +#else +// 不支持科学计数法的平台使用 ".17lf" 最大将会输出 330+ 字节的数据,对于此库来说占用太高了. +// 如果用户可以接受,就修改 RyanJsonDoubleBufferSize 宏,由你来决定RyanJson给 ".17lf" 提供多大缓冲区 +#define RyanJsonDoubleBufferSize 64 +#endif +#endif + /** * @brief 调试相关配置 * @@ -71,79 +100,19 @@ extern "C" { #endif /** - * @brief 判断是否支持未对齐访问 + * @brief 检查宏是否合法 * */ -#if UINT8_MAX == RyanJsonUnalignedAccessSupported -#undef RyanJsonUnalignedAccessSupported - -// Cortex-M0/M0+/M1 属于 ARMv6-M -#if defined(__ARM_ARCH_6M__) -#define RyanJsonUnalignedAccessSupported false - -// Cortex-M3/M4/M7 属于 ARMv7-M/EM -#elif defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) -#define RyanJsonUnalignedAccessSupported true - -// Cortex-M23/M33 属于 ARMv8-M -#elif defined(__ARM_ARCH_8M_BASE__) || defined(__ARM_ARCH_8M_MAIN__) -#define RyanJsonUnalignedAccessSupported true - -// Cortex-A/R 属于 ARMv7-A/R -#elif defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) -#define RyanJsonUnalignedAccessSupported true - -// ARM9/ARM11 等老核 -#elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5E__) -#define RyanJsonUnalignedAccessSupported false - -// ARMv8-A / ARM64 -#elif defined(__aarch64__) || defined(__ARM_ARCH_8A__) || defined(__ARM_ARCH_9__) -#define RyanJsonUnalignedAccessSupported true - -// RISC-V MCU 默认不支持未对齐访问 -#elif defined(__riscv) -#define RyanJsonUnalignedAccessSupported false - -// x86 / x86-64 -#elif defined(__i386__) || defined(__x86_64__) -#define RyanJsonUnalignedAccessSupported true - -// MIPS -#elif defined(__mips__) -#define RyanJsonUnalignedAccessSupported false - -// PowerPC -#elif defined(__powerpc__) || defined(__ppc__) -#define RyanJsonUnalignedAccessSupported false - -// SPARC -#elif defined(__sparc__) -#define RyanJsonUnalignedAccessSupported false - -// SuperH -#elif defined(__sh__) -#define RyanJsonUnalignedAccessSupported false - -// Alpha -#elif defined(__alpha__) -#define RyanJsonUnalignedAccessSupported true - -// Itanium -#elif defined(__ia64__) -#define RyanJsonUnalignedAccessSupported false - -#else -// 默认认为不支持未对齐访问 -#define RyanJsonUnalignedAccessSupported false +#if 0 != RyanJsonMallocHeaderSize && RyanJsonMallocHeaderSize % 4 != 0 +#error "RyanJsonMallocHeaderSize 必须是4的倍数" #endif -#endif // UINT8_MAX == RyanJsonUnalignedAccessSupported +#if 0 != RyanJsonMallocAlign && RyanJsonMallocAlign % 4 != 0 +#error "RyanJsonMallocAlign 必须是4的倍数" +#endif -#if true != RyanJsonUnalignedAccessSupported -#define RyanJsonAlign sizeof(void *) -#else -#define RyanJsonAlign sizeof(uint8_t) +#if RyanJsonDoubleBufferSize < 8 +#error "RyanJsonDoubleBufferSize 必须大于8" #endif #ifdef __cplusplus diff --git a/RyanJson/RyanJsonUtils.c b/RyanJson/RyanJsonUtils.c index ca6de9f..7f316ef 100644 --- a/RyanJson/RyanJsonUtils.c +++ b/RyanJson/RyanJsonUtils.c @@ -14,7 +14,8 @@ RyanJson_t RyanJsonGetObjectByKeys(RyanJson_t pJson, const char *key, ...) const char *nextKey = key; RyanJson_t nextItem = RyanJsonGetObjectByKey(pJson, nextKey); - RyanJsonCheckReturnNull(NULL != nextItem && RyanJsonIsKey(nextItem)); + RyanJsonCheckReturnNull(NULL != nextItem); + RyanJsonCheckNeverNoAssert(RyanJsonIsKey(nextItem)); va_list args; va_start(args, key); @@ -115,12 +116,12 @@ RyanJson_t RyanJsonCreateStringArray(const char **strings, uint32_t count) */ RyanJsonBool_e RyanJsonCompareOnlyKey(RyanJson_t leftJson, RyanJson_t rightJson) { - if (NULL == leftJson || NULL == rightJson) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(NULL != leftJson && NULL != rightJson); // 相同的对象相等 if (leftJson == rightJson) { return RyanJsonTrue; } - if (RyanJsonGetType(leftJson) != RyanJsonGetType(rightJson)) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(RyanJsonGetType(leftJson) == RyanJsonGetType(rightJson)); switch (RyanJsonGetType(leftJson)) { @@ -130,31 +131,27 @@ RyanJsonBool_e RyanJsonCompareOnlyKey(RyanJson_t leftJson, RyanJson_t rightJson) case RyanJsonTypeString: return RyanJsonTrue; case RyanJsonTypeArray: { - if (RyanJsonGetSize(leftJson) != RyanJsonGetSize(rightJson)) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(RyanJsonGetSize(leftJson) == RyanJsonGetSize(rightJson)); RyanJson_t item; uint32_t itemIndex = 0; RyanJsonArrayForEach(leftJson, item) { - if (RyanJsonTrue != RyanJsonCompareOnlyKey(item, RyanJsonGetObjectByIndex(rightJson, itemIndex))) - { - return RyanJsonFalse; - } + RyanJsonCheckReturnFalse(RyanJsonTrue == + RyanJsonCompareOnlyKey(item, RyanJsonGetObjectByIndex(rightJson, itemIndex))); itemIndex++; } return RyanJsonTrue; } case RyanJsonTypeObject: { - if (RyanJsonGetSize(leftJson) != RyanJsonGetSize(rightJson)) { return RyanJsonFalse; } + RyanJsonCheckReturnFalse(RyanJsonGetSize(leftJson) == RyanJsonGetSize(rightJson)); RyanJson_t item; RyanJsonObjectForEach(leftJson, item) { - if (RyanJsonTrue != RyanJsonCompareOnlyKey(item, RyanJsonGetObjectByKey(rightJson, RyanJsonGetKey(item)))) - { - return RyanJsonFalse; - } + RyanJsonCheckReturnFalse(RyanJsonTrue == + RyanJsonCompareOnlyKey(item, RyanJsonGetObjectByKey(rightJson, RyanJsonGetKey(item)))); } return RyanJsonTrue; } diff --git a/RyanJson/RyanJsonUtils.h b/RyanJson/RyanJsonUtils.h index 1891f10..e697f4d 100644 --- a/RyanJson/RyanJsonUtils.h +++ b/RyanJson/RyanJsonUtils.h @@ -1,5 +1,5 @@ -#ifndef __RyanJsonUtils__ -#define __RyanJsonUtils__ +#ifndef RyanJsonUtils +#define RyanJsonUtils #ifdef __cplusplus extern "C" { @@ -24,15 +24,12 @@ extern RyanJson_t RyanJsonGetObjectByIndexs(RyanJson_t pJson, uint32_t index, .. extern RyanJson_t RyanJsonGetObjectByKeys(RyanJson_t pJson, const char *key, ...); /** - * @brief 可使用此宏进行嵌套式查找,例如 RyanJsonGetObjectToKey(json, "test", "inter") - * + * @brief 可使用此宏进行嵌套式查找, + * 例如 RyanJsonGetObjectToKey(json, "test", "inter") + * 例如 RyanJsonGetObjectToIndex(json, 0, 2) + * */ #define RyanJsonGetObjectToKey(pJson, key, ...) RyanJsonGetObjectByKeys(pJson, (key), ##__VA_ARGS__, NULL) - -/** - * @brief 可使用此宏进行嵌套式查找,例如 RyanJsonGetObjectToIndex(json, 0, 2) - * - */ #define RyanJsonGetObjectToIndex(pJson, index, ...) RyanJsonGetObjectByIndexs(pJson, (index), ##__VA_ARGS__, UINT32_MAX) #define RyanJsonHasObjectToKey(pJson, key, ...) RyanJsonMakeBool(RyanJsonGetObjectByKeys(pJson, key, ##__VA_ARGS__, NULL)) diff --git a/example/RyanJsonExample.c b/example/RyanJsonExample.c index cfc7667..2d3e575 100644 --- a/example/RyanJsonExample.c +++ b/example/RyanJsonExample.c @@ -1,20 +1,12 @@ - -#include -#include -#include -#include -#include - #include "RyanJson.h" #include "RyanJsonUtils.h" -#include "valloc.h" /** * @brief 生成json示例 * - * @return int + * @return RyanJsonBool_e */ -static int createJsonExample(void) +static RyanJsonBool_e createJsonExample(void) { char *str = NULL; RyanJson_t jsonRoot, item; @@ -90,44 +82,87 @@ static int createJsonExample(void) RyanJsonFree(str); RyanJsonDelete(jsonRoot); - - return 0; + return RyanJsonTrue; } /** * @brief 序列化json文本示例 * - * @return int + * @return RyanJsonBool_e */ -static int loadJsonExample(void) +static RyanJsonBool_e loadJsonExample(void) { char *str = NULL; - RyanJson_t jsonRoot; - const char jsonstr[] = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null," - "\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\"," - "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89," - "16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\"," - "\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{" - "\"inter\":16,\"double\":16.89,\"string\":\"hello\"," - "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\"," - "\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]}"; + RyanJson_t jsonRoot = NULL; + const char *jsonstr = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null," + "\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\"," + "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89," + "16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\"," + "\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{" + "\"inter\":16,\"double\":16.89,\"string\":\"hello\"," + "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\"," + "\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]}"; // 解析json数据 jsonRoot = RyanJsonParse(jsonstr); - if (jsonRoot == NULL) + if (NULL == jsonRoot) { printf("%s:%d 序列化失败\r\n", __FILE__, __LINE__); - return -1; + return RyanJsonFalse; + } + + // 读取 int 数据 + int inter = RyanJsonGetIntValue(RyanJsonGetObjectByKey(jsonRoot, "inter")); + if (inter != 16) + { + printf("%s:%d 读取int失败\r\n", __FILE__, __LINE__); + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; + } + + // 读取 double 数据 + double doubleValue = RyanJsonGetDoubleValue(RyanJsonGetObjectByKey(jsonRoot, "double")); + if (RyanJsonFalse == RyanJsonCompareDouble(doubleValue, 16.89)) + { + printf("%s:%d 读取double失败\r\n", __FILE__, __LINE__); + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; + } + + // 读取 string 数据 + const char *strValue = RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot, "string")); + if (0 != strcmp(strValue, "hello")) + { + printf("%s:%d 读取string失败\r\n", __FILE__, __LINE__); + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; + } + + // 读取 bool 数据 + RyanJsonBool_e boolValue = RyanJsonGetBoolValue(RyanJsonGetObjectByKey(jsonRoot, "boolTrue")); + if (RyanJsonTrue != boolValue) + { + printf("%s:%d 读取bool失败\r\n", __FILE__, __LINE__); + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; + } + + // 读取 null 数据 + if (RyanJsonTrue != RyanJsonIsNull(RyanJsonGetObjectByKey(jsonRoot, "null"))) + { + printf("%s:%d 读取null失败\r\n", __FILE__, __LINE__); + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; } // 将序列化的数据以无格式样式打印出来,并和原始数据进行对比 str = RyanJsonPrint(jsonRoot, 250, RyanJsonFalse, NULL); if (strcmp(str, jsonstr) != 0) { - printf("%s:%d 序列化与反序列化后的数据不对应\r\n", __FILE__, __LINE__); + printf("%s:%d 序列化与反序列化后的数据不对应 %s\r\n", __FILE__, __LINE__, str); RyanJsonFree(str); RyanJsonDelete(jsonRoot); - return -1; + return RyanJsonFalse; } RyanJsonFree(str); @@ -140,36 +175,74 @@ static int loadJsonExample(void) // 删除json对象 RyanJsonDelete(jsonRoot); - return 0; + return RyanJsonTrue; } /** * @brief 修改json示例 * - * @return int + * @return RyanJsonBool_e */ -static int changeJsonExample(void) +static RyanJsonBool_e changeJsonExample(void) { char *str = NULL; RyanJson_t jsonRoot; - const char *jsonstr = "{\"name\":\"Mash\",\"star\":4,\"hits\":[2,2,1,3]}"; + const char *jsonstr = "{\"name\":\"Mash\",\"star\":4,\"doubleKey\":4.4,\"boolKey\":true,\"hits\":[2,2,1,3]}"; // 解析json数据 jsonRoot = RyanJsonParse(jsonstr); if (jsonRoot == NULL) { printf("%s:%d 序列化失败\r\n", __FILE__, __LINE__); - return -1; + return RyanJsonFalse; + } + + // 修改key + RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "name"), "name2"); + if (0 != strcmp("name2", RyanJsonGetKey(RyanJsonGetObjectByKey(jsonRoot, "name2")))) + { + printf("%s:%d 修改失败\r\n", __FILE__, __LINE__); + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; + } + + // 修改strValue + RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "name2"), "Ryan"); + if (0 != strcmp("Ryan", RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot, "name2")))) + { + printf("%s:%d 修改失败\r\n", __FILE__, __LINE__); + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; } - RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "name"), "Ryan"); - if (0 != strcmp("Ryan", RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot, "name")))) + // 修改intValue + RyanJsonChangeIntValue(RyanJsonGetObjectByKey(jsonRoot, "star"), 5); + if (5 != RyanJsonGetIntValue(RyanJsonGetObjectByKey(jsonRoot, "star"))) { printf("%s:%d 修改失败\r\n", __FILE__, __LINE__); RyanJsonDelete(jsonRoot); - return -1; + return RyanJsonFalse; } + // 修改doubleValue + RyanJsonChangeDoubleValue(RyanJsonGetObjectByKey(jsonRoot, "doubleKey"), 5.5); + if (RyanJsonFalse == RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectByKey(jsonRoot, "doubleKey")), 5.5)) + { + printf("%s:%d 修改失败\r\n", __FILE__, __LINE__); + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; + } + + // 修改boolValue + RyanJsonChangeBoolValue(RyanJsonGetObjectByKey(jsonRoot, "boolKey"), RyanJsonFalse); + if (RyanJsonFalse != RyanJsonGetBoolValue(RyanJsonGetObjectByKey(jsonRoot, "boolKey"))) + { + printf("%s:%d 修改失败\r\n", __FILE__, __LINE__); + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; + } + + // 替换节点(修改节点类型) RyanJsonReplaceByKey(jsonRoot, "star", RyanJsonCreateString("", "123456")); // 将序列化的数据以有格式样式打印出来 @@ -181,21 +254,23 @@ static int changeJsonExample(void) // 删除json对象 RyanJsonDelete(jsonRoot); - return 0; + return RyanJsonTrue; } RyanJsonBool_e RyanJsonExample(void) { - RyanJsonInitHooks(v_malloc, v_free, v_realloc); + RyanJsonInitHooks(malloc, free, NULL); printf("\r\n--------------------------- RyanJson 生成示例 --------------------------\r\n"); - createJsonExample(); + RyanJsonCheckReturnFalse(RyanJsonTrue == createJsonExample()); printf("\r\n--------------------------- RyanJson 序列化json文本示例 --------------------------\r\n"); - loadJsonExample(); + RyanJsonCheckReturnFalse(RyanJsonTrue == loadJsonExample()); printf("\r\n--------------------------- RyanJson 修改json示例 --------------------------\r\n"); - changeJsonExample(); + RyanJsonCheckReturnFalse(RyanJsonTrue == changeJsonExample()); + + // 更多功能请查看 RyanJson.h 文件,不了解的可以查看 test/baseTest 下的文件 return RyanJsonTrue; } diff --git a/run_base_coverage.sh b/run_base_coverage.sh new file mode 100755 index 0000000..c1854f7 --- /dev/null +++ b/run_base_coverage.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -e # 遇到错误立即退出 + +xmake +echo "xmake build success" + +# ================================ +# 1. 运行 +# ================================ +./build/linux/x86/release/RyanJson + + +# ================================ +# 2. 合并 profile 数据 +# ================================ +llvm-profdata merge -sparse default.profraw -o default.profdata + +# ================================ +# 3. 生成覆盖率报告(文本汇总) +# ================================ +# 注意:llvm-cov report 只支持汇总统计,不支持行级参数 +llvm-cov report ./build/linux/x86/release/RyanJson \ + -instr-profile=default.profdata \ + -show-mcdc-summary \ + -sources ./RyanJson + +# ================================ +# 4. 生成覆盖率报告(HTML详细) +# ================================ +llvm-cov show ./build/linux/x86/release/RyanJson \ + -instr-profile=default.profdata \ + -format=html \ + -output-dir=coverage/docs \ + -sources ./RyanJson +# -sources ./test/fuzzer ./RyanJson diff --git a/run_coverage.sh b/run_coverage.sh index b003e29..21cc53b 100755 --- a/run_coverage.sh +++ b/run_coverage.sh @@ -1,6 +1,10 @@ #!/bin/bash set -e # 遇到错误立即退出 +xmake +echo "xmake build success" + +# git clone -b coverage git@github.com:Ryan-CW-Code/RyanJson.git coverage # ================================ # 1. 运行 fuzzer # ================================ @@ -11,7 +15,7 @@ set -e # 遇到错误立即退出 -runs=99999999 \ -verbosity=0 \ -max_len=8192 \ - -workers=5 \ + -workers=10 \ -jobs=10 @@ -41,6 +45,7 @@ llvm-cov show ./build/linux/x86/release/RyanJson \ -format=html \ -output-dir=coverage/docs \ -show-mcdc-summary \ + -show-branches=count \ -show-expansions \ -show-regions \ -show-line-counts-or-regions \ diff --git a/test/RFC8259Test/RyanJsonRFC8259JsonTest.c b/test/RFC8259Test/RyanJsonRFC8259JsonTest.c new file mode 100644 index 0000000..fb5e35e --- /dev/null +++ b/test/RFC8259Test/RyanJsonRFC8259JsonTest.c @@ -0,0 +1,287 @@ +#include "RyanJsonTest.h" +#include "valloc.h" + +#define PrintfStrCmpEnable +#define TEST_FILE_PATH "./test/RFC8259JsonData" + +typedef RyanJsonBool_e (*jsonParseData)(char *fileName, char *data, uint32_t len); + +/* Read a file, parse, render back, etc. */ +static RyanJsonBool_e testFile(const char *path, jsonParseData jsonParseDataHandle) +{ + DIR *dir = opendir(path); + RyanJsonCheckReturnFalse(NULL != dir); + + struct dirent *entry = NULL; + uint32_t count = 0; + uint32_t used_count = 0; + + // 初始缓冲区 + uint32_t bufferCap = 4096; + char *data = (char *)malloc(bufferCap); + if (NULL == data) + { + (void)closedir(dir); + return RyanJsonFalse; + } + + while (NULL != (entry = readdir(dir))) // NOLINT(concurrency-mt-unsafe) + { + char *name = (char *)entry->d_name; + if (NULL == name || 0 == strlen(name)) { continue; } + if (0 == strcmp(name, ".") || 0 == strcmp(name, "..")) { continue; } + + char fullPath[512] = {0}; + int ret = snprintf(fullPath, sizeof(fullPath), "%s/%s", path, name); + if (ret < 0 || ret >= (int)sizeof(fullPath)) { continue; } + + FILE *f = fopen(fullPath, "rb"); + if (NULL == f) + { + (void)printf("打开文件失败: %s\n", fullPath); + continue; + } + + if (0 != fseek(f, 0, SEEK_END)) + { + (void)fclose(f); + continue; + } + + long fileSize = ftell(f); + if (fileSize < 0) + { + (void)fclose(f); + continue; + } + uint32_t len = (uint32_t)fileSize; + + if (0 != fseek(f, 0, SEEK_SET)) + { + (void)fclose(f); + continue; + } + + // 必要时自动扩容 + if (len + 1 > bufferCap) + { + bufferCap = len + 128; // 预留一点空间 + char *newData = (char *)realloc(data, bufferCap); + if (NULL == newData) + { + (void)fclose(f); + break; + } + data = newData; + } + + if (len != fread(data, 1, len, f)) + { + (void)fclose(f); + continue; + } + data[len] = '\0'; + (void)fclose(f); + + int32_t startUse = vallocGetUseByTlsf(); + RyanJsonBool_e status = jsonParseDataHandle(name, data, len); + used_count++; + + // 判定逻辑优化 + if (0 == strncmp("y_", name, 2)) + { + if (RyanJsonTrue == status) { count++; } + else + { + (void)printf("应该成功,但是失败: %s, len: %u\n", data, len); + } + } + else if (0 == strncmp("n_", name, 2)) + { + if (RyanJsonFalse == status) { count++; } + else + { + (void)printf("应该失败,但是成功: %s, len: %u\n", data, len); + } + } + else if (0 == strncmp("i_", name, 2)) { count++; } + + if (startUse != vallocGetUseByTlsf()) + { + int area = 0, use = 0; + v_mcheck(&area, &use); + (void)printf("内存泄漏 %s len: %u\r\n", data, len); + (void)printf("|||----------->>> area = %d, size = %d\r\n", area, use); + displayMem(); + break; + } + } + + free(data); + (void)closedir(dir); + + (void)printf("RFC 8259 JSON: (%u/%u)\r\n", count, used_count); + return RyanJsonTrue; +} + +#include "RyanJsonRFC8259TestUtil.h" + +static void checkJsonSemanticEquality(char *data, uint32_t len, char *str, uint32_t strLen, uint32_t *errorCount) +{ + if (0 != strcmp(data, str)) + { + if (!RyanJsonValueSemanticEqual(data, len, str, strLen)) + { + (*errorCount)++; + (void)printf("%d 数据不完全一致 -- 原始: %s -- 序列化: %s\n", *errorCount, data, str); + } + } +} + +/** + * @brief RyanJson 测试程序 + * + * @param fileName + * @param data + * @param len + * @return int + */ +static RyanJsonBool_e RyanJsonParseData(char *fileName, char *data, uint32_t len) +{ + + if (0 == strcmp(fileName, "n_structure_100000_opening_arrays.json") || 0 == strcmp(fileName, "n_structure_open_array_object.json")) + { + return RyanJsonFalse; + } + // printf("开始解析: %s\r\n", fileName); + RyanJson_t json = RyanJsonParseOptions(data, len, RyanJsonTrue, NULL); + RyanJsonCheckReturnFalse(NULL != json); + +#ifdef PrintfStrCmpEnable + int32_t strLen = 0; + char *str = RyanJsonPrint(json, 60, RyanJsonFalse, &strLen); + if (NULL == str) + { + printf("反序列化失败: [%s]\n", data); + goto err; + } + + RyanJsonMinify(data, (int32_t)len); + static uint32_t alksdjfCOunt = 0; + checkJsonSemanticEquality(data, len, str, strLen, &alksdjfCOunt); + + RyanJsonFree(str); +#endif + + (void)RyanJsonDelete(json); + return RyanJsonTrue; + +err: + (void)RyanJsonDelete(json); + return RyanJsonFalse; +} + +/** + * @brief cJson测试程序 + * + * @param fileName + * @param data + * @param len + * @return int + */ +static RyanJsonBool_e cJSONParseData(char *fileName, char *data, uint32_t len) +{ + + if (0 == strcmp(fileName, "n_structure_100000_opening_arrays.json") || 0 == strcmp(fileName, "n_structure_open_array_object.json")) + { + return RyanJsonFalse; + } + + cJSON *json = cJSON_ParseWithLengthOpts(data, len + sizeof(""), NULL, RyanJsonTrue); + RyanJsonCheckReturnFalse(NULL != json); + +#ifdef PrintfStrCmpEnable + char *str = cJSON_PrintBuffered(json, 60, RyanJsonFalse); + if (NULL == str) + { + printf("反序列化失败: [%s]\n", data); + goto err; + } + + cJSON_Minify(data); + static uint32_t alksdjfCOunt = 0; + checkJsonSemanticEquality(data, len, str, strlen(str), &alksdjfCOunt); + + cJSON_free(str); +#endif + + (void)cJSON_Delete(json); + return RyanJsonTrue; +err: + (void)cJSON_Delete(json); + return RyanJsonFalse; +} + +/** + * @brief cJson测试程序 + * + * @param fileName + * @param data + * @param len + * @return int + */ +static RyanJsonBool_e yyjsonParseData(char *fileName, char *data, uint32_t len) +{ + if (0 == strcmp(fileName, "n_structure_100000_opening_arrays.json") || 0 == strcmp(fileName, "n_structure_open_array_object.json")) + { + return RyanJsonFalse; + } + + yyjson_doc *doc = yyjson_read(data, len, 0); + RyanJsonCheckReturnFalse(NULL != doc); + +#ifdef PrintfStrCmpEnable + char *str = yyjson_write(doc, 0, NULL); + if (NULL == str) + { + printf("反序列化失败: [%s]\n", data); + goto err; + } + + cJSON_Minify(data); + static uint32_t alksdjfCOunt = 0; + checkJsonSemanticEquality(data, len, str, strlen(str), &alksdjfCOunt); + + free(str); +#endif + + (void)yyjson_doc_free(doc); + return RyanJsonTrue; +err: + (void)yyjson_doc_free(doc); + return RyanJsonFalse; +} + +// RFC 8259 JSON Test Suite +// https://github.com/nst/JSONTestSuite +static RyanJsonBool_e testRFC8259RyanJson(void) { return testFile(TEST_FILE_PATH, RyanJsonParseData); } + +static RyanJsonBool_e testRFC8259cJSON(void) { return testFile(TEST_FILE_PATH, cJSONParseData); } + +static RyanJsonBool_e testRFC8259yyjson(void) { return testFile(TEST_FILE_PATH, yyjsonParseData); } + +RyanJsonBool_e RFC8259JsonTest(void) +{ + int32_t result = 0; + uint32_t testRunCount = 0; + uint64_t funcStartMs; + + cJSON_Hooks hooks = {.malloc_fn = v_malloc_tlsf, .free_fn = v_free_tlsf}; + (void)cJSON_InitHooks(&hooks); + + runTestWithLogAndTimer(testRFC8259RyanJson); + runTestWithLogAndTimer(testRFC8259yyjson); + runTestWithLogAndTimer(testRFC8259cJSON); + + return RyanJsonTrue; +} diff --git a/test/RFC8259Test/RyanJsonRFC8259TestUtil.c b/test/RFC8259Test/RyanJsonRFC8259TestUtil.c new file mode 100644 index 0000000..0e3c678 --- /dev/null +++ b/test/RFC8259Test/RyanJsonRFC8259TestUtil.c @@ -0,0 +1,272 @@ +#include "RyanJson.h" +#include "RyanJsonRFC8259TestUtil.h" +#include +#include +#include +#include +#include + +static int hexval(int c) +{ + if (c >= '0' && c <= '9') { return c - '0'; } + c = (c >= 'a' && c <= 'f') ? (c - 'a' + 'A') : c; + if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } + return -1; +} + +static int decode_u4(const char *s, uint16_t *out) +{ + int h0 = hexval(s[0]), h1 = hexval(s[1]), h2 = hexval(s[2]), h3 = hexval(s[3]); + if (h0 < 0 || h1 < 0 || h2 < 0 || h3 < 0) { return 0; } + *out = (uint16_t)((h0 << 12) | (h1 << 8) | (h2 << 4) | h3); + return 1; +} + +int RyanJsonNormalizeString(const char *in, uint32_t inLen, unsigned char **out, uint32_t *outLen) +{ + uint32_t cap = inLen * 4 + 8; + unsigned char *buf = (unsigned char *)malloc(cap); + if (NULL == buf) { return 0; } + uint32_t pos = 0; + + for (uint32_t i = 0; i < inLen; i++) + { + unsigned char ch = (unsigned char)in[i]; + if (ch == '\\') + { + if (i + 1 >= inLen) + { + free(buf); + return 0; + } + unsigned char esc = (unsigned char)in[++i]; + switch (esc) + { + case '\"': buf[pos++] = '\"'; break; + case '\\': buf[pos++] = '\\'; break; + case '/': buf[pos++] = '/'; break; + case 'b': buf[pos++] = '\b'; break; + case 'f': buf[pos++] = '\f'; break; + case 'n': buf[pos++] = '\n'; break; + case 'r': buf[pos++] = '\r'; break; + case 't': buf[pos++] = '\t'; break; + case 'u': { + if (i + 4 >= inLen) + { + free(buf); + return 0; + } + uint16_t u1; + if (!decode_u4(&in[i + 1], &u1)) + { + free(buf); + return 0; + } + i += 4; + if (u1 >= 0xD800 && u1 <= 0xDBFF) + { + if (i + 2 >= inLen || in[i + 1] != '\\' || in[i + 2] != 'u' || i + 6 >= inLen) + { + free(buf); + return 0; + } + i += 2; + uint16_t u2; + if (!decode_u4(&in[i + 1], &u2)) + { + free(buf); + return 0; + } + i += 4; + if (!(u2 >= 0xDC00 && u2 <= 0xDFFF)) + { + free(buf); + return 0; + } + uint32_t cp = 0x10000 + (((uint32_t)(u1 - 0xD800) << 10) | (uint32_t)(u2 - 0xDC00)); + buf[pos++] = (unsigned char)(0xF0 | ((cp >> 18) & 0x07)); + buf[pos++] = (unsigned char)(0x80 | ((cp >> 12) & 0x3F)); + buf[pos++] = (unsigned char)(0x80 | ((cp >> 6) & 0x3F)); + buf[pos++] = (unsigned char)(0x80 | (cp & 0x3F)); + } + else if (u1 >= 0xDC00 && u1 <= 0xDFFF) + { + free(buf); + return 0; + } + else + { + if (u1 <= 0x007F) { buf[pos++] = (unsigned char)u1; } + else if (u1 <= 0x07FF) + { + buf[pos++] = (unsigned char)(0xC0 | ((u1 >> 6) & 0x1F)); + buf[pos++] = (unsigned char)(0x80 | (u1 & 0x3F)); + } + else + { + buf[pos++] = (unsigned char)(0xE0 | ((u1 >> 12) & 0x0F)); + buf[pos++] = (unsigned char)(0x80 | ((u1 >> 6) & 0x3F)); + buf[pos++] = (unsigned char)(0x80 | (u1 & 0x3F)); + } + } + break; + } + default: free(buf); return 0; + } + } + else + { + buf[pos++] = ch; + } + + if (pos + 8 > cap) + { + cap *= 2; + unsigned char *nb = (unsigned char *)realloc(buf, cap); + if (NULL == nb) + { + free(buf); + return 0; + } + buf = nb; + } + } + *out = buf; + *outLen = pos; + return 1; +} + +static void trim(const char **s, uint32_t *len) +{ + const char *p = *s; + uint32_t n = *len; + while (n && isspace((unsigned char)*p)) + { + p++; + n--; + } + while (n && isspace((unsigned char)p[n - 1])) { n--; } + *s = p; + *len = n; +} + +static int is_quoted_string(const char *s, uint32_t len) { return len >= 2 && s[0] == '\"' && s[len - 1] == '\"'; } + +int RyanJsonScalarSemanticEqual(const char *a, uint32_t aLen, const char *b, uint32_t bLen) +{ + trim(&a, &aLen); + trim(&b, &bLen); + + if (is_quoted_string(a, aLen) && is_quoted_string(b, bLen)) + { + unsigned char *na = NULL, *nb = NULL; + uint32_t nla = 0, nlb = 0; + if (!RyanJsonNormalizeString(a + 1, aLen - 2, &na, &nla)) { return 0; } + if (!RyanJsonNormalizeString(b + 1, bLen - 2, &nb, &nlb)) + { + free(na); + return 0; + } + int eq = (nla == nlb) && (0 == memcmp(na, nb, nla)); + free(na); + free(nb); + return eq; + } + + if (aLen == 4 && 0 == strncmp(a, "true", 4) && bLen == 4 && 0 == strncmp(b, "true", 4)) { return 1; } + if (aLen == 5 && 0 == strncmp(a, "false", 5) && bLen == 5 && 0 == strncmp(b, "false", 5)) { return 1; } + if (aLen == 4 && 0 == strncmp(a, "null", 4) && bLen == 4 && 0 == strncmp(b, "null", 4)) { return 1; } + + char *endA = NULL, *endB = NULL; + char *bufA = (char *)malloc(aLen + 1); + char *bufB = (char *)malloc(bLen + 1); + if (NULL != bufA && NULL != bufB) + { + memcpy(bufA, a, aLen); + bufA[aLen] = '\0'; + memcpy(bufB, b, bLen); + bufB[bLen] = '\0'; + double va = strtod(bufA, &endA); + double vb = strtod(bufB, &endB); + int okA = (endA && *endA == '\0'); + int okB = (endB && *endB == '\0'); + if (okA && okB) + { + free(bufA); + free(bufB); + + // 使用相对误差比较,处理极大极小值 + if (RyanJsonCompareDouble(va, vb)) { return 1; } + return 0; + + // if (va == vb) { return 1; } + // double absA = fabs(va), absB = fabs(vb); + // double maxAbs = (absA > absB) ? absA : absB; + // if (maxAbs < 1e-300) { return 1; } // 两者都接近零 + // return (fabs(va - vb) / maxAbs) < 1e-14; + } + } + free(bufA); + free(bufB); + return (aLen == bLen) && (0 == memcmp(a, b, aLen)); +} + +int RyanJsonExtractSingleArrayElement(const char *s, uint32_t len, const char **elem, uint32_t *elemLen) +{ + trim(&s, &len); + if (len < 2 || s[0] != '[' || s[len - 1] != ']') { return 0; } + const char *p = s + 1; + uint32_t n = len - 2; + trim(&p, &n); + if (0 == n) { return 0; } + + int in_str = 0, escape = 0; + for (uint32_t i = 0; i < n; i++) + { + char c = p[i]; + if (in_str) + { + if (escape) { escape = 0; } + else if (c == '\\') { escape = 1; } + else if (c == '\"') { in_str = 0; } + } + else + { + if (c == '\"') { in_str = 1; } + else if (c == ',') { return 0; } + } + } + *elem = p; + *elemLen = n; + return 1; +} + +int RyanJsonValueSemanticEqual(const char *a, uint32_t aLen, const char *b, uint32_t bLen) +{ + // 尝试将两个字符串解析为 JSON 对象进行语义比较 + // 这可以处理对象和数组的递归比较 + RyanJson_t jsonA = RyanJsonParseOptions(a, aLen, RyanJsonFalse, NULL); + RyanJson_t jsonB = RyanJsonParseOptions(b, bLen, RyanJsonFalse, NULL); + + if (NULL != jsonA && NULL != jsonB) + { + // 使用 RyanJsonCompare 进行完整的语义比较 + int result = RyanJsonCompare(jsonA, jsonB); + (void)RyanJsonDelete(jsonA); + (void)RyanJsonDelete(jsonB); + return result; + } + + // 解析失败时清理并回退到原有逻辑 + if (NULL != jsonA) { (void)RyanJsonDelete(jsonA); } + if (NULL != jsonB) { (void)RyanJsonDelete(jsonB); } + + // 回退:单元素数组提取比较 + const char *ae = NULL, *be = NULL; + uint32_t ale = 0, ble = 0; + if (RyanJsonExtractSingleArrayElement(a, aLen, &ae, &ale) && RyanJsonExtractSingleArrayElement(b, bLen, &be, &ble)) + { + return RyanJsonScalarSemanticEqual(ae, ale, be, ble); + } + return RyanJsonScalarSemanticEqual(a, aLen, b, bLen); +} diff --git a/test/RFC8259Test/RyanJsonRFC8259TestUtil.h b/test/RFC8259Test/RyanJsonRFC8259TestUtil.h new file mode 100644 index 0000000..0d12f2f --- /dev/null +++ b/test/RFC8259Test/RyanJsonRFC8259TestUtil.h @@ -0,0 +1,26 @@ +#ifndef RYAN_JSON_RFC8259_TEST_UTIL_H +#define RYAN_JSON_RFC8259_TEST_UTIL_H + +#include + +/** + * @brief 提取一元素数组的唯一元素;若不是一元素数组返回 0 + */ +int RyanJsonExtractSingleArrayElement(const char *s, uint32_t len, const char **elem, uint32_t *elemLen); + +/** + * @brief 值级语义比较:字符串(去引号并 normalize)、数字(含科学计数法)、布尔、null + */ +int RyanJsonScalarSemanticEqual(const char *a, uint32_t aLen, const char *b, uint32_t bLen); + +/** + * @brief JSON 语义比较:支持单元素数组剥离后进行标量比较 + */ +int RyanJsonValueSemanticEqual(const char *a, uint32_t aLen, const char *b, uint32_t bLen); + +/** + * @brief 将 JSON 字符串规范化为 UTF-8 字节序列 + */ +int RyanJsonNormalizeString(const char *in, uint32_t inLen, unsigned char **out, uint32_t *outLen); + +#endif // RYAN_JSON_RFC8259_TEST_UTIL_H diff --git a/test/RyanJsonMemoryFootprintTest.c b/test/RyanJsonMemoryFootprintTest.c index 55b3982..9791dff 100644 --- a/test/RyanJsonMemoryFootprintTest.c +++ b/test/RyanJsonMemoryFootprintTest.c @@ -1,74 +1,97 @@ #include "RyanJsonTest.h" -static void *yy_malloc(void *ctx, size_t size) { return v_malloc(size); } -static void *yy_realloc(void *ctx, void *ptr, size_t old_size, size_t size) { return v_realloc(ptr, size); } -static void yy_free(void *ctx, void *ptr) { v_free(ptr); } +static void *yy_malloc(void *ctx, size_t size) +{ + (void)(ctx); + return v_malloc_tlsf(size); +} +static void *yy_realloc(void *ctx, void *ptr, size_t oldSize, size_t size) +{ + (void)(ctx); + (void)(oldSize); + return v_realloc_tlsf(ptr, size); +} +static void yy_free(void *ctx, void *ptr) +{ + (void)(ctx); + v_free_tlsf(ptr); +} -static int RyanJsonMemoryFootprint(char *jsonstr) +static RyanJsonBool_e RyanJsonMemoryFootprint(char *jsonstr, int32_t *footprint) { - int32_t use = vallocGetUse(); - RyanJsonInitHooks(v_malloc, v_free, v_realloc); + int32_t use = vallocGetUseByTlsf(); + RyanJsonInitHooks(v_malloc_tlsf, v_free_tlsf, v_realloc_tlsf); RyanJson_t json = RyanJsonParse(jsonstr); - if (json == NULL) - { + RyanJsonCheckCode(NULL != json, { printf("%s:%d 解析失败\r\n", __FILE__, __LINE__); - return -1; - } + return RyanJsonFalse; + }); - use = vallocGetUse() - use; + use = vallocGetUseByTlsf() - use; RyanJsonDelete(json); - return use; + *footprint = use; + return RyanJsonTrue; } -static int cJSONMemoryFootprint(char *jsonstr) +static RyanJsonBool_e cJSONMemoryFootprint(char *jsonstr, int32_t *footprint) { - int32_t use = vallocGetUse(); - cJSON_Hooks hooks = {.malloc_fn = v_malloc, .free_fn = v_free}; + int32_t use = vallocGetUseByTlsf(); + cJSON_Hooks hooks = {.malloc_fn = v_malloc_tlsf, .free_fn = v_free_tlsf}; cJSON_InitHooks(&hooks); cJSON *json = cJSON_Parse(jsonstr); - if (json == NULL) - { + RyanJsonCheckCode(NULL != json, { printf("%s:%d 解析失败\r\n", __FILE__, __LINE__); - return -1; - } + return RyanJsonFalse; + }); - use = vallocGetUse() - use; + use = vallocGetUseByTlsf() - use; cJSON_Delete(json); - return use; + *footprint = use; + return RyanJsonTrue; } -static int yyjsonMemoryFootprint(char *jsonstr) +static RyanJsonBool_e yyjsonMemoryFootprint(char *jsonstr, int32_t *footprint) { static yyjson_alc yyalc = {yy_malloc, yy_realloc, yy_free, NULL}; + int32_t use = vallocGetUseByTlsf(); + // 先解析成只读文档(可用自定义分配器 yyalc) yyjson_doc *doc = yyjson_read_opts(jsonstr, strlen(jsonstr), YYJSON_READ_NOFLAG, &yyalc, NULL); - if (doc == NULL) { return -1; } + RyanJsonCheckReturnFalse(NULL != doc); // 从只读文档拷贝为可变文档(用于后续读写修改) yyjson_mut_doc *mdoc = yyjson_doc_mut_copy(doc, &yyalc); yyjson_doc_free(doc); - if (mdoc == NULL) { return -1; } + RyanJsonCheckReturnFalse(NULL != mdoc); // 统计当前分配器的占用 - int area = 0, use = 0; - v_mcheck(&area, &use); + use = vallocGetUseByTlsf() - use; // 用完释放可变文档 yyjson_mut_doc_free(mdoc); - return use; + *footprint = use; + return RyanJsonTrue; } -static void printfJsonCompera(char *jsonstr) +static RyanJsonBool_e printfJsonCompare(char *jsonstr) { - int RyanJsonCount = 0; - int cJSONCount = 0; - int yyjsonCount = 0; - RyanJsonCount = RyanJsonMemoryFootprint(jsonstr); - cJSONCount = cJSONMemoryFootprint(jsonstr); - yyjsonCount = yyjsonMemoryFootprint(jsonstr); + int32_t RyanJsonCount = 0; + int32_t cJSONCount = 0; + int32_t yyjsonCount = 0; + RyanJsonBool_e status = RyanJsonFalse; + + status = RyanJsonMemoryFootprint(jsonstr, &RyanJsonCount); + RyanJsonCheckReturnFalse(RyanJsonTrue == status); + + status = cJSONMemoryFootprint(jsonstr, &cJSONCount); + RyanJsonCheckReturnFalse(RyanJsonTrue == status); + + status = yyjsonMemoryFootprint(jsonstr, &yyjsonCount); + RyanJsonCheckReturnFalse(RyanJsonTrue == status); + printf("json原始文本长度为 %ld, 序列化后RyanJson内存占用: %d, cJSON内存占用: %d, yyjson内存占用: %d\r\n", strlen(jsonstr), RyanJsonCount, cJSONCount, yyjsonCount); @@ -76,61 +99,62 @@ static void printfJsonCompera(char *jsonstr) double save_vs_yyjson = 100.0 - ((double)RyanJsonCount * 100.0) / (double)yyjsonCount; printf("比cJSON节省: %.2f%% 内存占用, 比yyjson节省: %.2f%% 内存占用\r\n", save_vs_cjson, save_vs_yyjson); + return RyanJsonTrue; } -RyanJsonBool_e RyanJsonMemoryFootprintTest(void) +static RyanJsonBool_e testMixedJsonMemory(void) +{ + char *jsonstr = + "{\"item1\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null," + "\"item\":{\"inter\":16," + "\"double\":16.89,\"string\":\"hello\"," + "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89," + "16.89,16.89]," + "\"arrayString\":[\"hello\",\"hello\",\"hello\"," + "\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,\"double\":16.89," + "\"string\":\"hello\"," + "\"boolTrue\":true,\"boolFalse\":false," + "\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]" + "},\"item2\":{" + "\"inter\":16,\"double\":16.89,\"string\":" + "\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":{\"inter\":16,\"double\":16.89,\"string\":" + "\"hello\",\"boolTrue\":" + "true,\"boolFalse\":false,\"null\":null}," + "\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\",\"hello\"," + "\"hello\",\"hello\"," + "\"hello\"],\"array\":[16,16.89,\"hello\"," + "true,false,null],\"arrayItem\":[{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":" + "false,\"null\":null},{" + "\"inter\":16,\"double\":16.89,\"string\":" + "\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]},\"item3\":{\"inter\":16,\"double\":16.89,\"string\":" + "\"hello\",\"boolTrue\":" + "true,\"boolFalse\":false,\"null\":null," + "\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}," + "\"arrayInt\":[16,16,16,16," + "16],\"arrayDouble\":[16.89,16.89,16.89," + "16.89,16.89],\"arrayString\":[\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true," + "false,null]," + "\"arrayItem\":[{\"inter\":16,\"double\":16.89," + "\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":" + "\"hello\",\"boolTrue\":" + "true,\"boolFalse\":false,\"null\":null}]}" + ",\"item4\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null," + "\"item\":{\"inter\":16," + "\"double\":16.89,\"string\":\"hello\"," + "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89," + "16.89,16.89]," + "\"arrayString\":[\"hello\",\"hello\",\"hello\"," + "\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,\"double\":16.89," + "\"string\":\"hello\"," + "\"boolTrue\":true,\"boolFalse\":false," + "\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]" + "}}"; + return printfJsonCompare(jsonstr); +} + +static RyanJsonBool_e testObjectJsonMemory(void) { - char *jsonstr; - - printf("\r\n--------------------------- 混合类型json数据测试 --------------------------\r\n"); - jsonstr = "{\"item1\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null," - "\"item\":{\"inter\":16," - "\"double\":16.89,\"string\":\"hello\"," - "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89," - "16.89,16.89]," - "\"arrayString\":[\"hello\",\"hello\",\"hello\"," - "\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,\"double\":16.89," - "\"string\":\"hello\"," - "\"boolTrue\":true,\"boolFalse\":false," - "\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]" - "},\"item2\":{" - "\"inter\":16,\"double\":16.89,\"string\":" - "\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":{\"inter\":16,\"double\":16.89,\"string\":" - "\"hello\",\"boolTrue\":" - "true,\"boolFalse\":false,\"null\":null}," - "\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\",\"hello\"," - "\"hello\",\"hello\"," - "\"hello\"],\"array\":[16,16.89,\"hello\"," - "true,false,null],\"arrayItem\":[{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":" - "false,\"null\":null},{" - "\"inter\":16,\"double\":16.89,\"string\":" - "\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]},\"item3\":{\"inter\":16,\"double\":16.89,\"string\":" - "\"hello\",\"boolTrue\":" - "true,\"boolFalse\":false,\"null\":null," - "\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}," - "\"arrayInt\":[16,16,16,16," - "16],\"arrayDouble\":[16.89,16.89,16.89," - "16.89,16.89],\"arrayString\":[\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true," - "false,null]," - "\"arrayItem\":[{\"inter\":16,\"double\":16.89," - "\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":" - "\"hello\",\"boolTrue\":" - "true,\"boolFalse\":false,\"null\":null}]}" - ",\"item4\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null," - "\"item\":{\"inter\":16," - "\"double\":16.89,\"string\":\"hello\"," - "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89," - "16.89,16.89]," - "\"arrayString\":[\"hello\",\"hello\",\"hello\"," - "\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,\"double\":16.89," - "\"string\":\"hello\"," - "\"boolTrue\":true,\"boolFalse\":false," - "\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]" - "}}"; - printfJsonCompera(jsonstr); - - printf("\r\n--------------------------- 对象占多json数据测试 --------------------------\r\n"); - jsonstr = + char *jsonstr = "{\"message\":\"success感谢又拍云(upyun.com)提供CDN赞助\",\"status\":200,\"date\":\"20230822\",\"time\":\"2023-08-22 " "09:44:54\",\"cityInfo\":{\"city\":\"郑州市\",\"citykey\":\"101180101\",\"parent\":\"河南\",\"updateTime\":\"07:46\"}," "\"data\":{\"shidu\":" @@ -202,46 +226,77 @@ RyanJsonBool_e RyanJsonMemoryFootprintTest(void) "星期一\",\"sunrise\":\"05:50\",\"sunset\":\"19:07\",\"aqi\":60,\"fx\":\"西风\",\"fl\":\"2级\",\"type\":\"小雨\"," "\"notice\":" "\"雨虽小,注意保暖别感冒\"}}}"; - printfJsonCompera(jsonstr); - - printf("\r\n--------------------------- 数组占多json数据测试 --------------------------\r\n"); - jsonstr = "{\"item1\":{\"arrayInt\":[16,16,16,16,16,16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89,16.89,16.89,16." - "89,16.89,16.89]," - "\"arrayString\":[\"hello\",\"hello\"," - "\"hello\",\"hello\",\"hello\",\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true," - "false,null,16,16.89," - "\"hello\",true,false,null]},\"item2\":{" - "\"arrayInt\":[16,16,16,16,16,16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16." - "89],\"arrayString\":[" - "\"hello\",\"hello\",\"hello\",\"hello\"," - "\"hello\",\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null,16,16.89," - "\"hello\",true,false," - "null]},\"item3\":{\"arrayInt\":[16,16,16," - "16,16,16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89],\"arrayString\":[" - "\"hello\",\"hello\"," - "\"hello\",\"hello\",\"hello\",\"hello\"," - "\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null,16,16.89,\"hello\",true,false," - "null]},\"item4\":{" - "\"arrayInt\":[16,16,16,16,16,16,16,16,16,16]," - "\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\",\"hello\"," - "\"hello\",\"hello\"," - "\"hello\",\"hello\",\"hello\",\"hello\"," - "\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null,16,16.89,\"hello\",true,false,null]}}"; - printfJsonCompera(jsonstr); - - printf("\r\n--------------------------- 小对象json 混合类型内存占用测试 --------------------------\r\n"); - jsonstr = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}"; - printfJsonCompera(jsonstr); - - printf("\r\n--------------------------- 小对象json 纯字符串内存占用测试 --------------------------\r\n"); - jsonstr = "{\"inter\":\"16\",\"double\":\"16.89\",\"string\":\"hello\",\"boolTrue\":\"true\",\"boolFalse\":\"false\",\"null\":" - "\"null\"}"; - printfJsonCompera(jsonstr); - - /** - * @brief 反序列化为文本,内存占用没什么特别的优化点,和cjson实现思路差不多,内存占用也就差不多,就不进行对比了 - * - */ + return printfJsonCompare(jsonstr); +} + +static RyanJsonBool_e testArrayJsonMemory(void) +{ + char *jsonstr = + "{\"item1\":{\"arrayInt\":[16,16,16,16,16,16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89,16.89,16.89,16." + "89,16.89,16.89]," + "\"arrayString\":[\"hello\",\"hello\"," + "\"hello\",\"hello\",\"hello\",\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true," + "false,null,16,16.89," + "\"hello\",true,false,null]},\"item2\":{" + "\"arrayInt\":[16,16,16,16,16,16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16." + "89],\"arrayString\":[" + "\"hello\",\"hello\",\"hello\",\"hello\"," + "\"hello\",\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null,16,16.89," + "\"hello\",true,false," + "null]},\"item3\":{\"arrayInt\":[16,16,16," + "16,16,16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89],\"arrayString\":[" + "\"hello\",\"hello\"," + "\"hello\",\"hello\",\"hello\",\"hello\"," + "\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null,16,16.89,\"hello\",true,false," + "null]},\"item4\":{" + "\"arrayInt\":[16,16,16,16,16,16,16,16,16,16]," + "\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\",\"hello\"," + "\"hello\",\"hello\"," + "\"hello\",\"hello\",\"hello\",\"hello\"," + "\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null,16,16.89,\"hello\",true,false,null]}}"; + return printfJsonCompare(jsonstr); +} + +static RyanJsonBool_e testSmallMixedJsonMemory(void) +{ + char *jsonstr = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}"; + return printfJsonCompare(jsonstr); +} + +static RyanJsonBool_e testSmallStringJsonMemory(void) +{ + char *jsonstr = + "{\"inter\":\"16\",\"double\":\"16.89\",\"string\":\"hello\",\"boolTrue\":\"true\",\"boolFalse\":\"false\",\"null\":" + "\"null\"}"; + return printfJsonCompare(jsonstr); +} + +static RyanJsonBool_e testCompressedBusinessJsonMemory(void) +{ + char *jsonstr = + "{\"0\":\"0\",\"1\":\"189774523\",\"2\":{\"7\":\"3\",\"8\":\"103\",\"9\":\"37\",\"20\":\"0\",\"26\":\"37\",\"27\":" + "\"367\",\"28\":\"367\",\"s\":\"0\",\"t\":\"0\",\"a\":\"24.98\",\"2a\":\"0\",\"1p\":\"23628\"},\"3\":\"0\",\"22\":" + "\"epmgrow1105\",\"23\":\"0\",\"29\":\"0\",\"i\":\"4\",\"b\":\"900\",\"c\":\"1\",\"rsrp\":\"-111\",\"rsrq\":\"-4\"," + "\"sinr\":\"0\",\"soc\":\"XXXXXXX\",\"j\":\"0\",\"g\":\"898604asdf0210\",\"h\":\"866968798839\",\"d\":\"1.3.5." + "00.20991231\",\"f\":\"0\",\"k\":\"1\",\"l\":\"20000\",\"m\":\"20000\",\"u\":\"0\",\"v\":\"0\",\"e\":\"1\",\"w\":\"0." + "00\",\"n\":\"0\",\"2h\":\"0\",\"o\":\"30\",\"1v\":\"12000\",\"2c\":\"0\",\"p\":\"1\",\"q\":\"1\",\"x\":\"0\",\"y\":" + "\"167\",\"r\":\"0\",\"1x\":\"0\",\"1w\":\"0\",\"1y\":\"100.00\",\"1u\":\"0\"}"; + printfJsonCompare(jsonstr); + return RyanJsonTrue; +} + +RyanJsonBool_e RyanJsonMemoryFootprintTest(void) +{ + int32_t result = 0; + uint32_t testRunCount = 0; + uint64_t funcStartMs; + + runTestWithLogAndTimer(testMixedJsonMemory); + runTestWithLogAndTimer(testObjectJsonMemory); + runTestWithLogAndTimer(testArrayJsonMemory); + runTestWithLogAndTimer(testSmallMixedJsonMemory); + runTestWithLogAndTimer(testSmallStringJsonMemory); + runTestWithLogAndTimer(testCompressedBusinessJsonMemory); return RyanJsonTrue; } diff --git a/test/RyanJsonRFC8259JsonTest.c b/test/RyanJsonRFC8259JsonTest.c deleted file mode 100644 index e2fa14d..0000000 --- a/test/RyanJsonRFC8259JsonTest.c +++ /dev/null @@ -1,606 +0,0 @@ -#include "RyanJsonTest.h" - -#define PrintfStrCmpEnable - -typedef int (*jsonParseData)(char *fileName, char *data, uint32_t len); - -/* Read a file, parse, render back, etc. */ -static int testFile(const char *path, jsonParseData jsonParseDataHandle) -{ - - DIR *dir = NULL; - struct dirent *entry; - - int path_len = strlen(path); - int count = 0; - int used_count = 0; - if (!path || !path_len || !(dir = opendir(path))) { goto fail; } - - while ((entry = readdir(dir))) - { - - char *name = (char *)entry->d_name; - - if (!name || !strlen(name)) { continue; } - - if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } - - char aaa[300] = {0}; - snprintf(aaa, sizeof(aaa), "%s/%s", path, name); - - FILE *f = fopen(aaa, "rb"); - if (f == NULL) { goto fail; } - - fseek(f, 0, SEEK_END); - long len = ftell(f); - fseek(f, 0, SEEK_SET); - char *data = (char *)malloc(len + 10); - fread(data, 1, len, f); - data[len] = '\0'; - fclose(f); - int status = 0; - - int startUse = vallocGetUse(); - status = jsonParseDataHandle(name, data, len); - - used_count++; - if (0 == strncmp("y_", name, 2)) - { - if (0 == status) { count++; } - else - { - printf("应该成功,但是失败: %s, len: %ld\n", data, len); - } - } - else if (0 == strncmp("n_", name, 2)) - { - if (0 != status) { count++; } - else - { - printf("应该失败,但是成功: %s, len: %ld\n", data, len); - } - } - else if (0 == strncmp("i_", name, 2)) { count++; } - - if (startUse != vallocGetUse()) - { - int area = 0, use = 0; - v_mcheck(&area, &use); - printf("内存泄漏 %s len: %ld\r\n", data, len); - free(data); - // printf("内存泄漏 %x len: %ld\r\n", (unsigned int)data, len); - // printf("内存泄漏 %c len: %ld\r\n", (int)data, len); - printf("|||----------->>> area = %d, size = %d\r\n", area, use); - break; - } - - // if (use != (len + 10)) - // { - // int area = 0, use = 0; - // v_mcheck(&area, &use); - // free(data); - // printf("内存泄漏 %s len: %ld\r\n", data, len); - // // printf("内存泄漏 %x len: %ld\r\n", (unsigned int)data, len); - // // printf("内存泄漏 %c len: %ld\r\n", (int)data, len); - // printf("|||----------->>> area = %d, size = %d\r\n", area, use); - // break; - // } - free(data); - } - - closedir(dir); - - printf("RFC 8259 JSON: (%d/%d)\r\n", count, used_count); - return 0; - -fail: - if (dir) { closedir(dir); } - - return -1; -} - -typedef struct -{ - const char *p; - int32_t len; -} Slice; - -static int hexval(int c) -{ - if (c >= '0' && c <= '9') { return c - '0'; } - c = (c >= 'a' && c <= 'f') ? (c - 'a' + 'A') : c; - if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } - return -1; -} - -static int decode_u4(const char *s, uint16_t *out) -{ - int h0 = hexval(s[0]), h1 = hexval(s[1]), h2 = hexval(s[2]), h3 = hexval(s[3]); - if (h0 < 0 || h1 < 0 || h2 < 0 || h3 < 0) { return 0; } - *out = (uint16_t)((h0 << 12) | (h1 << 8) | (h2 << 4) | h3); - return 1; -} - -// 将 JSON 字符串(不含两端引号)规范化为 UTF-8 字节序列 -static int normalize_json_string(const char *in, int32_t in_len, unsigned char **out, int32_t *out_len) -{ - // 预留足够缓冲 - int32_t cap = in_len * 4 + 8; - unsigned char *buf = (unsigned char *)malloc(cap); - if (!buf) { return 0; } - int32_t pos = 0; - - for (int32_t i = 0; i < in_len; i++) - { - unsigned char ch = (unsigned char)in[i]; - - if (ch == '\\') - { - if (i + 1 >= in_len) - { - free(buf); - return 0; - } - unsigned char esc = (unsigned char)in[++i]; - switch (esc) - { - case '\"': buf[pos++] = '\"'; break; - case '\\': buf[pos++] = '\\'; break; - case '/': buf[pos++] = '/'; break; - case 'b': buf[pos++] = '\b'; break; - case 'f': buf[pos++] = '\f'; break; - case 'n': buf[pos++] = '\n'; break; - case 'r': buf[pos++] = '\r'; break; - case 't': buf[pos++] = '\t'; break; - case 'u': { - if (i + 4 >= in_len) - { - free(buf); - return 0; - } - uint16_t u1; - if (!decode_u4(&in[i + 1], &u1)) - { - free(buf); - return 0; - } - i += 4; - - if (u1 >= 0xD800 && u1 <= 0xDBFF) - { - // 高代理,必须跟 \uXXXX 低代理 - if (i + 2 >= in_len || in[i + 1] != '\\' || in[i + 2] != 'u' || i + 6 >= in_len) - { - free(buf); - return 0; - } - i += 2; - uint16_t u2; - if (!decode_u4(&in[i + 1], &u2)) - { - free(buf); - return 0; - } - i += 4; - if (!(u2 >= 0xDC00 && u2 <= 0xDFFF)) - { - free(buf); - return 0; - } - // 组合码点 - uint32_t cp = 0x10000 + (((uint32_t)(u1 - 0xD800) << 10) | (uint32_t)(u2 - 0xDC00)); - // 编码为 UTF-8 - buf[pos++] = (unsigned char)(0xF0 | ((cp >> 18) & 0x07)); - buf[pos++] = (unsigned char)(0x80 | ((cp >> 12) & 0x3F)); - buf[pos++] = (unsigned char)(0x80 | ((cp >> 6) & 0x3F)); - buf[pos++] = (unsigned char)(0x80 | (cp & 0x3F)); - } - else if (u1 >= 0xDC00 && u1 <= 0xDFFF) - { - // 单独低代理非法 - free(buf); - return 0; - } - else - { - // BMP 码点 → UTF-8 - if (u1 <= 0x007F) { buf[pos++] = (unsigned char)u1; } - else if (u1 <= 0x07FF) - { - buf[pos++] = (unsigned char)(0xC0 | ((u1 >> 6) & 0x1F)); - buf[pos++] = (unsigned char)(0x80 | (u1 & 0x3F)); - } - else - { - buf[pos++] = (unsigned char)(0xE0 | ((u1 >> 12) & 0x0F)); - buf[pos++] = (unsigned char)(0x80 | ((u1 >> 6) & 0x3F)); - buf[pos++] = (unsigned char)(0x80 | (u1 & 0x3F)); - } - } - break; - } - default: - // 非法转义 - free(buf); - return 0; - } - } - else - { - // 原始字节:直接拷贝(假设输入整体是合法 UTF-8) - buf[pos++] = ch; - } - - if (pos + 8 > cap) - { - cap *= 2; - unsigned char *nb = (unsigned char *)realloc(buf, cap); - if (!nb) - { - free(buf); - return 0; - } - buf = nb; - } - } - - *out = buf; - *out_len = pos; - return 1; -} - -// 比较:规范化两侧为 UTF-8 字节序列,再 memcmp -static int json_string_equal_semantic(const char *a, int32_t a_len, const char *b, int32_t b_len) -{ - unsigned char *na = NULL, *nb = NULL; - int32_t nla = 0, nlb = 0; - - if (!normalize_json_string(a, a_len, &na, &nla)) { return 0; } - if (!normalize_json_string(b, b_len, &nb, &nlb)) - { - free(na); - return 0; - } - - int eq = (nla == nlb) && (memcmp(na, nb, nla) == 0); - free(na); - free(nb); - return eq; -} - -/* 去空白 */ -static void trim(const char **s, int32_t *len) -{ - const char *p = *s; - int32_t n = *len; - while (n && isspace((unsigned char)*p)) - { - p++; - n--; - } - while (n && isspace((unsigned char)p[n - 1])) { n--; } - *s = p; - *len = n; -} - -/* 是否是带双引号的字符串值 */ -static int is_quoted_string(const char *s, int32_t len) { return len >= 2 && s[0] == '\"' && s[len - 1] == '\"'; } - -/* 值级语义比较:字符串(去引号并 normalize)、数字(含科学计数法)、布尔、null */ -static int json_scalar_equal(const char *a, int32_t a_len, const char *b, int32_t b_len) -{ - trim(&a, &a_len); - trim(&b, &b_len); - - /* 字符串:去掉引号后做转义规范化再比较字节 */ - if (is_quoted_string(a, a_len) && is_quoted_string(b, b_len)) - { - const char *as = a + 1; - int32_t al = a_len - 2; - const char *bs = b + 1; - int32_t bl = b_len - 2; - - unsigned char *na = NULL, *nb = NULL; - int32_t nla = 0, nlb = 0; - if (!normalize_json_string(as, al, &na, &nla)) { return 0; } - if (!normalize_json_string(bs, bl, &nb, &nlb)) - { - free(na); - return 0; - } - - int eq = (nla == nlb) && (memcmp(na, nb, nla) == 0); - free(na); - free(nb); - return eq; - } - - /* 布尔 / null */ - if (a_len == 4 && strncmp(a, "true", 4) == 0 && b_len == 4 && strncmp(b, "true", 4) == 0) { return 1; } - if (a_len == 5 && strncmp(a, "false", 5) == 0 && b_len == 5 && strncmp(b, "false", 5) == 0) { return 1; } - if (a_len == 4 && strncmp(a, "null", 4) == 0 && b_len == 4 && strncmp(b, "null", 4) == 0) { return 1; } - - /* 数字:用 strtod 支持科学计数法,最后一字节必须到达字符串末尾(避免部分解析) */ - { - char *endA = NULL, *endB = NULL; - - /* 拷贝到以 NUL 结尾的缓冲,避免 strtod 依赖外部长度 */ - char *bufA = (char *)malloc(a_len + 1); - char *bufB = (char *)malloc(b_len + 1); - if (!bufA || !bufB) - { - free(bufA); - free(bufB); - return 0; - } - - memcpy(bufA, a, a_len); - bufA[a_len] = '\0'; - memcpy(bufB, b, b_len); - bufB[b_len] = '\0'; - - double va = strtod(bufA, &endA); - double vb = strtod(bufB, &endB); - - int okA = (endA && *endA == '\0'); - int okB = (endB && *endB == '\0'); - - free(bufA); - free(bufB); - - if (okA && okB) - { - /* 直接相等(包含 -0 与 0)或允许极小误差 */ - if (va == vb) { return 1; } - if (fabs(va - vb) < 1e-15) { return 1; } - return 0; - } - } - - /* 其余作为纯文本兜底比较 */ - return (a_len == b_len) && (memcmp(a, b, a_len) == 0); -} - -/* 提取一元素数组的唯一元素;若不是一元素数组返回 0 */ -static int extract_single_array_element(const char *s, int32_t len, const char **elem, int32_t *elem_len) -{ - trim(&s, &len); - if (len < 2 || s[0] != '[' || s[len - 1] != ']') { return 0; } - - const char *p = s + 1; - int32_t n = len - 2; - - /* 去前后空白 */ - trim(&p, &n); - if (n == 0) { return 0; /* 空数组 */ } - - /* 扫描逗号,确保只有一个元素(字符串中的逗号不算) */ - int in_str = 0; - int escape = 0; - for (int32_t i = 0; i < n; i++) - { - char c = p[i]; - if (in_str) - { - if (escape) { escape = 0; } - else if (c == '\\') { escape = 1; } - else if (c == '\"') { in_str = 0; } - } - else - { - if (c == '\"') { in_str = 1; } - else if (c == ',') { return 0; /* 多元素数组 */ } - } - } - - /* 去尾部空白 */ - const char *q = p + n; - while (n && isspace((unsigned char)q[-1])) - { - q--; - n--; - } - - *elem = p; - *elem_len = n; - return 1; -} - -/* 顶层比较:若两侧都是一元素数组则剥离后比较;否则直接按值级比较 */ -static int json_value_equal(const char *a, int32_t a_len, const char *b, int32_t b_len) -{ - const char *ae = NULL, *be = NULL; - int32_t ale = 0, ble = 0; - - if (extract_single_array_element(a, a_len, &ae, &ale) && extract_single_array_element(b, b_len, &be, &ble)) - { - return json_scalar_equal(ae, ale, be, ble); - } - - return json_scalar_equal(a, a_len, b, b_len); -} - -static void checkadjfladjfl(char *data, uint32_t len, char *str, uint32_t strLen, uint32_t *alksdjfCOunt) -{ - if (0 != strcmp(data, str)) - { - // data/str 是去掉两端引号后的 JSON 字符串内容,并且有长度 - if (!json_value_equal(data, len, str, strLen)) - { - (*alksdjfCOunt)++; - // 打印时避免 %s,被 NUL 截断;可以打印十六进制 - printf("%d 数据不完全一致 -- 原始: %s -- 序列化: %s\n", *alksdjfCOunt, data, str); - } - } -} - -/** - * @brief RyanJson 测试程序 - * - * @param fileName - * @param data - * @param len - * @return int - */ -static int RyanJsonParseData(char *fileName, char *data, uint32_t len) -{ - - if (strcmp(fileName, "n_structure_100000_opening_arrays.json") == 0 || - strcmp(fileName, "n_structure_open_array_object.json") == 0 || strcmp(fileName, "n_structure_100000_opening_arrays.json") == 0) - { - return -1; - } - // printf("开始解析: %s\r\n", fileName); - RyanJson_t json = RyanJsonParseOptions(data, len, RyanJsonTrue, NULL); - if (NULL == json) { return -1; } - -#ifdef PrintfStrCmpEnable - int32_t strLen = 0; - char *str = RyanJsonPrint(json, 60, RyanJsonFalse, &strLen); - if (NULL == str) - { - printf("反序列化失败: [%s]\n", data); - goto err; - } - - RyanJsonMinify(data, len); - static uint32_t alksdjfCOunt = 0; - checkadjfladjfl(data, len, str, strLen, &alksdjfCOunt); - - RyanJsonFree(str); -#endif - - RyanJsonDelete(json); - return 0; - -err: - RyanJsonDelete(json); - return -1; -} - -/** - * @brief cJson测试程序 - * - * @param fileName - * @param data - * @param len - * @return int - */ -static int cJSONParseData(char *fileName, char *data, uint32_t len) -{ - - if (strcmp(fileName, "n_structure_100000_opening_arrays.json") == 0 || - strcmp(fileName, "n_structure_open_array_object.json") == 0 || strcmp(fileName, "n_structure_100000_opening_arrays.json") == 0) - { - return -1; - } - - cJSON *json = cJSON_ParseWithLengthOpts(data, len + sizeof(""), NULL, RyanJsonTrue); - if (NULL == json) { return -1; } - -#ifdef PrintfStrCmpEnable - char *str = cJSON_PrintBuffered(json, 60, RyanJsonFalse); - if (NULL == str) - { - printf("反序列化失败: [%s]\n", data); - goto err; - } - - cJSON_Minify(data); - static uint32_t alksdjfCOunt = 0; - checkadjfladjfl(data, len, str, strlen(str), &alksdjfCOunt); - - cJSON_free(str); -#endif - - cJSON_Delete(json); - return 0; -err: - cJSON_Delete(json); - return -1; -} - -/** - * @brief cJson测试程序 - * - * @param fileName - * @param data - * @param len - * @return int - */ -static int yyjsonParseData(char *fileName, char *data, uint32_t len) -{ - if (strcmp(fileName, "n_structure_100000_opening_arrays.json") == 0 || - strcmp(fileName, "n_structure_open_array_object.json") == 0 || strcmp(fileName, "n_structure_100000_opening_arrays.json") == 0) - { - return -1; - } - - yyjson_doc *doc = yyjson_read(data, len, 0); - if (NULL == doc) { return -1; } - -#ifdef PrintfStrCmpEnable - char *str = yyjson_write(doc, 0, NULL); - if (NULL == str) - { - printf("反序列化失败: [%s]\n", data); - goto err; - } - - cJSON_Minify(data); - static uint32_t alksdjfCOunt = 0; - checkadjfladjfl(data, len, str, strlen(str), &alksdjfCOunt); - - free(str); -#endif - - yyjson_doc_free(doc); - return 0; -err: - yyjson_doc_free(doc); - return -1; -} - -// RFC 8259 JSON Test Suite -// https://github.com/nst/JSONTestSuite -RyanJsonBool_e RFC8259JsonTest(void) -{ - int result = 0; - RyanJsonInitHooks(v_malloc, v_free, v_realloc); - - cJSON_Hooks hooks = {.malloc_fn = v_malloc, .free_fn = v_free}; - cJSON_InitHooks(&hooks); - - printf("开始 RFC 8259 JSON 测试"); - - printf("\r\n--------------------------- RFC8259 RyanJson --------------------------\r\n"); - result = testFile("../../../../test//RFC8259JsonData", RyanJsonParseData); - if (0 != result) - { - printf("%s:%d RyanJson RFC8259JsonTest fail\r\n", __FILE__, __LINE__); - goto err; - } - - printf("\r\n--------------------------- RFC8259 cJSON --------------------------\r\n"); - result = testFile("../../../../test//RFC8259JsonData", cJSONParseData); - if (0 != result) - { - printf("%s:%d cJSON RFC8259JsonTest fail\r\n", __FILE__, __LINE__); - goto err; - } - - printf("\r\n--------------------------- RFC8259 yyjson --------------------------\r\n"); - result = testFile("../../../../test//RFC8259JsonData", yyjsonParseData); - if (0 != result) - { - printf("%s:%d yyjson RFC8259JsonTest fail\r\n", __FILE__, __LINE__); - goto err; - } - - displayMem(); - return RyanJsonTrue; - -err: - displayMem(); - return RyanJsonFalse; -} diff --git a/test/RyanJsonTest.c b/test/RyanJsonTest.c index ce3d29b..092497d 100644 --- a/test/RyanJsonTest.c +++ b/test/RyanJsonTest.c @@ -1,4 +1,10 @@ +#include "RyanJson.h" #include "RyanJsonTest.h" +#include "tlsf.h" + +extern void printJsonDebug(RyanJson_t json); +#define jsonLogByTest(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#define LV_MEM_SIZE (1024 * 1024) static void printfTitle(char *title) { @@ -9,59 +15,149 @@ static void printfTitle(char *title) printf("*****************************************************************************\r\n"); } -#ifndef isEnableFuzzer -extern void printJsonDebug(RyanJson_t json); -int main(void) +uint64_t platformUptimeMs(void) +{ + struct timespec ts; + // CLOCK_MONOTONIC: 单调递增,不受系统时间修改影响,适合做耗时统计 + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +static tlsf_t tlsfHandler; + +static size_t total2 = LV_MEM_SIZE, used2 = 0, available = 0; +bool tlsf_walker_callback(void *ptr, size_t size, int used, void *user) +{ + (void)ptr; + (void)user; // suppress unused warnings + if (1 == used) { used2 += size + tlsf_alloc_overhead(); } + return true; +} + +void showMemoryInfo(void) { + size_t total = 0, used = 0, max_used = 0; + rt_memory_info22(tlsfHandler, &total, &used, &max_used); + jsonLogByTest("total: %zu, used: %zu, max_used: %zu, available: %zu\r\n", total, used, max_used, total - used); + + used2 = 0; + available = 0; + total2 = total; + tlsf_walk_pool(tlsf_get_pool(tlsfHandler), tlsf_walker_callback, NULL); + jsonLogByTest("total2: %zu, used2: %zu, max_used2: %d, available2: %zu\r\n", total2, used2, 0, total2 - used2); +} + +int32_t vallocGetUseByTlsf(void) +{ + size_t total = 0, used = 0, max_used = 0; + rt_memory_info22(tlsfHandler, &total, &used, &max_used); + return (int32_t)used; +} + +#define RyanJsonAlign(size, align) (((size) + (align) - 1) & ~((align) - 1)) +void *v_malloc_tlsf(size_t size) +{ + if (size == 0) { return NULL; } + return tlsf_malloc(tlsfHandler, RyanJsonAlign(size + RyanJsonMallocHeaderSize - 4, RyanJsonMallocAlign)); +} + +void v_free_tlsf(void *block) +{ + if (block) { tlsf_free(tlsfHandler, block); } +} + +void *v_realloc_tlsf(void *block, size_t size) +{ + void *newBlock = v_malloc_tlsf(size); + if (newBlock) { memmove(newBlock, block, size); } + + v_free_tlsf(block); + return newBlock; + + // return tlsf_realloc(tlsfHandler, block, size); +} + +RyanJsonBool_e RyanJsonTestFun(void) +{ + char *tlsfMemBuf = v_malloc(LV_MEM_SIZE); + tlsfHandler = tlsf_create_with_pool((void *)tlsfMemBuf, LV_MEM_SIZE, LV_MEM_SIZE); + jsonLogByTest("tlsf_size: %d\r\n", tlsf_size(tlsfHandler)); RyanJsonBool_e result = RyanJsonFalse; - RyanJsonInitHooks(v_malloc, v_free, v_realloc); + RyanJsonInitHooks(v_malloc_tlsf, v_free_tlsf, v_realloc_tlsf); for (uint32_t i = 0; i < 1; i++) { - char *str = NULL; RyanJson_t jsonRoot, item; + // const char *jsonstr = "{\"emoji\":\"\\uD83D\\uDE00\"} "; + const char *jsonstr = "{\"name\":\"Mash\",\"star\":0.2,\"hits\":[2,2,1,3]}"; + // const char *jsonstr = "{\"star\":4}"; + // const char *jsonstr = "\"name\""; // const char *jsonstr = - // "{\"emoji\":\"\\uD83D\\uDE00\"} "; - // const char *jsonstr = "{\"name\":\"Mash\",\"star\":4,\"hits\":[2,2,1,3]}"; - const char *jsonstr = "[1"; + // "{\"n\":0,\"q\":1,\"e\":1,\"p\":1,\"1u\":0,\"r\":0,\"w\":0.0,\"1w\":0,\"1x\":0,\"1y\":100.0,\"23\":0," + // "\"29\":0,\"2h\":0,\"o\":30,\"1v\":12000,\"2c\":0}"; // extern int LLVMFuzzerTestOneInput(const char *data, int32_t size); // LLVMFuzzerTestOneInput(jsonstr, strlen(jsonstr)); // 解析json数据 - // jsonRoot = RyanJsonParse(jsonstr); - // if (jsonRoot == NULL) { printf("%s:%d 序列化失败\r\n", __FILE__, __LINE__); } - // else - // { - // uint32_t len = 0; - // str = RyanJsonPrint(jsonRoot, 10, RyanJsonFalse, &len); // 以带格式方式将数据打印出来 - // printf("strLen: %d, data: %s\r\n", len, str); - - // RyanJsonFree(str); - // RyanJsonDelete(jsonRoot); - // } + jsonRoot = RyanJsonParse(jsonstr); + if (jsonRoot == NULL) { printf("%s:%d 序列化失败\r\n", __FILE__, __LINE__); } + else + { + uint32_t len = 0; + str = RyanJsonPrint(jsonRoot, 10, RyanJsonFalse, &len); // 以带格式方式将数据打印出来 + printf("strLen: %d, data: %s\r\n", len, str); + + RyanJsonFree(str); + RyanJsonDelete(jsonRoot); + } } - RyanJsonExample(); + showMemoryInfo(); + + // example内部会替换hooks,所以需要重新设置 + result = RyanJsonExample(); + RyanJsonInitHooks(v_malloc_tlsf, v_free_tlsf, v_realloc_tlsf); + // RyanJsonInitHooks(v_malloc, v_free, v_realloc); + if (RyanJsonTrue != result) + { + printf("%s:%d RyanJsonExample fail\r\n", __FILE__, __LINE__); + return RyanJsonFalse; + } result = RyanJsonBaseTest(); if (RyanJsonTrue != result) { - printf("%s:%d RyanJsonTest fail\r\n", __FILE__, __LINE__); - return -1; + printf("%s:%d RyanJsonBaseTest fail\r\n", __FILE__, __LINE__); + return RyanJsonFalse; } printfTitle("RyanJson / cJSON / yyjson RFC8259标准测试"); - RFC8259JsonTest(); + result = RFC8259JsonTest(); + if (RyanJsonTrue != result) + { + printf("%s:%d RFC8259JsonTest fail\r\n", __FILE__, __LINE__); + return RyanJsonFalse; + } printfTitle("RyanJson / cJSON / yyjson 内存对比程序"); RyanJsonMemoryFootprintTest(); printf("\r\nok\r\n"); + showMemoryInfo(); + v_free(tlsfMemBuf); displayMem(); + + return RyanJsonTrue; +} + +#ifndef isEnableFuzzer +int main(void) +{ + RyanJsonTestFun(); return 0; } diff --git a/test/RyanJsonTest.h b/test/RyanJsonTest.h index fbf3d8d..2742417 100644 --- a/test/RyanJsonTest.h +++ b/test/RyanJsonTest.h @@ -1,5 +1,5 @@ -#ifndef __RyanJsonTest__ -#define __RyanJsonTest__ +#ifndef RyanJsonTest +#define RyanJsonTest #ifdef __cplusplus extern "C" { @@ -13,6 +13,7 @@ extern "C" { #include #include #include +#include #include "valloc.h" #include "RyanJson.h" #include "RyanJsonUtils.h" @@ -27,25 +28,49 @@ extern "C" { v_mcheck(&area, &use); \ if (area != 0 || use != 0) \ { \ - RyanMqttLog_e("内存泄漏"); \ + printf("内存泄漏\r\n"); \ while (1) \ { \ v_mcheck(&area, &use); \ - RyanMqttLog_e("|||----------->>> area = %d, size = %d", area, use); \ + printf("|||----------->>> area = %d, size = %d\r\n", area, use); \ delay(3000); \ } \ } \ } while (0) // 定义枚举类型 +extern void *v_malloc_tlsf(size_t size); +extern void v_free_tlsf(void *block); +extern void *v_realloc_tlsf(void *block, size_t size); +extern int32_t vallocGetUseByTlsf(void); // 定义结构体类型 +uint64_t platformUptimeMs(void); + +#define runTestWithLogAndTimer(fun) \ + do \ + { \ + testRunCount++; \ + /* 开始执行:绿色高亮,文件名放在 [TEST n] 后面 */ \ + printf("\x1b[32m┌── [TEST %d | %s:%d] 开始执行: %s()\x1b[0m\r\n", testRunCount, __FILE__, __LINE__, #fun); \ + \ + funcStartMs = platformUptimeMs(); \ + result = fun(); \ + \ + /* 结束执行:根据结果显示绿色或红色,文件名放在 [TEST n] 后面 */ \ + printf("%s└── [TEST %" PRIu32 " | %s:%d] 结束执行: 结果 %s | 耗时: %" PRIu64 " ms\x1b[0m\r\n\r\n", \ + (result == RyanJsonTrue) ? "\x1b[32m" : "\x1b[31m", testRunCount, __FILE__, __LINE__, \ + (result == RyanJsonTrue) ? "✅" : "❌", (platformUptimeMs() - funcStartMs)); \ + \ + RyanJsonCheckCodeNoReturn(RyanJsonTrue == result, { return RyanJsonFalse; }); \ + } while (0) /* extern variables-----------------------------------------------------------*/ extern RyanJsonBool_e RyanJsonExample(void); extern RyanJsonBool_e RyanJsonBaseTest(void); extern RyanJsonBool_e RFC8259JsonTest(void); extern RyanJsonBool_e RyanJsonMemoryFootprintTest(void); +extern RyanJsonBool_e RyanJsonTestFun(void); #ifdef __cplusplus } #endif diff --git a/test/baseTest/RyanJsonBaseTest.c b/test/baseTest/RyanJsonBaseTest.c index 1ce7ba4..266b13f 100644 --- a/test/baseTest/RyanJsonBaseTest.c +++ b/test/baseTest/RyanJsonBaseTest.c @@ -40,42 +40,27 @@ static RyanJsonBool_e likeReferenceTest() return 0; } -uint64_t platformUptimeMs(void) -{ - struct timespec ts; - // CLOCK_MONOTONIC: 单调递增,不受系统时间修改影响,适合做耗时统计 - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; -} - RyanJsonBool_e RyanJsonBaseTest(void) { int32_t result = 0; - RyanJsonInitHooks(v_malloc, v_free, v_realloc); - uint32_t testRunCount = 0; uint64_t funcStartMs; -#define runTestWithLogAndTimer(fun) \ - do \ - { \ - testRunCount++; \ - printf("┌── [TEST %d] 开始执行: " #fun "()\r\n", testRunCount); \ - funcStartMs = platformUptimeMs(); \ - result = fun(); \ - printf("└── [TEST %" PRIu32 "] 结束执行: 返回值 = %" PRId32 " %s | 耗时: %" PRIu64 " ms\x1b[0m\r\n\r\n", testRunCount, \ - result, (result == RyanJsonTrue) ? "✅" : "❌", (platformUptimeMs() - funcStartMs)); \ - RyanJsonCheckCodeNoReturn(RyanJsonTrue == result, { goto __exit; }); \ - } while (0) - - runTestWithLogAndTimer(RyanJsonBaseTestChangeJson); // JSON 修改功能的条件覆盖测试 - runTestWithLogAndTimer(RyanJsonBaseTestCompareJson); // 节点比较与一致性验证 - runTestWithLogAndTimer(RyanJsonBaseTestCreateJson); // 节点创建与结构正确性检查 - runTestWithLogAndTimer(RyanJsonBaseTestDeleteJson); // JSON 删除功能的条件覆盖测试 - runTestWithLogAndTimer(RyanJsonBaseTestDetachJson); // 节点分离操作的条件覆盖测试 - runTestWithLogAndTimer(RyanJsonBaseTestDuplicateJson); // 节点复制的深拷贝与浅拷贝验证 - runTestWithLogAndTimer(RyanJsonBaseTestForEachJson); // 节点遍历与迭代稳定性测试 - runTestWithLogAndTimer(RyanJsonBaseTestLoadJson); // JSON 文本解析与加载能力验证 - runTestWithLogAndTimer(RyanJsonBaseTestReplaceJson); // 节点替换功能的条件覆盖测试 + + runTestWithLogAndTimer(RyanJsonBaseTestChangeJson); // 验证 JSON 动态更新及存储模式切换逻辑 + runTestWithLogAndTimer(RyanJsonBaseTestCompareJson); // 验证节点及其属性的深度一致性比较逻辑 + runTestWithLogAndTimer(RyanJsonBaseTestCreateJson); // 验证全类型节点的构造与初始化逻辑 + runTestWithLogAndTimer(RyanJsonBaseTestDeleteJson); // 验证节点及其子项的递归内存回收逻辑 + runTestWithLogAndTimer(RyanJsonBaseTestDetachJson); // 验证节点的分离操作及其所属权转移逻辑 + runTestWithLogAndTimer(RyanJsonBaseTestDuplicateJson); // 验证对象的深拷贝 (Deep Copy) 完整性逻辑 + runTestWithLogAndTimer(RyanJsonBaseTestForEachJson); // 验证数组与对象迭代器的稳定性与边界情况 + runTestWithLogAndTimer(RyanJsonBaseTestLoadJson); // 验证复杂 JSON 文本解析与内存映射的健壮性 + runTestWithLogAndTimer(RyanJsonBaseTestReplaceJson); // 验证节点就地替换与成员管理机制的有效性 + + // 验证节点属性一致性 + runTestWithLogAndTimer(RyanJsonBaseTestEqualityBool); // 验证布尔值一致性 + runTestWithLogAndTimer(RyanJsonBaseTestEqualityDouble); // 验证浮点数一致性 + runTestWithLogAndTimer(RyanJsonBaseTestEqualityInt); // 验证整数一致性 + runTestWithLogAndTimer(RyanJsonBaseTestEqualityString); // 验证字符串一致性 // result = likeReferenceTest(); // 模仿 引用类型实现 示例 // if (0 != result) diff --git a/test/baseTest/RyanJsonBaseTest.h b/test/baseTest/RyanJsonBaseTest.h index 77eb106..ab38582 100644 --- a/test/baseTest/RyanJsonBaseTest.h +++ b/test/baseTest/RyanJsonBaseTest.h @@ -16,6 +16,8 @@ extern "C" { #include "cJSON.h" #include "valloc.h" #include "RyanJsonTest.h" + +#undef jsonLog #define jsonLog(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__) // 定义枚举类型 @@ -24,7 +26,6 @@ extern "C" { /* extern variables-----------------------------------------------------------*/ -extern RyanJsonBool_e compare_double(double a, double b); extern void printJsonDebug(RyanJson_t json); extern RyanJsonBool_e rootNodeCheckTest(RyanJson_t json); extern RyanJsonBool_e itemNodeCheckTest(RyanJson_t json); @@ -42,6 +43,11 @@ extern RyanJsonBool_e RyanJsonBaseTestForEachJson(void); extern RyanJsonBool_e RyanJsonBaseTestLoadJson(void); extern RyanJsonBool_e RyanJsonBaseTestReplaceJson(void); +extern RyanJsonBool_e RyanJsonBaseTestEqualityBool(void); +extern RyanJsonBool_e RyanJsonBaseTestEqualityDouble(void); +extern RyanJsonBool_e RyanJsonBaseTestEqualityInt(void); +extern RyanJsonBool_e RyanJsonBaseTestEqualityString(void); + #ifdef __cplusplus } #endif diff --git a/test/baseTest/RyanJsonBaseTestChangeJson.c b/test/baseTest/RyanJsonBaseTestChangeJson.c index a1e368a..0ae4b10 100644 --- a/test/baseTest/RyanJsonBaseTestChangeJson.c +++ b/test/baseTest/RyanJsonBaseTestChangeJson.c @@ -12,92 +12,131 @@ RyanJsonBool_e RyanJsonBaseTestChangeJson(void) "\"array\":[16,16.89,\"hello\",true,false,null]," "\"arrayItem\":[{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}," "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]," - "\"string2222\":\"hello\"}"; + "\"string2222\":\"hello\",\"0\":\"1\",\"nameaaaaaaaaaaaaaaaaaaaaaaaaaaaa\":\"Mash\",\"2\":\"3\",\"name\":" + "\"Mashaaaaaaaaaaaaaaaaaaaaaaaa\"}"; - RyanJson_t json = RyanJsonParse(jsonstr); - RyanJsonCheckReturnFalse(NULL != json); + RyanJson_t jsonRoot = RyanJsonParse(jsonstr); + RyanJsonCheckReturnFalse(NULL != jsonRoot); /** * @brief 修改基本类型 */ - RyanJsonChangeIntValue(RyanJsonGetObjectToKey(json, "inter"), 20); - RyanJsonCheckCode(RyanJsonIsInt(RyanJsonGetObjectToKey(json, "inter")) && - 20 == RyanJsonGetIntValue(RyanJsonGetObjectToKey(json, "inter")), + RyanJsonChangeIntValue(RyanJsonGetObjectToKey(jsonRoot, "inter"), 20); + RyanJsonCheckCode(RyanJsonIsInt(RyanJsonGetObjectToKey(jsonRoot, "inter")) && + 20 == RyanJsonGetIntValue(RyanJsonGetObjectToKey(jsonRoot, "inter")), { goto err; }); - RyanJsonChangeDoubleValue(RyanJsonGetObjectToKey(json, "double"), 20.89); - RyanJsonCheckCode(RyanJsonIsDouble(RyanJsonGetObjectToKey(json, "double")) && - compare_double(RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(json, "double")), 20.89), + RyanJsonChangeDoubleValue(RyanJsonGetObjectToKey(jsonRoot, "double"), 20.89); + RyanJsonCheckCode(RyanJsonIsDouble(RyanJsonGetObjectToKey(jsonRoot, "double")) && + RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(jsonRoot, "double")), 20.89), { goto err; }); - RyanJsonChangeStringValue(RyanJsonGetObjectToKey(json, "string"), "world"); - RyanJsonCheckCode(RyanJsonIsString(RyanJsonGetObjectToKey(json, "string")) && - strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(json, "string")), "world") == 0, + // inline模式只修改key,并且不超过inline长度 + RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "0"), "type"); + RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "type")), "type") == 0 && + strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "type")), "1") == 0, { goto err; }); - RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(json, "boolTrue"), RyanJsonFalse); - RyanJsonCheckCode(RyanJsonIsBool(RyanJsonGetObjectToKey(json, "boolTrue")) && - RyanJsonGetBoolValue(RyanJsonGetObjectToKey(json, "boolTrue")) == RyanJsonFalse, + // inline模式修改key,并且超过inline长度,进入ptr模式 + RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "type"), "type000000000000000"); + RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "type000000000000000")), "type000000000000000") == 0 && + strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "type000000000000000")), "1") == 0, { goto err; }); - RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(json, "boolFalse"), RyanJsonTrue); - RyanJsonCheckCode(RyanJsonIsBool(RyanJsonGetObjectToKey(json, "boolFalse")) && - RyanJsonGetBoolValue(RyanJsonGetObjectToKey(json, "boolFalse")) == RyanJsonTrue, + // ptr模式只修改key,不超过inline长度,进入inline模式 + RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "nameaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), "na"); + RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "na")), "na") == 0 && + strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "na")), "Mash") == 0, + { goto err; }); + + // inline模式只修改Value,并且不超过inline长度 + RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "2"), "type"); + RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "2")), "2") == 0 && + strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "2")), "type") == 0, + { goto err; }); + + // ptr模式只修改Value,不超过inline长度,进入inline模式 + RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "name"), "Ma"); + RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "name")), "name") == 0 && + strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "name")), "Ma") == 0, + { goto err; }); + + // ptr模式只修改Value,超过inline长度,进入ptr模式 + RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "name"), "Mashaaaaaaaaaaaaaaaaaaaaaaaa"); + RyanJsonCheckCode( + strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "name")), "name") == 0 && + strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "name")), "Mashaaaaaaaaaaaaaaaaaaaaaaaa") == 0, + { goto err; }); + + RyanJsonChangeStringValue(RyanJsonGetObjectToKey(jsonRoot, "string"), "world"); + RyanJsonCheckCode(RyanJsonIsString(RyanJsonGetObjectToKey(jsonRoot, "string")) && + strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "string")), "world") == 0, + { goto err; }); + + RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(jsonRoot, "boolTrue"), RyanJsonFalse); + RyanJsonCheckCode(RyanJsonIsBool(RyanJsonGetObjectToKey(jsonRoot, "boolTrue")) && + RyanJsonGetBoolValue(RyanJsonGetObjectToKey(jsonRoot, "boolTrue")) == RyanJsonFalse, + { goto err; }); + + RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(jsonRoot, "boolFalse"), RyanJsonTrue); + RyanJsonCheckCode(RyanJsonIsBool(RyanJsonGetObjectToKey(jsonRoot, "boolFalse")) && + RyanJsonGetBoolValue(RyanJsonGetObjectToKey(jsonRoot, "boolFalse")) == RyanJsonTrue, { goto err; }); /** * @brief 修改数组元素 (arrayInt) */ - RyanJsonChangeIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayInt"), 0), 99); - RyanJsonCheckCode(RyanJsonIsInt(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayInt"), 0)) && - RyanJsonGetIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayInt"), 0)) == 99, + RyanJsonChangeIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayInt"), 0), 99); + RyanJsonCheckCode(RyanJsonIsInt(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayInt"), 0)) && + RyanJsonGetIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayInt"), 0)) == 99, { goto err; }); /** * @brief 修改数组元素 (arrayDouble) */ - RyanJsonChangeDoubleValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayDouble"), 1), 99.99); - RyanJsonCheckCode( - RyanJsonIsDouble(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayDouble"), 1)) && - compare_double(RyanJsonGetDoubleValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayDouble"), 1)), - 99.99), - { goto err; }); + RyanJsonChangeDoubleValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayDouble"), 1), 99.99); + RyanJsonCheckCode(RyanJsonIsDouble(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayDouble"), 1)) && + RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectToIndex( + RyanJsonGetObjectToKey(jsonRoot, "arrayDouble"), 1)), + 99.99), + { goto err; }); /** * @brief 修改数组元素 (arrayString) */ - RyanJsonChangeStringValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayString"), 2), "changedString"); - RyanJsonCheckCode(RyanJsonIsString(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayString"), 2)) && - strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayString"), 2)), - "changedString") == 0, - { goto err; }); + RyanJsonChangeStringValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayString"), 2), "changedString"); + RyanJsonCheckCode( + RyanJsonIsString(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayString"), 2)) && + strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayString"), 2)), + "changedString") == 0, + { goto err; }); /** * @brief 修改嵌套对象 */ - RyanJsonChangeStringValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(json, "item"), "string"), "nestedWorld"); - RyanJsonCheckCode(RyanJsonIsString(RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(json, "item"), "string")) && - strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(json, "item"), "string")), + RyanJsonChangeStringValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(jsonRoot, "item"), "string"), "nestedWorld"); + RyanJsonCheckCode(RyanJsonIsString(RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(jsonRoot, "item"), "string")) && + strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(jsonRoot, "item"), "string")), "nestedWorld") == 0, { goto err; }); /** * @brief 修改数组对象中的字段 (arrayItem[0].inter -> 123) */ - RyanJsonChangeIntValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayItem"), 0), "inter"), + RyanJsonChangeIntValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayItem"), 0), "inter"), 123); - RyanJsonCheckCode( - RyanJsonIsInt(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayItem"), 0), "inter")) && - RyanJsonGetIntValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayItem"), 0), - "inter")) == 123, - { goto err; }); + RyanJsonCheckCode(RyanJsonIsInt(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayItem"), 0), + "inter")) && + RyanJsonGetIntValue(RyanJsonGetObjectToKey( + RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayItem"), 0), "inter")) == 123, + { goto err; }); - char *str = RyanJsonPrint(json, 1024, RyanJsonTrue, NULL); + char *str = RyanJsonPrint(jsonRoot, 1024, RyanJsonTrue, NULL); RyanJsonFree(str); - RyanJsonDelete(json); + RyanJsonDelete(jsonRoot); return RyanJsonTrue; err: - RyanJsonDelete(json); + RyanJsonDelete(jsonRoot); return RyanJsonFalse; } diff --git a/test/baseTest/RyanJsonBaseTestCompareJson.c b/test/baseTest/RyanJsonBaseTestCompareJson.c index f5cb8fa..f83a0c6 100644 --- a/test/baseTest/RyanJsonBaseTestCompareJson.c +++ b/test/baseTest/RyanJsonBaseTestCompareJson.c @@ -20,65 +20,79 @@ RyanJsonBool_e RyanJsonBaseTestCompareJson(void) // 比较函数 RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompare(json, json), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonAddStringToObject(json2, "test", "hello"); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonAddIntToObject(json2, "test", 1); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonAddDoubleToObject(json2, "test", 2.0); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonAddBoolToObject(json2, "test", RyanJsonTrue); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonAddNullToObject(json2, "test"); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonAddIntToArray(RyanJsonGetObjectToKey(json2, "arrayInt"), 2); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonAddDoubleToArray(RyanJsonGetObjectToKey(json2, "arrayDouble"), 2.0); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonAddStringToArray(RyanJsonGetObjectToKey(json2, "arrayString"), "hello"); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonAddItemToArray(RyanJsonGetObjectToKey(json2, "arrayItem"), RyanJsonCreateString("test", "hello")); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonChangeKey(RyanJsonGetObjectToKey(json2, "inter"), "int2"); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonChangeIntValue(RyanJsonGetObjectToKey(json2, "inter"), 17); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonChangeDoubleValue(RyanJsonGetObjectToKey(json2, "double"), 20.89); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); if (RyanJsonFalse != RyanJsonCompare(json, json2)) { printf("%s:%d 解析失败\r\n", __FILE__, __LINE__); @@ -90,26 +104,31 @@ RyanJsonBool_e RyanJsonBaseTestCompareJson(void) RyanJsonDelete(RyanJsonDetachByKey(json2, "double")); RyanJsonAddIntToObject(json2, "double", 20); // 改为int RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonChangeStringValue(RyanJsonGetObjectToKey(json2, "string"), "49"); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(json2, "boolTrue"), RyanJsonFalse); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(json2, "item", "boolTrue"), RyanJsonFalse); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonChangeIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayInt"), 0), 17); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); @@ -120,32 +139,38 @@ RyanJsonBool_e RyanJsonBaseTestCompareJson(void) json2 = RyanJsonParse(jsonstr); RyanJsonChangeStringValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayString"), 0), "20.89"); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonChangeIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "array"), 0), 17); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonChangeIntValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayItem"), 0), "inter"), 17); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonDeleteByKey(json2, "arrayItem"); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonDeleteByIndex(RyanJsonGetObjectToKey(json2, "arrayInt"), 2); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json2); json2 = RyanJsonParse(jsonstr); RyanJsonDeleteByIndex(RyanJsonGetObjectToKey(json2, "arrayItem"), 0); RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; }); + RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; }); RyanJsonDelete(json); RyanJsonDelete(json2); diff --git a/test/baseTest/RyanJsonBaseTestForEachJson.c b/test/baseTest/RyanJsonBaseTestForEachJson.c index 128b8f4..497c8c1 100644 --- a/test/baseTest/RyanJsonBaseTestForEachJson.c +++ b/test/baseTest/RyanJsonBaseTestForEachJson.c @@ -20,7 +20,7 @@ RyanJsonBool_e RyanJsonBaseTestForEachJson(void) RyanJson_t item = NULL; RyanJsonArrayForEach(RyanJsonGetObjectToKey(json, "arrayDouble"), item) { - if (!RyanJsonIsDouble(item) || !compare_double(16.89, RyanJsonGetDoubleValue(item))) { goto err; } + if (!RyanJsonIsDouble(item) || !RyanJsonCompareDouble(16.89, RyanJsonGetDoubleValue(item))) { goto err; } } RyanJsonArrayForEach(RyanJsonGetObjectToKey(json, "arrayInt"), item) diff --git a/test/baseTest/RyanJsonBaseTestLoadJson.c b/test/baseTest/RyanJsonBaseTestLoadJson.c index 8597e26..068407e 100644 --- a/test/baseTest/RyanJsonBaseTestLoadJson.c +++ b/test/baseTest/RyanJsonBaseTestLoadJson.c @@ -7,17 +7,17 @@ RyanJsonBool_e RyanJsonBaseTestLoadJson(void) { char *str = NULL; RyanJson_t json; - char jsonstr[] = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":" - "{\"inter\":16,\"double\":16." - "89,\"string\":\"hello\"," - "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89," - "16.89,16.89,16.89]," - "\"arrayString\":[\"hello\",\"hello\"," - "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16," - "\"double\":16.89,\"string\":" - "\"hello\",\"boolTrue\":true," - "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true," - "\"boolFalse\":false,\"null\":null}]}"; + char *jsonstr = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":" + "{\"inter\":16,\"double\":16." + "89,\"string\":\"hello\"," + "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89," + "16.89,16.89,16.89]," + "\"arrayString\":[\"hello\",\"hello\"," + "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16," + "\"double\":16.89,\"string\":" + "\"hello\",\"boolTrue\":true," + "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true," + "\"boolFalse\":false,\"null\":null}],\"unicode\":\"😀\"}"; json = RyanJsonParse(jsonstr); RyanJsonCheckReturnFalse(NULL != json); @@ -38,6 +38,73 @@ RyanJsonBool_e RyanJsonBaseTestLoadJson(void) RyanJsonDelete(json); + /** + * @brief 测试 Unicode + * + */ + char printfBuf[1024] = {0}; + json = RyanJsonParse("{\"emoji\":\"\\uD83D\\uDE00\"}"); + RyanJsonCheckReturnFalse(NULL != json); + str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL); + RyanJsonDelete(json); + + // 测试数字 0-9 分支: \u0030 = '0', \u0039 = '9' + json = RyanJsonParse("{\"num\":\"\\u0030\\u0039\"}"); + RyanJsonCheckReturnFalse(NULL != json); + str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL); + RyanJsonCheckCode(0 == strcmp(str, "{\"num\":\"09\"}"), { + RyanJsonDelete(json); + return RyanJsonFalse; + }); + RyanJsonDelete(json); + + // 测试小写 a-f 分支: \u0061 = 'a', \u0066 = 'f' + json = RyanJsonParse("{\"lower\":\"\\u0061\\u0062\\u0063\\u0064\\u0065\\u0066\"}"); + RyanJsonCheckReturnFalse(NULL != json); + str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL); + RyanJsonCheckCode(0 == strcmp(str, "{\"lower\":\"abcdef\"}"), { + RyanJsonDelete(json); + return RyanJsonFalse; + }); + RyanJsonDelete(json); + + // 测试大写 A-F 分支: \u0041 = 'A', \u0046 = 'F' + json = RyanJsonParse("{\"upper\":\"\\u0041\\u0042\\u0043\\u0044\\u0045\\u0046\"}"); + RyanJsonCheckReturnFalse(NULL != json); + str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL); + RyanJsonCheckCode(0 == strcmp(str, "{\"upper\":\"ABCDEF\"}"), { + RyanJsonDelete(json); + return RyanJsonFalse; + }); + RyanJsonDelete(json); + + // 测试混合大小写: \uAbCd (混合大小写十六进制) + json = RyanJsonParse("{\"mixed\":\"\\uAbCd\"}"); + RyanJsonCheckReturnFalse(NULL != json); + RyanJsonFree(RyanJsonPrint(json, 50, RyanJsonFalse, NULL)); + RyanJsonDelete(json); + + // 测试 default 分支 (非法十六进制字符 'G') + json = RyanJsonParse("{\"invalid\":\"\\uGGGG\"}"); + RyanJsonCheckCode(NULL == json, { + RyanJsonDelete(json); + return RyanJsonFalse; + }); + + // 测试 default 分支 (非法十六进制字符 'Z') + json = RyanJsonParse("{\"invalid\":\"\\u00ZZ\"}"); + RyanJsonCheckCode(NULL == json, { + RyanJsonDelete(json); + return RyanJsonFalse; + }); + + // 测试 default 分支 (非法十六进制字符 '!') + json = RyanJsonParse("{\"invalid\":\"\\u00!!\"}"); + RyanJsonCheckCode(NULL == json, { + RyanJsonDelete(json); + return RyanJsonFalse; + }); + /** * @brief 测试序列化错误json结构 * diff --git a/test/baseTest/RyanJsonBaseTestUtile.c b/test/baseTest/RyanJsonBaseTestUtile.c index e7c9bfc..1402bb9 100644 --- a/test/baseTest/RyanJsonBaseTestUtile.c +++ b/test/baseTest/RyanJsonBaseTestUtile.c @@ -2,12 +2,6 @@ #include "RyanJsonBaseTest.h" /* --------------------------------------- jsonTest ------------------------------------------- */ -// !(fabs(RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(json, "double")) - 16.89) < 1e-6) -RyanJsonBool_e compare_double(double a, double b) -{ - double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); - return (fabs(a - b) <= maxVal * DBL_EPSILON); -} void printJsonDebug(RyanJson_t json) { @@ -24,7 +18,7 @@ RyanJsonBool_e rootNodeCheckTest(RyanJson_t json) } if (!RyanJsonIsDouble(RyanJsonGetObjectToKey(json, "double")) || - !compare_double(RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(json, "double")), 16.89)) + !RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(json, "double")), 16.89)) { RyanJsonCheckReturnFalse(NULL); } @@ -85,7 +79,7 @@ RyanJsonBool_e arrayNodeCheckTest(RyanJson_t json) } if (!RyanJsonIsDouble(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 1)) || - !compare_double(RyanJsonGetDoubleValue(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 1)), 16.89)) + !RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 1)), 16.89)) { printf("%s:%d 解析失败 %f\r\n", __FILE__, __LINE__, RyanJsonGetDoubleValue(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 1))); diff --git a/test/baseTest/equality/RyanJsonBaseTestEqualityBool.c b/test/baseTest/equality/RyanJsonBaseTestEqualityBool.c new file mode 100644 index 0000000..2c7f579 --- /dev/null +++ b/test/baseTest/equality/RyanJsonBaseTestEqualityBool.c @@ -0,0 +1,86 @@ +#include "RyanJsonBaseTest.h" + +// 布尔值一致性测试 +RyanJsonBool_e RyanJsonBaseTestEqualityBool(void) +{ + // 测试 true + { + const char *jsonBoolStr = "{\"bool\":true}"; + RyanJson_t jsonRoot = RyanJsonParse(jsonBoolStr); + RyanJsonCheckReturnFalse(NULL != jsonRoot); + RyanJsonCheckReturnFalse(RyanJsonIsBool(RyanJsonGetObjectToKey(jsonRoot, "bool"))); + RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonGetBoolValue(RyanJsonGetObjectToKey(jsonRoot, "bool"))); + + // 往返测试 + char *serializedStr = RyanJsonPrint(jsonRoot, 64, RyanJsonFalse, NULL); + RyanJsonDelete(jsonRoot); + + RyanJson_t roundtripJson = RyanJsonParse(serializedStr); + RyanJsonFree(serializedStr); + RyanJsonCheckReturnFalse(NULL != roundtripJson); + RyanJsonCheckReturnFalse(RyanJsonIsBool(RyanJsonGetObjectToKey(roundtripJson, "bool"))); + RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonGetBoolValue(RyanJsonGetObjectToKey(roundtripJson, "bool"))); + + RyanJsonDelete(roundtripJson); + } + + // 测试 false + { + const char *jsonBoolStr = "{\"bool\":false}"; + RyanJson_t jsonRoot = RyanJsonParse(jsonBoolStr); + RyanJsonCheckReturnFalse(NULL != jsonRoot); + RyanJsonCheckReturnFalse(RyanJsonIsBool(RyanJsonGetObjectToKey(jsonRoot, "bool"))); + RyanJsonCheckReturnFalse(RyanJsonFalse == RyanJsonGetBoolValue(RyanJsonGetObjectToKey(jsonRoot, "bool"))); + + // 往返测试 + char *serializedStr = RyanJsonPrint(jsonRoot, 64, RyanJsonFalse, NULL); + RyanJsonDelete(jsonRoot); + + RyanJson_t roundtripJson = RyanJsonParse(serializedStr); + RyanJsonFree(serializedStr); + RyanJsonCheckReturnFalse(NULL != roundtripJson); + RyanJsonCheckReturnFalse(RyanJsonIsBool(RyanJsonGetObjectToKey(roundtripJson, "bool"))); + RyanJsonCheckReturnFalse(RyanJsonFalse == RyanJsonGetBoolValue(RyanJsonGetObjectToKey(roundtripJson, "bool"))); + + RyanJsonDelete(roundtripJson); + } + + // 测试数组中的布尔值 + { + const char *jsonArrayStr = "[true, false, true, false]"; + RyanJson_t jsonRoot = RyanJsonParse(jsonArrayStr); + RyanJsonCheckReturnFalse(NULL != jsonRoot); + RyanJsonCheckReturnFalse(4 == RyanJsonGetArraySize(jsonRoot)); + + RyanJsonBool_e expected[] = {RyanJsonTrue, RyanJsonFalse, RyanJsonTrue, RyanJsonFalse}; + int idx = 0; + RyanJson_t item = NULL; + RyanJsonArrayForEach(jsonRoot, item) + { + RyanJsonCheckReturnFalse(RyanJsonIsBool(item)); + RyanJsonCheckReturnFalse(expected[idx] == RyanJsonGetBoolValue(item)); + idx++; + } + + // 往返测试 + char *serializedStr = RyanJsonPrint(jsonRoot, 64, RyanJsonFalse, NULL); + RyanJsonDelete(jsonRoot); + + RyanJson_t roundtripJson = RyanJsonParse(serializedStr); + RyanJsonFree(serializedStr); + RyanJsonCheckReturnFalse(NULL != roundtripJson); + RyanJsonCheckReturnFalse(4 == RyanJsonGetArraySize(roundtripJson)); + + idx = 0; + RyanJsonArrayForEach(roundtripJson, item) + { + RyanJsonCheckReturnFalse(RyanJsonIsBool(item)); + RyanJsonCheckReturnFalse(expected[idx] == RyanJsonGetBoolValue(item)); + idx++; + } + + RyanJsonDelete(roundtripJson); + } + + return RyanJsonTrue; +} diff --git a/test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c b/test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c new file mode 100644 index 0000000..69a29a5 --- /dev/null +++ b/test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c @@ -0,0 +1,182 @@ +#include "RyanJsonBaseTest.h" + +#define DoubleList \ + /* ========== 零值测试 ========== */ \ + X(0.0) \ + /* ========== 正负整数边界 ========== */ \ + X(1.0) \ + X(-1.0) \ + X(2.0) \ + X(-2.0) \ + X(10.0) \ + X(-10.0) \ + X(100.0) \ + X(1000.0) \ + X(10000.0) \ + X(100000.0) \ + /* ========== 简单小数(二进制精确表示) ========== */ \ + X(0.5) \ + X(-0.5) \ + X(0.25) \ + X(-0.25) \ + X(0.125) \ + X(0.0625) \ + X(0.03125) \ + X(0.015625) \ + /* ========== 常见小数 ========== */ \ + X(16.89) \ + X(-16.89) \ + X(123.456) \ + X(-123.456) \ + X(99.99) \ + X(-99.99) \ + X(1.5) \ + X(2.5) \ + X(3.5) \ + /* ========== 小于1的小数 ========== */ \ + X(0.001) \ + X(-0.001) \ + X(0.0001) \ + X(0.00001) \ + X(0.000001) \ + X(0.123456789) \ + X(0.987654321) \ + X(0.111111111111111) \ + /* ========== 大数测试 ========== */ \ + X(999999.999999) \ + X(-999999.999999) \ + X(12345678.9) \ + X(99999999.0) \ + X(123456789.123456) \ + X(9876543210.12345) \ + /* ========== 科学计数法 - 大数 ========== */ \ + X(1.5e10) \ + X(-1.5e10) \ + X(1.23e8) \ + X(9.99e12) \ + X(1.0e15) \ + X(1.0e18) \ + X(1.0e20) \ + X(5.55e15) \ + /* ========== 科学计数法 - 小数 ========== */ \ + X(1.5e-10) \ + X(-1.5e-10) \ + X(9.87e-5) \ + X(1.0e-15) \ + X(5.5e-8) \ + X(1.0e-18) \ + X(1.0e-20) \ + X(1.23e-3) \ + X(-9.87e-7) \ + /* ========== 数学常量 ========== */ \ + X(3.14159265358979) \ + X(2.71828182845904) \ + X(1.41421356237309) \ + X(1.73205080756888) \ + X(1.61803398874989) \ + X(0.69314718055994) \ + /* ========== 浮点精度经典测试 ========== */ \ + X(0.1) \ + X(0.2) \ + X(0.3) \ + X(0.6) \ + X(0.7) \ + X(0.9) \ + X(0.123456) \ + /* ========== 整数边界值 ========== */ \ + X(2147483647.0) \ + X(-2147483648.0) \ + X(4294967295.0) \ + X(9007199254740991.0) \ + X(-9007199254740991.0) \ + /* ========== 极端小值 ========== */ \ + X(1.0e-100) \ + X(-1.0e-100) \ + X(1.0e-200) \ + X(1.0e-300) \ + X(2.225073858507201e-308) \ + /* ========== 极端大值 ========== */ \ + X(1.0e100) \ + X(-1.0e100) \ + X(1.0e200) \ + X(1.0e300) \ + X(1.797693134862315e308) \ + /* ========== 特殊精度值 ========== */ \ + X(1.0000000000001) \ + X(0.9999999999999) \ + X(1.23456789012345) \ + X(9.87654321098765) \ + /* ========== 重复数字模式 ========== */ \ + X(1.1111111111111) \ + X(2.2222222222222) \ + X(9.9999999999999) \ + /* ========== 混合符号和指数 ========== */ \ + X(-1.23e-45) \ + X(-9.87e67) \ + X(1.11e-11) \ + X(-2.22e22) + +static const double DoubleValueTable[] = { +#define X(a) a, + DoubleList +#undef X +}; + +static const char *DoubleStringTable[] = { +#define X(a) "{\"double\":" #a "}", + DoubleList +#undef X +}; + +// 浮点数一致性测试 +RyanJsonBool_e RyanJsonBaseTestEqualityDouble(void) +{ + + for (uint32_t i = 0; i < sizeof(DoubleValueTable) / sizeof(DoubleValueTable[0]); i++) + { + const char *jsondoubleStr = DoubleStringTable[i]; + RyanJson_t jsonRoot = RyanJsonParse(jsondoubleStr); + RyanJsonCheckCode(NULL != jsonRoot, { + jsonLog("str: %s", jsondoubleStr); + goto err; + }); + + RyanJsonCheckReturnFalse(NULL != jsonRoot); + RyanJsonCheckReturnFalse(RyanJsonIsDouble(RyanJsonGetObjectToKey(jsonRoot, "double"))); + + // 验证解析后的数值是否正确 + double doubleValue = RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(jsonRoot, "double")); + RyanJsonCheckCode(RyanJsonCompareDouble(doubleValue, DoubleValueTable[i]), { + jsonLog("str: %s, expected: %g, got: %g", jsondoubleStr, DoubleValueTable[i], doubleValue); + RyanJsonDelete(jsonRoot); + goto err; + }); + + // 验证序列化后再解析,然后判断double是否一致(往返测试) + char *serializedStr = RyanJsonPrint(jsonRoot, 128, RyanJsonFalse, NULL); + RyanJsonCheckCode(NULL != serializedStr, { + printf("serializedStr: %s, doubleString: %s, doubleValue: %g\r\n", serializedStr, jsondoubleStr, doubleValue); + goto err; + }); + RyanJsonDelete(jsonRoot); + + RyanJson_t roundtripJson = RyanJsonParse(serializedStr); + RyanJsonFree(serializedStr); + RyanJsonCheckReturnFalse(NULL != roundtripJson); + RyanJsonCheckReturnFalse(RyanJsonIsDouble(RyanJsonGetObjectToKey(roundtripJson, "double"))); + + double roundtripValue = RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(roundtripJson, "double")); + RyanJsonCheckCode(RyanJsonCompareDouble(roundtripValue, DoubleValueTable[i]), { + jsonLog("roundtrip failed: expected: %g, got: %g ", DoubleValueTable[i], roundtripValue); + RyanJsonDelete(roundtripJson); + goto err; + }); + + RyanJsonDelete(roundtripJson); + } + + return RyanJsonTrue; + +err: + return RyanJsonFalse; +} diff --git a/test/baseTest/equality/RyanJsonBaseTestEqualityInt.c b/test/baseTest/equality/RyanJsonBaseTestEqualityInt.c new file mode 100644 index 0000000..0dcc8ca --- /dev/null +++ b/test/baseTest/equality/RyanJsonBaseTestEqualityInt.c @@ -0,0 +1,144 @@ +#include "RyanJsonBaseTest.h" + +#define IntList \ + /* ========== 零值测试 ========== */ \ + X(0) \ + /* ========== 正负边界 ========== */ \ + X(1) \ + X(-1) \ + X(2) \ + X(-2) \ + /* ========== 常见小整数 ========== */ \ + X(10) \ + X(-10) \ + X(100) \ + X(-100) \ + X(255) \ + X(-255) \ + X(256) \ + X(-256) \ + /* ========== 常见数值 ========== */ \ + X(1000) \ + X(-1000) \ + X(9999) \ + X(-9999) \ + X(12345) \ + X(-12345) \ + X(65535) \ + X(-65535) \ + X(65536) \ + X(-65536) \ + /* ========== 大整数 ========== */ \ + X(100000) \ + X(-100000) \ + X(1000000) \ + X(-1000000) \ + X(10000000) \ + X(-10000000) \ + X(100000000) \ + X(-100000000) \ + X(1000000000) \ + X(-1000000000) \ + /* ========== 8位边界 ========== */ \ + X(127) \ + X(-128) \ + /* ========== 16位边界 ========== */ \ + X(32767) \ + X(-32768) \ + /* ========== 32位边界 ========== */ \ + X(2147483647) \ + X(-2147483648) \ + /* ========== 特殊模式 ========== */ \ + X(1234567890) \ + X(-1234567890) \ + X(123456789) \ + X(-123456789) \ + /* ========== 2的幂次 ========== */ \ + X(2) \ + X(4) \ + X(8) \ + X(16) \ + X(32) \ + X(64) \ + X(128) \ + X(512) \ + X(1024) \ + X(2048) \ + X(4096) \ + X(8192) \ + X(16384) \ + X(32768) \ + X(65536) \ + X(131072) \ + X(262144) \ + X(524288) \ + X(1048576) \ + X(2097152) \ + X(4194304) \ + X(8388608) \ + X(16777216) \ + X(33554432) \ + X(67108864) \ + X(134217728) \ + X(268435456) \ + X(536870912) \ + X(1073741824) + +static const int32_t IntValueTable[] = { +#define X(a) a, + IntList +#undef X +}; + +static const char *IntStringTable[] = { +#define X(a) "{\"int\":" #a "}", + IntList +#undef X +}; + +// 整数一致性测试 +RyanJsonBool_e RyanJsonBaseTestEqualityInt(void) +{ + + for (uint32_t i = 0; i < sizeof(IntValueTable) / sizeof(IntValueTable[0]); i++) + { + const char *jsonIntStr = IntStringTable[i]; + RyanJson_t jsonRoot = RyanJsonParse(jsonIntStr); + RyanJsonCheckCode(NULL != jsonRoot, { + jsonLog("str: %s", jsonIntStr); + goto err; + }); + RyanJsonCheckReturnFalse(RyanJsonIsInt(RyanJsonGetObjectToKey(jsonRoot, "int"))); + + // 验证解析后的数值是否正确 + int32_t intValue = RyanJsonGetIntValue(RyanJsonGetObjectToKey(jsonRoot, "int")); + RyanJsonCheckCode(intValue == IntValueTable[i], { + jsonLog("str: %s, expected: %" PRId32 ", got: %" PRId32, jsonIntStr, IntValueTable[i], intValue); + RyanJsonDelete(jsonRoot); + goto err; + }); + + // 验证序列化后再解析,然后判断int是否一致(往返测试) + char *serializedStr = RyanJsonPrint(jsonRoot, 128, RyanJsonFalse, NULL); + RyanJsonDelete(jsonRoot); + + RyanJson_t roundtripJson = RyanJsonParse(serializedStr); + RyanJsonFree(serializedStr); + RyanJsonCheckReturnFalse(NULL != roundtripJson); + RyanJsonCheckReturnFalse(RyanJsonIsInt(RyanJsonGetObjectToKey(roundtripJson, "int"))); + + int32_t roundtripValue = RyanJsonGetIntValue(RyanJsonGetObjectToKey(roundtripJson, "int")); + RyanJsonCheckCode(roundtripValue == IntValueTable[i], { + jsonLog("roundtrip failed: expected: %" PRId32 ", got: %" PRId32, IntValueTable[i], roundtripValue); + RyanJsonDelete(roundtripJson); + goto err; + }); + + RyanJsonDelete(roundtripJson); + } + + return RyanJsonTrue; + +err: + return RyanJsonFalse; +} diff --git a/test/baseTest/equality/RyanJsonBaseTestEqualityString.c b/test/baseTest/equality/RyanJsonBaseTestEqualityString.c new file mode 100644 index 0000000..5f07cf7 --- /dev/null +++ b/test/baseTest/equality/RyanJsonBaseTestEqualityString.c @@ -0,0 +1,171 @@ +#include "RyanJsonBaseTest.h" + +// ========== 简单字符串(使用X-macro) ========== +#define SimpleStringList \ + X("") \ + X("hello") \ + X("world") \ + X("test") \ + X("RyanJson") \ + X("123") \ + X("0") \ + X("-1") \ + X("3.14") \ + X("1e10") \ + X("hello world") \ + X("path/to/file") \ + X("abcdefghijklmnopqrstuvwxyz") \ + X("ABCDEFGHIJKLMNOPQRSTUVWXYZ") \ + X("0123456789") \ + X("The quick brown fox jumps over the lazy dog") \ + X(" ") \ + X(" ") \ + X(" leading") \ + X("trailing ") \ + X(" both ") \ + X("true") \ + X("false") \ + X("null") \ + X("@#$%^&*()") \ + X("!@#$%") \ + X("a=b&c=d") \ + X("user@example.com") \ + X("中文测试") \ + X("日本語テスト") \ + X("한국어테스트") \ + X("混合Mixed混合") \ + X("Привет мир") \ + X("مرحبا بالعالم") \ + X("שלום עולם") + +static const char *SimpleStringValueTable[] = { +#define X(a) a, + SimpleStringList +#undef X +}; + +static const char *SimpleStringJsonTable[] = { +#define X(a) "{\"str\":\"" a "\"}", + SimpleStringList +#undef X +}; + +// ========== 转义字符(需分离JSON和值) ========== +typedef struct +{ + const char *json; // JSON字符串(带转义) + const char *expected; // 期望的C字符串值 +} EscapeTestCase; + +static const EscapeTestCase EscapeTestCases[] = { + // 制表符 + {"{\"str\":\"hello\\tworld\"}", "hello\tworld"}, + {"{\"str\":\"tab\\there\"}", "tab\there"}, + // 换行符 + {"{\"str\":\"hello\\nworld\"}", "hello\nworld"}, + {"{\"str\":\"line1\\nline2\\nline3\"}", "line1\nline2\nline3"}, + // 回车符 + {"{\"str\":\"hello\\rworld\"}", "hello\rworld"}, + // 引号转义 + {"{\"str\":\"quote\\\"inside\"}", "quote\"inside"}, + {"{\"str\":\"say \\\"hello\\\"\"}", "say \"hello\""}, + // 反斜杠转义 + {"{\"str\":\"backslash\\\\here\"}", "backslash\\here"}, + {"{\"str\":\"C:\\\\Windows\\\\System32\"}", "C:\\Windows\\System32"}, + {"{\"str\":\"path\\\\to\\\\file\"}", "path\\to\\file"}, + // 斜杠(可选转义) + {"{\"str\":\"a\\/b\"}", "a/b"}, + // 退格符 + {"{\"str\":\"back\\bspace\"}", "back\bspace"}, + // 换页符 + {"{\"str\":\"form\\ffeed\"}", "form\ffeed"}, + // 组合转义 + {"{\"str\":\"line1\\nline2\\ttab\"}", "line1\nline2\ttab"}, + {"{\"str\":\"\\\"quoted\\\" and \\\\escaped\\\\\"}", "\"quoted\" and \\escaped\\"}, + // Unicode转义 + {"{\"str\":\"\\u0048\\u0065\\u006C\\u006C\\u006F\"}", "Hello"}, + {"{\"str\":\"\\u4E2D\\u6587\"}", "中文"}, + {"{\"str\":\"euro: \\u20AC\"}", "euro: €"}, + {"{\"str\":\"smile: \\u263A\"}", "smile: ☺"}, +}; + +// 字符串一致性测试 +RyanJsonBool_e RyanJsonBaseTestEqualityString(void) +{ + // ========== 测试简单字符串 ========== + for (uint32_t i = 0; i < sizeof(SimpleStringValueTable) / sizeof(SimpleStringValueTable[0]); i++) + { + const char *jsonStrInput = SimpleStringJsonTable[i]; + RyanJson_t jsonRoot = RyanJsonParse(jsonStrInput); + RyanJsonCheckReturnFalse(NULL != jsonRoot); + RyanJsonCheckReturnFalse(RyanJsonIsString(RyanJsonGetObjectToKey(jsonRoot, "str"))); + + const char *strValue = RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "str")); + RyanJsonCheckCode(0 == strcmp(strValue, SimpleStringValueTable[i]), { + jsonLog("simple str failed: expected: %s, got: %s\n", SimpleStringValueTable[i], strValue); + RyanJsonDelete(jsonRoot); + goto err; + }); + + // 往返测试 + char *serializedStr = RyanJsonPrint(jsonRoot, 128, RyanJsonFalse, NULL); + RyanJsonDelete(jsonRoot); + + RyanJson_t roundtripJson = RyanJsonParse(serializedStr); + RyanJsonFree(serializedStr); + RyanJsonCheckReturnFalse(NULL != roundtripJson); + + const char *roundtripValue = RyanJsonGetStringValue(RyanJsonGetObjectToKey(roundtripJson, "str")); + RyanJsonCheckCode(0 == strcmp(roundtripValue, SimpleStringValueTable[i]), { + jsonLog("simple roundtrip failed: expected: %s, got: %s\n", SimpleStringValueTable[i], roundtripValue); + RyanJsonDelete(roundtripJson); + goto err; + }); + + RyanJsonDelete(roundtripJson); + } + + // ========== 测试转义字符 ========== + for (uint32_t i = 0; i < sizeof(EscapeTestCases) / sizeof(EscapeTestCases[0]); i++) + { + const EscapeTestCase *tc = &EscapeTestCases[i]; + RyanJson_t jsonRoot = RyanJsonParse(tc->json); + RyanJsonCheckCode(NULL != jsonRoot, { + jsonLog("escape parse failed: %s\n", tc->json); + goto err; + }); + RyanJsonCheckReturnFalse(RyanJsonIsString(RyanJsonGetObjectToKey(jsonRoot, "str"))); + + const char *strValue = RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "str")); + RyanJsonCheckCode(0 == strcmp(strValue, tc->expected), { + jsonLog("escape str failed: json=%s, expected=%s, got=%s\n", tc->json, tc->expected, strValue); + RyanJsonDelete(jsonRoot); + goto err; + }); + + // 往返测试 + char *serializedStr = RyanJsonPrint(jsonRoot, 128, RyanJsonFalse, NULL); + RyanJsonDelete(jsonRoot); + + RyanJson_t roundtripJson = RyanJsonParse(serializedStr); + RyanJsonFree(serializedStr); + RyanJsonCheckCode(NULL != roundtripJson, { + jsonLog("escape roundtrip parse failed\n"); + goto err; + }); + + const char *roundtripValue = RyanJsonGetStringValue(RyanJsonGetObjectToKey(roundtripJson, "str")); + RyanJsonCheckCode(0 == strcmp(roundtripValue, tc->expected), { + jsonLog("escape roundtrip failed: expected=%s, got=%s\n", tc->expected, roundtripValue); + RyanJsonDelete(roundtripJson); + goto err; + }); + + RyanJsonDelete(roundtripJson); + } + + return RyanJsonTrue; + +err: + return RyanJsonFalse; +} diff --git a/externalModule/cJSON/cJSON.c b/test/externalModule/cJSON/cJSON.c similarity index 100% rename from externalModule/cJSON/cJSON.c rename to test/externalModule/cJSON/cJSON.c diff --git a/externalModule/cJSON/cJSON.h b/test/externalModule/cJSON/cJSON.h similarity index 100% rename from externalModule/cJSON/cJSON.h rename to test/externalModule/cJSON/cJSON.h diff --git a/test/externalModule/tlsf/rtthread.h b/test/externalModule/tlsf/rtthread.h new file mode 100644 index 0000000..5455d60 --- /dev/null +++ b/test/externalModule/tlsf/rtthread.h @@ -0,0 +1,14 @@ +#pragma once + +// !这个文件仅为了tlsf的测试 +#include +#include +#include +#include +#include +#include +#include + +#define rt_memcpy memcpy +#define RT_ASSERT(EX) assert(EX) +#define rt_always_inline static inline __attribute__((always_inline)) diff --git a/test/externalModule/tlsf/tlsf.c b/test/externalModule/tlsf/tlsf.c new file mode 100644 index 0000000..65021de --- /dev/null +++ b/test/externalModule/tlsf/tlsf.c @@ -0,0 +1,702 @@ +/* + * SPDX-FileCopyrightText: 2006-2016 Matthew Conte + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include "tlsf.h" + +#undef printf +#define printf(...) + +#include "tlsf_block_functions.h" +#include "tlsf_control_functions.h" + +/* +** Static assertion mechanism. +*/ + +#define _tlsf_glue2(x, y) x##y +#define _tlsf_glue(x, y) _tlsf_glue2(x, y) +#define tlsf_static_assert(exp) typedef char _tlsf_glue(static_assert, __LINE__)[(exp) ? 1 : -1] + +/* This code has been tested on 32- and 64-bit (LP/LLP) architectures. */ +tlsf_static_assert(sizeof(int) * CHAR_BIT == 32); +tlsf_static_assert(sizeof(size_t) * CHAR_BIT >= 32); +tlsf_static_assert(sizeof(size_t) * CHAR_BIT <= 64); + +/* Clear structure and point all empty lists at the null block. */ +static control_t *control_construct(control_t *control, size_t bytes) +{ + // check that the requested size can at least hold the control_t. This will allow us + // to fill in the field of control_t necessary to determine the final size of + // the metadata overhead and check that the requested size can hold + // this data and at least a block of minimum size + if (bytes < sizeof(control_t)) { return NULL; } + + /* Find the closest power of two for first layer */ + control->fl_index_max = 32 - __builtin_clz(bytes); + + /* Adapt second layer to the pool */ + if (bytes <= 64 * 1024) { control->sl_index_count_log2 = 3; } + else if (bytes <= 256 * 1024) { control->sl_index_count_log2 = 4; } + else + { + control->sl_index_count_log2 = 5; + } + + control->fl_index_shift = (control->sl_index_count_log2 + ALIGN_SIZE_LOG2); + control->sl_index_count = 1 << control->sl_index_count_log2; + control->fl_index_count = control->fl_index_max - control->fl_index_shift + 1; + control->small_block_size = 1 << control->fl_index_shift; + + // the total size fo the metadata overhead is the size of the control_t + // added to the size of the sl_bitmaps and the size of blocks + control->size = sizeof(control_t) + (sizeof(*control->sl_bitmap) * control->fl_index_count) + + (sizeof(*control->blocks) * (control->fl_index_count * control->sl_index_count)); + + // check that the requested size can hold the whole control structure and + // a small block at least + if (bytes < control->size + block_size_min) { return NULL; } + + control->block_null.next_free = &control->block_null; + control->block_null.prev_free = &control->block_null; + + control->fl_bitmap = 0; + control->sl_bitmap = align_ptr(control + 1, sizeof(*control->sl_bitmap)); + control->blocks = align_ptr(control->sl_bitmap + control->fl_index_count, sizeof(*control->blocks)); + + /* SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage type. */ + tlsf_assert(sizeof(unsigned int) * CHAR_BIT >= control->sl_index_count && "CHAR_BIT less than sl_index_count"); + + /* Ensure we've properly tuned our sizes. */ + tlsf_assert(ALIGN_SIZE == control->small_block_size / control->sl_index_count); // ALIGN_SIZE does not match"); + + for (int i = 0; i < control->fl_index_count; ++i) + { + control->sl_bitmap[i] = 0; + for (int j = 0; j < control->sl_index_count; ++j) + { + control->blocks[i * control->sl_index_count + j] = &control->block_null; + } + } + + return control; +} + +/* +** Debugging utilities. +*/ + +typedef struct integrity_t +{ + int prev_status; + int status; +} integrity_t; + +#define tlsf_insist(x) \ + { \ + if (!(x)) { status--; } \ + } + +static bool integrity_walker(void *ptr, size_t size, int used, void *user) +{ + block_header_t *block = block_from_ptr(ptr); + integrity_t *integ = tlsf_cast(integrity_t *, user); + const int this_prev_status = block_is_prev_free(block) ? 1 : 0; + const int this_status = block_is_free(block) ? 1 : 0; + const size_t this_block_size = block_size(block); + + int status = 0; + tlsf_insist(integ->prev_status == this_prev_status && "prev status incorrect"); + tlsf_insist(size == this_block_size && "block size incorrect"); + + if (tlsf_check_hook != NULL) + { + /* block_size(block) returns the size of the usable memory when the block is allocated. + * As the block under test is free, we need to subtract to the block size the next_free + * and prev_free fields of the block header as they are not a part of the usable memory + * when the block is free. In addition, we also need to subtract the size of prev_phys_block + * as this field is in fact part of the current free block and not part of the next (allocated) + * block. Check the comments in block_split function for more details. + */ + const size_t actual_free_block_size = + used ? this_block_size : this_block_size - offsetof(block_header_t, next_free) - block_header_overhead; + + void *ptr_block = used ? (void *)block + block_start_offset : (void *)block + sizeof(block_header_t); + + tlsf_insist(tlsf_check_hook(ptr_block, actual_free_block_size, !used)); + } + + integ->prev_status = this_status; + integ->status += status; + + return true; +} + +int tlsf_check(tlsf_t tlsf) +{ + int i, j; + + control_t *control = tlsf_cast(control_t *, tlsf); + int status = 0; + + /* Check that the free lists and bitmaps are accurate. */ + for (i = 0; i < control->fl_index_count; ++i) + { + for (j = 0; j < control->sl_index_count; ++j) + { + const int fl_map = control->fl_bitmap & (1U << i); + const int sl_list = control->sl_bitmap[i]; + const int sl_map = sl_list & (1U << j); + const block_header_t *block = control->blocks[i * control->sl_index_count + j]; + + /* Check that first- and second-level lists agree. */ + if (!fl_map) { tlsf_insist(!sl_map && "second-level map must be null"); } + + if (!sl_map) + { + tlsf_insist(block == &control->block_null && "block list must be null"); + continue; + } + + /* Check that there is at least one free block. */ + tlsf_insist(sl_list && "no free blocks in second-level map"); + tlsf_insist(block != &control->block_null && "block should not be null"); + + while (block != &control->block_null) + { + int fli, sli; + const bool is_block_free = block_is_free(block); + tlsf_insist(is_block_free && "block should be free"); + tlsf_insist(!block_is_prev_free(block) && "blocks should have coalesced"); + tlsf_insist(!block_is_free(block_next(block)) && "blocks should have coalesced"); + tlsf_insist(block_is_prev_free(block_next(block)) && "block should be free"); + tlsf_insist(block_size(block) >= block_size_min && "block not minimum size"); + + mapping_insert(control, block_size(block), &fli, &sli); + tlsf_insist(fli == i && sli == j && "block size indexed in wrong list"); + + block = block->next_free; + } + } + } + + return status; +} + +#undef tlsf_insist + +static bool default_walker(void *ptr, size_t size, int used, void *user) +{ + (void)user; + printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, block_from_ptr(ptr)); + return true; +} + +void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void *user) +{ + tlsf_walker pool_walker = walker ? walker : default_walker; + block_header_t *block = offset_to_block(pool, -(int)block_header_overhead); + + bool ret_val = true; + while (block && !block_is_last(block) && ret_val == true) + { + ret_val = pool_walker(block_to_ptr(block), block_size(block), !block_is_free(block), user); + + if (ret_val == true) { block = block_next(block); } + } +} + +size_t tlsf_block_size(void *ptr) +{ + size_t size = 0; + if (ptr) + { + const block_header_t *block = block_from_ptr(ptr); + size = block_size(block); + } + return size; +} + +int tlsf_check_pool(pool_t pool) +{ + /* Check that the blocks are physically correct. */ + integrity_t integ = {0, 0}; + tlsf_walk_pool(pool, integrity_walker, &integ); + + return integ.status; +} + +size_t tlsf_fit_size(tlsf_t tlsf, size_t size) +{ + if (size == 0 || tlsf == NULL) { return 0; } + + control_t *control = tlsf_cast(control_t *, tlsf); + if (size < control->small_block_size) { return adjust_request_size(tlsf, size, ALIGN_SIZE); } + + /* because it's GoodFit, allocable size is one range lower */ + size_t sl_interval; + sl_interval = (1 << (32 - __builtin_clz(size) - 1)) / control->sl_index_count; + return size & ~(sl_interval - 1); +} + +/* +** Size of the TLSF structures in a given memory block passed to +** tlsf_create, equal to the size of a control_t +*/ +size_t tlsf_size(tlsf_t tlsf) +{ + if (tlsf == NULL) { return 0; } + control_t *control = tlsf_cast(control_t *, tlsf); + return control->size; +} + +/* +** Overhead of the TLSF structures in a given memory block passed to +** tlsf_add_pool, equal to the overhead of a free block and the +** sentinel block. +*/ +size_t tlsf_pool_overhead(void) { return 2 * block_header_overhead; } + +size_t tlsf_alloc_overhead(void) { return block_header_overhead; } + +pool_t tlsf_add_pool(tlsf_t tlsf, void *mem, size_t bytes) +{ + block_header_t *block; + block_header_t *next; + + const size_t pool_overhead = tlsf_pool_overhead(); + const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE); + + if (((ptrdiff_t)mem % ALIGN_SIZE) != 0) + { + printf("tlsf_add_pool: Memory must be aligned by %u bytes.\n", (unsigned int)ALIGN_SIZE); + return 0; + } + + if (pool_bytes < block_size_min || pool_bytes > tlsf_block_size_max(tlsf)) + { +#if defined(TLSF_64BIT) + printf("tlsf_add_pool: Memory size must be between 0x%x and 0x%x00 bytes.\n", + (unsigned int)(pool_overhead + block_size_min), (unsigned int)((pool_overhead + tlsf_block_size_max(tlsf)) / 256)); +#else + printf("tlsf_add_pool: Memory size must be between %u and %u bytes.\n", (unsigned int)(pool_overhead + block_size_min), + (unsigned int)(pool_overhead + tlsf_block_size_max(tlsf))); +#endif + return 0; + } + + /* + ** Create the main free block. Offset the start of the block slightly + ** so that the prev_phys_block field falls outside of the pool - + ** it will never be used. + */ + block = offset_to_block(mem, -(tlsfptr_t)block_header_overhead); + block_set_size(block, pool_bytes); + block_set_free(block); + block_set_prev_used(block); + block_insert(tlsf_cast(control_t *, tlsf), block); + + /* Split the block to create a zero-size sentinel block. */ + next = block_link_next(block); + block_set_size(next, 0); + block_set_used(next); + block_set_prev_free(next); + + return mem; +} + +void tlsf_remove_pool(tlsf_t tlsf, pool_t pool) +{ + control_t *control = tlsf_cast(control_t *, tlsf); + block_header_t *block = offset_to_block(pool, -(int)block_header_overhead); + + int fl = 0, sl = 0; + + tlsf_assert(block_is_free(block) && "block should be free"); + tlsf_assert(!block_is_free(block_next(block)) && "next block should not be free"); + tlsf_assert(block_size(block_next(block)) == 0 && "next block size should be zero"); + + mapping_insert(control, block_size(block), &fl, &sl); + remove_free_block(control, block, fl, sl); +} + +/* +** TLSF main interface. +*/ + +#if _DEBUG +int test_ffs_fls() +{ + /* Verify ffs/fls work properly. */ + int rv = 0; + rv += (tlsf_ffs(0) == -1) ? 0 : 0x1; + rv += (tlsf_fls(0) == -1) ? 0 : 0x2; + rv += (tlsf_ffs(1) == 0) ? 0 : 0x4; + rv += (tlsf_fls(1) == 0) ? 0 : 0x8; + rv += (tlsf_ffs(0x80000000) == 31) ? 0 : 0x10; + rv += (tlsf_ffs(0x80008000) == 15) ? 0 : 0x20; + rv += (tlsf_fls(0x80000008) == 31) ? 0 : 0x40; + rv += (tlsf_fls(0x7FFFFFFF) == 30) ? 0 : 0x80; + +#if defined(TLSF_64BIT) + rv += (tlsf_fls_sizet(0x80000000) == 31) ? 0 : 0x100; + rv += (tlsf_fls_sizet(0x100000000) == 32) ? 0 : 0x200; + rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400; +#endif + + if (rv) { printf("test_ffs_fls: %x ffs/fls tests failed.\n", rv); } + return rv; +} +#endif + +tlsf_t tlsf_create(void *mem, size_t max_bytes) +{ +#if _DEBUG + if (test_ffs_fls()) { return NULL; } +#endif + + if (mem == NULL) { return NULL; } + + if (((tlsfptr_t)mem % ALIGN_SIZE) != 0) + { + printf("tlsf_create: Memory must be aligned to %u bytes.\n", (unsigned int)ALIGN_SIZE); + return NULL; + } + + control_t *control_ptr = control_construct(tlsf_cast(control_t *, mem), max_bytes); + return tlsf_cast(tlsf_t, control_ptr); +} + +tlsf_t tlsf_create_with_pool(void *mem, size_t pool_bytes, size_t max_bytes) +{ + tlsf_t tlsf = tlsf_create(mem, max_bytes ? max_bytes : pool_bytes); + if (tlsf != NULL) + { + tlsf_add_pool(tlsf, (char *)mem + tlsf_size(tlsf), pool_bytes - tlsf_size(tlsf)); + control_t *control = tlsf_cast(control_t *, tlsf); + control->mem_rec.total = pool_bytes - tlsf_size(tlsf) - tlsf_pool_overhead(); + control->mem_rec.used = 0; + control->mem_rec.max_used = 0; + } + return tlsf; +} + +void tlsf_destroy(tlsf_t tlsf) +{ + /* Nothing to do. */ + (void)tlsf; +} + +pool_t tlsf_get_pool(tlsf_t tlsf) { return tlsf_cast(pool_t, (char *)tlsf + tlsf_size(tlsf)); } + +void *tlsf_malloc(tlsf_t tlsf, size_t size) +{ + control_t *control = tlsf_cast(control_t *, tlsf); + size_t adjust = adjust_request_size(tlsf, size, ALIGN_SIZE); + // Returned size is 0 when the requested size is larger than the max block + // size. + if (adjust == 0) { return NULL; } + // block_locate_free() may adjust our allocated size further. + block_header_t *block = block_locate_free(control, &adjust); + return block_prepare_used(control, block, adjust); +} + +/** + * @brief Allocate memory of at least `size` bytes at a given address in the pool. + * + * @param tlsf TLSF structure to allocate memory from. + * @param size Minimum size, in bytes, of the memory to allocate + * @param address address at which the allocation must be done + * + * @return pointer to free memory or NULL in case of incapacity to perform the malloc + */ +void *tlsf_malloc_addr(tlsf_t tlsf, size_t size, void *address) +{ + control_t *control = tlsf_cast(control_t *, tlsf); + + /* adjust the address to be ALIGN_SIZE bytes aligned. */ + const uintptr_t addr_adjusted = align_down(tlsf_cast(uintptr_t, address), ALIGN_SIZE); + + /* adjust the size to be ALIGN_SIZE bytes aligned. Add to the size the difference + * between the requested address and the address_adjusted. */ + size_t size_adjusted = align_up(size + (tlsf_cast(uintptr_t, address) - addr_adjusted), ALIGN_SIZE); + + /* find the free block that starts before the address in the pool and is big enough + * to support the size of allocation at the given address */ + block_header_t *block = offset_to_block(tlsf_get_pool(tlsf), -(int)block_header_overhead); + + const char *alloc_start = tlsf_cast(char *, addr_adjusted); + const char *alloc_end = alloc_start + size_adjusted; + bool block_found = false; + do + { + const char *block_start = tlsf_cast(char *, block_to_ptr(block)); + const char *block_end = tlsf_cast(char *, block_to_ptr(block)) + block_size(block); + if (block_start <= alloc_start && block_end > alloc_start) + { + /* A: block_end >= alloc_end. B: block is free */ + if (block_end < alloc_end || !block_is_free(block)) + { + /* not(A) || not(B) + * We won't find another suitable block from this point on + * so we can break and return NULL */ + break; + } + /* A && B + * The block can fit the alloc and is located at a position allowing for the alloc + * to be placed at the given address. We can return from the while */ + block_found = true; + } + else if (!block_is_last(block)) + { + /* the block doesn't match the expected criteria, continue with the next block */ + block = block_next(block); + } + + } while (!block_is_last(block) && block_found == false); + + if (!block_found) { return NULL; } + + /* remove block from the free list since a part of it will be used */ + block_remove(control, block); + + /* trim any leading space or add the leading space to the overall requested size + * if the leading space is not big enough to store a block of minimum size */ + const size_t space_before_addr_adjusted = addr_adjusted - tlsf_cast(uintptr_t, block_to_ptr(block)); + block_header_t *return_block = block; + if (space_before_addr_adjusted >= block_size_min) + { + return_block = block_trim_free_leading(control, block, space_before_addr_adjusted); + } + else + { + size_adjusted += space_before_addr_adjusted; + } + + /* trim trailing space if any and return a pointer to the first usable byte allocated */ + return block_prepare_used(control, return_block, size_adjusted); +} + +/** + * @brief Allocate memory of at least `size` bytes where byte at `data_offset` will be aligned to `alignment`. + * + * This function will allocate memory pointed by `ptr`. However, the byte at `data_offset` of + * this piece of memory (i.e., byte at `ptr` + `data_offset`) will be aligned to `alignment`. + * This function is useful for allocating memory that will internally have a header, and the + * usable memory following the header (i.e. `ptr` + `data_offset`) must be aligned. + * + * For example, a call to `multi_heap_aligned_alloc_impl_offs(heap, 64, 256, 20)` will return a + * pointer `ptr` to free memory of minimum 64 bytes, where `ptr + 20` is aligned on `256`. + * So `(ptr + 20) % 256` equals 0. + * + * @param tlsf TLSF structure to allocate memory from. + * @param align Alignment for the returned pointer's offset. + * @param size Minimum size, in bytes, of the memory to allocate INCLUDING + * `data_offset` bytes. + * @param data_offset Offset to be aligned on `alignment`. This can be 0, in + * this case, the returned pointer will be aligned on + * `alignment`. If it is not a multiple of CPU word size, + * it will be aligned up to the closest multiple of it. + * + * @return pointer to free memory. + */ +void *tlsf_memalign_offs(tlsf_t tlsf, size_t align, size_t size, size_t data_offset) +{ + control_t *control = tlsf_cast(control_t *, tlsf); + const size_t adjust = adjust_request_size(tlsf, size, ALIGN_SIZE); + const size_t off_adjust = align_up(data_offset, ALIGN_SIZE); + + /* + ** We must allocate an additional minimum block size bytes so that if + ** our free block will leave an alignment gap which is smaller, we can + ** trim a leading free block and release it back to the pool. We must + ** do this because the previous physical block is in use, therefore + ** the prev_phys_block field is not valid, and we can't simply adjust + ** the size of that block. + */ + const size_t gap_minimum = sizeof(block_header_t) + off_adjust; + /* The offset is included in both `adjust` and `gap_minimum`, so we + ** need to subtract it once. + */ + const size_t size_with_gap = adjust_request_size(tlsf, adjust + align + gap_minimum - off_adjust, align); + + /* + ** If alignment is less than or equal to base alignment, we're done, because + ** we are guaranteed that the size is at least sizeof(block_header_t), enough + ** to store next blocks' metadata. Plus, all pointers allocated will all be + ** aligned on a 4-byte bound, so ptr + data_offset will also have this + ** alignment constraint. Thus, the gap is not required. + ** If we requested 0 bytes, return null, as tlsf_malloc(0) does. + */ + size_t aligned_size = (adjust && align > ALIGN_SIZE) ? size_with_gap : adjust; + + block_header_t *block = block_locate_free(control, &aligned_size); + + /* This can't be a static assert. */ + tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead); + + if (block) + { + void *ptr = block_to_ptr(block); + void *aligned = align_ptr(ptr, align); + size_t gap = tlsf_cast(size_t, tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); + + /* + ** If gap size is too small or if there is no gap but we need one, + ** offset to next aligned boundary. + ** NOTE: No need for a gap if the alignment required is less than or is + ** equal to ALIGN_SIZE. + */ + if ((gap && gap < gap_minimum) || (!gap && off_adjust && align > ALIGN_SIZE)) + { + const size_t gap_remain = gap_minimum - gap; + const size_t offset = tlsf_max(gap_remain, align); + const void *next_aligned = tlsf_cast(void *, tlsf_cast(tlsfptr_t, aligned) + offset); + + aligned = align_ptr(next_aligned, align); + gap = tlsf_cast(size_t, tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); + } + + if (gap) + { + tlsf_assert(gap >= gap_minimum && "gap size too small"); + block = block_trim_free_leading(control, block, gap - off_adjust); + } + } + + /* Preparing the block will also the trailing free memory. */ + return block_prepare_used(control, block, adjust); +} + +/** + * @brief Same as `tlsf_memalign_offs` function but with a 0 offset. + * The pointer returned is aligned on `align`. + */ +void *tlsf_memalign(tlsf_t tlsf, size_t align, size_t size) { return tlsf_memalign_offs(tlsf, align, size, 0); } +void rt_memory_info22(tlsf_t tlsf, size_t *total, size_t *used, size_t *max_used) +{ + control_t *control = tlsf_cast(control_t *, tlsf); + if (total) { *total = control->mem_rec.total; } + if (used) { *used = control->mem_rec.used; } + if (max_used) { *max_used = control->mem_rec.max_used; } +} +void tlsf_free(tlsf_t tlsf, void *ptr) +{ + /* Don't attempt to free a NULL pointer. */ + if (ptr) + { + control_t *control = tlsf_cast(control_t *, tlsf); + block_header_t *block = block_from_ptr(ptr); + tlsf_assert(!block_is_free(block) && "block already marked as free"); + + control->mem_rec.used -= (block_size(block) + tlsf_alloc_overhead()); + + block_mark_as_free(block); + block = block_merge_prev(control, block); + block = block_merge_next(control, block); + block_insert(control, block); + } +} + +/* +** The TLSF block information provides us with enough information to +** provide a reasonably intelligent implementation of realloc, growing or +** shrinking the currently allocated block as required. +** +** This routine handles the somewhat esoteric edge cases of realloc: +** - a non-zero size with a null pointer will behave like malloc +** - a zero size with a non-null pointer will behave like free +** - a request that cannot be satisfied will leave the original buffer +** untouched +** - an extended buffer size will leave the newly-allocated area with +** contents undefined +*/ +void *tlsf_realloc(tlsf_t tlsf, void *ptr, size_t size) +{ + control_t *control = tlsf_cast(control_t *, tlsf); + void *p = 0; + + /* Zero-size requests are treated as free. */ + if (ptr && size == 0) { tlsf_free(tlsf, ptr); } + /* Requests with NULL pointers are treated as malloc. */ + else if (!ptr) { p = tlsf_malloc(tlsf, size); } + else + { + block_header_t *block = block_from_ptr(ptr); + block_header_t *next = block_next(block); + + const size_t cursize = block_size(block); + const size_t combined = cursize + block_size(next) + block_header_overhead; + const size_t adjust = adjust_request_size(tlsf, size, ALIGN_SIZE); + + // if adjust if equal to 0, the size is too big + if (adjust == 0) { return p; } + + tlsf_assert(!block_is_free(block) && "block already marked as free"); + + /* + ** If the next block is used, or when combined with the current + ** block, does not offer enough space, we must reallocate and copy. + */ + if (adjust > cursize && (!block_is_free(next) || adjust > combined)) + { + p = tlsf_malloc(tlsf, size); + if (p) + { + const size_t minsize = tlsf_min(cursize, size); + rt_memcpy(p, ptr, minsize); + tlsf_free(tlsf, ptr); + } + } + else + { + /* Do we need to expand to the next block? */ + if (adjust > cursize) + { + block_merge_next(control, block); + block_mark_as_used(block); + } + + /* Trim the resulting block and return the original pointer. */ + block_trim_used(control, block, adjust); + p = ptr; + + /* 更新统计信息:原地调整时需要修正 used */ + control->mem_rec.used += block_size(block); + control->mem_rec.used -= cursize; + + if(control->mem_rec.used > control->mem_rec.max_used) + control->mem_rec.max_used = control->mem_rec.used; + } + } + + return p; +} + +void *tlsf_find_containing_block(pool_t pool, void *ptr) +{ + block_header_t *block = offset_to_block(pool, -(int)block_header_overhead); + + while (block && !block_is_last(block)) + { + if (!block_is_free(block)) + { + void *block_end = block_to_ptr(block) + block_size(block); + if (block_to_ptr(block) <= ptr && block_end > ptr) + { + // we found the containing block, return + return block_to_ptr(block); + } + } + + block = block_next(block); + } + + return NULL; +} diff --git a/test/externalModule/tlsf/tlsf.h b/test/externalModule/tlsf/tlsf.h new file mode 100644 index 0000000..39e1367 --- /dev/null +++ b/test/externalModule/tlsf/tlsf.h @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2006-2016 Matthew Conte + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef INCLUDED_tlsf +#define INCLUDED_tlsf + +#include +#include +#include +#include "rtthread.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* tlsf_t: a TLSF structure. Can contain 1 to N pools. */ +/* pool_t: a block of memory that TLSF can manage. */ +typedef void* tlsf_t; +typedef void* pool_t; + +/* Create/destroy a memory pool. */ +tlsf_t tlsf_create(void* mem, size_t max_bytes); +tlsf_t tlsf_create_with_pool(void* mem, size_t pool_bytes, size_t max_bytes); +void tlsf_destroy(tlsf_t tlsf); +pool_t tlsf_get_pool(tlsf_t tlsf); + +/* Add/remove memory pools. */ +pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes); +void tlsf_remove_pool(tlsf_t tlsf, pool_t pool); + +/* malloc/memalign/realloc/free replacements. */ +void* tlsf_malloc(tlsf_t tlsf, size_t size); +void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size); +void* tlsf_memalign_offs(tlsf_t tlsf, size_t align, size_t size, size_t offset); +void* tlsf_malloc_addr(tlsf_t tlsf, size_t size, void *address); +void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size); +void tlsf_free(tlsf_t tlsf, void* ptr); + +/* Returns internal block size, not original request size */ +size_t tlsf_block_size(void* ptr); + +/* Overheads/limits of internal structures. */ +size_t tlsf_size(tlsf_t tlsf); +size_t tlsf_pool_overhead(void); +size_t tlsf_alloc_overhead(void); + +void rt_memory_info22(tlsf_t tlsf, size_t *total, size_t *used, size_t *max_used); + +/** + * @brief Return the allocable size based on the size passed + * as parameter + * + * @param tlsf Pointer to the tlsf structure + * @param size The allocation size + * @return size_t The updated allocation size + */ +size_t tlsf_fit_size(tlsf_t tlsf, size_t size); + +/* Debugging. */ +typedef bool (*tlsf_walker)(void* ptr, size_t size, int used, void* user); +void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user); +/* Returns nonzero if any internal consistency check fails. */ +int tlsf_check(tlsf_t tlsf); +int tlsf_check_pool(pool_t pool); + +/** + * @brief Find the block containing the pointer passed as parameter + * + * @param pool The pool into which to look for the block + * @param ptr The pointer we want to find the containing block of + * @return void* The pointer to the containing block if found, NULL if not. + */ +void* tlsf_find_containing_block(pool_t pool, void *ptr); + +/** + * @brief Weak function called on every free block of memory allowing the user to implement + * application specific checks on the memory. + * + * @param start The start pointer to the memory of a block + * @param size The size of the memory in the block + * @param is_free Set to true when the memory belongs to a free block. + * False if it belongs to an allocated block. + * @return true The checks found no inconsistency in the memory + * @return false The checks in the function highlighted an inconsistency in the memory + */ +__attribute__((weak)) bool tlsf_check_hook(void *start, size_t size, bool is_free); + +#if defined(__cplusplus) +}; +#endif + +#endif diff --git a/test/externalModule/tlsf/tlsf_block_functions.h b/test/externalModule/tlsf/tlsf_block_functions.h new file mode 100644 index 0000000..8d44abc --- /dev/null +++ b/test/externalModule/tlsf/tlsf_block_functions.h @@ -0,0 +1,161 @@ +/* + * SPDX-FileCopyrightText: 2006-2016 Matthew Conte + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#if defined(__cplusplus) +extern "C" { +#endif + +/* +** Constants definition for poisoning. +** These defines are used as 3rd argument of tlsf_poison_fill_region() for readability purposes. +*/ +#define POISONING_AFTER_FREE true +#define POISONING_AFTER_MALLOC !POISONING_AFTER_FREE + +/* A type used for casting when doing pointer arithmetic. */ +typedef ptrdiff_t tlsfptr_t; + +/* +** Cast and min/max macros. +*/ +#if !defined(tlsf_cast) +#define tlsf_cast(t, exp) ((t)(exp)) +#endif +#if !defined(tlsf_min) +#define tlsf_min(a, b) ((a) < (b) ? (a) : (b)) +#endif +#if !defined(tlsf_max) +#define tlsf_max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* +** Set assert macro, if it has not been provided by the user. +*/ +#if !defined(tlsf_assert) +#define tlsf_assert RT_ASSERT +#endif + +typedef struct block_header_t +{ + /* Points to the previous physical block. */ + struct block_header_t *prev_phys_block; + + /* The size of this block, excluding the block header. */ + size_t size; + + /* Next and previous free blocks. */ + struct block_header_t *next_free; + struct block_header_t *prev_free; +} block_header_t; + +/* User data starts directly after the size field in a used block. */ +#define block_start_offset (offsetof(block_header_t, size) + sizeof(size_t)) + +/* +** A free block must be large enough to store its header minus the size of +** the prev_phys_block field, and no larger than the number of addressable +** bits for FL_INDEX. +*/ +#define block_size_min (sizeof(block_header_t) - sizeof(block_header_t *)) + +/* +** Since block sizes are always at least a multiple of 4, the two least +** significant bits of the size field are used to store the block status: +** - bit 0: whether block is busy or free +** - bit 1: whether previous block is busy or free +*/ +#define block_header_free_bit (1UL << 0) +#define block_header_prev_free_bit (1UL << 1) + +/* +** The size of the block header exposed to used blocks is the size field. +** The prev_phys_block field is stored *inside* the previous free block. +*/ +#define block_header_overhead (sizeof(size_t)) + +/* +** block_header_t member functions. +*/ +#define tlsf_decl rt_always_inline + +tlsf_decl size_t block_size(const block_header_t *block) +{ return block->size & ~(block_header_free_bit | block_header_prev_free_bit); } + +tlsf_decl void block_set_size(block_header_t *block, size_t size) +{ + const size_t oldsize = block->size; + block->size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit)); +} + +tlsf_decl int block_is_last(const block_header_t *block) { return block_size(block) == 0; } + +tlsf_decl int block_is_free(const block_header_t *block) +{ return tlsf_cast(int, block->size &block_header_free_bit); } + +tlsf_decl void block_set_free(block_header_t *block) { block->size |= block_header_free_bit; } + +tlsf_decl void block_set_used(block_header_t *block) { block->size &= ~block_header_free_bit; } + +tlsf_decl int block_is_prev_free(const block_header_t *block) +{ return tlsf_cast(int, block->size &block_header_prev_free_bit); } + +tlsf_decl void block_set_prev_free(block_header_t *block) { block->size |= block_header_prev_free_bit; } + +tlsf_decl void block_set_prev_used(block_header_t *block) { block->size &= ~block_header_prev_free_bit; } + +tlsf_decl block_header_t *block_from_ptr(const void *ptr) +{ return tlsf_cast(block_header_t *, tlsf_cast(unsigned char *, ptr) - block_start_offset); } + +tlsf_decl void *block_to_ptr(const block_header_t *block) +{ return tlsf_cast(void *, tlsf_cast(unsigned char *, block) + block_start_offset); } + +/* Return location of next block after block of given size. */ +tlsf_decl block_header_t *offset_to_block(const void *ptr, size_t size) +{ return tlsf_cast(block_header_t *, tlsf_cast(tlsfptr_t, ptr) + size); } + +/* Return location of previous block. */ +tlsf_decl block_header_t *block_prev(const block_header_t *block) +{ + tlsf_assert(block_is_prev_free(block) && "previous block must be free"); + return block->prev_phys_block; +} + +/* Return location of next existing block. */ +tlsf_decl block_header_t *block_next(const block_header_t *block) +{ + block_header_t *next = offset_to_block(block_to_ptr(block), block_size(block) - block_header_overhead); + tlsf_assert(!block_is_last(block)); + return next; +} + +/* Link a new block with its physical neighbor, return the neighbor. */ +tlsf_decl block_header_t *block_link_next(block_header_t *block) +{ + block_header_t *next = block_next(block); + next->prev_phys_block = block; + return next; +} + +tlsf_decl void block_mark_as_free(block_header_t *block) +{ + /* Link the block to the next block, first. */ + block_header_t *next = block_link_next(block); + block_set_prev_free(next); + block_set_free(block); +} + +tlsf_decl void block_mark_as_used(block_header_t *block) +{ + block_header_t *next = block_next(block); + block_set_prev_used(next); + block_set_used(block); +} + +#if defined(__cplusplus) +}; +#endif diff --git a/test/externalModule/tlsf/tlsf_control_functions.h b/test/externalModule/tlsf/tlsf_control_functions.h new file mode 100644 index 0000000..04d503a --- /dev/null +++ b/test/externalModule/tlsf/tlsf_control_functions.h @@ -0,0 +1,630 @@ +/* + * SPDX-FileCopyrightText: 2024 Matthew Conte + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once +#include "tlsf_block_functions.h" + +#if defined(__cplusplus) +extern "C" { +#define tlsf_decl static inline +#else +#define tlsf_decl rt_always_inline +#endif + +enum tlsf_config +{ + /* All allocation sizes and addresses are aligned to 4 bytes. */ + ALIGN_SIZE_LOG2 = 2, + ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2), +}; + +typedef struct +{ + int32_t total; + int32_t used; + int32_t max_used; +} mem_record_t; + +/* The TLSF control structure. */ +typedef struct control_t +{ + /* Empty lists point at this block to indicate they are free. */ + block_header_t block_null; + + /* Local parameter for the pool. Given the maximum + * value of each field, all the following parameters + * can fit on 4 bytes when using bitfields + */ + unsigned int fl_index_count: 5; // 5 cumulated bits + unsigned int fl_index_shift: 3; // 8 cumulated bits + unsigned int fl_index_max: 6; // 14 cumulated bits + unsigned int sl_index_count: 6; // 20 cumulated bits + + /* log2 of number of linear subdivisions of block sizes. Larger + ** values require more memory in the control structure. Values of + ** 4 or 5 are typical. + */ + unsigned int sl_index_count_log2: 3; // 23 cumulated bits + unsigned int small_block_size: 8; // 31 cumulated bits + + /* size of the metadata ( size of control block, + * sl_bitmap and blocks ) + */ + size_t size; + + /* Bitmaps for free lists. */ + unsigned int fl_bitmap; + unsigned int *sl_bitmap; + + /* Head of free lists. */ + block_header_t **blocks; + mem_record_t mem_rec; +} control_t; + +/* +** Architecture-specific bit manipulation routines. +** +** TLSF achieves O(1) cost for malloc and free operations by limiting +** the search for a free block to a free list of guaranteed size +** adequate to fulfill the request, combined with efficient free list +** queries using bitmasks and architecture-specific bit-manipulation +** routines. +** +** Most modern processors provide instructions to count leading zeroes +** in a word, find the lowest and highest set bit, etc. These +** specific implementations will be used when available, falling back +** to a reasonably efficient generic implementation. +** +** NOTE: TLSF spec relies on ffs/fls returning value 0..31. +** ffs/fls return 1-32 by default, returning 0 for error. +*/ + +/* +** Detect whether or not we are building for a 32- or 64-bit (LP/LLP) +** architecture. There is no reliable portable method at compile-time. +*/ +#if defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) || defined(_WIN64) || defined(__LP64__) || defined(__LLP64__) +#define TLSF_64BIT +#endif + +/* +** gcc 3.4 and above have builtin support, specialized for architecture. +** Some compilers masquerade as gcc; patchlevel test filters them out. +*/ +#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__GNUC_PATCHLEVEL__) + +#if defined(__SNC__) +/* SNC for Playstation 3. */ + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __builtin_clz(reverse); + return bit - 1; +} + +#else + +tlsf_decl int tlsf_ffs(unsigned int word) { return __builtin_ffs(word) - 1; } + +#endif + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __builtin_clz(word) : 0; + return bit - 1; +} + +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64)) +/* Microsoft Visual C++ support on x86/X64 architectures. */ + +#include + +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) + +tlsf_decl int tlsf_fls(unsigned int word) +{ + unsigned long index; + return _BitScanReverse(&index, word) ? index : -1; +} + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + unsigned long index; + return _BitScanForward(&index, word) ? index : -1; +} + +#elif defined(_MSC_VER) && defined(_M_PPC) +/* Microsoft Visual C++ support on PowerPC architectures. */ + +#include + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = 32 - _CountLeadingZeros(word); + return bit - 1; +} + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - _CountLeadingZeros(reverse); + return bit - 1; +} + +#elif defined(__ARMCC_VERSION) +/* RealView Compilation Tools for ARM */ + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __clz(reverse); + return bit - 1; +} + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __clz(word) : 0; + return bit - 1; +} + +#elif defined(__ghs__) +/* Green Hills support for PowerPC */ + +#include + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __CLZ32(reverse); + return bit - 1; +} + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __CLZ32(word) : 0; + return bit - 1; +} + +#else +/* Fall back to generic implementation. */ + +tlsf_decl int tlsf_fls_generic(unsigned int word) +{ + int bit = 32; + + if (!word) { bit -= 1; } + if (!(word & 0xffff0000)) + { + word <<= 16; + bit -= 16; + } + if (!(word & 0xff000000)) + { + word <<= 8; + bit -= 8; + } + if (!(word & 0xf0000000)) + { + word <<= 4; + bit -= 4; + } + if (!(word & 0xc0000000)) + { + word <<= 2; + bit -= 2; + } + if (!(word & 0x80000000)) + { + word <<= 1; + bit -= 1; + } + + return bit; +} + +/* Implement ffs in terms of fls. */ +tlsf_decl int tlsf_ffs(unsigned int word) { return tlsf_fls_generic(word & (~word + 1)) - 1; } + +tlsf_decl int tlsf_fls(unsigned int word) { return tlsf_fls_generic(word) - 1; } + +#endif + +/* Possibly 64-bit version of tlsf_fls. */ +#if defined(TLSF_64BIT) +tlsf_decl int tlsf_fls_sizet(size_t size) +{ + int high = (int)(size >> 32); + int bits = 0; + if (high) { bits = 32 + tlsf_fls(high); } + else + { + bits = tlsf_fls((int)size & 0xffffffff); + } + return bits; +} +#else +#define tlsf_fls_sizet tlsf_fls +#endif + +tlsf_decl size_t align_up(size_t x, size_t align) +{ + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return (x + (align - 1)) & ~(align - 1); +} + +tlsf_decl size_t align_down(size_t x, size_t align) +{ + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return x - (x & (align - 1)); +} + +tlsf_decl void *align_ptr(const void *ptr, size_t align) +{ + const tlsfptr_t aligned = (tlsf_cast(tlsfptr_t, ptr) + (align - 1)) & ~(align - 1); + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return tlsf_cast(void *, aligned); +} + +tlsf_decl size_t tlsf_align_size(void) { return ALIGN_SIZE; } + +tlsf_decl size_t tlsf_block_size_min(void) { return block_size_min; } + +tlsf_decl size_t tlsf_block_size_max(control_t *control) +{ + if (control == NULL) { return 0; } + return tlsf_cast(size_t, 1) << control->fl_index_max; +} + +/* +** Adjust an allocation size to be aligned to word size, and no smaller +** than internal minimum. +*/ +tlsf_decl size_t adjust_request_size(control_t *control, size_t size, size_t align) +{ + size_t adjust = 0; + if (size) + { + const size_t aligned = align_up(size, align); + + /* aligned sized must not exceed block_size_max or we'll go out of bounds on sl_bitmap */ + if (aligned < tlsf_block_size_max(control)) { adjust = tlsf_max(aligned, block_size_min); } + } + return adjust; +} + +/* +** TLSF utility functions. In most cases, these are direct translations of +** the documentation found in the white paper. +*/ + +tlsf_decl void mapping_insert(control_t *control, size_t size, int *fli, int *sli) +{ + int fl, sl; + if (size < control->small_block_size) + { + /* Store small blocks in first list. */ + fl = 0; + sl = tlsf_cast(int, size) / (control->small_block_size / control->sl_index_count); + } + else + { + fl = tlsf_fls_sizet(size); + sl = tlsf_cast(int, size >> (fl - control->sl_index_count_log2)) ^ (1 << control->sl_index_count_log2); + fl -= (control->fl_index_shift - 1); + } + *fli = fl; + *sli = sl; +} + +/* This version rounds up to the next block size (for allocations) */ +tlsf_decl void mapping_search(control_t *control, size_t *size, int *fli, int *sli) +{ + if (*size >= control->small_block_size) + { + const size_t round = (1 << (tlsf_fls_sizet(*size) - control->sl_index_count_log2)); + *size = align_up(*size, round); + } + mapping_insert(control, *size, fli, sli); +} + +tlsf_decl block_header_t *search_suitable_block(control_t *control, int *fli, int *sli) +{ + int fl = *fli; + int sl = *sli; + + /* + ** First, search for a block in the list associated with the given + ** fl/sl index. + */ + unsigned int sl_map = control->sl_bitmap[fl] & (~0U << sl); + if (!sl_map) + { + /* No block exists. Search in the next largest first-level list. */ + const unsigned int fl_map = control->fl_bitmap & (~0U << (fl + 1)); + if (!fl_map) + { + /* No free blocks available, memory has been exhausted. */ + return 0; + } + + fl = tlsf_ffs(fl_map); + *fli = fl; + sl_map = control->sl_bitmap[fl]; + } + tlsf_assert(sl_map && "internal error - second level bitmap is null"); + sl = tlsf_ffs(sl_map); + *sli = sl; + + /* Return the first block in the free list. */ + return control->blocks[fl * control->sl_index_count + sl]; +} + +/* Remove a free block from the free list.*/ +tlsf_decl void remove_free_block(control_t *control, block_header_t *block, int fl, int sl) +{ + block_header_t *prev = block->prev_free; + block_header_t *next = block->next_free; + tlsf_assert(prev && "prev_free field can not be null"); + tlsf_assert(next && "next_free field can not be null"); + next->prev_free = prev; + prev->next_free = next; + + /* If this block is the head of the free list, set new head. */ + if (control->blocks[fl * control->sl_index_count + sl] == block) + { + control->blocks[fl * control->sl_index_count + sl] = next; + + /* If the new head is null, clear the bitmap. */ + if (next == &control->block_null) + { + control->sl_bitmap[fl] &= ~(1U << sl); + + /* If the second bitmap is now empty, clear the fl bitmap. */ + if (!control->sl_bitmap[fl]) { control->fl_bitmap &= ~(1U << fl); } + } + } +} + +/* Insert a free block into the free block list. */ +tlsf_decl void insert_free_block(control_t *control, block_header_t *block, int fl, int sl) +{ + block_header_t *current = control->blocks[fl * control->sl_index_count + sl]; + tlsf_assert(current && "free list cannot have a null entry"); + tlsf_assert(block && "cannot insert a null entry into the free list"); + block->next_free = current; + block->prev_free = &control->block_null; + current->prev_free = block; + + tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE) && "block not aligned properly"); + /* + ** Insert the new block at the head of the list, and mark the first- + ** and second-level bitmaps appropriately. + */ + control->blocks[fl * control->sl_index_count + sl] = block; + control->fl_bitmap |= (1U << fl); + control->sl_bitmap[fl] |= (1U << sl); +} + +/* Remove a given block from the free list. */ +tlsf_decl void block_remove(control_t *control, block_header_t *block) +{ + int fl, sl; + mapping_insert(control, block_size(block), &fl, &sl); + remove_free_block(control, block, fl, sl); +} + +/* Insert a given block into the free list. */ +tlsf_decl void block_insert(control_t *control, block_header_t *block) +{ + int fl, sl; + mapping_insert(control, block_size(block), &fl, &sl); + insert_free_block(control, block, fl, sl); +} + +tlsf_decl int block_can_split(block_header_t *block, size_t size) { return block_size(block) >= sizeof(block_header_t) + size; } + +/* Split a block into two, the second of which is free. */ +tlsf_decl block_header_t *block_split(block_header_t *block, size_t size) +{ + /* Calculate the amount of space left in the remaining block. + * REMINDER: remaining pointer's first field is `prev_phys_block` but this field is part of the + * previous physical block. */ + block_header_t *remaining = offset_to_block(block_to_ptr(block), size - block_header_overhead); + + /* `size` passed as an argument is the first block's new size, thus, the remaining block's size + * is `block_size(block) - size`. However, the block's data must be precedeed by the data size. + * This field is NOT part of the size, so it has to be substracted from the calculation. */ + const size_t remain_size = block_size(block) - (size + block_header_overhead); + + tlsf_assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE) && "remaining block not aligned properly"); + + tlsf_assert(block_size(block) == remain_size + size + block_header_overhead); + block_set_size(remaining, remain_size); + tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size"); + + block_set_size(block, size); + block_mark_as_free(remaining); + + /** + * Here is the final outcome of this function: + * + * block remaining (block_ptr + size - BHO) + * + + + * | | + * v v + * +----------------------------------------------------------------------+ + * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################| + * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################| + * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################| + * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################| + * +----------------------------------------------------------------------+ + * | | | | + * + +<------------------------->+ +<-------------------------> + * BHO `size` (argument) bytes BHO `remain_size` bytes + * + * Where BHO = block_header_overhead, + * 0: part of the memory owned by a `block`'s previous neighbour, + * x: part of the memory owned by `block`. + * #: part of the memory owned by `remaining`. + */ + + return remaining; +} + +/*! + * @brief Weak function filling the given memory with a given fill pattern. + * + * @param start: pointer to the start of the memory region to fill + * @param size: size of the memory region to fill + * @param is_free: Indicate if the pattern to use the fill the region should be + * an after free or after allocation pattern. + */ +__attribute__((weak)) void block_absorb_post_hook(void *start, size_t size, bool is_free); + +/* Absorb a free block's storage into an adjacent previous free block. */ +tlsf_decl block_header_t *block_absorb(block_header_t *prev, block_header_t *block) +{ + tlsf_assert(!block_is_last(prev) && "previous block can't be last"); + /* Note: Leaves flags untouched. */ + prev->size += block_size(block) + block_header_overhead; + block_link_next(prev); + + if (block_absorb_post_hook != NULL) { block_absorb_post_hook(block, sizeof(block_header_t), POISONING_AFTER_FREE); } + + return prev; +} + +/* Merge a just-freed block with an adjacent previous free block. */ +tlsf_decl block_header_t *block_merge_prev(control_t *control, block_header_t *block) +{ + if (block_is_prev_free(block)) + { + block_header_t *prev = block_prev(block); + tlsf_assert(prev && "prev physical block can't be null"); + tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such"); + block_remove(control, prev); + block = block_absorb(prev, block); + } + + return block; +} + +/* Merge a just-freed block with an adjacent free block. */ +tlsf_decl block_header_t *block_merge_next(control_t *control, block_header_t *block) +{ + block_header_t *next = block_next(block); + tlsf_assert(next && "next physical block can't be null"); + + if (block_is_free(next)) + { + tlsf_assert(!block_is_last(block) && "previous block can't be last"); + block_remove(control, next); + block = block_absorb(block, next); + } + + return block; +} + +/* Trim any trailing block space off the end of a block, return to pool. */ +tlsf_decl void block_trim_free(control_t *control, block_header_t *block, size_t size) +{ + tlsf_assert(block_is_free(block) && "block must be free"); + if (block_can_split(block, size)) + { + block_header_t *remaining_block = block_split(block, size); + block_link_next(block); + block_set_prev_free(remaining_block); + block_insert(control, remaining_block); + } +} + +/* Trim any trailing block space off the end of a used block, return to pool. */ +tlsf_decl void block_trim_used(control_t *control, block_header_t *block, size_t size) +{ + tlsf_assert(!block_is_free(block) && "block must be used"); + if (block_can_split(block, size)) + { + /* If the next block is free, we must coalesce. */ + block_header_t *remaining_block = block_split(block, size); + block_set_prev_used(remaining_block); + + remaining_block = block_merge_next(control, remaining_block); + block_insert(control, remaining_block); + } +} + +tlsf_decl block_header_t *block_trim_free_leading(control_t *control, block_header_t *block, size_t size) +{ + block_header_t *remaining_block = block; + if (block_can_split(block, size)) + { + /* We want to split `block` in two: the first block will be freed and the + * second block will be returned. */ + remaining_block = block_split(block, size - block_header_overhead); + + /* `remaining_block` is the second block, mark its predecessor (first + * block) as free. */ + block_set_prev_free(remaining_block); + + block_link_next(block); + + /* Put back the first block into the free memory list. */ + block_insert(control, block); + } + + return remaining_block; +} + +tlsf_decl block_header_t *block_locate_free(control_t *control, size_t *size) +{ + int fl = 0, sl = 0; + block_header_t *block = 0; + + if (*size) + { + mapping_search(control, size, &fl, &sl); + + /* + ** mapping_search can futz with the size, so for excessively large sizes it can sometimes wind up + ** with indices that are off the end of the block array. + ** So, we protect against that here, since this is the only callsite of mapping_search. + ** Note that we don't need to check sl, since it comes from a modulo operation that guarantees it's always in range. + */ + if (fl < control->fl_index_count) { block = search_suitable_block(control, &fl, &sl); } + } + + if (block) + { + tlsf_assert(block_size(block) >= *size); + remove_free_block(control, block, fl, sl); + } + + return block; +} + +tlsf_decl void *block_prepare_used(control_t *control, block_header_t *block, size_t size) +{ + void *p = 0; + if (block) + { + tlsf_assert(size && "size must be non-zero"); + block_trim_free(control, block, size); + block_mark_as_used(block); + p = block_to_ptr(block); + + control->mem_rec.used += (block_size(block) + tlsf_alloc_overhead()); + if (control->mem_rec.used > control->mem_rec.max_used) { control->mem_rec.max_used = control->mem_rec.used; } + } + return p; +} + +#undef tlsf_decl + +#if defined(__cplusplus) +}; +#endif diff --git a/test/valloc/valloc.c b/test/externalModule/valloc/valloc.c similarity index 98% rename from test/valloc/valloc.c rename to test/externalModule/valloc/valloc.c index 5852b64..78f8076 100644 --- a/test/valloc/valloc.c +++ b/test/externalModule/valloc/valloc.c @@ -6,7 +6,7 @@ #include "valloc.h" #define HEADER_SIZE sizeof(int) -#define MALLOC_HEADER_SIZE 12 +#define MALLOC_HEADER_SIZE 0 static int count = 0; static int use = 0; diff --git a/test/valloc/valloc.h b/test/externalModule/valloc/valloc.h similarity index 96% rename from test/valloc/valloc.h rename to test/externalModule/valloc/valloc.h index 75c0c51..db4aa94 100644 --- a/test/valloc/valloc.h +++ b/test/externalModule/valloc/valloc.h @@ -8,8 +8,8 @@ * \author Lamdonn * \details v1.0.0 ********************************************************************************************************/ -#ifndef __valloc_H -#define __valloc_H +#ifndef valloc +#define valloc #ifdef __cplusplus extern "C" { diff --git a/externalModule/yyjson/yyjson.c b/test/externalModule/yyjson/yyjson.c similarity index 100% rename from externalModule/yyjson/yyjson.c rename to test/externalModule/yyjson/yyjson.c diff --git a/externalModule/yyjson/yyjson.h b/test/externalModule/yyjson/yyjson.h similarity index 100% rename from externalModule/yyjson/yyjson.h rename to test/externalModule/yyjson/yyjson.h diff --git a/test/fuzzer/RyanJsonFuzzer.c b/test/fuzzer/RyanJsonFuzzer.c index 7132a38..89bf990 100644 --- a/test/fuzzer/RyanJsonFuzzer.c +++ b/test/fuzzer/RyanJsonFuzzer.c @@ -1,702 +1,10 @@ -#include "RyanJsonTest.h" -#include - -#define RyanJsonCheckGotoExit(EX) \ - RyanJsonCheckCode(EX, { \ - result = RyanJsonFalse; \ - goto __exit; \ - }) +#include "RyanJsonFuzzer.h" RyanJsonBool_e isEnableRandomMemFail = RyanJsonTrue; -static RyanJsonBool_e RyanJsonFuzzerTestByParseAndPrint(RyanJson_t pJson, const char *data, uint32_t size) -{ - RyanJsonAssert(NULL == RyanJsonPrint(NULL, 100, RyanJsonFalse, NULL)); - RyanJsonAssert(NULL == RyanJsonPrintPreallocated(NULL, NULL, 100, RyanJsonFalse, NULL)); - RyanJsonAssert(NULL == RyanJsonPrintPreallocated(pJson, NULL, 100, RyanJsonFalse, NULL)); - RyanJsonAssert(NULL == RyanJsonPrintPreallocated(NULL, data, 100, RyanJsonFalse, NULL)); - RyanJsonAssert(NULL == RyanJsonPrintPreallocated(pJson, data, 0, RyanJsonFalse, NULL)); - - uint32_t len = 0; - char *jsonStr = - RyanJsonPrint(pJson, size % 5 ? 100 : 0, size % 2 ? RyanJsonFalse : RyanJsonTrue, &len); // 以带格式方式将数据打印出来 - RyanJsonCheckReturnFalse(NULL != jsonStr && len > 0); - RyanJsonFree(jsonStr); - - uint32_t bufLen = len * 3; - if (bufLen < size * 2) { bufLen = size * 2; } - if (bufLen < 4096) { bufLen = 4096; } - char *buf = malloc((size_t)bufLen); - { - uint32_t len2 = 0; - char *jsonStr2 = RyanJsonPrintPreallocated(pJson, buf, bufLen, size % 2 ? RyanJsonFalse : RyanJsonTrue, &len2); - // printf("len: %d, len2: %d, str: %s\r\n", len, len2, NULL == jsonStr2 ? "NULL" : jsonStr2); - RyanJsonCheckCode(NULL != jsonStr2 && len == len2, { - free(buf); - return RyanJsonFalse; - }); - } - - memcpy(buf, data, (size_t)size); - buf[size] = 0; - RyanJson_t jsonRoot = RyanJsonParse(buf); - RyanJsonCheckCode(NULL != jsonRoot, { - free(buf); - return RyanJsonFalse; - }); - - // 测试多次打印结果是否一致 - { - uint32_t len3 = 0; - char *jsonStr3 = RyanJsonPrint(jsonRoot, 100, size % 2 ? RyanJsonFalse : RyanJsonTrue, &len3); // 以带格式方式将数据打印出来 - RyanJsonCheckCode(NULL != jsonStr3 && len == len3, { - free(buf); - if (jsonStr3) { RyanJsonFree(jsonStr3); } - RyanJsonDelete(jsonRoot); - return RyanJsonFalse; - }); - - RyanJsonFree(jsonStr3); - } - - { - RyanJsonPrintPreallocated(jsonRoot, buf, bufLen / 15, RyanJsonTrue, NULL); - } - - free(buf); - RyanJsonDelete(jsonRoot); - return RyanJsonTrue; -} - -static RyanJsonBool_e RyanJsonFuzzerTestByDup(RyanJson_t pJson) -{ - RyanJsonBool_e result = RyanJsonTrue; - char *jsonStr = NULL; - char *jsonStrDup = NULL; - RyanJson_t pJsonDup = NULL; - - // 测试打印和复制功能 - uint32_t len = 0; - - jsonStr = RyanJsonPrint(pJson, 100, RyanJsonFalse, &len); - RyanJsonCheckGotoExit(NULL != jsonStr && len > 0); - - pJsonDup = RyanJsonDuplicate(pJson); - RyanJsonCheckGotoExit(NULL != pJsonDup); - - // 测试dup失败情况 - RyanJsonCheckGotoExit(NULL == RyanJsonDuplicate(NULL)); - - // 判断复制json的size是否一致 - RyanJsonCheckGotoExit(0 == RyanJsonGetSize(NULL)); - RyanJsonCheckGotoExit(RyanJsonGetSize(pJson) == RyanJsonGetSize(pJsonDup)); - RyanJsonCompare(pJson, pJsonDup); - RyanJsonCompareOnlyKey(pJson, pJsonDup); - // assert(RyanJsonTrue == RyanJsonCompare(pJson, pJsonDup)); // 大浮点数判断容易出错 - // RyanJsonCheckGotoExit(RyanJsonTrue == RyanJsonCompareOnlyKey(pJson, pJsonDup)); // 重复key也会失败 - - // 测试compare特殊情况 - RyanJsonCheckGotoExit(RyanJsonTrue == RyanJsonCompare(pJson, pJson)); - RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompare(NULL, pJsonDup)); - RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompare(pJson, NULL)); - RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompare(NULL, NULL)); - - // 测试compareKey特殊情况 - RyanJsonCheckGotoExit(RyanJsonTrue == RyanJsonCompareOnlyKey(pJson, pJson)); - RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompareOnlyKey(NULL, pJsonDup)); - RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompareOnlyKey(pJson, NULL)); - RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompareOnlyKey(NULL, NULL)); - - uint32_t dupLen = 0; - jsonStrDup = RyanJsonPrint(pJsonDup, 100, RyanJsonFalse, &dupLen); // 以带格式方式将数据打印出来 - RyanJsonCheckGotoExit(NULL != jsonStrDup && dupLen > 0); - - RyanJsonCheckCode(len == dupLen && 0 == memcmp(jsonStr, jsonStrDup, (size_t)len), { - printf("len:%" PRIu32 ", dupLen:%" PRIu32 "\r\n", len, dupLen); - printf("jsonStr:%s, jsonStrDup:%s\r\n", jsonStr, jsonStrDup); - RyanJsonCheckGotoExit(0); - }); - - if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) - { - // 测试size不相等 - RyanJsonDelete(RyanJsonDetachByIndex(pJson, 0)); - - if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) - { - // 改变key - RyanJson_t item; - RyanJsonObjectForEach(pJson, item) - { - if (RyanJsonIsKey(item)) - { - RyanJsonChangeKey(item, "key12231123"); - break; - } - } - - // 改变value - RyanJsonObjectForEach(pJson, item) - { - if (RyanJsonIsBool(item)) - { - RyanJsonChangeBoolValue(item, !RyanJsonGetBoolValue(item)); - break; - } - } - - // 改变obj的key - RyanJsonObjectForEach(pJson, item) - { - if (RyanJsonIsKey(item) && RyanJsonIsObject(item)) - { - RyanJsonChangeKey(item, "key12231123"); - break; - } - } - } - - RyanJsonCompare(pJson, pJsonDup); - RyanJsonCompareOnlyKey(pJson, pJsonDup); - } -__exit: - - if (jsonStr) - { - RyanJsonFree(jsonStr); - jsonStr = NULL; - } - if (pJsonDup) - { - RyanJsonDelete(pJsonDup); - pJsonDup = NULL; - } - if (jsonStrDup) - { - RyanJsonFree(jsonStrDup); - jsonStrDup = NULL; - } - - return result; -} - -static RyanJsonBool_e RyanJsonFuzzerTestByForEachChange(RyanJson_t pJson, uint32_t size) -{ - RyanJsonIsNull(pJson); - - if (RyanJsonIsKey(pJson)) - { - char *key = malloc(strlen(RyanJsonGetKey(pJson)) + 1); - if (key) - { - memcpy(key, RyanJsonGetKey(pJson), strlen(RyanJsonGetKey(pJson))); - key[strlen(RyanJsonGetKey(pJson))] = 0; - - RyanJsonChangeKey(pJson, "key"); - RyanJsonChangeKey(pJson, key); - free(key); - } - } - if (RyanJsonIsBool(pJson)) { RyanJsonChangeBoolValue(pJson, !RyanJsonGetBoolValue(pJson)); } - if (RyanJsonIsNumber(pJson)) - { - if (RyanJsonIsInt(pJson)) - { - int32_t value = RyanJsonGetIntValue(pJson); - RyanJsonChangeIntValue(pJson, (int32_t)size); - RyanJsonChangeIntValue(pJson, value); - } - if (RyanJsonIsDouble(pJson)) - { - double value = RyanJsonGetDoubleValue(pJson); - RyanJsonChangeDoubleValue(pJson, size * 1.123456789); - RyanJsonChangeDoubleValue(pJson, value); - } - } - - if (RyanJsonIsString(pJson)) - { - char *value = malloc(strlen(RyanJsonGetStringValue(pJson)) + 1); - if (value) - { - memcpy(value, RyanJsonGetStringValue(pJson), strlen(RyanJsonGetStringValue(pJson))); - value[strlen(RyanJsonGetStringValue(pJson))] = 0; - - RyanJsonChangeStringValue(pJson, "hello world"); - RyanJsonChangeStringValue(pJson, value); - - free(value); - } - } - - if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) - { - RyanJson_t item; - RyanJsonArrayForEach(pJson, item) - { RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonFuzzerTestByForEachChange(item, size)); } - } - - return RyanJsonTrue; -} - -static RyanJsonBool_e RyanJsonFuzzerTestByForEachGet2(RyanJson_t lastJson, RyanJson_t pJson, uint32_t index, uint32_t size) -{ - RyanJsonIsNull(pJson); - - RyanJsonAssert(NULL == RyanJsonGetValue(NULL)); - RyanJsonAssert(NULL == RyanJsonGetKey(NULL)); - RyanJsonAssert(NULL == RyanJsonGetStringValue(NULL)); - - RyanJsonAssert(NULL == RyanJsonGetObjectByKey(NULL, NULL)); - RyanJsonAssert(NULL == RyanJsonGetObjectByKey(pJson, NULL)); - RyanJsonAssert(NULL == RyanJsonGetObjectByKey(NULL, "NULL")); - if (!RyanJsonIsObject(pJson)) // pJson类型错误 - { - RyanJsonAssert(NULL == RyanJsonGetObjectByKey(pJson, "NULL")); - } - - RyanJsonAssert(NULL == RyanJsonGetObjectByIndex(NULL, 10)); - if (!RyanJsonIsArray(pJson) && !RyanJsonIsObject(pJson)) // pJson类型错误 - { - RyanJsonAssert(NULL == RyanJsonGetObjectByIndex(pJson, 0)); - } - - if (RyanJsonIsKey(pJson)) { RyanJsonGetObjectToKey(lastJson, RyanJsonGetKey(pJson)); } - else - { - RyanJsonGetObjectToIndex(lastJson, index); - } - - if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) - { - RyanJson_t item; - RyanJsonObjectForEach(pJson, item) { RyanJsonFuzzerTestByForEachGet2(pJson, item, index, size); } - } - - return RyanJsonTrue; -} - -static RyanJsonBool_e RyanJsonFuzzerTestByForEachGet(RyanJson_t pJson, uint32_t size) -{ - - if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) - { - RyanJson_t item; - uint32_t index = 0; - RyanJsonObjectForEach(pJson, item) - { - RyanJsonFuzzerTestByForEachGet2(pJson, item, index, size); - index++; - } - } - return RyanJsonTrue; -} - -static RyanJson_t RyanJsonFuzzerCreateRandomNode(RyanJson_t pJson) -{ - static int32_t count = 0; - static int32_t count2 = 0; - count++; - char *key = "true"; - if (count % 10 > 5) { key = NULL; } - switch (count % 50) - { - case 0: return RyanJsonCreateArray(); - case 1: return RyanJsonCreateObject(); - case 2: - count2++; - if (0 == count2 % 10) { return RyanJsonDuplicate(pJson); } - case 11: - case 12: - case 13: return RyanJsonCreateBool(key, RyanJsonTrue); - case 20: - case 21: - case 22: return RyanJsonCreateInt(key, count); - case 31: - case 32: - case 33: return RyanJsonCreateDouble(key, count * 1.123456789); - - default: return RyanJsonCreateString(key, "true"); - } -} - -static RyanJsonBool_e RyanJsonFuzzerTestByForEachCreate(RyanJson_t pJson, uint32_t size) -{ - // RyanJsonInsert的特殊情况 - RyanJsonCheckReturnFalse(RyanJsonFalse == RyanJsonInsert(NULL, UINT32_MAX, RyanJsonCreateString("key", "string"))); - RyanJsonCheckReturnFalse(RyanJsonFalse == RyanJsonInsert(pJson, UINT32_MAX, NULL)); - RyanJsonCheckReturnFalse(RyanJsonFalse == RyanJsonInsert(NULL, 0, NULL)); - - RyanJsonAssert(NULL == RyanJsonCreateString(NULL, NULL)); - RyanJsonAssert(NULL == RyanJsonCreateString("NULL", NULL)); - - RyanJsonAssert(RyanJsonFalse == RyanJsonChangeStringValue(NULL, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonChangeStringValue(pJson, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonChangeStringValue(NULL, "NULL")); - if (!RyanJsonIsKey(pJson) && !RyanJsonIsString(pJson)) // pJson类型错误 - { - RyanJsonAssert(RyanJsonFalse == RyanJsonChangeStringValue(pJson, "NULL")); - } - - RyanJsonAssert(RyanJsonFalse == RyanJsonChangeKey(NULL, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonChangeKey(pJson, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonChangeKey(NULL, "NULL")); - if (!RyanJsonIsKey(pJson) && !RyanJsonIsString(pJson)) // pJson类型错误 - { - RyanJsonAssert(RyanJsonFalse == RyanJsonChangeKey(pJson, "NULL")); - } - - RyanJsonAssert(RyanJsonFalse == RyanJsonAddItemToObject(NULL, NULL, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonAddItemToObject(pJson, NULL, NULL)); - - RyanJsonAssert(NULL == RyanJsonCreateIntArray(NULL, 0)); - RyanJsonAssert(NULL == RyanJsonCreateDoubleArray(NULL, 0)); - RyanJsonAssert(NULL == RyanJsonCreateStringArray(NULL, 0)); - - RyanJsonAssert(RyanJsonFalse == RyanJsonHasObjectToKey(NULL, "0", "1", "2", "3")); - RyanJsonAssert(RyanJsonFalse == RyanJsonHasObjectToIndex(NULL, 0, 1, 2, 3)); - RyanJsonAssert(RyanJsonFalse == RyanJsonHasObjectToKey(pJson, "0", "1", "2", "3")); - RyanJsonAssert(RyanJsonFalse == RyanJsonHasObjectToIndex(pJson, 0, 1, 2, 3)); - - char *key = "keyaaa"; - RyanJsonAddNullToObject(pJson, key); - if (RyanJsonIsKey(pJson)) { key = RyanJsonGetKey(pJson); } - if (RyanJsonIsBool(pJson)) { RyanJsonAddBoolToObject(pJson, key, RyanJsonGetBoolValue(pJson)); } - if (RyanJsonIsNumber(pJson)) - { - if (RyanJsonIsInt(pJson)) - { - RyanJsonAddIntToObject(pJson, key, RyanJsonGetIntValue(pJson)); - int arrayInt[] = {RyanJsonGetIntValue(pJson), RyanJsonGetIntValue(pJson), RyanJsonGetIntValue(pJson), - RyanJsonGetIntValue(pJson), RyanJsonGetIntValue(pJson)}; - RyanJsonAddItemToObject(pJson, (size % 2) ? key : "arrayString", - RyanJsonCreateIntArray(arrayInt, sizeof(arrayInt) / sizeof(arrayInt[0]))); - } - if (RyanJsonIsDouble(pJson)) - { - RyanJsonAddDoubleToObject(pJson, key, RyanJsonGetDoubleValue(pJson)); - double arrayDouble[] = {RyanJsonGetDoubleValue(pJson), RyanJsonGetDoubleValue(pJson), RyanJsonGetDoubleValue(pJson), - RyanJsonGetDoubleValue(pJson), RyanJsonGetDoubleValue(pJson)}; - RyanJsonAddItemToObject(pJson, (size % 2) ? key : "arrayString", - RyanJsonCreateDoubleArray(arrayDouble, sizeof(arrayDouble) / sizeof(arrayDouble[0]))); - } - } - - if (RyanJsonIsString(pJson)) - { - RyanJsonAddStringToObject(pJson, key, RyanJsonGetStringValue(pJson)); - const char *arrayString[] = {RyanJsonGetStringValue(pJson), RyanJsonGetStringValue(pJson), RyanJsonGetStringValue(pJson), - RyanJsonGetStringValue(pJson), RyanJsonGetStringValue(pJson)}; - RyanJsonAddItemToObject(pJson, (size % 2) ? key : "arrayString", - RyanJsonCreateStringArray(arrayString, sizeof(arrayString) / sizeof(arrayString[0]))); - } - - if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) - { - RyanJson_t item; - - RyanJsonObjectForEach(pJson, item) { RyanJsonFuzzerTestByForEachCreate(item, size); } - - RyanJson_t pJson2 = RyanJsonFuzzerCreateRandomNode(pJson); - RyanJsonAddItemToObject(pJson, key, pJson2); - - if (RyanJsonIsArray(pJson)) - { - RyanJsonAddNullToArray(pJson); - RyanJsonAddBoolToArray(pJson, size % 2 ? RyanJsonTrue : RyanJsonFalse); - RyanJsonAddItemToArray(pJson, RyanJsonFuzzerCreateRandomNode(RyanJsonGetArrayValue(pJson))); - } - } - - return RyanJsonTrue; -} - -/** - * @brief 测试 Json 的 Replace 功能(保护根节点) - * - * @param pJson 待测试的 Json 节点 - * @param size 用于计算 index 的模数 - * @param isFirst 是否为第一次调用(根节点) - * @return RyanJsonBool_e - */ -static RyanJsonBool_e RyanJsonFuzzerTestByForEachReplace(RyanJson_t pJson, uint32_t size) -{ - { - isEnableRandomMemFail = RyanJsonFalse; - RyanJson_t strItem = RyanJsonCreateString("", "NULL"); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(NULL, NULL, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(pJson, NULL, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(NULL, "NULL", NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(NULL, NULL, strItem)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(pJson, "NULL", NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(NULL, "NULL", strItem)); - if (!RyanJsonIsObject(pJson)) // pJson类型错误 - { - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(pJson, "NULL", strItem)); - } - - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(NULL, 0, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(pJson, 0, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(NULL, 0, strItem)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(pJson, 0, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(NULL, 0, strItem)); - if (!RyanJsonIsArray(pJson) && !RyanJsonIsObject(pJson)) // pJson类型错误 - { - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(pJson, 0, strItem)); - } - - RyanJson_t objItem = RyanJsonCreateObject(); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(objItem, "NULL", strItem)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(objItem, 0, strItem)); - - RyanJsonAddItemToObject(objItem, "item", RyanJsonCreateObject()); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(objItem, "NULL222", strItem)); - RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(objItem, INT32_MAX, strItem)); - isEnableRandomMemFail = RyanJsonTrue; - - RyanJsonDelete(objItem); - RyanJsonDelete(strItem); - } - - // 只处理数组或对象 - if (!(RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson))) { return RyanJsonTrue; } - - // 递归替换子节点 - RyanJson_t item = NULL; - RyanJson_t LastItem = NULL; - RyanJsonObjectForEach(pJson, item) - { - if (RyanJsonTrue != RyanJsonFuzzerTestByForEachReplace(item, size)) { return RyanJsonFalse; } - LastItem = item; - } - - // 只有非根节点才做替换 - - // 按 key 替换(仅对象) - // 不要动第一个节点 - if (RyanJsonIsObject(pJson)) - { - if (LastItem && RyanJsonIsKey(LastItem)) - { - RyanJson_t newNode = RyanJsonFuzzerCreateRandomNode(pJson); - if (RyanJsonFalse == RyanJsonReplaceByKey(pJson, RyanJsonGetKey(LastItem), newNode)) - { - if (newNode) { RyanJsonDelete(newNode); } - return RyanJsonFalse; - } - } - } - - // 按 index 替换 - { - uint32_t idx = RyanJsonGetSize(pJson) % size; - RyanJson_t newNode = RyanJsonFuzzerCreateRandomNode(pJson); - if (RyanJsonFalse == RyanJsonReplaceByIndex(pJson, (size % 25) ? idx : 0, newNode)) - { - if (newNode) { RyanJsonDelete(newNode); } - return RyanJsonFalse; - } - } - - return RyanJsonTrue; -} - -/** - * @brief 测试 Json 的 Detach 分离功能(保护根节点) - * - * @param pJson 待测试的 Json 节点 - * @param size 用于计算 index 的模数 - * @param isFirst 是否为第一次调用(根节点) - * @return RyanJsonBool_e - */ -static RyanJsonBool_e RyanJsonFuzzerTestByForEachDetach(RyanJson_t pJson, uint32_t size) -{ - RyanJsonAssert(RyanJsonFalse == RyanJsonDetachByKey(NULL, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonDetachByKey(pJson, NULL)); - RyanJsonAssert(RyanJsonFalse == RyanJsonDetachByKey(NULL, "NULL")); - if (!RyanJsonIsObject(pJson)) // pJson类型错误 - { - RyanJsonAssert(NULL == RyanJsonDetachByKey(pJson, "NULL")); - } - - RyanJsonAssert(NULL == RyanJsonDetachByIndex(NULL, 10)); - if (!RyanJsonIsArray(pJson) && !RyanJsonIsObject(pJson)) // pJson类型错误 - { - RyanJsonAssert(NULL == RyanJsonDetachByIndex(pJson, 0)); - } - - if (!(RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson))) { return RyanJsonTrue; } - - // 递归遍历子节点 - RyanJson_t item = NULL; - RyanJson_t LastItem = NULL; - RyanJsonObjectForEach(pJson, item) - { - RyanJsonFuzzerTestByForEachDetach(item, size); - LastItem = item; - } - - // 只有非根节点才做 detach - - // 按 key 分离(仅对象) - if (RyanJsonIsObject(pJson)) - { - if (LastItem && RyanJsonIsKey(LastItem)) - { - RyanJson_t detached = RyanJsonDetachByKey(pJson, RyanJsonGetKey(LastItem)); - if (detached) { RyanJsonDelete(detached); } - - // RyanJsonAssert(RyanJsonFalse == RyanJsonDetachByKey(pJson, RyanJsonGetKey(LastItem))); - } - } - - // 按 index 分离 - { - uint32_t idx = RyanJsonGetSize(pJson) % size; - RyanJson_t detached = RyanJsonDetachByIndex(pJson, (size % 25) ? idx : 0); - if (detached) { RyanJsonDelete(detached); } - } - - return RyanJsonTrue; -} - -/** - * @brief 测试 Json 的 Delete 功能(保护根节点) - * - * @param pJson 待测试的 Json 节点 - * @param size 用于计算 index 的模数 - * @param isFirst 是否为第一次调用(根节点) - * @return RyanJsonBool_e - */ -static RyanJsonBool_e RyanJsonFuzzerTestByForEachDelete(RyanJson_t pJson, uint32_t size) -{ - if (!(RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson))) { return RyanJsonTrue; } - - // -------- 测试错误的 delete 调用 -------- - // Key 删除错误用例 - RyanJsonDeleteByKey(pJson, "non_exist_key"); - RyanJsonDeleteByKey(NULL, "some_key"); - RyanJsonDeleteByKey(pJson, NULL); - RyanJsonDeleteByKey(NULL, NULL); - - // Index 删除错误用例 - RyanJsonDeleteByIndex(pJson, RyanJsonGetSize(pJson)); // 越界 - RyanJsonDeleteByIndex(NULL, (RyanJsonGetSize(pJson) % size)); - RyanJsonDeleteByIndex(pJson, -size); // 负数 - RyanJsonDeleteByIndex(NULL, -size); - - // 递归遍历子节点 - RyanJson_t item = NULL; - RyanJson_t LastItem = NULL; - RyanJsonObjectForEach(pJson, item) - { - RyanJsonFuzzerTestByForEachDelete(item, size); - LastItem = item; - } - - // -------- 正常删除逻辑(保护根节点) -------- - - // 按 key 删除(仅对象) - if (RyanJsonIsObject(pJson)) - { - if (LastItem && RyanJsonIsKey(LastItem)) - { - - // printf("key is %d %s\r\n", RyanJsonGetType(LastItem), - // RyanJsonGetKey(LastItem) == NULL ? "NULL" : RyanJsonGetKey(LastItem)); - RyanJsonDeleteByKey(pJson, RyanJsonGetKey(LastItem)); - } - } - - // 按 index 删除 - uint32_t idx = RyanJsonGetSize(pJson) % size; - RyanJsonDeleteByIndex(pJson, (size % 25) ? idx : 0); - - return RyanJsonTrue; -} - -static RyanJsonBool_e RyanJsonFuzzerTestByMinify(const char *data, uint32_t size) -{ - char *buf = malloc(size + 100); - memcpy(buf, data, size); - memset(buf + size, 0, 100); - - uint32_t size2 = RyanJsonMinify(buf, (int32_t)size); - // 非法情况 - { - RyanJsonCheckReturnFalse(0 == RyanJsonMinify(NULL, 0)); - RyanJsonCheckReturnFalse(0 == RyanJsonMinify(NULL, 10)); - RyanJsonCheckReturnFalse(0 == RyanJsonMinify(NULL, -10)); - RyanJsonCheckReturnFalse(0 == RyanJsonMinify(buf, -10)); - } - - // 内存泄漏就是上面出错了 - RyanJson_t pJson2 = RyanJsonParseOptions(buf, size2, size % 2 ? RyanJsonTrue : RyanJsonFalse, NULL); - free(buf); - if (NULL != pJson2) - { - uint32_t len = 0; - char *jsonStr = RyanJsonPrint(pJson2, 100, RyanJsonFalse, &len); // 以带格式方式将数据打印出来 - RyanJsonCheckCode(NULL != jsonStr && len > 0, { - RyanJsonDelete(pJson2); - return RyanJsonFalse; - }); - RyanJsonFree(jsonStr); - RyanJsonDelete(pJson2); - } - else - { - return RyanJsonFalse; - } - - return RyanJsonTrue; -} - -static void *RyanJsonFuzzerMalloc(size_t size) -{ - static int32_t count = 0; - count++; - if (RyanJsonTrue == isEnableRandomMemFail) - { - if (0 == count % 598) { return NULL; } - } - return (char *)v_malloc(size); -} - -static void RyanJsonFuzzerFree(void *block) { v_free(block); } - -static void *RyanJsonFuzzerRealloc(void *block, size_t size) -{ - static int32_t count = 0; - count++; - if (RyanJsonTrue == isEnableRandomMemFail) - { - if (0 == count % 508) { return NULL; } - } - return (char *)v_realloc(block, size); -} - int LLVMFuzzerTestOneInput(const char *data, uint32_t size) { - // !检查分支覆盖率的时候要把这个取消掉,否则不知道是这个测试用例还是Fuzzer触发的,期望的是Fuzzer触发 - { - // // 执行基础测试 - // static bool isFirst = true; - // if (true == isFirst) - // { - // RyanJsonBool_e result = RyanJsonBaseTest(); - // if (RyanJsonTrue != result) - // { - // printf("%s:%d RyanJsonTest fail\r\n", __FILE__, __LINE__); - // return -1; - // } - - // RFC8259JsonTest(); - // isFirst = false; - // } - } - // for (int i = 0; i < size; i++) { printf("%c", size, data[i]); } // printf("\r\n"); @@ -705,13 +13,13 @@ int LLVMFuzzerTestOneInput(const char *data, uint32_t size) RyanJsonInitHooks(RyanJsonFuzzerMalloc, RyanJsonFuzzerFree, NULL); RyanJsonInitHooks(NULL, NULL, NULL); - RyanJsonInitHooks(RyanJsonFuzzerMalloc, RyanJsonFuzzerFree, size % 2 ? NULL : RyanJsonFuzzerRealloc); + RyanJsonInitHooks(RyanJsonFuzzerMalloc, RyanJsonFuzzerFree, 0 != size % 2 ? NULL : RyanJsonFuzzerRealloc); RyanJsonAssert(NULL == RyanJsonParseOptions(NULL, 100, RyanJsonFalse, NULL)); RyanJsonAssert(NULL == RyanJsonParseOptions(data, 0, RyanJsonFalse, NULL)); const char *parseEndPtr = NULL; - RyanJson_t pJson = RyanJsonParseOptions(data, size, size % 3 ? RyanJsonTrue : RyanJsonFalse, &parseEndPtr); + RyanJson_t pJson = RyanJsonParseOptions(data, size, 0 != size % 3 ? RyanJsonTrue : RyanJsonFalse, &parseEndPtr); if (NULL != pJson) { assert(NULL != parseEndPtr && parseEndPtr - data <= size); @@ -720,9 +28,9 @@ int LLVMFuzzerTestOneInput(const char *data, uint32_t size) isEnableRandomMemFail = RyanJsonFalse; RyanJson_t pJson2 = RyanJsonDuplicate(pJson); isEnableRandomMemFail = RyanJsonTrue; - RyanJsonCheckCode(RyanJsonFuzzerTestByForEachDelete(pJson2, size), { + RyanJsonCheckCode(RyanJsonFuzzerTestDelete(pJson2, size), { RyanJsonDelete(pJson2); - goto __exit; + goto exit__; }); RyanJsonDelete(pJson2); } @@ -731,28 +39,28 @@ int LLVMFuzzerTestOneInput(const char *data, uint32_t size) isEnableRandomMemFail = RyanJsonFalse; RyanJson_t pJson2 = RyanJsonDuplicate(pJson); isEnableRandomMemFail = RyanJsonTrue; - RyanJsonCheckCode(RyanJsonFuzzerTestByForEachDetach(pJson2, size), { + RyanJsonCheckCode(RyanJsonFuzzerTestDetach(pJson2, size), { RyanJsonDelete(pJson2); - goto __exit; + goto exit__; }); RyanJsonDelete(pJson2); } - RyanJsonFuzzerTestByMinify(data, size); - RyanJsonFuzzerTestByParseAndPrint(pJson, data, size); - RyanJsonFuzzerTestByForEachGet(pJson, size); + RyanJsonFuzzerTestMinify(data, size); + RyanJsonFuzzerTestParse(pJson, data, size); + RyanJsonFuzzerTestGet(pJson, size); - RyanJsonFuzzerTestByDup(pJson); - RyanJsonCheckCode(RyanJsonFuzzerTestByForEachChange(pJson, size), { goto __exit; }); - RyanJsonCheckCode(RyanJsonFuzzerTestByForEachCreate(pJson, size), { goto __exit; }); - RyanJsonCheckCode(RyanJsonFuzzerTestByForEachReplace(pJson, size), { goto __exit; }); + RyanJsonFuzzerTestDuplicate(pJson); + RyanJsonCheckCode(RyanJsonFuzzerTestModify(pJson, size), { goto exit__; }); + RyanJsonCheckCode(RyanJsonFuzzerTestCreate(pJson, size), { goto exit__; }); + RyanJsonCheckCode(RyanJsonFuzzerTestReplace(pJson, size), { goto exit__; }); RyanJsonDelete(pJson); } return 0; -__exit: +exit__: RyanJsonDelete(pJson); return 0; } diff --git a/test/fuzzer/RyanJsonFuzzer.dict b/test/fuzzer/RyanJsonFuzzer.dict index e1c6f73..c74ede9 100644 --- a/test/fuzzer/RyanJsonFuzzer.dict +++ b/test/fuzzer/RyanJsonFuzzer.dict @@ -1,253 +1,312 @@ -# 基本关键字 +# ===================================================== +# RyanJson Fuzzer Dictionary +# 用于 libFuzzer 的 JSON 模糊测试字典 +# ===================================================== + +# =================== +# 基本 JSON 关键字 +# =================== "true" "false" "null" -# 对象结构 +# =================== +# JSON 结构符号 +# =================== +# 对象 "{" "}" ":" "," -# 数组结构 +# 数组 "[" "]" -# 常见字符串模式 +# 字符串 +"\"" + +# =================== +# 常见键名 +# =================== "\"key\"" "\"value\"" "\"name\"" "\"id\"" -"\"string\"" -"\"number\"" -"\"message\"" "\"data\"" "\"status\"" "\"error\"" -""" -"\\" - -# 数字边界 +"\"message\"" +"\"type\"" +"\"result\"" +"\"code\"" +"\"items\"" +"\"count\"" +"\"total\"" +"\"list\"" + +# =================== +# 整数值 +# =================== "0" "1" "-1" -"1234567890" +"123" +"-123" +"2147483647" +"-2147483648" +"9223372036854775807" +"-9223372036854775808" +"999999999999999999999999999" +"-999999999999999999999999999" + +# =================== +# 浮点数值 +# =================== +"0.0" +"0.1" +"-0.1" "3.14159" +"3.141592653589793" +"1.7976931348623157e308" +"-1.7976931348623157e308" +"2.2250738585072014e-308" "1e10" +"1e-10" +"-1e10" "-1e-10" -"999999999999999999999999999" -"-999999999999999999999999999" -"-" -"000-000" -"-0045.12348" +"1E10" +"1E-10" +"1e+10" +"1.5e+9999" +"1e9999" +"-1e9999" +"123e100000" +"-123e100000" +"123e-10000000" +"123.456e-789" + +# =================== +# 特殊浮点数边界 +# =================== +"0.000001" +"0.0000001" +"999999.999999" +"1000000.0" +"0.123456789012345678901234567890" + +# =================== +# 科学计数法错误 +# =================== +"1e" +"1e+" +"1e-" +"1eE" +"1eAbc" +".1e10" +"1." +"1.e10" +"-.1" + +# =================== +# 前导零错误 +# =================== +"00" +"01" "0123" -"0123.123" - -# 嵌套结构 +"-00" +"-01" +"-0123" +"00.123" + +# =================== +# 简单结构 +# =================== +"{}" +"[]" "{\"a\":1}" +"{\"a\":\"b\"}" +"[1]" "[1,2,3]" +"[true,false,null]" + +# =================== +# 嵌套结构 +# =================== "{\"obj\":{\"nested\":true}}" -"{\"arr\":[{\"x\":1},{\"y\":2}]}" +"{\"arr\":[1,2,3]}" +"[{\"id\":1},{\"id\":2}]" "{\"deep\":{\"nest\":{\"more\":{\"inner\":{\"flag\":true}}}}}" -"[{\"id\":1,\"val\":true},{\"id\":2,\"val\":false}]" - -# 复杂对象 -"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}" -"{\"items\":[{\"id\":1},{\"id\":2},{\"id\":3}]}" -"{\"user\":{\"id\":123,\"name\":\"Alice\",\"roles\":[\"admin\",\"editor\"]}}" -"{\"response\":{\"status\":200,\"data\":[{\"id\":1},{\"id\":2}]}}" +"[[[[[[1]]]]]]" +"{\"a\":{\"b\":{\"c\":{\"d\":{\"e\":null}}}}}" + +# =================== +# 混合类型示例 +# =================== +"{\"int\":16,\"double\":16.89,\"string\":\"hello\",\"bool\":true,\"null\":null}" +"[16,16.89,\"hello\",true,false,null]" +"{\"mixed\":[1,\"two\",true,null,{\"nested\":\"obj\"}]}" + +# =================== +# 转义字符 +# =================== +"\\n" +"\\r" +"\\t" +"\\b" +"\\f" +"\\\"" +"\\\\" +"\\/" +"\\u0000" +"\\u0020" +"\\u00FF" +"\\uFFFF" + +# =================== +# Unicode 转义 +# =================== +"\\u4F60\\u597D" +"\\u4E16\\u754C" +"\\uD83D\\uDE00" +"\\uD83C\\uDF0D" + +# Unicode 代理对边界 +"\\uD800\\uDC00" +"\\uDBFF\\uDFFF" -# 边界结构 -# 键未加引号 +# 错误的 Unicode +"\\uD800" +"\\uDFFF" +"\\uZZZZ" +"\\u12" +"\\u123" +"\\uD800\\u0041" + +# =================== +# 特殊字符串 +# =================== +"{\"empty\":\"\"}" +"{\"space\":\" \"}" +"{\"tab\":\"\\t\"}" +"{\"newline\":\"\\n\"}" +"{\"unicode\":\"\\u4E2D\\u6587\"}" +"{\"emoji\":\"\\uD83D\\uDE00\"}" + +# =================== +# RESTful 风格响应 +# =================== +"{\"status\":200,\"message\":\"OK\"}" +"{\"status\":404,\"error\":\"Not Found\"}" +"{\"status\":500,\"error\":\"Internal Server Error\"}" +"{\"code\":0,\"data\":null}" +"{\"success\":true,\"data\":[]}" +"{\"success\":false,\"message\":\"Error\"}" + +# =================== +# 分页响应 +# =================== +"{\"page\":1,\"pageSize\":10,\"total\":100,\"data\":[]}" +"{\"offset\":0,\"limit\":20,\"items\":[]}" + +# =================== +# 重复键(测试处理) +# =================== +"{\"key\":1,\"key\":2}" +"{\"a\":\"first\",\"a\":\"second\"}" +"{\"dup\":true,\"dup\":false}" + +# =================== +# 语法错误示例 +# =================== +# 缺少引号 "{a:1}" +"{key:\"value\"}" # 缺少冒号 "{\"a\" 1}" # 缺少逗号 "{\"a\":1 \"b\":2}" +"[1 2 3]" # 多余逗号 "{\"a\":1,}" - -# 数组尾逗号 "[1,2,3,]" - -# 空对象错误 "{,}" - -# 空数组错误 "[,]" +"[1,,2]" -# 未闭合字符串 -"{\"a\":\"value}" - -# 单引号字符串 -"{\"a\":'value'}" - -# 非法转义 -"{\"a\":\"\\q\"}" +# 未闭合 +"{\"a\":1" +"[1,2,3" +"{\"str\":\"unclosed" +"[" +"{" -# 非法 Unicode 转义 -"{\"a\":\"\\uZZZZ\"}" +# 单引号(非法) +"{'a':'b'}" -# 位数不足 -"{\"a\":\"\\u12\"}" +# 注释(非法) +"// comment" +"/* block */" +"{\"a\":1}// trailing" +# =================== # 控制字符 -"{\"a\":\"\\x07\"}" - -# 小数点后无数字 -"1." - -# 科学计数法错误 -"1e" - -# 科学计数法错误 -"1e+" - -# 超大指数 -"1e9999" +# =================== +"\x00" +"\x01" +"\x1F" +"\x7F" + +# =================== +# 非法字节序列 +# =================== +"\xC0\xAF" +"\xFF\xFF" +"\xFE\xFF" +"\x80" +"\xBF" -# 拼写错误 +# =================== +# 边缘 token +# =================== "True" "False" "Null" +"TRUE" +"FALSE" +"NULL" "tru" +"fals" "nul" -# 顶层错误 -"string" -"123" -"true" -"null" - -# 注释错误:单行 -"// comment" -"/* block comment */" - -# 非 UTF-8 字节流 -"\xC0\xAF" -"\xFF\xFF" -"\xFE\xFF" - -# 空结构 -"{}" -"[]" - -# 特殊转义 -"\\n" -"\\r" -"\\t" -"\\b" -"\\f" -"\\u0000" -"\\uD800\\uDC00" -"\\uDBFF\\uDFFF" -"\\\"" -"\\\\" -"/" - -# 一维对象,覆盖所有 JSON 类型 -"{\"string\":\"hello\",\"number\":123,\"boolean_true\":true,\"boolean_false\":false,\"null_value\":null,\"array\":[1,\"two\",false,null],\"object\":{\"nested_key\":\"nested_value\"}}" - -# 错误重复结构 -"[{\"error\":4,\"error\":\"45\"}]" -"[{\"id\":1,\"id\":2}]" -"[{\"name\":\"Alice\",\"name\":\"Bob\"}]" -"[{\"value\":true,\"value\":false}]" -"[{\"unicode\":\"\\u4F60\\u597D\",\"unicode\":\"\\u4E16\\u754C\"}]" -"[{\"data\":[1,2,3],\"data\":{\"x\":1}}]" -"[{\"nested\":{\"a\":1},\"nested\":{\"a\":2}}]" -"[{\"error\":null,\"error\":999}]" -"[{\"flag\":false,\"flag\":true}]" -"[{\"number\":123,\"number\":\"123\"}]" -"[{\"list\":[1,2],\"list\":[3,4]}]" - -# Unicode 示例(转义形式) -"{\"unicode\":\"\\u4F60\\u597D\"}" -"{\"unicode\":\"\\u4E16\\u754C\"}" -"{\"unicode\":\"\\uD83C\\uDF0D\"}" -"{\"unicode\":\"\\uD83D\\uDE00\"}" -# 孤立高代理 -"{\"unicode\":\"\\uD800\"}" -# 孤立低代理 -"{\"unicode\":\"\\uDFFF\"}" -# 超出最大码点 -"{\"unicode\":\"\\u110000\"}" - # 非十六进制 -"{\"unicode\":\"\\uZZZZ\"}" -# 位数不足 -"{\"unicode\":\"\\u12\"}" -# 错误代理配对 -"{\"unicode\":\"\\uD800\\u0041\"}" - -# RESTful 风格常见响应 -"{\"status\":200,\"message\":\"OK\"}" -"{\"status\":404,\"error\":\"Not Found\"}" -"{\"status\":500,\"error\":\"Internal Server Error\"}" -"{\"status\":401,\"error\":\"Unauthorized\"}" -"{\"status\":403,\"error\":\"Forbidden\"}" - -# RESTful 数据响应 -"{\"status\":200,\"data\":{\"id\":1,\"name\":\"Alice\"}}" -"{\"status\":200,\"data\":[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"}]}" -"{\"status\":200,\"data\":{\"items\":[{\"id\":1,\"value\":true},{\"id\":2,\"value\":false}]}}" - -# RESTful 分页响应 -"{\"status\":200,\"page\":1,\"pageSize\":10,\"total\":100,\"data\":[{\"id\":1},{\"id\":2}]}" -"{\"status\":200,\"meta\":{\"page\":2,\"limit\":20},\"data\":[{\"id\":21},{\"id\":22}]}" - -# RESTful 错误响应 -"{\"error\":{\"code\":1234,\"message\":\"Invalid request\"}}" -"{\"error\":{\"code\":5678,\"message\":\"Timeout\"}}" - -# RESTful 响应 -"{\"status\":200,\"user\":{\"id\":123,\"name\":\"Alice\",\"roles\":[\"admin\",\"editor\"]},\"token\":\"abcdef123456\"}" -"{\"status\":200,\"config\":{\"theme\":\"dark\",\"language\":\"en\"},\"features\":[\"chat\",\"upload\",\"search\"]}" - -# 循环/深度嵌套场景 -"{\"a\":{\"b\":{\"c\":{\"d\":{\"e\":{\"f\":{\"g\":true}}}}}}}" -"[[[[[[[[[[1]]]]]]]]]]" -"[{\"a\":[{\"b\":[{\"c\":[{\"d\":true}]}]}]}]" -"{\"a\":[{\"b\":[{\"c\":[{\"d\":[{\"e\":false}]}]}]}]}" -"{\"node\":{\"id\":1,\"child\":{\"id\":2,\"child\":{\"id\":3,\"child\":{\"id\":4}}}}}" -"{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":null}}}}}}" -"{\"status\":200,\"data\":{\"items\":[{\"id\":1,\"children\":[{\"id\":2,\"children\":[{\"id\":3}]}]}]}}" -"{\"mixed\":[{\"obj\":{\"arr\":[{\"obj\":{\"arr\":[{\"obj\":{\"arr\":[true]}]}]}]}}]}" - -# 极端情况 -"{\"long\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}" -"{\"big\":999999999999999999999999999}" -"[[],[],[],[],[],[],[],[],[],[]]" -"{\"nestedArray\":[[1,2],[3,4],[5,[6,[7,[8]]]]]}" -"{\"escape\":\"line1\\nline2\\tTabbed\"}" -"{\"mixed\":[16,16.89,\"hello\",true,false,null,{\"deep\":{}}]}" - -# 非法/边界片段(测试错误处理) -",{}" -":[]" -"[[]]" -"{{}}" -",[]" -":{}" -"''" -"\\x00" -"\\0" - -"//" -"/**/" - -"\x01\x00" +# =================== +# 极端嵌套 +# =================== +"[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]" +"{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{}}}}}}}}}}}" + +# =================== +# 超长字符串 +# =================== +"{\"long\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}" + +# =================== +# 空白字符变体 +# =================== +" { } " +"\x09{\x09}\x09" +"\x0a[\x0a]\x0a" +"\x0d\x0a{\x0d\x0a}\x0d\x0a" +"{ \"a\" : 1 }" +"[ 1 , 2 , 3 ]" + +# =================== +# 二进制边界测试 +# =================== "\x00\x00\x00\x00" -"\x00\x00\x00\x00\x00\x00\x00\x00" -"\x00\x00\x00\x00\x00\x00\x00\x01" -"\x01\x00\x00\x00\x00\x00\x00\x00" -"\x10\x00\x00\x00\x00\x00\x00\x00" - -"\xff\xff" -"\xfe\xff\xff\xee" -"\xff\xff\xff\xff" -"\xfe\xff\xff\xff\xff\xff\xff\xfa" -"\xfb\xff\xff\xff\xff\xff\xff\xff" -"\xff\xff\xff\xff\xff\xff\xff\xff" \ No newline at end of file +"\xFF\xFF\xFF\xFF" +"\x01\x00\x00\x00" +"\x00\x00\x00\x01" \ No newline at end of file diff --git a/test/fuzzer/RyanJsonFuzzer.h b/test/fuzzer/RyanJsonFuzzer.h new file mode 100644 index 0000000..8b20894 --- /dev/null +++ b/test/fuzzer/RyanJsonFuzzer.h @@ -0,0 +1,63 @@ +#ifndef RyanJsonFuzzer_h +#define RyanJsonFuzzer_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include "RyanJsonTest.h" +#include + +/** + * @brief 公共宏定义 + */ +#define RyanJsonCheckGotoExit(EX) \ + RyanJsonCheckCode(EX, { \ + result = RyanJsonFalse; \ + goto exit__; \ + }) + +/** + * @brief 全局变量声明 + */ +extern RyanJsonBool_e isEnableRandomMemFail; + +/** + * @brief 内存钩子函数 + */ +extern void *RyanJsonFuzzerMalloc(size_t size); +extern void RyanJsonFuzzerFree(void *block); +extern void *RyanJsonFuzzerRealloc(void *block, size_t size); + +/** + * @brief 解析打印测试函数 + */ +extern RyanJsonBool_e RyanJsonFuzzerTestParse(RyanJson_t pJson, const char *data, uint32_t size); +extern RyanJsonBool_e RyanJsonFuzzerTestMinify(const char *data, uint32_t size); + +/** + * @brief 复制比较测试函数 + */ +extern RyanJsonBool_e RyanJsonFuzzerTestDuplicate(RyanJson_t pJson); + +/** + * @brief 修改操作测试函数 + */ +extern RyanJsonBool_e RyanJsonFuzzerTestModify(RyanJson_t pJson, uint32_t size); +extern RyanJsonBool_e RyanJsonFuzzerTestGet(RyanJson_t pJson, uint32_t size); +extern RyanJsonBool_e RyanJsonFuzzerVerifyGet(RyanJson_t lastJson, RyanJson_t pJson, uint32_t index, uint32_t size); + +/** + * @brief 创建操作测试函数 + */ +extern RyanJson_t RyanJsonFuzzerCreateRandomNode(RyanJson_t pJson); +extern RyanJsonBool_e RyanJsonFuzzerTestCreate(RyanJson_t pJson, uint32_t size); +extern RyanJsonBool_e RyanJsonFuzzerTestReplace(RyanJson_t pJson, uint32_t size); +extern RyanJsonBool_e RyanJsonFuzzerTestDetach(RyanJson_t pJson, uint32_t size); +extern RyanJsonBool_e RyanJsonFuzzerTestDelete(RyanJson_t pJson, uint32_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/fuzzer/RyanJsonFuzzerCreate.c b/test/fuzzer/RyanJsonFuzzerCreate.c new file mode 100644 index 0000000..59d8af4 --- /dev/null +++ b/test/fuzzer/RyanJsonFuzzerCreate.c @@ -0,0 +1,307 @@ +#include "RyanJsonFuzzer.h" + +RyanJson_t RyanJsonFuzzerCreateRandomNode(RyanJson_t pJson) +{ + static int32_t count = 0; + static int32_t count2 = 0; + count++; + char *key = "true"; + if (0 != count % 10 && 5 < count % 10) { key = NULL; } + switch (count % 50) + { + case 0: return RyanJsonCreateArray(); + case 1: return RyanJsonCreateObject(); + case 2: + count2++; + if (0 == count2 % 10) { return RyanJsonDuplicate(pJson); } + // fallthrough + case 11: + case 12: + case 13: return RyanJsonCreateBool(key, RyanJsonTrue); + case 20: + case 21: + case 22: return RyanJsonCreateInt(key, count); + case 31: + case 32: + case 33: return RyanJsonCreateDouble(key, count * 1.123456789); + + default: return RyanJsonCreateString(key, "true"); + } +} + +RyanJsonBool_e RyanJsonFuzzerTestCreate(RyanJson_t pJson, uint32_t size) +{ + // RyanJsonInsert的特殊情况 + RyanJsonAssert(RyanJsonFalse == RyanJsonInsert(NULL, UINT32_MAX, RyanJsonCreateString("key", "string"))); + RyanJsonAssert(RyanJsonFalse == RyanJsonInsert(pJson, UINT32_MAX, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonInsert(NULL, 0, NULL)); + + RyanJsonAssert(NULL == RyanJsonCreateString(NULL, NULL)); + RyanJsonAssert(NULL == RyanJsonCreateString("NULL", NULL)); + + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeStringValue(NULL, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeStringValue(pJson, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeStringValue(NULL, "NULL")); + if (RyanJsonFalse == RyanJsonIsKey(pJson) && RyanJsonFalse == RyanJsonIsString(pJson)) // pJson类型错误 + { + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeStringValue(pJson, "NULL")); + } + + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeKey(NULL, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeKey(pJson, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeKey(NULL, "NULL")); + if (RyanJsonFalse == RyanJsonIsKey(pJson) && RyanJsonFalse == RyanJsonIsString(pJson)) // pJson类型错误 + { + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeKey(pJson, "NULL")); + } + + // 测试没有key但是有strValue的 + if (RyanJsonFalse == RyanJsonIsKey(pJson) && RyanJsonTrue == RyanJsonIsString(pJson)) { RyanJsonAssert(RyanJsonFalse == RyanJsonChangeKey(pJson, "NULL")); } + + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeIntValue(NULL, 0)); + RyanJsonAssert(RyanJsonFalse == RyanJsonChangeDoubleValue(NULL, 0)); + + RyanJsonAssert(RyanJsonFalse == RyanJsonAddItemToObject(NULL, NULL, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonAddItemToObject(pJson, NULL, NULL)); + + RyanJsonAssert(NULL == RyanJsonCreateIntArray(NULL, 0)); + RyanJsonAssert(NULL == RyanJsonCreateDoubleArray(NULL, 0)); + RyanJsonAssert(NULL == RyanJsonCreateStringArray(NULL, 0)); + + RyanJsonAssert(RyanJsonFalse == RyanJsonHasObjectToKey(NULL, "0", "1", "2", "3")); + RyanJsonAssert(RyanJsonFalse == RyanJsonHasObjectToIndex(NULL, 0, 1, 2, 3)); + RyanJsonAssert(RyanJsonFalse == RyanJsonHasObjectToKey(pJson, "0", "1", "2", "3")); + RyanJsonAssert(RyanJsonFalse == RyanJsonHasObjectToIndex(pJson, 0, 1, 2, 3)); + + char *key = "keyaaa"; + RyanJsonAddNullToObject(pJson, key); + if (RyanJsonTrue == RyanJsonIsKey(pJson)) { key = RyanJsonGetKey(pJson); } + if (RyanJsonTrue == RyanJsonIsBool(pJson)) { RyanJsonAddBoolToObject(pJson, key, RyanJsonGetBoolValue(pJson)); } + if (RyanJsonTrue == RyanJsonIsNumber(pJson)) + { + if (RyanJsonTrue == RyanJsonIsInt(pJson)) + { + RyanJsonAddIntToObject(pJson, key, RyanJsonGetIntValue(pJson)); + int32_t arrayInt[] = {RyanJsonGetIntValue(pJson), RyanJsonGetIntValue(pJson), RyanJsonGetIntValue(pJson), + RyanJsonGetIntValue(pJson), RyanJsonGetIntValue(pJson)}; + RyanJsonAddItemToObject(pJson, (0 != size % 2) ? key : "arrayString", + RyanJsonCreateIntArray(arrayInt, sizeof(arrayInt) / sizeof(arrayInt[0]))); + } + if (RyanJsonTrue == RyanJsonIsDouble(pJson)) + { + RyanJsonAddDoubleToObject(pJson, key, RyanJsonGetDoubleValue(pJson)); + double arrayDouble[] = {RyanJsonGetDoubleValue(pJson), RyanJsonGetDoubleValue(pJson), RyanJsonGetDoubleValue(pJson), + RyanJsonGetDoubleValue(pJson), RyanJsonGetDoubleValue(pJson)}; + RyanJsonAddItemToObject(pJson, (0 != size % 2) ? key : "arrayString", + RyanJsonCreateDoubleArray(arrayDouble, sizeof(arrayDouble) / sizeof(arrayDouble[0]))); + } + } + + if (RyanJsonTrue == RyanJsonIsString(pJson)) + { + RyanJsonAddStringToObject(pJson, key, RyanJsonGetStringValue(pJson)); + const char *arrayString[] = {RyanJsonGetStringValue(pJson), RyanJsonGetStringValue(pJson), RyanJsonGetStringValue(pJson), + RyanJsonGetStringValue(pJson), RyanJsonGetStringValue(pJson)}; + RyanJsonAddItemToObject(pJson, (0 != size % 2) ? key : "arrayString", + RyanJsonCreateStringArray(arrayString, sizeof(arrayString) / sizeof(arrayString[0]))); + } + + if (RyanJsonTrue == RyanJsonIsArray(pJson) || RyanJsonTrue == RyanJsonIsObject(pJson)) + { + RyanJson_t item; + + RyanJsonObjectForEach(pJson, item) { RyanJsonFuzzerTestCreate(item, size); } + + RyanJson_t pJson2 = RyanJsonFuzzerCreateRandomNode(pJson); + RyanJsonAddItemToObject(pJson, key, pJson2); + + if (RyanJsonTrue == RyanJsonIsArray(pJson)) + { + RyanJsonAddNullToArray(pJson); + RyanJsonAddBoolToArray(pJson, 0 != size % 2 ? RyanJsonTrue : RyanJsonFalse); + RyanJsonAddItemToArray(pJson, RyanJsonFuzzerCreateRandomNode(RyanJsonGetArrayValue(pJson))); + } + } + + return RyanJsonTrue; +} + +RyanJsonBool_e RyanJsonFuzzerTestReplace(RyanJson_t pJson, uint32_t size) +{ + { + isEnableRandomMemFail = RyanJsonFalse; + RyanJson_t strItem = RyanJsonCreateString("", "NULL"); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(NULL, NULL, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(pJson, NULL, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(NULL, "NULL", NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(NULL, NULL, strItem)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(pJson, "NULL", NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(NULL, "NULL", strItem)); + if (RyanJsonFalse == RyanJsonIsObject(pJson)) // pJson类型错误 + { + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(pJson, "NULL", strItem)); + } + + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(NULL, 0, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(pJson, 0, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(NULL, 0, strItem)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(pJson, 0, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(NULL, 0, strItem)); + if (RyanJsonFalse == RyanJsonIsArray(pJson) && RyanJsonFalse == RyanJsonIsObject(pJson)) // pJson类型错误 + { + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(pJson, 0, strItem)); + } + + RyanJson_t objItem = RyanJsonCreateObject(); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(objItem, "NULL", strItem)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(objItem, 0, strItem)); + + RyanJsonAddItemToObject(objItem, "item", RyanJsonCreateObject()); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByKey(objItem, "NULL222", strItem)); + RyanJsonAssert(RyanJsonFalse == RyanJsonReplaceByIndex(objItem, INT32_MAX, strItem)); + isEnableRandomMemFail = RyanJsonTrue; + + RyanJsonDelete(objItem); + RyanJsonDelete(strItem); + } + + // 只处理数组或对象 + if (RyanJsonFalse == RyanJsonIsArray(pJson) && RyanJsonFalse == RyanJsonIsObject(pJson)) { return RyanJsonTrue; } + + // 递归替换子节点 + RyanJson_t item = NULL; + RyanJson_t LastItem = NULL; + RyanJsonObjectForEach(pJson, item) + { + if (RyanJsonTrue != RyanJsonFuzzerTestReplace(item, size)) { return RyanJsonFalse; } + LastItem = item; + } + + // 只有非根节点才做替换 + + // 按 key 替换(仅对象) + // 不要动第一个节点 + if (RyanJsonTrue == RyanJsonIsObject(pJson)) + { + if (NULL != LastItem && RyanJsonTrue == RyanJsonIsKey(LastItem)) + { + RyanJson_t newNode = RyanJsonFuzzerCreateRandomNode(pJson); + if (RyanJsonFalse == RyanJsonReplaceByKey(pJson, RyanJsonGetKey(LastItem), newNode)) + { + if (NULL != newNode) { RyanJsonDelete(newNode); } + return RyanJsonFalse; + } + } + } + + // 按 index 替换 + { + uint32_t idx = RyanJsonGetSize(pJson) % size; + RyanJson_t newNode = RyanJsonFuzzerCreateRandomNode(pJson); + if (RyanJsonFalse == RyanJsonReplaceByIndex(pJson, (0 != size % 25) ? idx : 0, newNode)) + { + if (NULL != newNode) { RyanJsonDelete(newNode); } + return RyanJsonFalse; + } + } + + return RyanJsonTrue; +} + +RyanJsonBool_e RyanJsonFuzzerTestDetach(RyanJson_t pJson, uint32_t size) +{ + RyanJsonAssert(RyanJsonFalse == RyanJsonDetachByKey(NULL, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonDetachByKey(pJson, NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonDetachByKey(NULL, "NULL")); + if (RyanJsonFalse == RyanJsonIsObject(pJson)) // pJson类型错误 + { + RyanJsonAssert(NULL == RyanJsonDetachByKey(pJson, "NULL")); + } + + RyanJsonAssert(NULL == RyanJsonDetachByIndex(NULL, 10)); + if (RyanJsonFalse == RyanJsonIsArray(pJson) && RyanJsonFalse == RyanJsonIsObject(pJson)) // pJson类型错误 + { + RyanJsonAssert(NULL == RyanJsonDetachByIndex(pJson, 0)); + } + + if (RyanJsonFalse == RyanJsonIsArray(pJson) && RyanJsonFalse == RyanJsonIsObject(pJson)) { return RyanJsonTrue; } + + // 递归遍历子节点 + RyanJson_t item = NULL; + RyanJson_t LastItem = NULL; + RyanJsonObjectForEach(pJson, item) + { + RyanJsonFuzzerTestDetach(item, size); + LastItem = item; + } + + // 只有非根节点才做 detach + + // 按 key 分离(仅对象) + if (RyanJsonTrue == RyanJsonIsObject(pJson)) + { + if (NULL != LastItem && RyanJsonTrue == RyanJsonIsKey(LastItem)) + { + RyanJson_t detached = RyanJsonDetachByKey(pJson, RyanJsonGetKey(LastItem)); + if (NULL != detached) { RyanJsonDelete(detached); } + + // RyanJsonAssert(RyanJsonFalse == RyanJsonDetachByKey(pJson, RyanJsonGetKey(LastItem))); + } + } + + // 按 index 分离 + { + uint32_t idx = RyanJsonGetSize(pJson) % size; + RyanJson_t detached = RyanJsonDetachByIndex(pJson, (0 != size % 25) ? idx : 0); + if (NULL != detached) { RyanJsonDelete(detached); } + } + + return RyanJsonTrue; +} + +RyanJsonBool_e RyanJsonFuzzerTestDelete(RyanJson_t pJson, uint32_t size) +{ + if (RyanJsonFalse == RyanJsonIsArray(pJson) && RyanJsonFalse == RyanJsonIsObject(pJson)) { return RyanJsonTrue; } + + // -------- 测试错误的 delete 调用 -------- + // Key 删除错误用例 + RyanJsonDeleteByKey(pJson, "non_exist_key"); + RyanJsonDeleteByKey(NULL, "some_key"); + RyanJsonDeleteByKey(pJson, NULL); + RyanJsonDeleteByKey(NULL, NULL); + + // Index 删除错误用例 + RyanJsonDeleteByIndex(pJson, RyanJsonGetSize(pJson)); // 越界 + RyanJsonDeleteByIndex(NULL, (RyanJsonGetSize(pJson) % size)); + RyanJsonDeleteByIndex(pJson, (uint32_t)(-(int32_t)size)); // 负数 + RyanJsonDeleteByIndex(NULL, (uint32_t)(-(int32_t)size)); + + // 递归遍历子节点 + RyanJson_t item = NULL; + RyanJson_t LastItem = NULL; + RyanJsonObjectForEach(pJson, item) + { + RyanJsonFuzzerTestDelete(item, size); + LastItem = item; + } + + // -------- 正常删除逻辑(保护根节点) -------- + + // 按 key 删除(仅对象) + if (RyanJsonTrue == RyanJsonIsObject(pJson)) + { + if (NULL != LastItem && RyanJsonTrue == RyanJsonIsKey(LastItem)) + { + + // printf("key is %d %s\r\n", RyanJsonGetType(LastItem), + // RyanJsonGetKey(LastItem) == NULL ? "NULL" : RyanJsonGetKey(LastItem)); + RyanJsonDeleteByKey(pJson, RyanJsonGetKey(LastItem)); + } + } + + // 按 index 删除 + uint32_t idx = RyanJsonGetSize(pJson) % size; + RyanJsonDeleteByIndex(pJson, (0 != size % 25) ? idx : 0); + + return RyanJsonTrue; +} diff --git a/test/fuzzer/RyanJsonFuzzerDup.c b/test/fuzzer/RyanJsonFuzzerDup.c new file mode 100644 index 0000000..ad7df11 --- /dev/null +++ b/test/fuzzer/RyanJsonFuzzerDup.c @@ -0,0 +1,115 @@ +#include "RyanJsonFuzzer.h" + +RyanJsonBool_e RyanJsonFuzzerTestDuplicate(RyanJson_t pJson) +{ + RyanJsonBool_e result = RyanJsonTrue; + char *jsonStr = NULL; + char *jsonStrDup = NULL; + RyanJson_t pJsonDup = NULL; + + // 测试打印和复制功能 + uint32_t len = 0; + uint32_t dupLen = 0; + + jsonStr = RyanJsonPrint(pJson, 100, RyanJsonFalse, &len); + RyanJsonCheckGotoExit(NULL != jsonStr && len > 0); + + pJsonDup = RyanJsonDuplicate(pJson); + RyanJsonCheckGotoExit(NULL != pJsonDup); + + // 测试dup失败情况 + RyanJsonCheckGotoExit(NULL == RyanJsonDuplicate(NULL)); + + // 判断复制json的size是否一致 + RyanJsonCheckGotoExit(0 == RyanJsonGetSize(NULL)); + RyanJsonCheckGotoExit(RyanJsonGetSize(pJson) == RyanJsonGetSize(pJsonDup)); + RyanJsonCompare(pJson, pJsonDup); + RyanJsonCompareOnlyKey(pJson, pJsonDup); + // assert(RyanJsonTrue == RyanJsonCompare(pJson, pJsonDup)); // 大浮点数判断容易出错 + // RyanJsonCheckGotoExit(RyanJsonTrue == RyanJsonCompareOnlyKey(pJson, pJsonDup)); // 重复key也会失败 + + // 测试compare特殊情况 + RyanJsonCheckGotoExit(RyanJsonTrue == RyanJsonCompare(pJson, pJson)); + RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompare(NULL, pJsonDup)); + RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompare(pJson, NULL)); + RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompare(NULL, NULL)); + + // 测试compareKey特殊情况 + RyanJsonCheckGotoExit(RyanJsonTrue == RyanJsonCompareOnlyKey(pJson, pJson)); + RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompareOnlyKey(NULL, pJsonDup)); + RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompareOnlyKey(pJson, NULL)); + RyanJsonCheckGotoExit(RyanJsonFalse == RyanJsonCompareOnlyKey(NULL, NULL)); + + jsonStrDup = RyanJsonPrint(pJsonDup, 100, RyanJsonFalse, &dupLen); // 以带格式方式将数据打印出来 + RyanJsonCheckGotoExit(NULL != jsonStrDup && dupLen > 0); + + RyanJsonCheckCode(len == dupLen && 0 == memcmp(jsonStr, jsonStrDup, (size_t)len), { + printf("len:%" PRIu32 ", dupLen:%" PRIu32 "\r\n", len, dupLen); + printf("jsonStr:%s, jsonStrDup:%s\r\n", jsonStr, jsonStrDup); + RyanJsonCheckGotoExit(0); + }); + + if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) + { + // 测试size不相等 + RyanJsonDelete(RyanJsonDetachByIndex(pJson, 0)); + // 增加分支覆盖率 + if (RyanJsonGetSize(pJson) > 2) { RyanJsonDelete(RyanJsonDetachByIndex(pJson, 1)); } + + if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) + { + // 改变key + RyanJson_t item; + RyanJsonObjectForEach(pJson, item) + { + if (RyanJsonIsKey(item)) + { + RyanJsonChangeKey(item, "key12231123"); + break; + } + } + + // 改变value + RyanJsonObjectForEach(pJson, item) + { + if (RyanJsonIsBool(item)) + { + RyanJsonChangeBoolValue(item, !RyanJsonGetBoolValue(item)); + break; + } + } + + // 改变obj的key + RyanJsonObjectForEach(pJson, item) + { + if (RyanJsonIsKey(item) && RyanJsonIsObject(item)) + { + RyanJsonChangeKey(item, "key12231123"); + break; + } + } + } + + RyanJsonCompare(pJson, pJsonDup); + RyanJsonCompareOnlyKey(pJson, pJsonDup); + } +exit__: + + if (jsonStr) + { + RyanJsonFree(jsonStr); + jsonStr = NULL; + } + if (pJsonDup) + { + RyanJsonDelete(pJsonDup); + pJsonDup = NULL; + } + if (jsonStrDup) + { + RyanJsonFree(jsonStrDup); + jsonStrDup = NULL; + } + + return result; +} diff --git a/test/fuzzer/RyanJsonFuzzerMemory.c b/test/fuzzer/RyanJsonFuzzerMemory.c new file mode 100644 index 0000000..866ee38 --- /dev/null +++ b/test/fuzzer/RyanJsonFuzzerMemory.c @@ -0,0 +1,26 @@ +#include "RyanJsonFuzzer.h" + +static int32_t mallocCount = 0; +static int32_t reallocCount = 0; + +void *RyanJsonFuzzerMalloc(size_t size) +{ + mallocCount++; + if (RyanJsonTrue == isEnableRandomMemFail) + { + if (0 == mallocCount % 598) { return NULL; } + } + return (char *)v_malloc(size); +} + +void RyanJsonFuzzerFree(void *block) { v_free(block); } + +void *RyanJsonFuzzerRealloc(void *block, size_t size) +{ + reallocCount++; + if (RyanJsonTrue == isEnableRandomMemFail) + { + if (0 == reallocCount % 508) { return NULL; } + } + return (char *)v_realloc(block, size); +} diff --git a/test/fuzzer/RyanJsonFuzzerModify.c b/test/fuzzer/RyanJsonFuzzerModify.c new file mode 100644 index 0000000..c82eee8 --- /dev/null +++ b/test/fuzzer/RyanJsonFuzzerModify.c @@ -0,0 +1,132 @@ +#include "RyanJsonFuzzer.h" + +RyanJsonBool_e RyanJsonFuzzerTestModify(RyanJson_t pJson, uint32_t size) +{ + RyanJsonIsNull(pJson); + + if (RyanJsonIsKey(pJson)) + { + char *key = (char *)malloc(strlen(RyanJsonGetKey(pJson)) + 1); + if (key) + { + memcpy(key, RyanJsonGetKey(pJson), strlen(RyanJsonGetKey(pJson))); + key[strlen(RyanJsonGetKey(pJson))] = 0; + + RyanJsonChangeKey(pJson, "key"); + RyanJsonChangeKey(pJson, key); + free(key); + } + } + if (RyanJsonIsBool(pJson)) { RyanJsonChangeBoolValue(pJson, !RyanJsonGetBoolValue(pJson)); } + if (RyanJsonIsNumber(pJson)) + { + if (RyanJsonIsInt(pJson)) + { + int32_t value = RyanJsonGetIntValue(pJson); + RyanJsonChangeIntValue(pJson, (int32_t)size); + RyanJsonChangeIntValue(pJson, value); + } + if (RyanJsonIsDouble(pJson)) + { + double value = RyanJsonGetDoubleValue(pJson); + RyanJsonChangeDoubleValue(pJson, size * 1.123456789); + RyanJsonChangeDoubleValue(pJson, value); + } + } + + if (RyanJsonIsString(pJson)) + { + char *value = (char *)malloc(strlen(RyanJsonGetStringValue(pJson)) + 1); + if (value) + { + memcpy(value, RyanJsonGetStringValue(pJson), strlen(RyanJsonGetStringValue(pJson))); + value[strlen(RyanJsonGetStringValue(pJson))] = 0; + + RyanJsonChangeStringValue(pJson, "hello world"); + RyanJsonChangeStringValue(pJson, "h"); + RyanJsonChangeStringValue(pJson, value); + + free(value); + } + } + + if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) + { + RyanJson_t item; + RyanJsonArrayForEach(pJson, item) + { + RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonFuzzerTestModify(item, size)); + } + } + + return RyanJsonTrue; +} + +RyanJsonBool_e RyanJsonFuzzerVerifyGet(RyanJson_t lastJson, RyanJson_t pJson, uint32_t index, uint32_t size) +{ + RyanJsonIsNull(pJson); + + RyanJsonAssert(NULL == RyanJsonGetKey(NULL)); + RyanJsonAssert(NULL == RyanJsonGetStringValue(NULL)); + RyanJsonAssert(0 == RyanJsonGetIntValue(NULL)); + RyanJsonAssert(RyanJsonTrue == RyanJsonCompareDouble(RyanJsonGetDoubleValue(NULL), 0.0)); + RyanJsonAssert(NULL == RyanJsonGetObjectValue(NULL)); + RyanJsonAssert(NULL == RyanJsonGetArrayValue(NULL)); + + RyanJsonAssert(NULL == RyanJsonGetObjectByKey(NULL, NULL)); + RyanJsonAssert(NULL == RyanJsonGetObjectByKey(pJson, NULL)); + RyanJsonAssert(NULL == RyanJsonGetObjectByKey(NULL, "NULL")); + + RyanJsonAssert(NULL == RyanJsonGetObjectByKeys(NULL, NULL)); + RyanJsonAssert(NULL == RyanJsonGetObjectByKeys(pJson, NULL)); + RyanJsonAssert(NULL == RyanJsonGetObjectByKeys(NULL, "NULL")); + if (!RyanJsonIsObject(pJson)) // pJson类型错误 + { + RyanJsonAssert(NULL == RyanJsonGetObjectByKey(pJson, "NULL")); + } + + RyanJsonAssert(NULL == RyanJsonGetObjectByIndex(NULL, 10)); + if (!RyanJsonIsArray(pJson) && !RyanJsonIsObject(pJson)) // pJson类型错误 + { + RyanJsonAssert(NULL == RyanJsonGetObjectByIndex(pJson, 0)); + } + + if (RyanJsonIsKey(pJson)) { RyanJsonGetObjectToKey(lastJson, RyanJsonGetKey(pJson)); } + else + { + RyanJsonGetObjectToIndex(lastJson, index); + } + + if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) + { + RyanJson_t item; + RyanJsonObjectForEach(pJson, item) { RyanJsonFuzzerVerifyGet(pJson, item, index, size); } + } + + return RyanJsonTrue; +} + +RyanJsonBool_e RyanJsonFuzzerTestGet(RyanJson_t pJson, uint32_t size) +{ + RyanJsonAssert(RyanJsonFalse == RyanJsonIsKey(NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonIsNull(NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonIsBool(NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonIsNumber(NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonIsString(NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonIsArray(NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonIsObject(NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonIsInt(NULL)); + RyanJsonAssert(RyanJsonFalse == RyanJsonIsDouble(NULL)); + + if (RyanJsonIsArray(pJson) || RyanJsonIsObject(pJson)) + { + RyanJson_t item; + uint32_t index = 0; + RyanJsonObjectForEach(pJson, item) + { + RyanJsonFuzzerVerifyGet(pJson, item, index, size); + index++; + } + } + return RyanJsonTrue; +} diff --git a/test/fuzzer/RyanJsonFuzzerParse.c b/test/fuzzer/RyanJsonFuzzerParse.c new file mode 100644 index 0000000..552d8ec --- /dev/null +++ b/test/fuzzer/RyanJsonFuzzerParse.c @@ -0,0 +1,126 @@ +#include "RyanJsonFuzzer.h" + +RyanJsonBool_e RyanJsonFuzzerTestParse(RyanJson_t pJson, const char *data, uint32_t size) +{ + isEnableRandomMemFail = RyanJsonFalse; + RyanJson_t testItem = RyanJsonCreateObject(); + RyanJson_t testItem2 = RyanJsonCreateObject(); + RyanJsonSetType(testItem, 0); + RyanJsonSetType(testItem2, 0); + RyanJsonAssert(NULL == RyanJsonPrint(testItem, 100, RyanJsonFalse, NULL)); + RyanJsonAssert(NULL == RyanJsonDuplicate(testItem)); + RyanJsonAssert(RyanJsonFalse == RyanJsonCompare(testItem, testItem2)); + RyanJsonAssert(RyanJsonFalse == RyanJsonCompareOnlyKey(testItem, testItem2)); + + // 测试pJson类型错误情况 + RyanJsonAssert(RyanJsonFalse == RyanJsonInsert(testItem, 0, RyanJsonCreateString("key", "true"))); + RyanJsonAssert(RyanJsonFalse == RyanJsonInsert(testItem2, UINT32_MAX, RyanJsonCreateString("key", "true"))); + + RyanJsonSetType(testItem, RyanJsonTypeObject); + RyanJsonSetType(testItem2, RyanJsonTypeObject); + + // 测试pJson为obj,但是item没有key的情况 + RyanJsonAssert(RyanJsonFalse == RyanJsonInsert(pJson, 0, RyanJsonCreateString(NULL, "true"))); + RyanJsonAssert(RyanJsonFalse == RyanJsonInsert(pJson, UINT32_MAX, RyanJsonCreateString(NULL, "true"))); + + RyanJsonAssert(RyanJsonTrue == RyanJsonInsert(pJson, 0, RyanJsonCreateString("key", "true"))); + RyanJsonDelete(testItem); + RyanJsonDelete(testItem2); + isEnableRandomMemFail = RyanJsonTrue; + + RyanJsonAssert(NULL == RyanJsonPrint(NULL, 100, RyanJsonFalse, NULL)); + RyanJsonAssert(NULL == RyanJsonPrintPreallocated(NULL, NULL, 100, RyanJsonFalse, NULL)); + RyanJsonAssert(NULL == RyanJsonPrintPreallocated(pJson, NULL, 100, RyanJsonFalse, NULL)); + RyanJsonAssert(NULL == RyanJsonPrintPreallocated(NULL, (char *)data, 100, RyanJsonFalse, NULL)); + RyanJsonAssert(NULL == RyanJsonPrintPreallocated(pJson, (char *)data, 0, RyanJsonFalse, NULL)); + + uint32_t len = 0; + char *jsonStr = RyanJsonPrint(pJson, size % 5 ? 100 : 0, size % 2 ? RyanJsonFalse : RyanJsonTrue, &len); + RyanJsonCheckReturnFalse(NULL != jsonStr && len > 0); + + char *jsonStrCopy = RyanJsonPrint(pJson, size % 5 ? 100 : 0, size % 2 ? RyanJsonFalse : RyanJsonTrue, NULL); // 不传递len + RyanJsonAssert(0 == strncmp(jsonStr, jsonStrCopy, len)); + RyanJsonFree(jsonStr); + RyanJsonFree(jsonStrCopy); + + uint32_t bufLen = len * 3; + if (bufLen < size * 2) { bufLen = size * 2; } + if (bufLen < 2048) { bufLen = 2048; } + char *buf = (char *)malloc((size_t)bufLen); + { + uint32_t len2 = 0; + char *jsonStr2 = RyanJsonPrintPreallocated(pJson, buf, bufLen, size % 2 ? RyanJsonFalse : RyanJsonTrue, &len2); + // printf("len: %d, len2: %d, str: %s\r\n", len, len2, NULL == jsonStr2 ? "NULL" : jsonStr2); + RyanJsonCheckCode(NULL != jsonStr2 && len == len2, { + free(buf); + return RyanJsonFalse; + }); + } + + memcpy(buf, data, (size_t)size); + buf[size] = 0; + RyanJson_t jsonRoot = RyanJsonParse(buf); + RyanJsonCheckCode(NULL != jsonRoot, { + free(buf); + return RyanJsonFalse; + }); + + // 测试多次打印结果是否一致 + { + uint32_t len3 = 0; + char *jsonStr3 = RyanJsonPrint(jsonRoot, 100, size % 2 ? RyanJsonFalse : RyanJsonTrue, &len3); // 以带格式方式将数据打印出来 + RyanJsonCheckCode(NULL != jsonStr3 && len == len3, { + free(buf); + if (jsonStr3) { RyanJsonFree(jsonStr3); } + RyanJsonDelete(jsonRoot); + return RyanJsonFalse; + }); + + RyanJsonFree(jsonStr3); + } + + { + RyanJsonPrintPreallocated(jsonRoot, buf, bufLen / 15, RyanJsonTrue, NULL); + } + + free(buf); + RyanJsonDelete(jsonRoot); + return RyanJsonTrue; +} + +RyanJsonBool_e RyanJsonFuzzerTestMinify(const char *data, uint32_t size) +{ + char *buf = (char *)malloc(size + 100); + memcpy(buf, data, size); + memset(buf + size, 0, 100); + + uint32_t size2 = RyanJsonMinify(buf, (int32_t)size); + // 非法情况 + { + RyanJsonCheckReturnFalse(0 == RyanJsonMinify(NULL, 0)); + RyanJsonCheckReturnFalse(0 == RyanJsonMinify(NULL, 10)); + RyanJsonCheckReturnFalse(0 == RyanJsonMinify(NULL, -10)); + RyanJsonCheckReturnFalse(0 == RyanJsonMinify(buf, -10)); + } + + // 内存泄漏就是上面出错了 + RyanJson_t pJson2 = RyanJsonParseOptions(buf, size2, size % 2 ? RyanJsonTrue : RyanJsonFalse, NULL); + free(buf); + if (NULL != pJson2) + { + uint32_t len = 0; + char *jsonStr = RyanJsonPrint(pJson2, 100, RyanJsonFalse, &len); // 以带格式方式将数据打印出来 + RyanJsonCheckCode(NULL != jsonStr && len > 0, { + RyanJsonDelete(pJson2); + return RyanJsonFalse; + }); + RyanJsonFree(jsonStr); + RyanJsonDelete(pJson2); + } + else + { + return RyanJsonFalse; + } + + return RyanJsonTrue; +} diff --git a/xmake.lua b/xmake.lua index b1c270f..5aa7af6 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,68 +1,73 @@ +-- 自动生成 compile_commands.json,方便 VSCode/Clangd 做代码补全与跳转 add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"}) -target("RyanJson",function() + +target("RyanJson", function() + -- 目标类型:二进制可执行文件 set_kind("binary") - -- set_toolchains("gcc") -- 确保使用 GCC - set_toolchains("clang") - set_plat("linux") - set_arch("x86") - set_languages("gnu99") -- 关键!启用 GNU 扩展 - add_defines("isEnableFuzzer") - add_cxflags("-fsanitize=fuzzer", "-fprofile-instr-generate", "-fcoverage-mapping", {force = true} ) - add_ldflags("-fsanitize=fuzzer", "-fprofile-instr-generate", "-fcoverage-mapping", {force = true} ) + -- 编译工具链与平台配置 + -- set_toolchains("gcc") -- 使用 GCC + set_toolchains("clang") -- 使用 Clang 编译器 + set_plat("linux") -- 平台:Linux + set_arch("x86") -- 架构:x86(32位) + set_languages("gnu99") -- 使用 GNU C99 标准,启用 GNU 扩展 - set_policy("build.ccache", false) - -- set_optimize("smallest") -- -Os - -- set_optimize("faster") -- -O2 - set_optimize("fastest") -- -O3 - -- set_optimize("aggressive") -- -Ofast + -- 编译优化策略 + set_policy("build.ccache", false) -- 禁用 ccache 缓存 + set_optimize("fastest") -- 使用 -O3,最高级别优化 - -- 启用全部警告 - set_warnings("everything") -- -Wall -Wextra -Weffc++ / -Weverything + -- 警告设置:启用所有警告(Clang 下相当于 -Weverything) + set_warnings("everything") - -- 链接器选项:生成 map 文件 - -- add_ldflags("-Wl,-Map=$(buildir)/RyanJson.map") + add_defines("RyanJsonSnprintfSupportScientific=true") + add_defines("RyanJsonLinuxTestEnv") - -- 开启库加固(需与 -O2 以上配合) - -- add_defines("_FORTIFY_SOURCE=2") -- glibc 格式/内存函数加固 + -- 定义宏:启用 Fuzzer 功能 + -- Fuzzer 与覆盖率相关编译/链接选项 + add_defines("isEnableFuzzer") + add_cxflags("-fsanitize=fuzzer", {force = true}) + add_ldflags("-fsanitize=fuzzer", {force = true}) + add_cxflags("-fprofile-instr-generate", "-fcoverage-mapping", {force = true}) + add_ldflags("-fprofile-instr-generate", "-fcoverage-mapping", {force = true}) - -- 链接器安全硬化与优化 + -- 链接器安全硬化与优化选项 add_ldflags( - "-flto", -- 链接时优化(启用 LTO,便于 CFI 等) + "-flto", -- 启用 LTO(链接时优化) "-fPIE", -- 位置无关可执行 "-pie", -- 与 -fPIE 搭配,启用 ASLR - "-fno-omit-frame-pointer", -- 保留帧指针,便于崩溃分析 - "-fstack-clash-protection", -- 栈碰撞保护(平台支持时有效) - "-Wl,-z,relro", -- 只读重定位(硬化) - "-Wl,-z,now", -- 立即绑定(与 relro 搭配) + "-fno-omit-frame-pointer", -- 保留帧指针,便于调试和崩溃分析 + "-fstack-clash-protection", -- 栈碰撞保护 + "-Wl,-z,relro", -- 重定位表只读 + "-Wl,-z,now", -- 立即绑定符号 "-Wl,-z,noexecstack", -- 栈不可执行 - "-Wl,-z,separate-code", -- 代码与数据段分离 + "-Wl,-z,separate-code", -- 代码段与数据段分离 {force = true} ) - -- Sanitizer 检测项(运行时错误) + -- Sanitizer 检测项:运行时错误检测 + add_cxflags("-fsanitize=alignment", "-fno-sanitize-recover=undefined", {force = true}) add_ldflags( - "-fsanitize=address", -- 内存越界、释放后使用 - "-fsanitize=leak", -- 内存泄漏 - "-fsanitize=undefined", -- 未定义行为(除零、溢出、无效移位等) - "-fsanitize=pointer-compare", -- 无效指针比较 - "-fsanitize=pointer-subtract", -- 无效指针相减 - "-fsanitize=bounds", -- 数组越界 - "-fsanitize=float-divide-by-zero", -- 浮点除零 - "-fsanitize=float-cast-overflow", -- 浮点转整数溢出 + "-fsanitize=alignment", -- 检查未对齐访问 + "-fno-sanitize-recover=undefined", -- 遇到未定义行为立即终止 + "-fsanitize=address", -- 内存越界、释放后使用 + "-fsanitize=leak", -- 内存泄漏 + "-fsanitize=undefined", -- 常见未定义行为 + "-fsanitize=pointer-compare", -- 无效指针比较 + "-fsanitize=pointer-subtract", -- 无效指针相减 + "-fsanitize=bounds", -- 数组越界 + "-fsanitize=float-divide-by-zero", -- 浮点除零 + "-fsanitize=float-cast-overflow", -- 浮点转整数溢出 -- "-fsanitize=thread", -- 多线程数据竞争 -- "-fsanitize=memory", -- 未初始化内存使用 -- "-fsanitize=safe-stack", -- 栈分离机制 -- "-fsanitize=cfi", -- 控制流完整性(需 LTO 与 Clang) - -- "-fsanitize=alignment", -- 检测未对齐的内存访问 - -- "-fno-sanitize=alignment", -- 某些平台不兼容 {force = true} ) - -- 编译器警告与静态分析(开发期错误检测,Clang 兼容) + -- 编译器警告与静态分析 add_cxflags( - "-g3", -- 生成调试信息" - "-pedantic", -- 强制遵循 ISO C 标准 + "-g3", -- 生成详细的调试信息 + "-pedantic", -- 严格遵循 ISO C 标准 "-Wall", -- 常见警告 "-Wextra", -- 额外警告 "-Wconversion", -- 隐式类型转换风险 @@ -72,12 +77,12 @@ target("RyanJson",function() "-Wold-style-definition", -- 检测旧式函数定义 "-Wimplicit-fallthrough", -- switch/case 未显式 fallthrough "-Wshadow", -- 局部变量遮蔽 - "-Wcast-align", -- 类型转换对齐问题 + "-Wcast-align", -- 类型转换可能导致未对齐 "-Wpointer-arith", -- 指针运算风险 "-Warray-bounds", -- 数组越界访问 - "-Wshift-overflow", -- 位移造成的溢出 - "-Wformat-truncation", -- 格式化字符串被截断风险(替代 stringop-truncation) - "-Walloc-size", -- 分配大小问题(替代 alloc-zero) + "-Wshift-overflow", -- 位移溢出 + "-Wformat-truncation", -- 格式化字符串截断风险 + "-Walloc-size", -- 分配大小问题 "-Wnull-dereference", -- 空指针解引用 "-Wtautological-compare", -- 恒真/恒假的比较 "-Wstrict-overflow", -- 有符号溢出优化假设 @@ -86,49 +91,61 @@ target("RyanJson",function() "-Wredundant-decls", -- 重复声明 "-Wunreachable-code", -- 不可达代码 "-Wtype-limits", -- 比较恒真/恒假的表达式(如 unsigned < 0) - "-Wshift-negative-value", -- 对负数进行移位 + "-Wshift-negative-value", -- 对负数移位 "-Wdiv-by-zero", -- 除以零(编译期可分析) "-Wformat-security", -- 格式化字符串安全问题 "-Wdisabled-optimization", -- 被禁用的优化 "-Wreturn-local-addr", -- 返回局部变量地址 - "-Wdeprecated", -- 使用已弃用的特性 - -- "-Wunsafe-buffer-usage", -- 不安全的数组/指针用法(Clang 新增) + "-Wdeprecated", -- 使用已弃用特性 + -- "-Wunsafe-buffer-usage", -- 不安全的数组/指针用法 "-Wuninitialized", -- 使用未初始化变量 "-fstack-protector-strong",-- 栈保护 - -- 进一步增强(可选,按需开启) - "-Wmissing-include-dirs", -- 头文件目录缺失 - "-Wcast-qual", -- 丢弃 const/volatile 限定符的转换 + "-Wmissing-include-dirs", -- 头文件目录缺失 + "-Wcast-qual", -- 丢弃 const/volatile 限定符 "-Wconditional-uninitialized", -- 条件路径未初始化 "-Wcovered-switch-default", -- default 覆盖所有枚举值 - "-Wformat-nonliteral", -- 非字面量格式串 - "-Wformat-signedness", -- 格式化与符号性不匹配 - "-Wvla", -- 可变长度数组(不安全/不建议使用) - "-fno-common", -- 禁止旧式多重定义(链接期更严格) + "-Wformat-nonliteral", -- 非字面量格式串 + "-Wformat-signedness", -- 格式化与符号性不匹配 + "-Wvla", -- 可变长度数组 + "-fno-common", -- 禁止旧式多重定义 "-fno-strict-aliasing", -- 禁止严格别名优化,减少别名相关 UB 风险 - "-Wno-documentation", -- 临时变比 - "-Wno-parentheses-equality", + "-Wdocumentation", + "-Wparentheses-equality", + "-Wno-documentation", -- 临时关闭文档警告 + -- "-Wno-parentheses-equality", -- 临时关闭括号比较警告 + "-Wno-extra-semi-stmt", -- 关闭分号警告 + "-Wno-unsafe-buffer-usage", -- 关闭不安全的数组/指针用法警告 + "-Wno-declaration-after-statement", -- 关闭声明在语句后的警告 + "-Wno-padded", -- 关闭结构体填充警告 + "-Wno-switch-default", -- 关闭 switch 语句缺少 default 的警告 + "-Wno-unused-macros", -- 关闭未使用的宏定义警告 + "-Wno-unused-includes", -- 关闭未使用的头文件警告 {force = true} ) - add_includedirs('./test/fuzzer', {public = true}) - add_files('./test/fuzzer/*.c', {public = true}) - - --加入代码和头文件 + -- 头文件 add_includedirs('./RyanJson', {public = true}) - add_files('./RyanJson/*.c', {public = true}) - add_includedirs('./example', {public = true}) + add_includedirs('./test/fuzzer', {public = true}) add_includedirs('./test', {public = true}) - add_includedirs('./test/valloc', {public = true}) add_includedirs('./test/baseTest', {public = true}) - add_includedirs('./externalModule/cJSON', {public = true}) - add_includedirs('./externalModule/yyjson', {public = true}) + add_includedirs('./test/baseTest/equality', {public = true}) + add_includedirs('./test/RFC8259Test', {public = true}) + add_includedirs('./test/externalModule/valloc', {public = true}) + add_includedirs('./test/externalModule/tlsf', {public = true}) + add_includedirs('./test/externalModule/cJSON', {public = true}) + add_includedirs('./test/externalModule/yyjson', {public = true}) + -- 源文件 + add_files('./RyanJson/*.c', {public = true}) add_files('./example/*.c', {public = true}) - add_files('./test/*.c', {public = true}, {cxflags = "-w"}) - add_files('./test/valloc/*.c', {public = true}, {cxflags = "-w"}) - add_files('./test/baseTest/*.c', {public = true}, {cxflags = "-w"}) - add_files('./externalModule/cJSON/*.c', {public = true}, {cxflags = "-w"}) - add_files('./externalModule/yyjson/*.c', {public = true}, {cxflags = "-w"}) - -end) \ No newline at end of file + add_files('./test/fuzzer/*.c', {public = true}) + add_files('./test/*.c', {public = true}, {cxflags = "-w"}) -- 测试代码,关闭警告 + add_files('./test/baseTest/*.c', {public = true}, {cxflags = "-w"}) -- 基础测试,关闭警告 + add_files('./test/baseTest/equality/*.c', {public = true}, {cxflags = "-w"}) -- 一致性测试 + add_files('./test/RFC8259Test/*.c', {public = true}, {cxflags = "-w"}) -- + add_files('./test/externalModule/valloc/*.c', {public = true}, {cxflags = "-w"}) -- valloc,关闭警告 + add_files('./test/externalModule/tlsf/*.c', {public = true}, {cxflags = "-w"}) -- tlsf,关闭警告 + add_files('./test/externalModule/cJSON/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 cJSON,关闭警告 + add_files('./test/externalModule/yyjson/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 yyjson,关闭警告 +end)