Skip to content

Conversation

@Ryan-CW-Code
Copy link
Owner

@Ryan-CW-Code Ryan-CW-Code commented Jan 6, 2026

Summary by CodeRabbit

  • New Features

    • 新增统一且类型安全的公开取/改值接口、可配置内存钩子、RFC‑8259 合规性测试入口与完整模糊测试套件。
  • Enhancements

    • 完成 README 与设计文档重写,详述内存布局与存储策略;引入基于 TLSF 的测试堆管理,扩展字符串/数值/布尔回归用例与模糊词典。
    • 构建系统与编辑器配置改进,自动生成编译数据库并增强覆盖采集脚本。
  • Chores

    • 多处测试、示例与工具化脚本重组与清理。

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

Walkthrough

引入节点内联/指针混合存储与类型化公有 getter/setter,注册可替换内存钩子并集成 TLSF 分配器;大幅扩展并重组测试(RFC8259、相等性、模糊器);调整构建、编辑器配置与文档说明。(<=50字)

Changes

Cohort / File(s) 变更摘要
构建与编辑器配置
.clang-tidy, .clangd, .vscode/settings.json, xmake.lua, Makefile, run_base_coverage.sh, .clang-format-ignore, run_coverage.sh
调整 clang-tidy 检查规则与 clangd/VSCode 设置;启用 compile_commands 自动生成;重写 xmake/Makefile 构建配置、覆盖采集脚本与 coverage 选项;收紧 clang-format 忽略路径。
核心库(实现与头)
RyanJson/RyanJson.c, RyanJson/RyanJson.h, RyanJson/RyanJsonConfig.h, RyanJson/RyanJsonUtils.h, RyanJson/RyanJsonUtils.c
引入 inline vs ptr 混合存储(bit7 标志与访问宏);新增/替换为类型化公有 getter/setter(int/double/string/object/array)与 RyanJsonInitHooks;重构解析/打印、数值处理与内存路径;调整配置宏(MallocHeaderSize/Align、嵌套限制、容差等)。
TLSF 分配器与适配层
test/externalModule/tlsf/tlsf.h, .../tlsf.c, .../tlsf_block_functions.h, .../tlsf_control_functions.h, .../rtthread.h
新增完整 TLSF 实现与头文件:池管理、对齐分配、in-place realloc、遍历/检测、内存统计与公共 API,用作测试/运行时的自定义分配器。
测试套件:内存/钩子/基线改动
test/RyanJsonMemoryFootprintTest.c, test/RyanJsonTest.c, test/RyanJsonTest.h, test/externalModule/valloc/*, test/externalModule/tlsf/*
将测试分配钩子迁移到 TLSF(v_malloc_tlsf/...),更新内存计量函数为 vallocGetUseByTlsf,增加 TLSF 诊断显示与若干测试签名变更,扩展内存足迹场景。
RFC8259 测试与语义工具
test/RFC8259Test/RyanJsonRFC8259JsonTest.c, test/RFC8259Test/RyanJsonRFC8259TestUtil.c, test/RFC8259Test/RyanJsonRFC8259TestUtil.h
新增 RFC8259 测试框架与工具:文件遍历、UTF‑8 归一化、标量/值语义比较、单元素数组提取,并添加 RFC8259JsonTest 入口。
基础测试重组与新增相等性/模糊测试
test/baseTest/*, test/baseTest/equality/*, test/fuzzer/*, test/fuzzer/*.dict
重组 baseTest 注册;新增相等性测试(Bool/Double/Int/String);大幅扩展并重构模糊测试(多源文件、内存故障注入、创建/替换/删除/遍历/复制等用例);更新词典样例。
示例与接口适配
example/RyanJsonExample.c
示例函数返回类型改为 RyanJsonBool_e 并使用新布尔返回约定,增强验证与错误处理;初始化钩子调用调整。
文档
README.md
重写内存布局与内联/指针阈值描述、节点位域说明、测试/基准与兼容性注记,更新示例与工具指引。
杂项与格式
.gitignore, test/externalModule/valloc/*, 若干头文件
微调 .gitignore;若干 include-guard 宏重命名;新增/修改外部模块头与宏,少量格式与注释调整。

Sequence Diagram(s)

sequenceDiagram
    participant Parser as JSON解析器
    participant Storage as 存储层 (节点)
    participant TLSF as TLSF分配器
    participant NodeAPI as 公有 Node API

    Parser->>Storage: 解析并请求创建/填充节点
    Storage->>Storage: 检查负载大小并设置 bit7 (inline 或 ptr)
    alt 内联(小载荷)
        Storage->>Storage: 在节点内写入数据(无需外部分配)
    else 指针(大载荷)
        Storage->>TLSF: 请求分配内存
        TLSF-->>Storage: 返回指针
        Storage->>Storage: 存储指针并标记 bit7
    end
    Client->>NodeAPI: 调用 Get/Change 接口
    NodeAPI->>Storage: 根据 bit7 选择 inline 或 间接路径读取/修改
    alt 释放或重分配
        NodeAPI->>TLSF: 调用 tlsf_free / tlsf_realloc
    end
Loading
sequenceDiagram
    participant Test as 测试框架
    participant TLSF as TLSF 内存器
    participant FileSys as 测试文件系统
    participant Ryan as RyanJson
    participant cJSON as cJSON
    participant yyjson as yyjson
    participant Compare as 语义比较器

    Test->>TLSF: 初始化 TLSF 并注册分配钩子
    Test->>FileSys: 遍历 RFC8259 测试文件
    loop 每个测试文件
        FileSys->>Test: 提供 JSON 数据
        par RyanJson
            Test->>Ryan: 解析与序列化
            Ryan->>Compare: 语义比较(原始 vs 序列化)
        and cJSON
            Test->>cJSON: 解析与序列化
            cJSON->>Compare: 语义比较
        and yyjson
            Test->>yyjson: 解析与序列化
            yyjson->>Compare: 语义比较
        end
        Test->>TLSF: 查询并记录内存足迹
    end
    Test->>Test: 汇总并报告结果
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Next #6 — 涉及对 RyanJson 公共 API、头文件、测试/模糊器与 TLSF/测试基础设施的相似改动,可能有重叠实现或历史上下文。

🐰 代码地里新洞开,
bit7 插旗把路来,
小串入腹大串借,TLSF 堆里量摆排;
测试跑过 RFC 与等价,文档更新夜未灰;
我在键盘边蹦跳,欢喜把变更埋。

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive PR标题"Next"过于简洁且缺乏描述性,未能清晰传达本次变更的主要内容。 建议使用更具体的标题来概括主要变更,例如"Add inline string storage and public API getters for RyanJson"或"Integrate TLSF memory allocator and refactor test infrastructure"。
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch next

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @Ryan-CW-Code, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在通过引入智能字符串存储策略和集成先进的 TLSF 内存分配器来大幅优化 RyanJson 库的内存使用效率,尤其关注嵌入式环境下的性能表现。此外,它还通过更新 VSCode 配置和 xmake.lua 构建脚本,显著提升了开发体验和代码质量保障,包括更全面的静态分析、运行时错误检测和安全硬化措施。

Highlights

  • 内存管理优化: 引入了字符串的内联/指针存储模式,显著减少了小字符串的内存分配次数,降低了 malloc 的开销,特别适用于嵌入式系统。同时,集成了 TLSF (Two-Level Segregated Fit) 内存分配器,并在测试中启用,以提供更高效和碎片更少的内存管理方案。
  • 开发环境增强: 新增 .clangd 配置文件,为 VSCode 提供了更强大的 Clangd 支持,包括后台索引和禁用不必要的诊断。更新了 .vscode/settings.json,禁用了 Lua 语言服务,并配置了 Clangd 作为主要的 C/C++ 智能感知引擎,同时关闭了微软 C/C++ 扩展的部分功能,并为 Clang-Tidy 添加了目标架构参数。
  • 构建系统与代码质量: 更新了 xmake.lua 构建脚本,引入了更严格的编译器警告(everything)、运行时 Sanitizer 检查(如 alignmentaddressleakundefined 等),以及链接器安全硬化选项(如 LTO、PIE、栈保护等),全面提升了代码质量和安全性检查。
  • 代码重构与可维护性: 将 RyanJson.c 中多个宏定义的操作(如获取/设置值、创建节点)重构为独立的函数,增强了类型安全性和代码可读性。同时,统一了错误处理的 goto 标签,并优化了 RyanJsonNode 结构体的注释,详细解释了内存布局和存储策略。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🤖 Fix all issues with AI Agents
In @.clangd:
- Around line 1-6: The .clangd config points CompilationDatabase to ".vscode"
but compile_commands.json is missing; fix by either generating a compilation
database (e.g., enable CMake's CMAKE_EXPORT_COMPILE_COMMANDS or run
bear/scan-build to create compile_commands.json) and placing it in the ".vscode"
directory, or update the CompilationDatabase field in .clangd to the actual
directory containing the generated compile_commands.json (or remove the explicit
setting to use the project root). Ensure the file is named exactly
compile_commands.json so clangd can discover it.

In @README.md:
- Line 31: The bold markdown on the README line "RyanJson设计时借鉴了
[json](https://api.gitee.com/Lamdonn/json) 和
[cJSON](https://github.com/DaveGamble/cJSON) !" has an extra space before the
closing ** which breaks emphasis; remove the space before the final ** (so the
closing delimiter immediately follows the exclamation mark) or reformat to a
correct markdown bold/italic pattern to ensure proper rendering of the phrase
and links.

In @RyanJson/RyanJson.c:
- Around line 2010-2022: The change functions lack a type check and can
overwrite non-number nodes; update RyanJsonChangeIntValue and
RyanJsonChangeDoubleValue to first verify the node is a numeric type (e.g., call
RyanJsonGetType(pJson) or the project’s equivalent and compare to the Number
numeric enum) and return RyanJsonFalse if not a number, then proceed to call
RyanJsonMemcpy(RyanJsonGetValue(pJson), ...) only when the type check passes;
keep the existing RyanJsonCheckReturnFalse(NULL != pJson) precondition and
ensure both functions mirror this validation.

In @test/fuzzer/RyanJsonFuzzer.c:
- Around line 17-18: The test casts away const when calling
RyanJsonPrintPreallocated, hiding a type-safety bug; either change the API or
the test: if RyanJsonPrintPreallocated does not write to the buffer, update its
signature to take const char *buffer (update declaration/definition and any
callers to match); otherwise, in the fuzzer replace the (char *) cast by
allocating a real writable buffer (e.g., malloc or a local char array) and pass
that buffer to RyanJsonPrintPreallocated, and free it after use; update tests to
exercise the correct buffer type rather than removing const with a cast.

In @test/RyanJsonMemoryFootprintTest.c:
- Around line 242-250: The printed test title is duplicated; update the printf
call that currently prints "--------------------------- 小对象json 纯字符串内存占用测试
--------------------------" (the string passed just before jsonstr and the
printfJsonCompera(jsonstr) call) to a distinct, descriptive title (e.g.,
"复杂对象json 纯字符串内存占用测试" or "中等对象json 混合类型内存占用测试") so this test is clearly
differentiated from the earlier one while leaving jsonstr and the
printfJsonCompera(jsonstr) invocation unchanged.

In @test/tlsf/tlsf_block_functions.h:
- Around line 1-11: The header tlsf_block_functions.h is missing required
includes for ptrdiff_t, size_t and the rt_always_inline macro; add #include
<stddef.h> to provide size_t/ptrdiff_t and include the RT-Thread header that
defines rt_always_inline (e.g., #include "rtthread.h" or the project-specific
header that defines rt_always_inline) at the top of tlsf_block_functions.h
(inside the extern "C" guard placement as appropriate) so functions/inline
attributes referencing ptrdiff_t, size_t and rt_always_inline compile correctly.

In @test/tlsf/tlsf_control_functions.h:
- Around line 24-29: The mem_record_t struct uses int32_t for total, used, and
max_used which can overflow on 64-bit large-pool builds (TLSF_64BIT enabled);
update mem_record_t to use a 64-bit signed type (e.g., int64_t or ssize_t) when
TLSF_64BIT is defined (or always use int64_t) so total/used/max_used can
represent large allocations without overflow, and ensure any code
reading/writing these fields is adjusted for the wider type (references:
mem_record_t, total, used, max_used, TLSF_64BIT).
- Around line 7-15: The header uses fixed-width and size types (int32_t, size_t)
and the macro rt_always_inline but doesn't include their definitions; update
tlsf_control_functions.h to include the proper standard headers (<stdint.h> and
<stddef.h>) before those types are used and ensure rt_always_inline is available
in non-C++ builds by either including the RT-Thread/platform header that defines
rt_always_inline (e.g., the platform's rtdef.h) or provide a guarded fallback
definition when rt_always_inline is not defined; keep the existing tlsf_decl
macro and extern "C" logic but add these includes/guarded definitions before
tlsf_decl is declared so int32_t/size_t and rt_always_inline are defined.
- Around line 196-233: tlsf_fls_generic mishandles word==0 because it only
decrements bit and continues shifting; change the initial check in
tlsf_fls_generic to immediately return 0 when word is zero (e.g., if (!word)
return 0;), so tlsf_fls (which returns tlsf_fls_generic(word) - 1) yields -1 for
zero and tlsf_ffs behavior remains correct when calling tlsf_fls_generic(word &
(~word + 1)).

In @test/tlsf/tlsf.c:
- Line 653: The call uses RT-Thread specific rt_memcpy which is undefined on
non-RT-Thread platforms; replace rt_memcpy with the standard memcpy (already
available via <string.h>) or add a compatibility wrapper in rtthread.h that maps
rt_memcpy to memcpy so the call in tlsf.c (the line invoking rt_memcpy(p, ptr,
minsize)) compiles portably.
- Around line 670-676: The if statement that updates control->mem_rec.max_used
lacks braces, risking accidental misassociation of following statements; add
explicit braces around the assignment so the condition clearly encloses
control->mem_rec.max_used = control->mem_rec.used, e.g. wrap that assignment in
{ ... } following the if(condition) around the code block that should execute
only when control->mem_rec.used > control->mem_rec.max_used to prevent future
control-flow bugs related to block_size(block)/cursize updates to
control->mem_rec.used.

In @test/tlsf/tlsf.h:
- Line 89: The declaration of tlsf_check_hook uses GCC/Clang-specific
__attribute__((weak)), which breaks MSVC; replace it with a portability macro
(e.g., define WEAK_HOOK to __attribute__((weak)) for GCC/Clang and to empty for
MSVC/_other_) and apply WEAK_HOOK to the tlsf_check_hook prototype, or
alternatively remove weak usage and implement an explicit registration/callback
mechanism (referencing tlsf_check_hook) so the symbol is not compiler-specific.

In @test/valloc/valloc.c:
- Line 9: The code defines MALLOC_HEADER_SIZE as 0 while actual allocations use
HEADER_SIZE (so allocated bytes = size + HEADER_SIZE) but accounting updates use
MALLOC_HEADER_SIZE, causing the use counter to under-report; either change the
accounting to use HEADER_SIZE everywhere (replace occurrences of
MALLOC_HEADER_SIZE in the bookkeeping code with HEADER_SIZE and ensure use is
updated by size + HEADER_SIZE) or, if setting MALLOC_HEADER_SIZE to 0 is
intentional for TLSF, add a clear comment near the MALLOC_HEADER_SIZE definition
explaining the rationale and explicitly assert/update bookkeeping to match the
chosen behavior.

In @xmake.lua:
- Line 138: Update the mistaken comment for the add_files call that currently
reads "valloc 测试" to accurately reflect the source path './test/tlsf/*.c' by
changing it to "tlsf 测试"; locate the add_files invocation
(add_files('./test/tlsf/*.c', {public = true}, {cxflags = "-w"})) and correct
only the comment text to match the TLSF tests.
- Around line 136-141: The add_files calls currently pass two separate option
tables (e.g., add_files('./test/*.c', {public = true}, {cxflags = "-w"})), which
causes the second table to be ignored; fix each call (add_files for
'./test/*.c', './test/valloc/*.c', './test/tlsf/*.c', './test/baseTest/*.c',
'./externalModule/cJSON/*.c', './externalModule/yyjson/*.c') by merging options
into a single table like {public = true, cxflags = "-w"} so cxflags is applied,
and correct the comment on the tlsf target from "valloc 测试" to "tlsf 测试".
🧹 Nitpick comments (14)
.clangd (1)

8-10: 谨慎使用诊断抑制。

禁用 UnusedIncludes 和 MissingIncludes 诊断会隐藏潜在的代码质量问题:

  • 未使用的头文件会增加编译时间并使依赖关系不清晰
  • 缺失的头文件可能导致跨平台编译问题

建议仅在有明确理由时(如大量误报)禁用这些诊断。

test/valloc/valloc.h (1)

11-12: 建议使用更具体的头文件保护符。

虽然去除前导下划线是正确的改进,但新的保护符 valloc 过于通用,可能与其他头文件冲突。建议使用更具描述性的名称,例如:

  • RYANJSON_TEST_VALLOC_H
  • TEST_VALLOC_H
  • VALLOC_TEST_H
🔎 建议的改进
-#ifndef valloc
-#define valloc
+#ifndef TEST_VALLOC_H
+#define TEST_VALLOC_H

并在文件末尾更新:

-#endif
+#endif // TEST_VALLOC_H
test/tlsf/rtthread.h (1)

1-14: 兼容性头文件实现合理。

此文件为 TLSF 测试提供了 RT-Thread 环境的兼容层。实现简洁清晰。

注意 __attribute__((always_inline)) 是 GCC/Clang 特定的扩展。如果将来需要支持其他编译器,可以考虑添加条件编译:

🔎 可选的跨编译器兼容性改进
+#if defined(__GNUC__) || defined(__clang__)
 #define rt_always_inline static inline __attribute__((always_inline))
+#elif defined(_MSC_VER)
+#define rt_always_inline static __forceinline
+#else
+#define rt_always_inline static inline
+#endif
test/tlsf/tlsf_block_functions.h (1)

97-98: 位操作表达式缺少空格。

block->size &block_header_free_bit&block_header_free_bit 之间缺少空格,虽然不影响功能,但影响可读性。同样的问题也出现在第 105 行。

🔎 建议修改
 tlsf_decl int block_is_free(const block_header_t *block)
-{ return tlsf_cast(int, block->size &block_header_free_bit); }
+{ return tlsf_cast(int, block->size & block_header_free_bit); }
RyanJson/RyanJsonConfig.h (1)

47-54: 默认值定义可能永远不会被使用。

第 47-49 行和 52-54 行的默认值定义可能永远不会生效,因为在 RT_VER_NUM 分支和 else 分支中都已经定义了 RyanJsonMallocAlignRyanJsonMallocHeaderSize。这些 #ifndef 块只有在用户手动定义这些宏时才有意义。

建议添加注释说明这是为用户自定义预留的:

🔎 建议修改
+// 用户可以在包含此头文件前自定义这些值
 #ifndef RyanJsonMallocAlign
 #define RyanJsonMallocAlign 8U
 #endif

+// 用户可以在包含此头文件前自定义这些值
 #ifndef RyanJsonMallocHeaderSize
 #define RyanJsonMallocHeaderSize 8U
 #endif
test/tlsf/tlsf.h (1)

10-13: 头文件包含可能限制可移植性。

直接包含 rtthread.h 会限制此头文件仅在 RT-Thread 环境中使用。如果需要在非 RT-Thread 环境中使用 TLSF,建议使用条件编译。

🔎 建议修改
 #include <assert.h>
 #include <stddef.h>
 #include <stdbool.h>
+
+#ifdef RT_VER_NUM
 #include "rtthread.h"
+#else
+#define RT_ASSERT assert
+#endif
test/tlsf/tlsf_control_functions.h (1)

266-271: 断言应在计算之前执行

align_ptr 中的对齐有效性断言位于计算之后(第 269 行)。如果 align 不是 2 的幂次,第 268 行的计算已经产生了错误结果。建议将断言移至计算之前,与 align_up/align_down 保持一致。

🔎 建议修复
 tlsf_decl void *align_ptr(const void *ptr, size_t align)
 {
+	tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
 	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);
 }
test/tlsf/tlsf.c (2)

130-130: void* 指针算术是非标准 GCC 扩展

第 130 行对 void* 进行指针算术运算 (void *)block + block_start_offset。这是 GCC 扩展,在严格 C 标准下不可移植。建议转换为 char* 后再进行算术运算。

🔎 建议修复
-		void *ptr_block = used ? (void *)block + block_start_offset : (void *)block + sizeof(block_header_t);
+		void *ptr_block = used ? (void *)((char *)block + block_start_offset) : (void *)((char *)block + sizeof(block_header_t));

582-588: 函数命名不规范:rt_memory_info22

函数名末尾的 22 后缀看起来像是临时命名或版本标记。建议在合并前将其重命名为更具描述性的名称,如 tlsf_memory_infort_memory_info

xmake.lua (2)

107-109: 冲突的警告标志:-Wdocumentation-Wno-documentation

第 107 行启用了 -Wdocumentation,但第 109 行又用 -Wno-documentation 禁用了它。这两个标志相互矛盾,后者会覆盖前者。建议删除其中一个以避免混淆。

🔎 建议修复
-        "-Wdocumentation",
-        "-Wparentheses-equality",
-        "-Wno-documentation",      -- 临时关闭文档警告
+        "-Wparentheses-equality",
+        -- "-Wdocumentation",      -- 如需启用文档警告可取消注释

42-60: Sanitizer 编译标志不完整

add_ldflags 包含多个 sanitizer(如 -fsanitize=address),但 add_cxflags(第 43 行)仅包含 -fsanitize=alignment。为确保 AddressSanitizer 等工具完整生效,建议在编译标志中也添加相应的 sanitizer 选项。

🔎 建议修复
     -- Sanitizer 检测项:运行时错误检测
-    add_cxflags("-fsanitize=alignment", "-fno-sanitize-recover=undefined", {force = true})
+    add_cxflags(
+        "-fsanitize=alignment",
+        "-fsanitize=address",
+        "-fsanitize=undefined",
+        "-fno-sanitize-recover=undefined",
+        {force = true}
+    )
RyanJson/RyanJson.h (3)

1-2: 头文件保护宏命名不当

RyanJson 作为头文件保护宏名称不够规范,可能与类型名、函数名或其他标识符冲突。建议使用更明确的命名格式,如 RYANJSON_H_RYANJSON_H_

🔎 建议修复
-#ifndef RyanJson
-#define RyanJson
+#ifndef RYANJSON_H
+#define RYANJSON_H

同时更新文件末尾的 #endif 注释(如果有的话)。


330-330: 移除或说明被注释的代码

第 330 行包含一个被注释的宏定义,看起来像是开发过程中的遗留代码。建议删除此行或添加注释说明保留原因。


38-142: 设计文档详尽,建议考虑分离

结构体设计文档非常详细,对理解内存布局和存储策略很有帮助。不过,这段文档较长(约 100 行),可考虑将详细设计说明移至单独的 README 或设计文档中,头文件中保留简要说明即可。这只是可选建议,当前形式也可接受。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 401deb9 and 9c9d896.

📒 Files selected for processing (22)
  • .clang-tidy
  • .clangd
  • .vscode/settings.json
  • README.md
  • RyanJson/RyanJson.c
  • RyanJson/RyanJson.h
  • RyanJson/RyanJsonConfig.h
  • RyanJson/RyanJsonUtils.h
  • test/RyanJsonMemoryFootprintTest.c
  • test/RyanJsonRFC8259JsonTest.c
  • test/RyanJsonTest.c
  • test/RyanJsonTest.h
  • test/baseTest/RyanJsonBaseTest.c
  • test/fuzzer/RyanJsonFuzzer.c
  • test/tlsf/rtthread.h
  • test/tlsf/tlsf.c
  • test/tlsf/tlsf.h
  • test/tlsf/tlsf_block_functions.h
  • test/tlsf/tlsf_control_functions.h
  • test/valloc/valloc.c
  • test/valloc/valloc.h
  • xmake.lua
💤 Files with no reviewable changes (1)
  • test/baseTest/RyanJsonBaseTest.c
🧰 Additional context used
🧬 Code graph analysis (8)
test/RyanJsonTest.h (1)
test/RyanJsonTest.c (4)
  • v_malloc_tlsf (46-51)
  • v_free_tlsf (53-58)
  • v_realloc_tlsf (60-60)
  • vallocGetUseByTlsf (39-44)
test/RyanJsonMemoryFootprintTest.c (2)
test/RyanJsonTest.c (4)
  • v_malloc_tlsf (46-51)
  • v_realloc_tlsf (60-60)
  • v_free_tlsf (53-58)
  • vallocGetUseByTlsf (39-44)
RyanJson/RyanJson.c (1)
  • RyanJsonInitHooks (582-590)
test/tlsf/tlsf.c (2)
test/tlsf/tlsf_block_functions.h (14)
  • block_header_t (111-112)
  • block_header_t (118-119)
  • block_header_t (122-126)
  • block_header_t (129-134)
  • block_header_t (137-142)
  • size_t (86-87)
  • void (89-93)
  • void (100-100)
  • void (102-102)
  • void (107-107)
  • void (109-109)
  • void (114-115)
  • void (144-150)
  • void (152-157)
test/tlsf/tlsf_control_functions.h (19)
  • block_header_t (335-365)
  • block_header_t (432-477)
  • block_header_t (490-500)
  • block_header_t (503-515)
  • block_header_t (518-531)
  • block_header_t (561-581)
  • block_header_t (583-608)
  • size_t (254-258)
  • size_t (260-264)
  • size_t (277-281)
  • size_t (287-298)
  • void (266-271)
  • void (273-273)
  • void (275-275)
  • void (305-322)
  • void (325-333)
  • void (368-391)
  • void (394-411)
  • void (414-419)
RyanJson/RyanJsonConfig.h (1)
RyanJson/RyanJson.c (2)
  • RyanJsonSnprintf (29-40)
  • RyanJsonMemmove (44-62)
test/tlsf/tlsf.h (1)
test/tlsf/tlsf.c (21)
  • tlsf_create (358-374)
  • tlsf_destroy (390-394)
  • tlsf_get_pool (396-396)
  • tlsf_add_pool (268-312)
  • tlsf_remove_pool (314-327)
  • tlsf_malloc (398-408)
  • tlsf_memalign (581-581)
  • tlsf_memalign_offs (509-575)
  • tlsf_malloc_addr (419-484)
  • tlsf_realloc (620-680)
  • tlsf_free (589-605)
  • tlsf_block_size (215-224)
  • tlsf_size (252-257)
  • tlsf_pool_overhead (264-264)
  • tlsf_alloc_overhead (266-266)
  • rt_memory_info22 (582-588)
  • tlsf_fit_size (235-246)
  • tlsf_walk_pool (201-213)
  • tlsf_check (141-190)
  • tlsf_check_pool (226-233)
  • tlsf_find_containing_block (682-702)
test/tlsf/tlsf_block_functions.h (1)
test/tlsf/tlsf_control_functions.h (32)
  • size_t (254-258)
  • size_t (260-264)
  • size_t (277-281)
  • size_t (287-298)
  • void (266-271)
  • void (273-273)
  • void (275-275)
  • void (305-322)
  • void (325-333)
  • void (368-391)
  • void (394-411)
  • void (414-419)
  • void (422-427)
  • void (534-544)
  • void (547-559)
  • void (610-624)
  • int (102-107)
  • int (111-111)
  • int (115-119)
  • int (129-133)
  • int (135-139)
  • int (146-150)
  • int (152-157)
  • int (162-167)
  • int (169-173)
  • int (180-185)
  • int (187-191)
  • int (196-228)
  • int (231-231)
  • int (233-233)
  • int (239-249)
  • int (429-429)
test/RyanJsonTest.c (4)
test/baseTest/RyanJsonBaseTestUtile.c (1)
  • printJsonDebug (12-17)
test/tlsf/tlsf.c (5)
  • tlsf_alloc_overhead (266-266)
  • rt_memory_info22 (582-588)
  • tlsf_walk_pool (201-213)
  • tlsf_create_with_pool (376-388)
  • tlsf_size (252-257)
test/valloc/valloc.c (2)
  • v_malloc (14-27)
  • v_free (37-48)
RyanJson/RyanJson.c (4)
  • RyanJsonInitHooks (582-590)
  • RyanJsonPrint (1614-1642)
  • RyanJsonFree (684-687)
  • RyanJsonDelete (657-677)
test/tlsf/tlsf_control_functions.h (2)
test/tlsf/tlsf_block_functions.h (17)
  • int (95-95)
  • int (97-98)
  • int (104-105)
  • size_t (86-87)
  • void (89-93)
  • void (100-100)
  • void (102-102)
  • void (107-107)
  • void (109-109)
  • void (114-115)
  • void (144-150)
  • void (152-157)
  • block_header_t (111-112)
  • block_header_t (118-119)
  • block_header_t (122-126)
  • block_header_t (129-134)
  • block_header_t (137-142)
test/tlsf/tlsf.c (1)
  • tlsf_alloc_overhead (266-266)
🪛 Clang (14.0.6)
test/tlsf/tlsf_block_functions.h

[error] 21-21: unknown type name 'ptrdiff_t'

(clang-diagnostic-error)


[error] 49-49: unknown type name 'size_t'

(clang-diagnostic-error)


[error] 86-86: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 86-86: expected ';' after top level declarator

(clang-diagnostic-error)

test/tlsf/tlsf_control_functions.h

[error] 26-26: unknown type name 'int32_t'

(clang-diagnostic-error)


[error] 27-27: unknown type name 'int32_t'

(clang-diagnostic-error)


[error] 28-28: unknown type name 'int32_t'

(clang-diagnostic-error)


[error] 56-56: unknown type name 'size_t'

(clang-diagnostic-error)


[error] 111-111: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 115-115: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 239-239: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 239-239: unknown type name 'size_t'

(clang-diagnostic-error)


[error] 254-254: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 254-254: expected ';' after top level declarator

(clang-diagnostic-error)


[error] 490-490: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 490-490: expected ';' after top level declarator

(clang-diagnostic-error)

🪛 Cppcheck (2.19.0)
test/tlsf/tlsf.c

[error] 124-124: syntax error

(syntaxError)

🪛 LanguageTool
README.md

[uncategorized] ~228-~228: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...果** #### 内存占用测试 (20251222 malloc头部空间8字节,内存对齐4字节)测试代码可在本项目根目录RyanJsonExample...

(wa5)


[uncategorized] ~228-~228: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...## 内存占用测试 (20251222 malloc头部空间8字节,内存对齐4字节)测试代码可在本项目根目录RyanJsonExample文件夹查看。 ...

(wa5)


[uncategorized] ~256-~256: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...6.67% 内存占用 ``` RT-Thread平台 malloc头部空间12字节,内存对齐8字节测试代码可在本项目根目录RyanJsonExample文...

(wa5)


[uncategorized] ~256-~256: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...用 ``` RT-Thread平台 malloc头部空间12字节,内存对齐8字节测试代码可在本项目根目录RyanJsonExample文件夹查看 ``...

(wa5)

🪛 markdownlint-cli2 (0.18.1)
README.md

31-31: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)


50-50: Hard tabs
Column: 1

(MD010, no-hard-tabs)


51-51: Hard tabs
Column: 1

(MD010, no-hard-tabs)


52-52: Hard tabs
Column: 1

(MD010, no-hard-tabs)


53-53: Hard tabs
Column: 1

(MD010, no-hard-tabs)


54-54: Hard tabs
Column: 1

(MD010, no-hard-tabs)


55-55: Hard tabs
Column: 1

(MD010, no-hard-tabs)


56-56: Hard tabs
Column: 1

(MD010, no-hard-tabs)


57-57: Hard tabs
Column: 1

(MD010, no-hard-tabs)


58-58: Hard tabs
Column: 1

(MD010, no-hard-tabs)


59-59: Hard tabs
Column: 1

(MD010, no-hard-tabs)


60-60: Hard tabs
Column: 1

(MD010, no-hard-tabs)


61-61: Hard tabs
Column: 1

(MD010, no-hard-tabs)


62-62: Hard tabs
Column: 1

(MD010, no-hard-tabs)


71-71: Hard tabs
Column: 1

(MD010, no-hard-tabs)


72-72: Hard tabs
Column: 1

(MD010, no-hard-tabs)


73-73: Hard tabs
Column: 1

(MD010, no-hard-tabs)


74-74: Hard tabs
Column: 1

(MD010, no-hard-tabs)


75-75: Hard tabs
Column: 1

(MD010, no-hard-tabs)


76-76: Hard tabs
Column: 1

(MD010, no-hard-tabs)


77-77: Hard tabs
Column: 1

(MD010, no-hard-tabs)


78-78: Hard tabs
Column: 1

(MD010, no-hard-tabs)


79-79: Hard tabs
Column: 1

(MD010, no-hard-tabs)


80-80: Hard tabs
Column: 1

(MD010, no-hard-tabs)


81-81: Hard tabs
Column: 1

(MD010, no-hard-tabs)


82-82: Hard tabs
Column: 1

(MD010, no-hard-tabs)


83-83: Hard tabs
Column: 1

(MD010, no-hard-tabs)


84-84: Hard tabs
Column: 1

(MD010, no-hard-tabs)


85-85: Hard tabs
Column: 1

(MD010, no-hard-tabs)


86-86: Hard tabs
Column: 1

(MD010, no-hard-tabs)


116-116: Hard tabs
Column: 1

(MD010, no-hard-tabs)


117-117: Hard tabs
Column: 1

(MD010, no-hard-tabs)


118-118: Hard tabs
Column: 1

(MD010, no-hard-tabs)


133-133: Hard tabs
Column: 1

(MD010, no-hard-tabs)


230-230: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


258-258: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (36)
.clang-tidy (1)

30-32: 审查 llvm- 检查配置逻辑。*

当前配置:

  • 第 30 行:从全局模式中移除了 llvm-*
  • 第 32 行:添加 'llvm-*,-llvm-include-order'(启用所有 llvm-* 检查但禁用 llvm-include-order)

这意味着启用了除 llvm-include-order 外的所有 llvm 检查。请确认这是预期行为。如果目的是只启用 llvm-include-order,应改为 'llvm-include-order'

RyanJson/RyanJsonUtils.h (2)

1-2: 优秀的头文件保护符现代化改进。

将头文件保护符从 __RyanJsonUtils__ 改为 RyanJsonUtils 是正确的做法。带有前导下划线的标识符(特别是双下划线)保留给实现使用,这个改动符合 C/C++ 标准。


27-30: 文档格式更新。

注释格式的调整使示例更清晰,没有功能变化。

test/fuzzer/RyanJsonFuzzer.c (8)

7-7: 退出标签重命名。

将退出标签从 __exit 改为 exit__ 避免了保留标识符命名空间的冲突。这是正确的改进,与项目范围内的标签重命名保持一致。


10-10: 适当的静态链接。

isEnableRandomMemFail 从外部链接改为静态内部链接是正确的,因为该变量仅在此测试文件中使用,减少了全局命名空间污染。


28-29: 验证缓冲区大小调整的合理性。

缓冲区大小阈值从 4096 降低到 2048。这个改动可能:

  • 减少内存使用
  • 增加缓冲区不足的风险

请确认这个改动是基于实际测试数据还是特定的测试需求。


80-80: 变量声明改进。

添加 dupLen 变量用于跟踪重复打印的长度,提高了代码的可读性和清晰度。


250-252: 优秀的空指针检查增强。

RyanJsonGetIntValueRyanJsonGetDoubleValueRyanJsonGetObjectValue 添加了 NULL 参数的边界检查。这是良好的防御性编程实践,确保 API 在错误输入下的健壮性。


353-354: 完善的 NULL 参数验证。

RyanJsonChangeIntValueRyanJsonChangeDoubleValue 添加 NULL 检查,与其他 Change 函数的错误处理保持一致。


236-238: 提高代码清晰度。

RyanJsonArrayForEach 循环体添加大括号,即使只有单条语句。这遵循了最佳实践,防止未来修改时引入的作用域错误。


162-162: 一致的退出标签使用。

所有退出标签从 __exit 更新为 exit__,保持了整个文件的一致性。这与第 7 行的宏定义更改相匹配。

Also applies to: 714-714, 725-725, 735-737, 744-744

test/RyanJsonRFC8259JsonTest.c (1)

448-448: 代码清理做得很好!

移除了三个解析函数中重复的条件检查 "n_structure_100000_opening_arrays.json",提高了代码的清晰度和可维护性。

Also applies to: 491-491, 531-531

test/RyanJsonTest.h (2)

1-2: 头文件保护符改进符合标准!

将头文件保护符从 __RyanJsonTest__ 重命名为 RyanJsonTest,避免使用保留标识符(双下划线前缀),符合 C 标准最佳实践。


41-44: TLSF 集成声明正确!

添加的 TLSF 内存管理函数外部声明与实现(test/RyanJsonTest.c)一致,正确支持 TLSF 内存分配器的集成。

.vscode/settings.json (1)

1-43: 开发环境配置更新合理。

VS Code 配置更新启用 clangd 并调整了静态分析规则,与项目构建系统(xmake.lua 生成 compile_commands.json)和 .clang-tidy 配置保持一致。这些更改改善了开发体验。

test/RyanJsonMemoryFootprintTest.c (2)

3-5: TLSF 内存包装函数实现正确。

这些静态包装函数正确地将 yyjson 的分配器接口适配到 TLSF 分配器。ctx 参数被正确忽略,old_sizeyy_realloc 中也被正确忽略,因为 TLSF 的 tlsf_realloc 不需要旧大小。


7-23: 内存占用测量逻辑正确。

函数正确地在解析前后获取 TLSF 内存使用量,并通过差值计算内存占用。RyanJsonInitHooks 在解析前正确初始化。

test/RyanJsonTest.c (3)

65-70: TLSF 池初始化和钩子设置正确。

使用 v_malloc 分配 1MB 缓冲区,然后创建 TLSF 池,最后初始化 RyanJson 钩子。内存管理流程清晰,在程序结束时正确释放。


27-37: 双重内存统计方法设计合理。

showMemoryInfo 使用两种方法统计内存:直接查询 TLSF 控制结构和遍历所有块。这种双重验证有助于调试和确保内存统计的准确性。


60-60: v_realloc_tlsf 已正确处理边界条件。

tlsf_realloc 实现中已明确处理了边界情况:当 size 为 0 时调用 tlsf_free 并返回 NULL(第625-626行),当 block 为 NULL 时调用 tlsf_malloc(第627-628行)。v_realloc_tlsf 正确地委托给 tlsf_realloc,无需在包装函数中重复处理。代码已符合标准 realloc 语义。

Likely an incorrect or invalid review comment.

README.md (1)

49-153: 节点结构文档更新详尽。

新增的节点结构文档清晰地解释了内存布局、flag 位定义、inline/ptr 存储策略。这对理解 RyanJson 的内存优化设计非常有帮助。文档中包含的链表结构示例也有助于理解数据组织方式。

test/tlsf/tlsf_block_functions.h (1)

43-54: TLSF 块头结构定义正确。

block_header_t 结构体包含物理前驱指针、大小字段和自由链表指针,符合 TLSF 算法的标准实现。

RyanJson/RyanJsonConfig.h (2)

85-91: 编译时检查确保配置正确。

编译时检查确保 RyanJsonMallocHeaderSize >= 4RyanJsonMallocAlign 是 4 的倍数,这能在编译阶段捕获配置错误。


22-24: RyanJsonMemmove 的条件定义说明清晰。

注释说明了 RT-Thread 的 rt_memmove 实现较简单,可以选择使用 RyanJson 内部实现以获得更好的性能。这种灵活配置是良好的设计。

test/tlsf/tlsf.h (1)

19-40: TLSF API 设计完整且结构清晰。

API 分为生命周期管理、内存分配/释放、调试诊断三类,涵盖了 TLSF 分配器的所有必要功能。opaque 类型 tlsf_tpool_t 隐藏了实现细节,是良好的 API 设计实践。

RyanJson/RyanJson.c (7)

43-63: RyanJsonMemmove 实现正确且优化合理。

实现正确处理了三种情况:

  1. 源和目标相同或长度为 0 时直接返回
  2. 无重叠时使用更快的 memcpy
  3. 有重叠时从后向前复制

使用 #ifndef RyanJsonMemmove 条件编译允许平台提供优化版本。


194-199: 内联字符串大小计算与文档一致。

计算公式与 README 中描述的一致:利用 flag 后的填充空间、指针空间以及 malloc 头部空间的一半,然后向上对齐。这是内联存储优化的核心计算。


623-639: 数值获取函数使用 memcpy 规避对齐问题。

RyanJsonGetIntValueRyanJsonGetDoubleValue 使用 memcpy 而非直接指针解引用,正确规避了潜在的非对齐访问问题,这在嵌入式平台上尤为重要。


365-377: 节点替换逻辑正确。

RyanJsonReplaceNode 正确处理了前驱和后继的链接:

  • 链接前驱到新节点(如果存在)
  • 链接新节点到旧节点的后继
  • 断开旧节点的链接

调用处(第 1944、1989 行)正确处理了 prev 为 NULL 的情况。


997-1000: 错误处理标签命名统一。

所有错误处理标签统一命名为 error__,并正确释放已分配资源后返回 RyanJsonFalse。这种一致性提高了代码可维护性。

Also applies to: 1079-1082, 1151-1155


3-5: Fuzzer 模式嵌套限制合理。

Fuzzer 模式下将嵌套限制降至 350,低于正常的 500,这有助于在压力测试时减少栈溢出风险。


546-572: RyanJsonCreateItem 实现合理。

函数正确处理了两种情况:

  1. 如果输入是 Array/Object,转移子节点并释放原节点
  2. 否则将输入节点设为新节点的子节点

这种设计使得带 key 的对象可以方便地通过 replace 替换。

test/tlsf/tlsf.c (2)

33-89: LGTM - control_construct 函数逻辑正确

内存布局计算和参数验证逻辑合理。使用 __builtin_clz 内建函数与构建配置中指定的 Clang/GCC 工具链一致。


666-676: 内存使用统计逻辑需验证

block_trim_used 可能会分割块并将剩余部分插回空闲列表。第 671-672 行的统计更新基于修改后的 block_size(block),这可能导致统计不准确,因为分割后的剩余块未被计入 used 的减少量。

RyanJson/RyanJson.h (1)

289-293: LGTM - API 从宏改为函数声明

RyanJsonGetIntValueRyanJsonGetDoubleValue 等从宏改为 extern 函数声明是更好的设计。这提供了更好的类型安全性和调试体验,同时保持了 API 兼容性。

test/tlsf/tlsf_control_functions.h (1)

487-500: 无问题 - POISONING_AFTER_FREE 宏已通过 #include "tlsf_block_functions.h"(第8行)正确包含并在 tlsf_block_functions.h 第17行定义为 true,可在此文件中正常使用。

Comment on lines +1 to +6
Index:
Background: Build # 开启后台索引

# 如果你的 compile_commands.json 不在根目录,需要在这里显式指定
CompileFlags:
CompilationDatabase: ".vscode"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if .clangd file exists and read its content
if [ -f ".clangd" ]; then
  echo "=== .clangd file content ==="
  cat -n .clangd
else
  echo ".clangd file not found"
fi

Repository: Ryan-CW-Code/RyanJson

Length of output: 409


🏁 Script executed:

# Search for compile_commands.json in common locations
echo "=== Searching for compile_commands.json ==="
find . -name "compile_commands.json" -type f 2>/dev/null | head -20

Repository: Ryan-CW-Code/RyanJson

Length of output: 108


🏁 Script executed:

# Check if .vscode directory exists and list its contents
if [ -d ".vscode" ]; then
  echo "=== .vscode directory contents ==="
  ls -la .vscode/ 2>/dev/null | head -20
else
  echo ".vscode directory does not exist"
fi

Repository: Ryan-CW-Code/RyanJson

Length of output: 273


compile_commands.json 文件缺失。

CompilationDatabase 配置指向 ".vscode" 目录,但 compile_commands.json 在该目录(以及根目录)中都不存在。clangd 无法找到编译数据库,将无法正确索引和分析代码。需要生成 compile_commands.json 文件或将配置路径指向正确位置。

🤖 Prompt for AI Agents
In @.clangd around lines 1-6, The .clangd config points CompilationDatabase to
".vscode" but compile_commands.json is missing; fix by either generating a
compilation database (e.g., enable CMake's CMAKE_EXPORT_COMPILE_COMMANDS or run
bear/scan-build to create compile_commands.json) and placing it in the ".vscode"
directory, or update the CompilationDatabase field in .clangd to the actual
directory containing the generated compile_commands.json (or remove the explicit
setting to use the project root). Ensure the file is named exactly
compile_commands.json so clangd can discover it.

Comment on lines +1 to +11
/*
* SPDX-FileCopyrightText: 2006-2016 Matthew Conte
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#pragma once

#if defined(__cplusplus)
extern "C" {
#endif
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

缺少必要的头文件包含。

静态分析显示 ptrdiff_tsize_trt_always_inline 类型未定义。需要添加相应的头文件包含:

  • <stddef.h> 用于 size_tptrdiff_t
  • rtthread.h 或定义 rt_always_inline
🔎 建议修改
 #pragma once

+#include <stddef.h>
+#include <stdbool.h>
+
+/* Define rt_always_inline if not using RT-Thread */
+#ifndef rt_always_inline
+#define rt_always_inline static inline
+#endif
+
 #if defined(__cplusplus)
 extern "C" {
 #endif
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/*
* SPDX-FileCopyrightText: 2006-2016 Matthew Conte
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#pragma once
#if defined(__cplusplus)
extern "C" {
#endif
/*
* SPDX-FileCopyrightText: 2006-2016 Matthew Conte
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#pragma once
#include <stddef.h>
#include <stdbool.h>
/* Define rt_always_inline if not using RT-Thread */
#ifndef rt_always_inline
#define rt_always_inline static inline
#endif
#if defined(__cplusplus)
extern "C" {
#endif
🤖 Prompt for AI Agents
In @test/tlsf/tlsf_block_functions.h around lines 1-11, The header
tlsf_block_functions.h is missing required includes for ptrdiff_t, size_t and
the rt_always_inline macro; add #include <stddef.h> to provide size_t/ptrdiff_t
and include the RT-Thread header that defines rt_always_inline (e.g., #include
"rtthread.h" or the project-specific header that defines rt_always_inline) at
the top of tlsf_block_functions.h (inside the extern "C" guard placement as
appropriate) so functions/inline attributes referencing ptrdiff_t, size_t and
rt_always_inline compile correctly.

Comment on lines +670 to +676
/* 更新统计信息:原地调整时需要修正 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;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

缺少大括号导致潜在的控制流错误

第 674-675 行的 if 语句体缺少大括号,当前缩进可能误导读者。这是一个危险的代码风格,未来修改时容易引入错误。

🔎 建议修复
-            if(control->mem_rec.used > control->mem_rec.max_used)
-			control->mem_rec.max_used = control->mem_rec.used;
+			if (control->mem_rec.used > control->mem_rec.max_used) {
+				control->mem_rec.max_used = control->mem_rec.used;
+			}
🤖 Prompt for AI Agents
In @test/tlsf/tlsf.c around lines 670-676, The if statement that updates
control->mem_rec.max_used lacks braces, risking accidental misassociation of
following statements; add explicit braces around the assignment so the condition
clearly encloses control->mem_rec.max_used = control->mem_rec.used, e.g. wrap
that assignment in { ... } following the if(condition) around the code block
that should execute only when control->mem_rec.used > control->mem_rec.max_used
to prevent future control-flow bugs related to block_size(block)/cursize updates
to control->mem_rec.used.

* @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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Examine the file and its context
head -100 test/tlsf/tlsf.h | tail -15

Repository: Ryan-CW-Code/RyanJson

Length of output: 593


🏁 Script executed:

# Check if there are any compiler-specific macros or abstractions in the project
fd -e h -e c | xargs grep -l "__attribute__" | head -10

Repository: Ryan-CW-Code/RyanJson

Length of output: 228


🏁 Script executed:

# Look for any existing patterns handling compiler-specific features
rg -i "msvc|_MSC_VER|pragma" --type h --type c -A 2 | head -30

Repository: Ryan-CW-Code/RyanJson

Length of output: 2149


🌐 Web query:

How to define weak symbols portably in C for GCC Clang MSVC

💡 Result:

Summary + portable macro recipes.

  • GCC/Clang (ELF/macOS): use the weak attribute.

    • Functions/variables: attribute((weak)). Example:
      • void foo(void) attribute((weak));
      • int opt_value attribute((weak)) = 0;
    • (Clang docs / GCC docs). [1][2]
  • MSVC (MSVC toolchain): there is no direct weak-for-functions equivalent.

    • For data/link-once semantics use __declspec(selectany) (selects one definition when multiples exist). [3]
    • To emulate a “weak function that becomes NULL when not provided” you must use linker tricks (ALTERNATENAME) or pragma-comment with the mangled name, e.g.:
      • __pragma(comment(linker, "/alternatename:??foo@@yaxxz=_default_foo@0")) // example — needs the linker-visible (mangled) names or extern "C".
      • This is fragile and platform-/mangler-dependent. [4][5]
  • Portable macro pattern (recommended):

    • Prefer to write your API so missing symbols are handled by function-pointer checks or by providing explicit registration hooks. If you must use weak semantics, detect compilers and map to the closest behavior:

      #if defined(GNUC) || defined(clang)

      define WEAK_SYM attribute((weak))

      #elif defined(_MSC_VER)
      /* data: selectany; functions: none portable — use registration or provide default implementation
      For data: */

      define WEAK_DATA __declspec(selectany)

      define WEAK_SYM /* no-op for functions on MSVC */

      #else

      define WEAK_SYM /* unknown compiler */

      #endif

    • Use extern "C" for unmangled names if you plan to use linker alternatename on MSVC. Prefer clang-cl on Windows if you want true attribute((weak)) behavior. [2][4]

Caveats and recommendations

  • Weak symbols are not part of standard C/C++; behavior differs across platforms (ELF vs COFF), and name-mangling makes MSVC linker tricks brittle. Use explicit registration, function-pointer fallbacks, or build with clang/LLVM on Windows if you need consistent weak semantics. [1][2][3][4]

Sources:

  1. GCC Function/Variable attributes (weak). [GCC docs]. (gcc.gnu.org)
  2. Clang attribute: weak. (releases.llvm.org)
  3. __declspec(selectany) / selectany semantics. (gcc.gnu.org)
  4. MSVC alternatives: /ALTERNATENAME and pragma-comment discussion; MSVC lacks native weak functions — use selectany or alternatename; prefer clang-cl for true weak. (ofekshilon.com)

If you want, I can paste a ready-to-use header with macros and an MSVC alternatename example (with guidance on name-mangling).

Citations:


添加编译器兼容性处理,以支持 MSVC 等其他编译器。

__attribute__((weak)) 是 GCC/Clang 特定语法。MSVC 不提供等效的弱函数机制(仅 __declspec(selectany) 用于数据)。建议采用条件编译宏来提升可移植性:

#if defined(__GNUC__) || defined(__clang__)
#  define WEAK_HOOK __attribute__((weak))
#elif defined(_MSC_VER)
#  define WEAK_HOOK /* MSVC 不支持弱函数 */
#else
#  define WEAK_HOOK
#endif

WEAK_HOOK bool tlsf_check_hook(void *start, size_t size, bool is_free);

或考虑采用显式注册/回调机制,避免依赖弱符号。

🤖 Prompt for AI Agents
In @test/tlsf/tlsf.h around line 89, The declaration of tlsf_check_hook uses
GCC/Clang-specific __attribute__((weak)), which breaks MSVC; replace it with a
portability macro (e.g., define WEAK_HOOK to __attribute__((weak)) for GCC/Clang
and to empty for MSVC/_other_) and apply WEAK_HOOK to the tlsf_check_hook
prototype, or alternatively remove weak usage and implement an explicit
registration/callback mechanism (referencing tlsf_check_hook) so the symbol is
not compiler-specific.

@@ -6,7 +6,7 @@
#include "valloc.h"

#define HEADER_SIZE sizeof(int)
#define MALLOC_HEADER_SIZE 12
#define MALLOC_HEADER_SIZE 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

内存使用统计与实际分配存在账目不匹配。

MALLOC_HEADER_SIZE 设置为 0(第 9 行)导致内存使用统计不一致:

  • 第 18 行实际分配使用 size + HEADER_SIZE(12 字节开销)
  • 第 24、45 行账目统计仅使用 size + MALLOC_HEADER_SIZE(0 字节开销)

这意味着 use 变量跟踪的内存使用量将低于实际分配量,可能影响内存泄漏检测的准确性。如果这是为适配 TLSF 集成的有意设计,请补充注释说明理由;否则应该统一使用 HEADER_SIZE

🤖 Prompt for AI Agents
In @test/valloc/valloc.c around line 9, The code defines MALLOC_HEADER_SIZE as 0
while actual allocations use HEADER_SIZE (so allocated bytes = size +
HEADER_SIZE) but accounting updates use MALLOC_HEADER_SIZE, causing the use
counter to under-report; either change the accounting to use HEADER_SIZE
everywhere (replace occurrences of MALLOC_HEADER_SIZE in the bookkeeping code
with HEADER_SIZE and ensure use is updated by size + HEADER_SIZE) or, if setting
MALLOC_HEADER_SIZE to 0 is intentional for TLSF, add a clear comment near the
MALLOC_HEADER_SIZE definition explaining the rationale and explicitly
assert/update bookkeeping to match the chosen behavior.

xmake.lua Outdated
Comment on lines 136 to 141
add_files('./test/*.c', {public = true}, {cxflags = "-w"}) -- 测试代码,关闭警告
add_files('./test/valloc/*.c', {public = true}, {cxflags = "-w"}) -- valloc 测试,关闭警告
add_files('./test/tlsf/*.c', {public = true}, {cxflags = "-w"}) -- valloc 测试,关闭警告
add_files('./test/baseTest/*.c', {public = true}, {cxflags = "-w"}) -- 基础测试,关闭警告
add_files('./externalModule/cJSON/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 cJSON,关闭警告
add_files('./externalModule/yyjson/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 yyjson,关闭警告
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

xmake add_files function parameters cxflags syntax documentation

💡 Result:

Short answer — usage and syntax

  • add_files accepts an optional final table of per-file options (same keys as target-level APIs). To set C/C++ flags for those files use the cxflags key in that table:
    Example: add_files("src/foo.c", {cxflags = "-DTEST -O2"}). (xmake.io)

  • If you need to force flags to be passed directly (bypass xmake’s flag detection/validation) use the force table with cxflags inside:
    Example: add_files("src/foo.c", {force = {cxflags = "-DTEST"}}). (xmake.io)

  • Notes:

    • cxflags apply to both C and C++ compilation (use cflags/cxxflags for C-only/C++-only). (xmake.io)
    • You can also use compiler-specific flag forms (e.g. "clang::-flag") at the target/flag APIs when needed. (xmake.io)

If you want, I can fetch the exact page sections or more examples.

Citations:


🌐 Web query:

xmake add_files public parameter per-file options target

💡 Result:

Yes — xmake's add_files accepts a per-file options table where you can set visibility (public/interface/private) plus per-file defines, languages, includedirs, flags, etc.

Example:

target("mylib")
set_kind("static")
add_files("src/a.cpp", {defines = "A_FLAG", includedirs = "include/a", public = true})
add_files("src/b.cpp", {cxxflags = "-O0", private = true})

Effect: a.cpp's compilation options are exported as public (visible to dependents); b.cpp's options are private to the target. The add_files option table supports defines, languages, includedirs, rules, force, sourcekind, cxxflags/cflags/ldflags, and the visibility keys public|interface|private. [1]

References:

  • xmake add_files API (per-file options and visibility). [1]

修复 add_files 参数语法

第 136-141 行的 add_files 调用传递了两个独立的表 {public = true}{cxflags = "-w"},但 xmake 的 add_files 应该接收一个合并的选项表。按当前语法,第二个表会被忽略,导致 cxflags = "-w" 不生效,警告抑制失效。

🔎 建议修复
-    add_files('./test/*.c', {public = true}, {cxflags = "-w"})          -- 测试代码,关闭警告
-    add_files('./test/valloc/*.c', {public = true}, {cxflags = "-w"})   -- valloc 测试,关闭警告
-    add_files('./test/tlsf/*.c', {public = true}, {cxflags = "-w"})   -- valloc 测试,关闭警告
-    add_files('./test/baseTest/*.c', {public = true}, {cxflags = "-w"}) -- 基础测试,关闭警告
-    add_files('./externalModule/cJSON/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 cJSON,关闭警告
-    add_files('./externalModule/yyjson/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 yyjson,关闭警告
+    add_files('./test/*.c', {public = true, cxflags = "-w"})          -- 测试代码,关闭警告
+    add_files('./test/valloc/*.c', {public = true, cxflags = "-w"})   -- valloc 测试,关闭警告
+    add_files('./test/tlsf/*.c', {public = true, cxflags = "-w"})     -- tlsf 测试,关闭警告
+    add_files('./test/baseTest/*.c', {public = true, cxflags = "-w"}) -- 基础测试,关闭警告
+    add_files('./externalModule/cJSON/*.c', {public = true, cxflags = "-w"}) -- 第三方库 cJSON,关闭警告
+    add_files('./externalModule/yyjson/*.c', {public = true, cxflags = "-w"}) -- 第三方库 yyjson,关闭警告

另外,第 139 行的注释 "valloc 测试" 应改为 "tlsf 测试"。

🤖 Prompt for AI Agents
In @xmake.lua around lines 136-141, The add_files calls currently pass two
separate option tables (e.g., add_files('./test/*.c', {public = true}, {cxflags
= "-w"})), which causes the second table to be ignored; fix each call (add_files
for './test/*.c', './test/valloc/*.c', './test/tlsf/*.c', './test/baseTest/*.c',
'./externalModule/cJSON/*.c', './externalModule/yyjson/*.c') by merging options
into a single table like {public = true, cxflags = "-w"} so cxflags is applied,
and correct the comment on the tlsf target from "valloc 测试" to "tlsf 测试".

xmake.lua Outdated
add_files('./test/fuzzer/*.c', {public = true})
add_files('./test/*.c', {public = true}, {cxflags = "-w"}) -- 测试代码,关闭警告
add_files('./test/valloc/*.c', {public = true}, {cxflags = "-w"}) -- valloc 测试,关闭警告
add_files('./test/tlsf/*.c', {public = true}, {cxflags = "-w"}) -- valloc 测试,关闭警告
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

注释与路径不匹配

第 138 行的注释写的是 "valloc 测试",但实际路径是 test/tlsf/*.c。这是复制粘贴错误,应修正为 "tlsf 测试"。

🤖 Prompt for AI Agents
In @xmake.lua around line 138, Update the mistaken comment for the add_files
call that currently reads "valloc 测试" to accurately reflect the source path
'./test/tlsf/*.c' by changing it to "tlsf 测试"; locate the add_files invocation
(add_files('./test/tlsf/*.c', {public = true}, {cxflags = "-w"})) and correct
only the comment text to match the TLSF tests.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

这一个重要的更新,引入了内存布局优化(内联字符串)、扩展了API,并完善了文档和构建配置。代码的整体质量有显著提升,特别是对嵌入式环境的内存优化考虑得非常周到。然而,在核心的内存布局实现中,我发现了一个关于可变长度字段处理的关键不一致性,可能导致内存损坏。此外,文档中也有一处描述与实现不符。请查看我的具体建议。

Comment on lines 234 to 242
static void RyanJsonSetLenKey(RyanJson_t pJson, uint32_t value)
{
RyanJsonCheckAssert(NULL != pJson);
uint8_t *buf = RyanJsonGetHiddenPtrAt(pJson, 0);
uint8_t *buf = RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize;
uint8_t len = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson);
RyanJsonCheckAssert(len <= 4);

union RyanJsonUint32Bytes tmpLenField = {.value = value};
for (uint8_t i = 0; i < len; i++) { buf[i] = tmpLenField.buf[i]; }
RyanJsonMemcpy(buf, &value, len);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

这里对 key 长度字段的写入方式与 RyanJsonSetHiddePrt 中对该字段的偏移量计算方式存在严重的不一致性,这可能导致内存访问错误和程序崩溃。

RyanJsonSetLenKey 根据 RyanJsonGetPayloadEncodeKeyLenByFlag 的返回值(1-4字节)来决定拷贝多少字节,实现了可变长度的 key 长度字段。

然而,在 RyanJsonSetHiddePrt (line 218) 和 RyanJsonGetHiddePrt (line 207) 中,获取隐藏指针的偏移量是基于一个固定的 sizeof(uint32_t),如注释 // uint32_t是记录key的长度空间 所示。这假设 key 长度字段总是占用4字节,以保证后续指针的对齐。

这种不一致性是危险的。如果 key 长度字段实际只占用了1个字节,SetHiddePrt 就会在错误的地址写入指针,导致内存损坏。

为了保证内存布局的一致性和正确性,建议统一 key 长度字段的处理方式。 考虑到对齐和实现的简洁性,将其固定为4字节可能更安全。这需要修改本函数以及 RyanJsonGetLenKey 来始终读写4字节。请注意,此修改会影响到所有依赖 key 长度的偏移计算,例如 RyanJsonGetKeyRyanJsonGetStringValue,需要一并检查和修正。

static void RyanJsonSetLenKey(RyanJson_t pJson, uint32_t value)
{
	RyanJsonCheckAssert(NULL != pJson);
	uint8_t *buf = RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize;

	// 始终写入4字节以匹配指针偏移的假设
	RyanJsonMemcpy(buf, &value, sizeof(uint32_t));
}

}
}

if ((mallocSize + keyLenField + 1) <= RyanJsonGetInlineStringSize()) { RyanJsonSetPayloadStrIsPtrByFlag(pJson, RyanJsonFalse); }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

在判断是否可以内联存储时,这个条件 (mallocSize + keyLenField + 1) <= RyanJsonGetInlineStringSize() 中的 + 1 显得有些多余和费解。

根据上下文,mallocSize 已经包含了 keystrValue 的长度以及它们的 \0 结束符。keyLenFieldkey 长度字段本身占用的字节数。因此,内联存储所需的总空间应该是 mallocSize + keyLenField

这个额外的 + 1 可能会导致对所需空间的错误判断。如果 RyanJsonGetInlineStringSize() 的计算是精确的,这里可能会在边界条件下导致缓冲区溢出;如果 RyanJsonGetInlineStringSize() 预留了额外空间,那么这个 +1 也是不必要的,并且使代码意图不清晰。建议移除这个 +1

if ((mallocSize + keyLenField) <= RyanJsonGetInlineStringSize()) { RyanJsonSetPayloadStrIsPtrByFlag(pJson, RyanJsonFalse); }

README.md Outdated
Comment on lines 83 to 84
* - bit7 : 表示key / strValue 存储模式
* 1:inline 模式, 0=ptr 模式

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

文档描述有误。根据 RyanJson.c 中的实现,当 strIsPtr 标志位为 RyanJsonTrue (1) 时,表示使用的是指针模式(ptr 模式);为 RyanJsonFalse (0) 时,表示使用的是内联模式(inline 模式)。

当前的文档描述 1:inline 模式, 0=ptr 模式 与实现正好相反,容易误导开发者。请修正此处的描述。

Suggested change
* - bit7 : 表示key / strValue 存储模式
* 1:inline 模式, 0=ptr 模式
* - bit7 : 表示key / strValue 存储模式
* 0:inline 模式, 1=ptr 模式

Comment on lines 72 to 73
* - bit7 : 表示key / strValue 存储模式
* 1:inline 模式, 0=ptr 模式

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

文档描述与实现不符。bit71 时代表指针模式(ptr mode),为 0 时代表内联模式(inline mode)。当前的描述是相反的,请修正以避免误解。

 * - bit7   : 表示key / strValue 存储模式
 *            0:inline 模式, 1=ptr 模式

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI Agents
In @RyanJson/RyanJson.c:
- Around line 2017-2029: The functions RyanJsonChangeIntValue and
RyanJsonChangeDoubleValue write without validating the node type; add a type
check after the existing NULL check using RyanJsonGetType(pJson) and ensure it
equals the numeric type (e.g., RyanJsonNumber or whatever enum value represents
numbers in this codebase); if not, return RyanJsonFalse (use
RyanJsonCheckReturnFalse or explicit return) to avoid memcpy into wrong nodes,
then proceed to RyanJsonMemcpy(RyanJsonGetValue(pJson), ...) only when the type
is confirmed numeric.
- Around line 202-221: The hidden-pointer offset uses a fixed "RyanJsonFlagSize
+ RyanJsonKeyFeidLenMaxSize" in RyanJsonGetHiddePrt and RyanJsonSetHiddePrt but
key field length is variable; compute uint8_t keyFieldLen =
RyanJsonGetPayloadEncodeKeyLenByFlag(pJson) and use RyanJsonFlagSize +
keyFieldLen for the memcpy source/target offsets in both RyanJsonGetHiddePrt and
RyanJsonSetHiddePrt so the pointer is read/written at the same variable offset
as RyanJsonGetKey/SetKeyLen.

In @RyanJson/RyanJsonConfig.h:
- Around line 46-53: The redundant fallback macro guards for RyanJsonMallocAlign
and RyanJsonMallocHeaderSize should be removed: delete the final
#ifndef/#define/#endif block that defines RyanJsonMallocAlign and
RyanJsonMallocHeaderSize since they are already defined inside the RT_VER_NUM
conditional (the RT and non-RT branches that set RyanJsonMallocAlign and
RyanJsonMallocHeaderSize), leaving only those conditional definitions.
🧹 Nitpick comments (1)
test/RyanJsonMemoryFootprintTest.c (1)

43-63: 函数逻辑正确,TLSF 集成一致。

yyjson 内存占用测试函数的内存统计逻辑准确,TLSF 分配器集成与其他测试函数保持一致。两步解析(只读文档 → 可变文档)是 yyjson 的标准用法。

第 47 行存在一个多余的空行,可以在方便时删除。

🔎 建议删除多余空行
 	static yyjson_alc yyalc = {yy_malloc, yy_realloc, yy_free, NULL};
 	int32_t use = vallocGetUseByTlsf();
-
 	// 先解析成只读文档(可用自定义分配器 yyalc)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c9d896 and aa5fb75.

📒 Files selected for processing (4)
  • RyanJson/RyanJson.c
  • RyanJson/RyanJson.h
  • RyanJson/RyanJsonConfig.h
  • test/RyanJsonMemoryFootprintTest.c
🧰 Additional context used
🧬 Code graph analysis (2)
test/RyanJsonMemoryFootprintTest.c (2)
test/RyanJsonTest.c (4)
  • v_malloc_tlsf (46-51)
  • v_realloc_tlsf (60-60)
  • v_free_tlsf (53-58)
  • vallocGetUseByTlsf (39-44)
RyanJson/RyanJson.c (1)
  • RyanJsonInitHooks (586-594)
RyanJson/RyanJsonConfig.h (1)
RyanJson/RyanJson.c (2)
  • RyanJsonMemmove (44-62)
  • RyanJsonSnprintf (29-40)
🔇 Additional comments (24)
test/RyanJsonMemoryFootprintTest.c (5)

3-5: LGTM!

yyjson 分配器适配函数正确地将 TLSF 分配器接口适配到 yyjson 的分配器规范,实现简洁准确。


7-23: LGTM!

RyanJson 内存占用测试函数正确集成了 TLSF 分配器,内存统计逻辑准确(捕获初始值后计算差值),错误处理完备。


25-41: LGTM!

cJSON 内存占用测试函数正确集成了 TLSF 分配器,实现模式与 RyanJsonMemoryFootprint 保持一致,逻辑准确。


133-133: 改进测试标题,表达更清晰。

将标题从"对象占多json数据测试"更改为"全是对象json数据测试",语法更准确,语义更清晰。


242-250: 新增测试场景,标题已修正。

新增的"压缩后的json业务对象内存占用测试"场景为内存占用测试增添了有价值的真实业务用例(使用短键名的压缩 JSON)。测试标题现在是唯一的,已解决之前评审中提到的标题重复问题。

RyanJson/RyanJsonConfig.h (3)

1-2: LGTM!

包含保护宏重命名为 RyanJsonConfig,符合更好的命名规范(避免以双下划线开头的保留标识符)。


84-90: LGTM!

编译时检查确保 RyanJsonMallocHeaderSize >= 4RyanJsonMallocAlign 是 4 的倍数,这是良好的防御性编程实践,能在编译阶段捕获配置错误。


59-59: 验证嵌套深度限制的配置与实际应用中的兼容性。

RyanJsonNestingLimit 在 RyanJsonConfig.h 配置为 500U(支持 #ifndef 覆盖),而在启用模糊测试时于 RyanJson.c 中被重新定义为 350U。这是针对嵌入式系统栈空间限制的可配置参数,在 RFC 8259 测试集中包含深层嵌套场景验证。建议检查实际应用中是否有超过 350U 深度的嵌套需求;如需支持更深的嵌套,可通过 #define RyanJsonNestingLimit 覆盖此值。

RyanJson/RyanJson.h (6)

1-2: LGTM!

包含保护宏统一重命名为 RyanJson,与配置文件保持一致的命名风格。


38-142: LGTM!

节点结构和存储策略的文档非常详尽,清晰解释了:

  • Flag 位域布局(包括 bit7 的 inline/ptr 模式:0=inline,1=ptr)
  • 内联存储优化策略
  • 动态分配的触发条件
  • 设计考量和权衡

文档质量高,有助于理解复杂的内存布局设计。


198-199: LGTM!

新增的 RyanJsonGetPayloadStrIsPtrByFlagRyanJsonSetPayloadStrIsPtrByFlag 宏与现有的标志位操作模式保持一致,用于控制字符串存储模式(inline vs ptr)。


210-210: LGTM!

RyanJsonInitHooks 参数名称更新为 userMallocuserFreeuserRealloc,更加语义化和易懂。


289-293: LGTM!

新增的取值函数 RyanJsonGetIntValueRyanJsonGetDoubleValueRyanJsonGetObjectValueRyanJsonGetArrayValue 与现有 API 保持一致,RyanJsonGetBoolValue 保留为宏定义以访问标志位。


326-327: API 声明正确,但实现需要类型检查。

RyanJsonChangeIntValueRyanJsonChangeDoubleValue 的声明符合 API 设计。不过,根据之前的审查意见,这些函数的实现需要添加类型检查(将在 RyanJson.c 中进一步审查)。

RyanJson/RyanJson.c (10)

43-63: LGTM!

自定义的 RyanJsonMemmove 实现逻辑正确:

  • 针对无重叠或 dest < src 的情况优化使用 memcpy
  • 针对 dest > src 的重叠情况使用反向拷贝

这是一个合理的回退实现,当平台未提供 memmove 或性能不佳时使用。


190-200: LGTM!

RyanJsonGetInlineStringSize 计算内联字符串大小的逻辑与头文件文档中的说明一致:

  • 利用 flag 后的填充空间
  • 利用指针存储空间
  • 利用 malloc 头部空间的一半

这是内联存储优化的核心计算。


258-271: LGTM!

RyanJsonGetValue 正确处理了内联字符串存储:对于 key 或 string 类型,在 flag 后增加 RyanJsonGetInlineStringSize() 偏移量以跳过内联字符串区域。


360-380: LGTM!

新增的 RyanJsonReplaceNode 辅助函数正确实现了链表节点替换逻辑,提高了代码复用性。


541-576: LGTM!

RyanJsonCreateItem 静态函数正确处理了为 item 添加 key 的逻辑:

  • 对于 array/object 类型,提取其内部值并释放原始容器
  • 对于其他类型,直接设置为值

这使得 API 更加灵活,允许为无 key 的对象添加 key。


578-594: LGTM!

RyanJsonInitHooks 实现与头文件声明一致,正确验证了必需的 userMallocuserFree 参数,允许 userRealloc 为 NULL。


596-657: LGTM!

新增的 getter 函数实现正确:

  • RyanJsonGetKeyRyanJsonGetStringValue 正确区分 inline 和 ptr 模式
  • RyanJsonGetIntValueRyanJsonGetDoubleValueRyanJsonGetObjectValue 使用 memcpy 安全读取数据,避免对齐问题
  • 所有函数都包含 NULL 检查

885-1004: LGTM!

统一使用 error__ 错误标签替代之前不一致的命名,提高了代码的可读性和维护性。这是良好的代码规范实践。

Also applies to: 1052-1163


382-386: LGTM!

RyanJsonChangeObjectValue 使用 memcpy 安全地更新对象值指针,在多处正确调用(分离、插入、替换等操作)。


2061-2087: 确认内部使用新增的 Change 函数是安全的。

RyanJsonCreateIntRyanJsonCreateDouble 内部调用 RyanJsonChangeIntValueRyanJsonChangeDoubleValue。这些调用是安全的,因为节点类型在创建时已确定为 Number 类型。即使在 Change 函数中添加类型检查后,这些内部调用仍然有效。

Comment on lines 202 to 221
static 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)
{
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 *));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, find the file and explore its structure
find . -name "RyanJson.c" -type f

Repository: Ryan-CW-Code/RyanJson

Length of output: 86


🏁 Script executed:

# Search for RyanJsonKeyFeidLenMaxSize definition
rg "RyanJsonKeyFeidLenMaxSize" -B 2 -A 2

Repository: Ryan-CW-Code/RyanJson

Length of output: 2290


🏁 Script executed:

# Search for RyanJsonSetKeyLen implementation
rg "RyanJsonSetKeyLen" -B 2 -A 15

Repository: Ryan-CW-Code/RyanJson

Length of output: 2024


🏁 Script executed:

# Search for RyanJsonGetKeyLen implementation
rg "RyanJsonGetKeyLen" -B 2 -A 10

Repository: Ryan-CW-Code/RyanJson

Length of output: 2713


🏁 Script executed:

# Search for how key length field is actually written/read
rg "memcpy|RyanJsonMemcpy" -B 3 -A 3 | head -100

Repository: Ryan-CW-Code/RyanJson

Length of output: 5923


隐藏指针偏移计算中的关键长度字段不一致问题需要修复。

RyanJsonGetHiddePrtRyanJsonSetHiddePrt 中的偏移计算使用固定值 RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize(恒为 1 + 4 = 5 字节),但实际的 key 长度字段是可变的(1-4 字节)。

RyanJsonSetKeyLenRyanJsonGetKeyLen 中,使用的是动态获取的 keyFieldLen

uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson);  // 1-4 字节
RyanJsonMemcpy(buf, &value, keyFieldLen);  // 仅复制 keyFieldLen 字节

而在 RyanJsonGetKey 中也正确使用可变长度:

return (char *)(RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + keyFieldLen);

keyFieldLen < 4 时,隐藏指针的读写位置将错误地向后偏移 1-3 字节,导致指针被存储/读取到错误的内存位置,引发内存损坏风险。

建议将隐藏指针的偏移计算改为使用可变 keyFieldLen,与其他函数保持一致。

🤖 Prompt for AI Agents
In @RyanJson/RyanJson.c around lines 202 - 221, The hidden-pointer offset uses a
fixed "RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize" in RyanJsonGetHiddePrt and
RyanJsonSetHiddePrt but key field length is variable; compute uint8_t
keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson) and use
RyanJsonFlagSize + keyFieldLen for the memcpy source/target offsets in both
RyanJsonGetHiddePrt and RyanJsonSetHiddePrt so the pointer is read/written at
the same variable offset as RyanJsonGetKey/SetKeyLen.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI Agents
In @RyanJson/RyanJson.c:
- Around line 636-643: RyanJsonGetObjectValue currently returns the integer
literal 0 when pJson is NULL; change the early return to return NULL to reflect
the pointer return type and improve clarity and type safety. Locate the check in
RyanJsonGetObjectValue (the RyanJsonCheckCodeNoReturn call) and replace the "{
return 0; }" branch with "{ return NULL; }", ensuring the function continues to
return a RyanJson_t pointer consistently.
- Around line 2005-2017: Both functions write raw bytes into the node value
without verifying the node is a numeric type; add a type check at the start of
RyanJsonChangeIntValue and RyanJsonChangeDoubleValue (use the project's type
accessor, e.g., RyanJsonGetType(pJson) or pJson->type) to ensure the node is a
Number (or appropriate numeric subtype) and return RyanJsonFalse if not, before
calling RyanJsonGetValue and memcpy; keep existing NULL check and only perform
the memcpy when the type matches.

In @RyanJson/RyanJsonConfig.h:
- Around line 43-50: The duplicate fallback #ifndef/#define blocks for
RyanJsonMallocAlign and RyanJsonMallocHeaderSize are redundant because those
macros are already defined inside the RT_VER_NUM conditional branches; remove
the trailing fallback guards (the #ifndef/#define/#endif for RyanJsonMallocAlign
and RyanJsonMallocHeaderSize starting at the shown diff) so only the
RT_VER_NUM-specific definitions remain, leaving no duplicate macro definitions.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa5fb75 and 9111db2.

📒 Files selected for processing (3)
  • RyanJson/RyanJson.c
  • RyanJson/RyanJsonConfig.h
  • test/baseTest/RyanJsonBaseTestChangeJson.c
🧰 Additional context used
🧬 Code graph analysis (1)
test/baseTest/RyanJsonBaseTestChangeJson.c (3)
RyanJson/RyanJson.c (5)
  • compare_double (300-312)
  • RyanJsonChangeKey (1991-1996)
  • RyanJsonGetObjectByKey (1725-1741)
  • RyanJsonGetKey (584-595)
  • RyanJsonGetStringValue (597-616)
test/baseTest/RyanJsonBaseTestUtile.c (1)
  • compare_double (6-10)
externalModule/cJSON/cJSON.c (1)
  • compare_double (584-588)
🔇 Additional comments (9)
test/baseTest/RyanJsonBaseTestChangeJson.c (1)

5-142: 测试用例结构良好,覆盖了关键的 inline/ptr 模式转换场景。

测试涵盖了以下场景:

  • 基本类型修改(int、double、string、bool)
  • inline 模式下 key/value 修改不超过长度限制
  • inline 模式下 key/value 修改超过长度限制,转换为 ptr 模式
  • ptr 模式下 key/value 修改不超过长度限制,转换为 inline 模式
  • 嵌套对象和数组元素的修改

错误处理路径正确清理了 jsonRoot

RyanJson/RyanJsonConfig.h (1)

81-87: 编译时验证宏配置合法性是一个良好的防御性编程实践。

添加的编译时检查确保:

  • RyanJsonMallocHeaderSize >= 4
  • RyanJsonMallocAlign 是 4 的倍数

这有助于在编译阶段捕获配置错误。

RyanJson/RyanJson.c (7)

414-447: inline 存储逻辑看起来正确。

条件 (mallocSize + keyLenField + 1) <= RyanJsonGetInlineStringSize() 中:

  • mallocSize 包含 key 和 strValue 及其 \0 终止符
  • keyLenFieldRyanJsonCalcLenBytes 返回值(0-3)
  • keyLenField + 1 得到实际的 key 长度字段大小(1-4 字节)

这个计算是正确的,因为 RyanJsonCalcLenBytes 返回的是编码所需的额外字节数。


574-582: RyanJsonInitHooks 实现正确,但请确保在使用其他 API 前调用。

函数正确验证了 userMallocuserFree 非空,userRealloc 可选为 NULL。

注意:全局函数指针的设置没有线程同步保护。如果在多线程环境中,应确保此函数在任何其他 RyanJson API 调用之前完成初始化。


538-564: RyanJsonCreateItem 实现了所有权转移语义。

该函数正确地:

  1. 创建带有 key 的新节点
  2. 将原 item 的子对象值转移到新节点
  3. 释放原 item 的字符串存储和节点本身

这是一种类似 C++ move 语义的实现,用于在不复制子对象的情况下添加 key。


992-996: 错误标签重命名为 error__ 是正确的做法。

C 标准规定以双下划线开头的标识符是保留的。将 __error 改为 error__ 避免了潜在的命名冲突。


300-312: 浮点数比较函数实现正确。

使用相对误差(DBL_EPSILON * maxVal)和绝对误差(1e-12)的组合是浮点数比较的标准做法,能够处理不同数量级的数值。


584-616: RyanJsonGetKeyRyanJsonGetStringValue 正确处理 inline/ptr 两种存储模式。

两个函数根据 RyanJsonGetPayloadStrIsPtrByFlag 的结果选择正确的偏移计算方式:

  • inline 模式:使用 keyFieldLen 计算 payload 内的偏移
  • ptr 模式:通过 RyanJsonGetHiddenPtrAt 获取外部存储地址

3-41: 模糊测试基础设施实现合理。

通过 isEnableFuzzer 宏条件编译:

  • 降低嵌套限制以加快测试
  • 随机注入 RyanJsonSnprintf 失败以测试错误处理路径
  • 使用 Xorshift64* 算法生成随机数

这是一个良好的测试实践。

Comment on lines +43 to 50
#ifndef RyanJsonMallocAlign
#define RyanJsonMallocAlign 8U
#endif

//
#ifndef RyanJsonMallocHeaderSize
#define RyanJsonMallocHeaderSize 8U
#endif
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

冗余的后备宏定义保护应被删除。

RyanJsonMallocAlignRyanJsonMallocHeaderSize 已在 #ifdef RT_VER_NUM 条件块内完整定义(RT 路径在第 26-27 行,非 RT 路径在第 36-37 行)。由于 RT_VER_NUM 条件分支覆盖了所有情况,这两个宏在执行到第 43 行时已经必然被定义,因此这些 #ifndef 保护永远不会被触发。

🔎 建议删除冗余代码
-#ifndef RyanJsonMallocAlign
-#define RyanJsonMallocAlign 8U
-#endif
-
-//
-#ifndef RyanJsonMallocHeaderSize
-#define RyanJsonMallocHeaderSize 8U
-#endif
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#ifndef RyanJsonMallocAlign
#define RyanJsonMallocAlign 8U
#endif
//
#ifndef RyanJsonMallocHeaderSize
#define RyanJsonMallocHeaderSize 8U
#endif
🤖 Prompt for AI Agents
In @RyanJson/RyanJsonConfig.h around lines 43 - 50, The duplicate fallback
#ifndef/#define blocks for RyanJsonMallocAlign and RyanJsonMallocHeaderSize are
redundant because those macros are already defined inside the RT_VER_NUM
conditional branches; remove the trailing fallback guards (the
#ifndef/#define/#endif for RyanJsonMallocAlign and RyanJsonMallocHeaderSize
starting at the shown diff) so only the RT_VER_NUM-specific definitions remain,
leaving no duplicate macro definitions.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
README.md (3)

219-219: 为代码块指定语言标识。

这两处代码块缺少语言标识,建议为 JSON 测试输出分别添加语言标识以改善代码高亮效果。

🔎 建议修改
-```
+```json
 *****************************************************************************
 *************************** RyanJson / cJSON / yyjson 内存对比程序 **************************
 *****************************************************************************

在第 251 行处进行相同的修改。

Also applies to: 251-251


285-287: 将强调语法转换为正确的标题格式。

第 285 行使用强调标记(**...**)来表示标题内容,应该转换为 Markdown 标题语法(#######)以确保文档结构清晰。

🔎 建议修改
-**RyanJson的重点不在兼容Unicode字符集,大部分嵌入式场景不会出现极为特殊的Unicode字符集**
+#### Unicode 字符集兼容性说明
+
+RyanJson的重点不在兼容Unicode字符集,大部分嵌入式场景不会出现极为特殊的Unicode字符集。

此修改将增强文档层级结构的清晰度,同时保留原意。


47-84: 处理代码块中的制表符格式。

C 结构代码块中存在多处硬制表符(Hard tabs),可能导致不同编辑器渲染不一致。考虑将其统一转换为空格以保证显示一致性。

虽然这些硬制表符可能是有意的代码格式化,但为了与 Markdown 最佳实践一致并确保跨平台一致性,建议在代码块中统一使用空格缩进。

Also applies to: 114-116, 131-131, 151-151

example/RyanJsonExample.c (1)

5-17: 消除代码重复:复用现有的 RyanJsonCompareDouble 实现

此函数与 RyanJson/RyanJson.c:305-317 中的实现完全相同。建议将该函数从 RyanJson.c 中导出为公共 API,或者在公共头文件中声明为 static inline,以避免维护两份相同的代码。

🔎 建议的重构方案

方案 1(推荐): 如果 RyanJsonCompareDouble 已在 RyanJson.h 或其他公共头文件中导出,直接移除此处的重复定义。

方案 2: 如果该函数尚未导出,考虑将其添加到 RyanJsonUtils.h 中作为公共工具函数:

RyanJsonUtils.h 中添加:

+static inline RyanJsonBool_e RyanJsonCompareDouble(double a, double b)
+{
+	double diff = fabs(a - b);
+	double absA = fabs(a);
+	double absB = fabs(b);
+	double maxVal = (absA > absB ? absA : absB);
+	double epsilon = DBL_EPSILON * maxVal;
+	double minTolerance = 1e-12;
+	return diff <= (epsilon > minTolerance ? epsilon : minTolerance);
+}

然后在此文件中移除重复定义:

-static inline RyanJsonBool_e RyanJsonCompareDouble(double a, double b)
-{
-	double diff = fabs(a - b);
-	double absA = fabs(a);
-	double absB = fabs(b);
-	double maxVal = (absA > absB ? absA : absB);
-
-	// 允许的容差:相对误差 + 绝对误差
-	double epsilon = DBL_EPSILON * maxVal;
-	double minTolerance = 1e-12; // 可调的绝对容差
-
-	return diff <= (epsilon > minTolerance ? epsilon : minTolerance);
-}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d15c73 and e7a1efd.

📒 Files selected for processing (4)
  • README.md
  • example/RyanJsonExample.c
  • test/RyanJsonTest.c
  • test/baseTest/RyanJsonBaseTest.c
🧰 Additional context used
🧬 Code graph analysis (3)
test/baseTest/RyanJsonBaseTest.c (3)
test/baseTest/RyanJsonBaseTestChangeJson.c (1)
  • RyanJsonBaseTestChangeJson (5-142)
test/baseTest/RyanJsonBaseTestCompareJson.c (1)
  • RyanJsonBaseTestCompareJson (4-158)
test/baseTest/RyanJsonBaseTestLoadJson.c (1)
  • RyanJsonBaseTestLoadJson (6-278)
test/RyanJsonTest.c (3)
test/baseTest/RyanJsonBaseTestUtile.c (1)
  • printJsonDebug (12-17)
test/tlsf/tlsf.c (6)
  • tlsf_alloc_overhead (266-266)
  • rt_memory_info22 (582-588)
  • tlsf_walk_pool (201-213)
  • tlsf_realloc (620-680)
  • tlsf_create_with_pool (376-388)
  • tlsf_size (252-257)
test/valloc/valloc.c (2)
  • v_malloc (14-27)
  • v_free (37-48)
example/RyanJsonExample.c (1)
RyanJson/RyanJson.c (1)
  • RyanJsonCompareDouble (306-318)
🪛 LanguageTool
README.md

[uncategorized] ~217-~217: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...参考) #### 内存占用测试 (20251222 malloc头部空间8字节,内存对齐4字节)测试代码可在本项目根目录RyanJsonExample...

(wa5)


[uncategorized] ~217-~217: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...## 内存占用测试 (20251222 malloc头部空间8字节,内存对齐4字节)测试代码可在本项目根目录RyanJsonExample文件夹查看。 ...

(wa5)


[uncategorized] ~249-~249: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ... ``` RT-Thread平台使用最小内存算法默认 malloc头部空间12字节,内存对齐8字节测试代码可在本项目根目录RyanJsonExample文...

(wa5)


[uncategorized] ~249-~249: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...Thread平台使用最小内存算法默认 malloc头部空间12字节,内存对齐8字节测试代码可在本项目根目录RyanJsonExample文件夹查看 ``...

(wa5)

🪛 markdownlint-cli2 (0.18.1)
README.md

29-29: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)


47-47: Hard tabs
Column: 1

(MD010, no-hard-tabs)


48-48: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)


50-50: Hard tabs
Column: 1

(MD010, no-hard-tabs)


51-51: Hard tabs
Column: 1

(MD010, no-hard-tabs)


52-52: Hard tabs
Column: 1

(MD010, no-hard-tabs)


53-53: Hard tabs
Column: 1

(MD010, no-hard-tabs)


54-54: Hard tabs
Column: 1

(MD010, no-hard-tabs)


55-55: Hard tabs
Column: 1

(MD010, no-hard-tabs)


56-56: Hard tabs
Column: 1

(MD010, no-hard-tabs)


57-57: Hard tabs
Column: 1

(MD010, no-hard-tabs)


58-58: Hard tabs
Column: 1

(MD010, no-hard-tabs)


59-59: Hard tabs
Column: 1

(MD010, no-hard-tabs)


60-60: Hard tabs
Column: 1

(MD010, no-hard-tabs)


69-69: Hard tabs
Column: 1

(MD010, no-hard-tabs)


70-70: Hard tabs
Column: 1

(MD010, no-hard-tabs)


71-71: Hard tabs
Column: 1

(MD010, no-hard-tabs)


72-72: Hard tabs
Column: 1

(MD010, no-hard-tabs)


73-73: Hard tabs
Column: 1

(MD010, no-hard-tabs)


74-74: Hard tabs
Column: 1

(MD010, no-hard-tabs)


75-75: Hard tabs
Column: 1

(MD010, no-hard-tabs)


76-76: Hard tabs
Column: 1

(MD010, no-hard-tabs)


77-77: Hard tabs
Column: 1

(MD010, no-hard-tabs)


78-78: Hard tabs
Column: 1

(MD010, no-hard-tabs)


79-79: Hard tabs
Column: 1

(MD010, no-hard-tabs)


80-80: Hard tabs
Column: 1

(MD010, no-hard-tabs)


81-81: Hard tabs
Column: 1

(MD010, no-hard-tabs)


82-82: Hard tabs
Column: 1

(MD010, no-hard-tabs)


83-83: Hard tabs
Column: 1

(MD010, no-hard-tabs)


84-84: Hard tabs
Column: 1

(MD010, no-hard-tabs)


114-114: Hard tabs
Column: 1

(MD010, no-hard-tabs)


115-115: Hard tabs
Column: 1

(MD010, no-hard-tabs)


116-116: Hard tabs
Column: 1

(MD010, no-hard-tabs)


131-131: Hard tabs
Column: 1

(MD010, no-hard-tabs)


151-151: Hard tabs
Column: 1

(MD010, no-hard-tabs)


219-219: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


251-251: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


285-285: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🔇 Additional comments (9)
example/RyanJsonExample.c (3)

24-102: 函数重构正确,逻辑清晰

返回类型从 int 改为 RyanJsonBool_e 提升了代码的类型安全性和可读性。JSON 对象的创建和添加逻辑正确。


109-195: 新增的数据验证逻辑健壮且完整

函数增加了对解析结果的全面验证,包括 int、double、string、bool、null 类型的检查。每个失败分支都正确执行了 RyanJsonDelete(jsonRoot) 清理,避免内存泄漏。


202-274: 修改操作的验证逻辑完善

新增的验证代码覆盖了 key 修改、string/int/double/bool value 修改等场景,确保了新 API(RyanJsonChangeKeyRyanJsonChangeIntValueRyanJsonChangeDoubleValue 等)的正确性。错误处理和资源清理得当。

test/baseTest/RyanJsonBaseTest.c (1)

69-77: 测试用例注释更新清晰

为每个测试函数添加的注释准确描述了其验证目标,提升了测试套件的可维护性和可读性。

test/RyanJsonTest.c (5)

1-7: TLSF 集成的头文件和宏定义合理

引入 tlsf.h 并定义 1MB 的内存池大小对于测试场景是充足的。日志宏 jsonLogByTest 提供了文件和行号上下文,便于调试。


18-37: TLSF 内存诊断功能实现正确

showMemoryInfo 通过 rt_memory_info22tlsf_walk_pool 两种方式统计内存使用情况,提供了交叉验证机制。Walker 回调正确累加了块大小和分配开销。


39-60: TLSF 包装函数实现安全

三个包装函数(v_malloc_tlsfv_free_tlsfv_realloc_tlsf)正确处理了边界情况:零大小分配和空指针释放。实现符合标准内存管理语义。


65-100: TLSF 初始化和测试流程集成正确

TLSF 池的初始化序列合理:

  1. 使用非 TLSF 的 v_malloc 分配初始缓冲区(引导阶段)
  2. 创建 TLSF 实例并注册钩子
  3. 执行测试并处理错误

测试流程保留了原有的错误处理逻辑。


102-129: 内存诊断和清理流程完整

在测试的关键节点调用 showMemoryInfo() 提供了内存使用的可见性。清理顺序正确:先执行 showMemoryInfo(),再释放 TLSF 缓冲区,最后调用 displayMem() 进行最终诊断。

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI Agents
In @RyanJson/RyanJson.c:
- Around line 631-658: The guard in RyanJsonGetObjectValue currently returns 0
on NULL input; change the early return to return NULL to match pointer semantics
(update the RyanJsonCheckCodeNoReturn invocation in RyanJsonGetObjectValue so
the lambda returns NULL instead of 0). Keep the rest of the function (memcpy
into objValue and return objValue) unchanged; no other behavior needs
modification—RyanJsonGetArrayValue can still call RyanJsonGetObjectValue.
- Around line 195-214: RyanJsonGetHiddePrt and RyanJsonSetHiddePrt compute the
hidden-pointer offset using a fixed RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize
which is wrong when the key-length field is variable; change both functions to
read the actual keyFieldLen via
RyanJsonGetPayloadEncodeKeyLenByFlag(RyanJsonGetPayloadPtr(pJson)) (or
equivalent helper used elsewhere), compute offset = RyanJsonFlagSize +
keyFieldLen, and use that offset in the RyanJsonMemcpy source/target so the
pointer is read/written at the correct variable position (keep sizeof(void*) and
existing asserts).
- Around line 2016-2028: Both functions lack a node-type check and may memcpy
into the wrong location for non-number nodes; update RyanJsonChangeIntValue and
RyanJsonChangeDoubleValue to verify the node is a numeric type before writing:
call the existing type-query API (e.g., RyanJsonGetType or RyanJsonIsNumber) on
pJson and if the type is not a number return RyanJsonFalse (use the same
RyanJsonCheckReturnFalse pattern), only then call
RyanJsonMemcpy(RyanJsonGetValue(pJson), &number, sizeof(number)) and return
RyanJsonTrue.
- Around line 432-464: The inline-storage size check uses an extra +1 in the
condition (mallocSize + keyLenField + 1) <= RyanJsonGetInlineStringSize which is
likely incorrect because mallocSize already includes key, strValue and their NUL
terminators; update the conditional to (mallocSize + keyLenField) <=
RyanJsonGetInlineStringSize, verify mallocSize semantics to ensure it includes
both terminators, and keep the existing copy logic (RyanJsonMemcpy,
null-terminations) unchanged; if mallocSize does not include terminators instead
add them to mallocSize calculation, ensure no off-by-one by unit-testing
boundary cases, and remove the redundant +1 so
RyanJsonSetHiddePrt/RyanJsonMemcpy paths are safe.

In @test/fuzzer/RyanJsonFuzzer.c:
- Around line 671-672: The allocation using malloc(size + 100) can overflow when
size is large; before allocating check that size <= SIZE_MAX - 100 (or otherwise
validate/limit size) to prevent wraparound, handle allocation failure, and only
call memcpy(buf, data, size) when the buffer is actually at least size bytes;
update the code around the malloc/memcpy (symbols: buf, size, malloc, memcpy) to
validate size, compute a safe allocation length, and bail out or truncate inputs
when validation fails.
🧹 Nitpick comments (1)
RyanJson/RyanJson.h (1)

40-144: 可选:考虑精简文档长度。

节点结构的文档非常详尽(超过 100 行),涵盖了内存布局、标志位语义、存储策略等。虽然信息很有价值,但建议考虑:

  1. 将设计权衡和实现细节移至独立的设计文档或 README
  2. 保留头文件中的核心 API 使用说明
  3. 使用更简洁的语言描述关键概念

这样可以提高代码可读性,同时保留重要的技术细节。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7a1efd and 0b97df2.

📒 Files selected for processing (5)
  • RyanJson/RyanJson.c
  • RyanJson/RyanJson.h
  • RyanJson/RyanJsonUtils.c
  • test/fuzzer/RyanJsonFuzzer.c
  • test/fuzzer/RyanJsonFuzzer.dict
🧰 Additional context used
🧬 Code graph analysis (2)
test/fuzzer/RyanJsonFuzzer.c (1)
RyanJson/RyanJson.c (26)
  • RyanJsonPrint (1615-1643)
  • RyanJsonCompare (2297-2360)
  • RyanJsonInsert (1868-1905)
  • RyanJsonCreateString (2095-2101)
  • RyanJsonDelete (665-685)
  • RyanJsonPrintPreallocated (1655-1673)
  • RyanJsonGetSize (1681-1696)
  • RyanJsonGetKey (597-608)
  • RyanJsonGetStringValue (610-629)
  • RyanJsonChangeStringValue (2009-2014)
  • RyanJsonGetIntValue (631-638)
  • RyanJsonGetDoubleValue (640-647)
  • RyanJsonGetObjectValue (649-656)
  • RyanJsonGetArrayValue (658-658)
  • RyanJsonIsKey (2128-2128)
  • RyanJsonIsNull (2129-2129)
  • RyanJsonIsBool (2130-2130)
  • RyanJsonIsNumber (2131-2134)
  • RyanJsonIsString (2135-2138)
  • RyanJsonIsArray (2139-2139)
  • RyanJsonIsObject (2140-2143)
  • RyanJsonIsInt (2144-2147)
  • RyanJsonIsDouble (2148-2151)
  • RyanJsonChangeKey (2002-2007)
  • RyanJsonChangeIntValue (2016-2021)
  • RyanJsonChangeDoubleValue (2023-2028)
RyanJson/RyanJsonUtils.c (1)
RyanJson/RyanJson.c (4)
  • RyanJsonGetSize (1681-1696)
  • RyanJsonGetObjectByIndex (1705-1722)
  • RyanJsonGetObjectByKey (1731-1749)
  • RyanJsonGetKey (597-608)
🔇 Additional comments (24)
test/fuzzer/RyanJsonFuzzer.dict (1)

28-28: 模糊测试字典扩展 LGTM!

新增的测试用例覆盖了重要的边界场景:

  • "\"name" 测试未闭合字符串的解析容错
  • "0000" 测试前导零的处理
  • "-21474836470" 超出 INT32_MIN 约10倍,有效测试整数溢出处理

Also applies to: 33-33, 40-40

test/fuzzer/RyanJsonFuzzer.c (6)

10-37: 测试脚手架扩展设计合理。

  • isEnableRandomMemFail 改为 static 链接符合模糊测试文件的内部作用域需求
  • 在测试设置期间禁用随机内存失败(第14/37行)确保了测试初始化的确定性
  • 新增的类型错误和插入失败测试用例(第17-32行)有效覆盖了边界条件

56-57: 缓冲区大小调整合理。

将最小缓冲区阈值设置为 2048 字节可确保预分配打印有足够空间处理小型 JSON 结构。


267-269: 代码风格改进。

为单语句块添加大括号是良好的实践,可提高可维护性并防止未来因遗漏括号导致的错误。


281-284: NULL 输入检查扩展 LGTM。

这些断言验证了 getter 函数对 NULL 输入的防御性处理,与实现一致。

小建议:第282行 0 == RyanJsonGetDoubleValue(NULL) 使用 == 比较浮点数。虽然在此场景下函数显式返回 0,可正常工作,但若追求严谨可考虑使用近似比较。


317-325: 类型检查函数的 NULL 安全测试完善。

这些断言全面验证了所有 RyanJsonIs* 函数对 NULL 输入返回 RyanJsonFalse,与实现中的 NULL 检查逻辑一致。


394-398: Change 函数的 NULL 检查测试扩展合理。

新增的断言覆盖了 RyanJsonChangeKeyRyanJsonChangeIntValueRyanJsonChangeDoubleValue 对 NULL 输入的处理,提升了测试覆盖率。

RyanJson/RyanJson.h (4)

1-2: LGTM!移除了保留的双下划线前缀。

头文件保护符从 __RyanJson__ 改为 RyanJson 是正确的做法。以双下划线开头的标识符是为编译器实现保留的,应避免在用户代码中使用。


26-31: LGTM!断言宏的实现符合预期。

当未启用断言时,宏正确地展开为 ((void)0),避免了未使用变量的警告。启用时,宏会调用断言检查。


200-201: LGTM!新增的内联/指针模式标志位宏定义正确。

RyanJsonGetPayloadStrIsPtrByFlagRyanJsonSetPayloadStrIsPtrByFlag 宏正确地访问第 7 位标志,用于区分内联存储模式(0)和指针模式(1)。实现与文档描述一致。


212-212: 宏转换为函数声明提升了接口类型安全性。

将取值和修改值的接口从宏改为 extern 函数声明是好的改进:

  • 提供更好的编译时类型检查
  • 便于调试和错误处理
  • 保持简单的布尔值访问器为宏以优化性能

需要注意的是:根据头文件注释,RyanJsonChangeIntValue 和 RyanJsonChangeDoubleValue 的实现不包含类型检查。头文件明确说明(第326行):"修改函数没有对入参做校验"。这些函数仅验证指针非空,类型验证责任由调用方负责——调用这些函数前必须先用 RyanJsonIsXXXX 判断类型,否则会导致内存访问错误。这是设计决策,非实现缺陷。

适用于:291-295, 328-330

RyanJson/RyanJsonUtils.c (1)

118-118: LGTM!将显式检查替换为宏保护提升了代码一致性。

所有更改都是将显式的条件检查(如 if (!condition) return false;)替换为 RyanJsonCheckReturnFalse 宏。这种重构:

  • 保持了原有的功能行为
  • 与代码库的宏驱动错误处理模式保持一致
  • 提高了代码可读性和维护性

Also applies to: 123-123, 133-133, 140-140, 147-147, 152-153

RyanJson/RyanJson.c (12)

1-41: LGTM!Fuzzer 配置增强了测试覆盖率。

isEnableFuzzer 模式下引入随机失败注入是很好的实践,有助于发现边界情况和错误处理路径中的问题。这些更改不会影响生产代码。


179-193: LGTM!内联字符串大小的计算考虑周全。

RyanJsonGetInlineStringSize 的计算考虑了:

  • Flag 后的填充空间
  • 指针变量的空间
  • Malloc 头部空间的一半

通过内联存储优化,可以减少小字符串的 malloc 调用次数,这对嵌入式平台尤其重要。使用宏定义而非函数可以让编译器在编译时计算,提高效率。


229-255: LGTM!键长度读写实现了大小端无关的正确逻辑。

RyanJsonSetKeyLenRyanJsonGetKeyLen 正确地:

  • 使用可变长度字段 keyFieldLen(1-4 字节)
  • 实现了大小端无关的字节序处理
  • 确保了跨平台的可移植性

257-272: LGTM!值指针获取函数正确计算了偏移量。

RyanJsonGetValue 正确地计算了值存储区域的偏移:

  • 对于有键的节点,跳过 Flag 和内联字符串存储区域
  • 返回指向实际值存储位置的指针

注释表明当前场景只需处理有键的情况,这与实现相符。


587-595: LGTM!内存钩子初始化函数正确实现。

RyanJsonInitHooks 允许用户提供自定义内存分配器,支持 TLSF 等替代分配器的集成(如 PR 摘要所述)。实现正确地:

  • 验证必需的 mallocfree 参数非空
  • 允许可选的 realloc 参数为 NULL

597-629: LGTM!键和字符串值的获取函数正确实现了内联/指针模式切换。

RyanJsonGetKeyRyanJsonGetStringValue 正确地:

  • 检查内联/指针存储模式标志
  • 使用可变 keyFieldLen 计算偏移量
  • 返回内联存储或隐藏指针位置的字符串

实现与新的存储策略一致。


885-999: LGTM!使用 goto error__ 模式集中处理错误清理。

在解析函数中使用 goto error__ 标签进行错误处理是良好的 C 语言实践:

  • 集中管理资源释放逻辑
  • 减少代码重复
  • 确保所有错误路径都正确释放已分配的资源

实现正确且一致。


1039-1156: LGTM!解析函数中的标签命名一致且清晰。

RyanJsonParseArrayRyanJsonParseObject 使用统一的标签约定:

  • next__ 用于成功路径的继续
  • error__ 用于错误清理

这种一致的模式提高了代码的可读性和可维护性。


2060-2086: 创建函数正确使用了 ChangeValue 辅助函数。

RyanJsonCreateIntRyanJsonCreateDouble 在创建节点后使用 RyanJsonChangeIntValueRyanJsonChangeDoubleValue 设置值。由于节点刚创建,类型已正确设置,这里的使用是安全的。

但如前所述,在 Change 函数中添加类型检查仍然重要,以防止在其他调用点被误用。


381-385: LGTM!内部对象值修改函数实现正确。

RyanJsonChangeObjectValue 作为内部静态函数,使用 memcpy 安全地更新对象/数组的子节点指针,实现符合预期。


367-379: LGTM!节点替换函数正确处理了链表操作。

RyanJsonReplaceNode 正确实现了链表节点替换:

  • 处理前驱节点为 NULL 的情况(替换首节点)
  • 正确链接前驱、新节点和后继
  • 将旧节点从链表中分离(设置 next 为 NULL)

2297-2360: LGTM!比较函数的重构保持了一致性。

RyanJsonCompare 函数中将显式检查替换为 RyanJsonCheckReturnFalse 宏,与 RyanJsonUtils.c 中的类似更改一致,提升了代码的统一性和可维护性。

Comment on lines 195 to 214
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 *));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

【关键问题】隐藏指针偏移计算使用固定值,与可变长度键字段不一致。

RyanJsonGetHiddePrtRyanJsonSetHiddePrt 在 Line 201 和 Line 212 使用固定偏移 RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize(即 1 + 4 = 5 字节),但实际的键长度字段是可变的(1-4 字节),由 RyanJsonGetPayloadEncodeKeyLenByFlag 决定。

keyFieldLen < 4 时,指针会在错误的位置读写,导致内存损坏。这与 RyanJsonGetKey(Line 604)和 RyanJsonSetKeyLen(Line 233)中使用可变 keyFieldLen 的做法不一致。

建议修改为使用可变偏移量:

🔎 建议修复
 static inline uint8_t *RyanJsonGetHiddePrt(RyanJson_t pJson)
 {
 	RyanJsonCheckAssert(NULL != pJson);
+	uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson);
 
 	// 用memcpy规避非对齐警告
 	void *tmpPtr = NULL;
-	RyanJsonMemcpy((void *)&tmpPtr, (RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize), sizeof(void *));
+	RyanJsonMemcpy((void *)&tmpPtr, (RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + keyFieldLen), sizeof(void *));
 	return (uint8_t *)tmpPtr;
 }

 static inline void RyanJsonSetHiddePrt(RyanJson_t pJson, uint8_t *hiddePrt)
 {
 	RyanJsonCheckAssert(NULL != pJson);
 	RyanJsonCheckAssert(NULL != hiddePrt);
+	uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson);
 
 	// 用memcpy规避非对齐警告
 	void *tmpPtr = hiddePrt;
-	// uint8_t是flag,uint32_t是记录key的长度空间
-	RyanJsonMemcpy((RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize), (const void *)&tmpPtr,
+	RyanJsonMemcpy((RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + keyFieldLen), (const void *)&tmpPtr,
 		       sizeof(void *));
 }

基于历史审查评论。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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 *));
}
static inline uint8_t *RyanJsonGetHiddePrt(RyanJson_t pJson)
{
RyanJsonCheckAssert(NULL != pJson);
uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson);
// 用memcpy规避非对齐警告
void *tmpPtr = NULL;
RyanJsonMemcpy((void *)&tmpPtr, (RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + keyFieldLen), sizeof(void *));
return (uint8_t *)tmpPtr;
}
static inline void RyanJsonSetHiddePrt(RyanJson_t pJson, uint8_t *hiddePrt)
{
RyanJsonCheckAssert(NULL != pJson);
RyanJsonCheckAssert(NULL != hiddePrt);
uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson);
// 用memcpy规避非对齐警告
void *tmpPtr = hiddePrt;
RyanJsonMemcpy((RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + keyFieldLen), (const void *)&tmpPtr,
sizeof(void *));
}
🤖 Prompt for AI Agents
In @RyanJson/RyanJson.c around lines 195 - 214, RyanJsonGetHiddePrt and
RyanJsonSetHiddePrt compute the hidden-pointer offset using a fixed
RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize which is wrong when the key-length
field is variable; change both functions to read the actual keyFieldLen via
RyanJsonGetPayloadEncodeKeyLenByFlag(RyanJsonGetPayloadPtr(pJson)) (or
equivalent helper used elsewhere), compute offset = RyanJsonFlagSize +
keyFieldLen, and use that offset in the RyanJsonMemcpy source/target so the
pointer is read/written at the correct variable position (keep sizeof(void*) and
existing asserts).

Comment on lines +432 to 464
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);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

【重要】内联存储阈值判断中的 +1 可能不必要。

Line 434 的条件 (mallocSize + keyLenField + 1) <= RyanJsonGetInlineStringSize 中的 +1 可能是多余的:

  • mallocSize 已包含 key、strValue 及其 \0 结束符
  • keyLenField 是键长度字段本身的字节数(1-4)
  • 所需空间应为 mallocSize + keyLenField

这个额外的 +1 可能导致:

  1. 如果 RyanJsonGetInlineStringSize() 计算精确,可能在边界条件下导致缓冲区溢出
  2. 如果预留了额外空间,则 +1 不必要且使代码意图不清晰

建议验证并考虑移除 +1

基于历史审查评论。

🤖 Prompt for AI Agents
In @RyanJson/RyanJson.c around lines 432 - 464, The inline-storage size check
uses an extra +1 in the condition (mallocSize + keyLenField + 1) <=
RyanJsonGetInlineStringSize which is likely incorrect because mallocSize already
includes key, strValue and their NUL terminators; update the conditional to
(mallocSize + keyLenField) <= RyanJsonGetInlineStringSize, verify mallocSize
semantics to ensure it includes both terminators, and keep the existing copy
logic (RyanJsonMemcpy, null-terminations) unchanged; if mallocSize does not
include terminators instead add them to mallocSize calculation, ensure no
off-by-one by unit-testing boundary cases, and remove the redundant +1 so
RyanJsonSetHiddePrt/RyanJsonMemcpy paths are safe.

Comment on lines 631 to 658
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); }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

【轻微问题】RyanJsonGetObjectValue 应返回 NULL 而非 0

Lines 631-647 的 RyanJsonGetIntValueRyanJsonGetDoubleValue 使用 memcpy 正确实现了类型安全的值提取。

但是 Line 651 的 RyanJsonGetObjectValuepJson 为 NULL 时返回 0 而非 NULL。虽然在大多数平台上数值相同,但对于指针类型应使用 NULL 以保持语义清晰和类型安全。

🔎 建议修复
 RyanJson_t RyanJsonGetObjectValue(RyanJson_t pJson)
 {
-	RyanJsonCheckCodeNoReturn(NULL != pJson, { return 0; });
+	RyanJsonCheckCodeNoReturn(NULL != pJson, { return NULL; });
 
 	RyanJson_t objValue;
 	RyanJsonMemcpy((void *)&objValue, RyanJsonGetValue(pJson), sizeof(void *));
 	return objValue;
 }

基于历史审查评论。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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); }
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 NULL; });
RyanJson_t objValue;
RyanJsonMemcpy((void *)&objValue, RyanJsonGetValue(pJson), sizeof(void *));
return objValue;
}
RyanJson_t RyanJsonGetArrayValue(RyanJson_t pJson) { return RyanJsonGetObjectValue(pJson); }
🤖 Prompt for AI Agents
In @RyanJson/RyanJson.c around lines 631 - 658, The guard in
RyanJsonGetObjectValue currently returns 0 on NULL input; change the early
return to return NULL to match pointer semantics (update the
RyanJsonCheckCodeNoReturn invocation in RyanJsonGetObjectValue so the lambda
returns NULL instead of 0). Keep the rest of the function (memcpy into objValue
and return objValue) unchanged; no other behavior needs
modification—RyanJsonGetArrayValue can still call RyanJsonGetObjectValue.

Comment on lines +2016 to +2028
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;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

【关键问题】缺少类型检查可能导致内存损坏。

RyanJsonChangeIntValueRyanJsonChangeDoubleValue 仅检查 pJson 是否为 NULL,但未验证节点类型是否为 Number。如果对非数字类型的节点调用这些函数:

  • RyanJsonGetValue 返回的偏移量将不正确
  • memcpy 会写入错误的内存位置
  • 可能导致内存损坏或数据破坏

建议添加类型检查:

🔎 建议修复
 RyanJsonBool_e RyanJsonChangeIntValue(RyanJson_t pJson, int32_t number)
 {
 	RyanJsonCheckReturnFalse(NULL != pJson);
+	RyanJsonCheckReturnFalse(RyanJsonIsNumber(pJson));
 	RyanJsonMemcpy(RyanJsonGetValue(pJson), &number, sizeof(number));
 	return RyanJsonTrue;
 }

 RyanJsonBool_e RyanJsonChangeDoubleValue(RyanJson_t pJson, double number)
 {
 	RyanJsonCheckReturnFalse(NULL != pJson);
+	RyanJsonCheckReturnFalse(RyanJsonIsNumber(pJson));
 	RyanJsonMemcpy(RyanJsonGetValue(pJson), &number, sizeof(number));
 	return RyanJsonTrue;
 }

基于历史审查评论。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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;
}
RyanJsonBool_e RyanJsonChangeIntValue(RyanJson_t pJson, int32_t number)
{
RyanJsonCheckReturnFalse(NULL != pJson);
RyanJsonCheckReturnFalse(RyanJsonIsNumber(pJson));
RyanJsonMemcpy(RyanJsonGetValue(pJson), &number, sizeof(number));
return RyanJsonTrue;
}
RyanJsonBool_e RyanJsonChangeDoubleValue(RyanJson_t pJson, double number)
{
RyanJsonCheckReturnFalse(NULL != pJson);
RyanJsonCheckReturnFalse(RyanJsonIsNumber(pJson));
RyanJsonMemcpy(RyanJsonGetValue(pJson), &number, sizeof(number));
return RyanJsonTrue;
}
🤖 Prompt for AI Agents
In @RyanJson/RyanJson.c around lines 2016 - 2028, Both functions lack a
node-type check and may memcpy into the wrong location for non-number nodes;
update RyanJsonChangeIntValue and RyanJsonChangeDoubleValue to verify the node
is a numeric type before writing: call the existing type-query API (e.g.,
RyanJsonGetType or RyanJsonIsNumber) on pJson and if the type is not a number
return RyanJsonFalse (use the same RyanJsonCheckReturnFalse pattern), only then
call RyanJsonMemcpy(RyanJsonGetValue(pJson), &number, sizeof(number)) and return
RyanJsonTrue.

Comment on lines 671 to 672
char *buf = (char *)malloc(size + 100);
memcpy(buf, data, size);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

size + 100 存在潜在整数溢出风险。

size 接近 UINT32_MAXsize + 100 会发生回绕,导致分配的缓冲区远小于预期。随后第672行的 memcpy(buf, data, size) 将造成堆缓冲区溢出。

虽然这是测试代码,但模糊测试可能产生极端输入值。

🔎 建议的修复方案
 static RyanJsonBool_e RyanJsonFuzzerTestByMinify(const char *data, uint32_t size)
 {
+	if (size > UINT32_MAX - 100) { return RyanJsonFalse; }
 	char *buf = (char *)malloc(size + 100);
+	if (NULL == buf) { return RyanJsonFalse; }
 	memcpy(buf, data, size);
🧰 Tools
🪛 Cppcheck (2.19.0)

[warning] 672-672: If memory allocation fails, then there is a possible null pointer dereference

(nullPointerOutOfMemory)

🤖 Prompt for AI Agents
In @test/fuzzer/RyanJsonFuzzer.c around lines 671 - 672, The allocation using
malloc(size + 100) can overflow when size is large; before allocating check that
size <= SIZE_MAX - 100 (or otherwise validate/limit size) to prevent wraparound,
handle allocation failure, and only call memcpy(buf, data, size) when the buffer
is actually at least size bytes; update the code around the malloc/memcpy
(symbols: buf, size, malloc, memcpy) to validate size, compute a safe allocation
length, and bail out or truncate inputs when validation fails.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/baseTest/RyanJsonBaseTestUtile.c (1)

119-123: 逻辑错误:arrayDouble 验证条件有误

第122行的条件逻辑存在两个问题:

  1. 比较的目标值是 16.8,但根据上下文 arrayDouble 的期望值应该是 16.89
  2. 条件使用 < 0.001 判断相等,但整体逻辑 || 后跟 RyanJsonCheckReturnFalse 表示失败条件,意味着当值相等时会失败,这与预期相反

此处应该使用 RyanJsonCompareDouble 以保持一致性,并修正判断逻辑。

🐛 建议修复
 	for (int32_t count = 0; count < RyanJsonGetSize(RyanJsonGetObjectToKey(json, "arrayDouble")); count++)
 	{
 		item = RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "arrayDouble"), count);
-		if (!RyanJsonIsDouble(item) || fabs(RyanJsonGetDoubleValue(item) - 16.8) < 0.001) { RyanJsonCheckReturnFalse(NULL); }
+		if (!RyanJsonIsDouble(item) || !RyanJsonCompareDouble(RyanJsonGetDoubleValue(item), 16.89)) { RyanJsonCheckReturnFalse(NULL); }
 	}
🤖 Fix all issues with AI agents
In @example/RyanJsonExample.c:
- Around line 132-139: RyanJsonGetStringValue can return NULL, so avoid passing
its result directly to strcmp; after calling
RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot, "string")) store the
result in a variable (strValue) and check if strValue == NULL before calling
strcmp, and if NULL perform the same error handling (printf,
RyanJsonDelete(jsonRoot), return RyanJsonFalse); only call strcmp when strValue
is non-NULL and keep the existing strcmp-based check for the expected "hello"
value.

In @run_base_coverage.sh:
- Line 10: The script currently uses a hardcoded platform-specific binary path
"./build/linux/x86/release/RyanJson"; replace that with a variable obtained from
xmake (e.g., use xmake show -t RyanJson to populate BINARY_PATH), add a
file-existence check for BINARY_PATH (if not found, print an error and exit 1),
and then execute "$BINARY_PATH" so the script works across platforms and
architectures.

In @RyanJson/RyanJson.c:
- Around line 623-630: The function RyanJsonGetObjectValue returns the integer
literal 0 on the NULL-check failure which is inconsistent for a pointer return;
change the failure return from 0 to NULL inside RyanJsonGetObjectValue (the
branch inside RyanJsonCheckCodeNoReturn) so the function consistently returns a
pointer (NULL) when pJson is invalid, leaving the rest of the function
(RyanJson_t objValue; RyanJsonMemcpy(...); return objValue;) unchanged.

In @test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c:
- Around line 145-148: The code calls RyanJsonGetObjectToKey(jsonRoot, "double")
and passes its result directly to RyanJsonIsDouble and RyanJsonGetDoubleValue
without NULL checks; store the return of RyanJsonGetObjectToKey in a local
variable (e.g., obj = RyanJsonGetObjectToKey(...)), check if obj is NULL and
handle the failure (use RyanJsonCheckReturnFalse or return early) before calling
RyanJsonIsDouble or RyanJsonGetDoubleValue, and apply the same pattern to the
other occurrences noted (the calls around the locations corresponding to Line
162 and Line 164).

In @test/baseTest/equality/RyanJsonBaseTestEqualityInt.c:
- Around line 111-114: The code calls RyanJsonGetObjectToKey(jsonRoot, "int")
and passes its result directly to RyanJsonIsInt and RyanJsonGetIntValue without
checking for NULL; add a NULL check by storing the result of
RyanJsonGetObjectToKey in a local variable (e.g., obj =
RyanJsonGetObjectToKey(jsonRoot, "int")), verify obj != NULL before calling
RyanJsonIsInt or RyanJsonGetIntValue, handle the NULL case (fail the test or
return early) and apply the same pattern for the other occurrences referenced
(the calls around Line 128 and Line 130) to avoid dereferencing NULL.

In @test/baseTest/equality/RyanJsonBaseTestEqualityString.c:
- Around line 101-103: The code calls RyanJsonGetObjectToKey(jsonRoot, "str")
and directly passes its result to RyanJsonIsString and RyanJsonGetStringValue,
risking a NULL dereference; fix by assigning the result to a local pointer
(e.g., obj = RyanJsonGetObjectToKey(jsonRoot, "str")), check that obj is not
NULL (use RyanJsonCheckReturnFalse or equivalent) before calling
RyanJsonIsString(obj) and RyanJsonGetStringValue(obj), and apply the same
pattern to the other occurrences referenced (the calls at the locations using
keys for lines 118, 137, 139, and 157).

In @test/externalModule/tlsf/tlsf_block_functions.h:
- Around line 7-11: The header is missing necessary includes for standard and
RT-Thread types; add #include <stddef.h> to provide ptrdiff_t and size_t, and
include the appropriate RT-Thread header (e.g., <rtthread.h> or <rtdef.h>) so
rt_always_inline and RT_ASSERT are defined; place these #include lines before or
immediately after the extern "C" block start in tlsf_block_functions.h so
references to ptrdiff_t, size_t, rt_always_inline, and RT_ASSERT compile
correctly.

In @test/externalModule/tlsf/tlsf_control_functions.h:
- Around line 196-233: tlsf_fls_generic returns a nonzero value for input 0,
causing tlsf_fls(0) to evaluate to 30 instead of the expected -1; fix by adding
an early zero check in tlsf_fls_generic (e.g., at the top of tlsf_fls_generic)
that returns 0 when word == 0 so that tlsf_fls (which returns
tlsf_fls_generic(word) - 1) and tlsf_ffs behave consistently with the
platform-specific implementations.
- Around line 7-15: tlsf_block_functions.h and tlsf_control_functions.h are
missing standard/RT-Thread headers causing undefined types/macros; update
tlsf_block_functions.h to include <stddef.h> and "rtthread.h" so
rt_always_inline and types like size_t/ptrdiff_t are defined (used around the
rt_always_inline macro and line ~84), and update tlsf_control_functions.h to
include <stddef.h> and <stdint.h> so int32_t and size_t are available; ensure
these includes are placed before any use of rt_always_inline, size_t, ptrdiff_t,
or int32_t so each header compiles standalone.

In @test/externalModule/valloc/valloc.h:
- Around line 11-12: The include-guard macro `valloc` is too generic and risks
colliding with system symbols (including a possible valloc() function); update
the guard in valloc.h to a conventional, unique macro (for example
TEST_EXTERNALMODULE_VALLOC_H or VALLOC_H) by replacing the `#ifndef valloc` /
`#define valloc` pair with `#ifndef <NEW_GUARD>` / `#define <NEW_GUARD>` and
ensure the matching `#endif` at the end of the file uses the same new macro;
keep the macro uppercase with underscores to follow header-guard conventions.

In @test/RFC8259Test/RyanJsonRFC8259JsonTest.c:
- Around line 129-139: checkJsonSemanticEquality is being called with the
original len even though data is modified in-place by RyanJsonMinify; update the
length passed after minification (or have checkJsonSemanticEquality compute the
current length via strlen) so comparisons use the post-minify length. Locate the
call site around RyanJsonParseData and replace the original len argument with
the new length (e.g., compute uint32_t newLen = (uint32_t)strlen(data) after
RyanJsonMinify and pass newLen) or alter checkJsonSemanticEquality to
accept/derive the correct current length before calling
RyanJsonValueSemanticEqual.
- Around line 160-162: The local variable strLen is declared as int32_t but
RyanJsonPrint expects a uint32_t*; change the declaration of strLen to uint32_t
to match the function signature (and include <stdint.h> if not already present).
Ensure the call to RyanJsonPrint(&strLen) uses the updated uint32_t variable and
adjust any subsequent uses of strLen to treat it as unsigned.
- Around line 275-276: The local variable `result` is declared but never used;
either remove the declaration `int32_t result = 0;` or use it to capture and
check return values where intended (e.g., assign function return codes to
`result` and assert/handle them), so eliminate the unused symbol `result` or
convert its use into meaningful checks in the test flow (keep `testRunCount`
as-is if still used).
- Around line 170-171: The test uses a poorly named static variable alksdjfCOunt
which is both unclear and unsafe for concurrent tests; change it to a clearly
named, non-static local variable (e.g., jsonComparisonCount or
semanticEqualityCount) and pass its address to checkJsonSemanticEquality so it
is not shared across threads; if cross-thread sharing is required instead use a
proper atomic type (e.g., atomic_uint/atomic_uint_fast32_t with stdatomic.h) and
use atomic operations, otherwise simply remove the static storage duration and
rename the variable where it is declared and where
checkJsonSemanticEquality(data, len, str, strLen, &alksdjfCOunt) is called.
- Around line 251-253: The yyjson parser implementation incorrectly calls
cJSON_Minify in yyjsonParseData; replace that call with the same minify approach
used by the other parsers (or use yyjson's own minify/compaction routine) so
yyjsonParseData no longer depends on cJSON_Minify. Locate yyjsonParseData and
remove the cJSON_Minify(data) invocation, then call the shared minify helper (or
yyjson-specific API) on the input buffer before parsing and ensure the
buffer/length passed to checkJsonSemanticEquality remains correct.

In @test/RyanJsonMemoryFootprintTest.c:
- Around line 284-286: The testCompressedBusinessJsonMemory function currently
calls printfJsonCompare(jsonstr) but ignores its return value and always returns
RyanJsonTrue; change it to return the result of printfJsonCompare(jsonstr) (or
store the result and return that) so the function mirrors other tests and
propagates failures, referencing printfJsonCompare and the function
testCompressedBusinessJsonMemory and removing the hardcoded RyanJsonTrue return.
- Around line 290-292: The variable `result` is declared but never used; remove
the `int32_t result = 0;` declaration (or if it was intended to capture a return
value, assign the return of the call site instead) to eliminate the unused
variable warning; leave `uint64_t funcStartMs` as-is if it is referenced by the
`runTestWithLogAndTimer` macro and keep `testRunCount` untouched if it’s used
elsewhere.

In @test/RyanJsonTest.c:
- Around line 69-78: v_realloc_tlsf currently allocates a new block and memcpy's
'size' bytes from the old block, which can read past the original block and
cause UB; replace the custom logic with the TLSF allocator's built-in realloc by
calling tlsf_realloc(tlsfHandler, block, size) inside v_realloc_tlsf (remove the
v_malloc_tlsf/v_free_tlsf/memmove approach) so the TLSF implementation can
correctly handle sizing and copying.
- Around line 57-62: The alignment subtraction in v_malloc_tlsf uses a hardcoded
"- 4" which mismatches configurable RyanJsonMallocHeaderSize (8 or 12) and
should follow the library's adaptive formula; update the call in v_malloc_tlsf
to use "- (RyanJsonMallocHeaderSize / 2)" instead of "- 4" (or, if you
intentionally must keep 4, add a clear comment next to the RyanJsonAlign usage
explaining why a fixed 4 is correct) so the allocation matches the behavior of
the main library; target the v_malloc_tlsf function and the
RyanJsonMallocHeaderSize constant and RyanJsonAlign macro when applying the
change.
🧹 Nitpick comments (19)
Makefile (2)

32-33: all 目标声明为 PHONY。

all 目标应该声明为伪目标,以避免与名为 "all" 的文件产生冲突,并确保该规则始终执行。

根据静态分析工具的建议,在文件开头添加:

♻️ 建议的修复

在 line 43 的 .PHONY: clean 上方添加:

+.PHONY: all clean
-.PHONY: clean

或者在 line 32 之前添加:

+.PHONY: all
+
 # 默认规则
 all: $(target)

32-45: 考虑添加 test 伪目标。

根据静态分析工具的建议,考虑添加一个 test 目标以符合常见的 Makefile 约定。这可以用于构建并运行测试,提供更清晰的构建流程。

♻️ 可选的改进建议

在 clean 规则之前添加:

.PHONY: test
test: $(target)
	./$(target)
run_base_coverage.sh (4)

1-2: 错误处理设置良好。

使用 set -e 可以在遇到错误时立即退出,这是一个好的实践。

♻️ 可选:考虑添加更严格的错误处理

如果需要更严格的错误检测,可以考虑添加:

 #!/bin/bash
-set -e  # 遇到错误立即退出
+set -euo pipefail  # 遇到错误立即退出,未定义变量报错,管道中任何命令失败都会导致退出

16-16: 建议在合并前清理旧的覆盖率数据。

如果之前运行留下的 default.profrawdefault.profdata 文件仍然存在,可能会导致覆盖率数据不准确或混合。

♻️ 建议:添加数据清理和验证
+# 清理旧的覆盖率数据
+rm -f default.profraw default.profdata
+
 xmake
 echo "xmake build success"

 # ================================
 # 1. 运行
 # ================================
 ./build/linux/x86/release/RyanJson

+# 验证 profraw 文件是否生成
+if [ ! -f "default.profraw" ]; then
+  echo "错误: default.profraw 文件未生成"
+  exit 1
+fi

 # ================================
 # 2. 合并 profile 数据
 # ================================
 llvm-profdata merge -sparse default.profraw -o default.profdata

24-27: 建议添加依赖检查和输出目录创建。

脚本假设 llvm-covllvm-profdata 工具已安装且在 PATH 中可用,并且输出目录会自动创建。如果这些假设不成立,脚本可能会失败。

♻️ 建议:添加依赖验证和目录创建

在脚本开头添加依赖检查:

#!/bin/bash
set -e  # 遇到错误立即退出

# 检查必需的工具
for tool in xmake llvm-profdata llvm-cov; do
  if ! command -v $tool &> /dev/null; then
    echo "错误: 未找到必需的工具 '$tool'"
    exit 1
  fi
done

在生成 HTML 报告前创建输出目录:

 # ================================
 # 4. 生成覆盖率报告(HTML详细)
 # ================================
+mkdir -p coverage/docs
 llvm-cov show ./build/linux/x86/release/RyanJson \
   -instr-profile=default.profdata \
   -format=html \
   -output-dir=coverage/docs \
   -sources ./RyanJson

Also applies to: 32-36


37-37: 可选:澄清注释掉的代码用途。

第 37 行有一个注释掉的 -sources 参数,包含了额外的路径。如果这是一个示例或备选配置,建议添加说明注释;如果不再需要,可以删除。

♻️ 建议:添加说明或删除

如果是示例配置:

-#   -sources ./test/fuzzer ./RyanJson
+# 如需包含 fuzzer 测试的覆盖率,取消注释下面一行:
+#   -sources ./test/fuzzer ./RyanJson

或者如果不再需要,直接删除:

   -sources ./RyanJson
-#   -sources ./test/fuzzer ./RyanJson
test/baseTest/RyanJsonBaseTestLoadJson.c (1)

81-85: 建议验证打印结果

第 84 行立即释放了 RyanJsonPrint 的结果而没有验证其内容。建议要么检查返回值是否为 NULL,要么捕获结果进行字符串比较验证,以确保混合大小写的 Unicode 转义序列被正确处理。

♻️ 建议的改进
 	// 测试混合大小写: \uAbCd (混合大小写十六进制)
 	json = RyanJsonParse("{\"mixed\":\"\\uAbCd\"}");
 	RyanJsonCheckReturnFalse(NULL != json);
-	RyanJsonFree(RyanJsonPrint(json, 50, RyanJsonFalse, NULL));
+	str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL);
+	RyanJsonCheckReturnFalse(NULL != str);
+	// 可选:添加字符串比较验证混合大小写是否正确解析
 	RyanJsonDelete(json);
test/baseTest/equality/RyanJsonBaseTestEqualityBool.c (1)

1-86: 扎实的布尔值往返测试实现

该测试文件结构良好,提供了全面的布尔值等值验证:

  • ✅ 测试 truefalse 字面量
  • ✅ 测试布尔值数组
  • ✅ 每个场景都包含往返测试(解析 → 序列化 → 重新解析)
  • ✅ 正确的内存管理和错误处理

代码清晰、可读性强,使用宏进行一致的错误检查。

可选的增强建议:

  • 考虑添加空数组测试场景
  • 考虑测试嵌套对象中的布尔值

但当前的测试覆盖对于基础布尔值验证已经很充分。

test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c (1)

139-144: 冗余的 NULL 检查

Line 144 的 NULL 检查是冗余的,因为 Line 139 已经检查了 jsonRoot 是否为 NULL,并且在失败时会跳转到 err 标签。

♻️ 建议的优化
 		RyanJson_t jsonRoot = RyanJsonParse(jsondoubleStr);
 		RyanJsonCheckCode(NULL != jsonRoot, {
 			jsonLog("str: %s", jsondoubleStr);
 			goto err;
 		});
-
-		RyanJsonCheckReturnFalse(NULL != jsonRoot);
 		RyanJsonCheckReturnFalse(RyanJsonIsDouble(RyanJsonGetObjectToKey(jsonRoot, "double")));
RyanJson/RyanJsonConfig.h (1)

43-50: 冗余的宏定义回退

RyanJsonMallocAlignRyanJsonMallocHeaderSize 在 RT_VER_NUM 分支(第26-27行)和 else 分支(第36-37行)中都已定义。这些 #ifndef 回退定义永远不会被执行。

♻️ 建议移除或添加注释说明用途

如果这些回退是为了允许用户在包含此头文件前自定义这些宏,则应将条件分支中的定义也改为 #ifndef 形式:

 #else
 #include <assert.h>
 #define RyanJsonMemset             memset
 ...
+#ifndef RyanJsonMallocHeaderSize
 #define RyanJsonMallocHeaderSize   8U
+#endif
+#ifndef RyanJsonMallocAlign
 #define RyanJsonMallocAlign        4U
+#endif
 #endif

-#ifndef RyanJsonMallocAlign
-#define RyanJsonMallocAlign 8U
-#endif
-
-//
-#ifndef RyanJsonMallocHeaderSize
-#define RyanJsonMallocHeaderSize 8U
-#endif
test/RFC8259Test/RyanJsonRFC8259TestUtil.c (1)

180-211: 数值比较的内存分配处理可简化

当前实现在 bufAbufB 分配失败时仍会继续执行 strtod,虽然最终逻辑正确(因为 if (NULL != bufA && NULL != bufB) 检查),但可以提前返回以提高清晰度。

♻️ 可选优化
 	char *endA = NULL, *endB = NULL;
 	char *bufA = (char *)malloc(aLen + 1);
+	if (NULL == bufA) { return (aLen == bLen) && (0 == memcmp(a, b, aLen)); }
 	char *bufB = (char *)malloc(bLen + 1);
-	if (NULL != bufA && NULL != bufB)
+	if (NULL == bufB)
 	{
+		free(bufA);
+		return (aLen == bLen) && (0 == memcmp(a, b, aLen));
+	}
+	{
 		memcpy(bufA, a, aLen);
test/externalModule/tlsf/tlsf_block_functions.h (1)

97-98: 宏展开中缺少空格

block->size &block_header_free_bit&block_header_free_bit 之间缺少空格,虽然在语法上正确,但可读性差。

♻️ 建议修复
 tlsf_decl int block_is_free(const block_header_t *block)
-{ return tlsf_cast(int, block->size &block_header_free_bit); }
+{ return tlsf_cast(int, block->size & block_header_free_bit); }

同样适用于第104-105行的 block_is_prev_free

test/externalModule/tlsf/tlsf.h (1)

10-14: 无条件包含 rtthread.h 限制了可移植性

rtthread.h 被无条件包含,这使得此 TLSF 实现只能在 RT-Thread 环境中使用。考虑添加条件编译或提供替代方案。

♻️ 建议修复
 #include <assert.h>
 #include <stddef.h>
 #include <stdbool.h>
+
+#ifdef RT_VER_NUM
 #include "rtthread.h"
+#else
+// 为非 RT-Thread 环境提供兼容定义
+#endif

请确认此 TLSF 实现是否仅用于 RT-Thread 环境。如果需要跨平台支持,需要添加适当的条件编译。

RyanJson/RyanJson.c (1)

179-182: 宏定义的复杂计算建议添加注释

RyanJsonGetInlineStringSize 宏的计算逻辑较复杂,建议保留被注释掉的函数版本作为文档,或添加更详细的注释说明各部分的含义。

下方被注释的函数版本很好地解释了计算逻辑,建议保留作为文档参考。

test/externalModule/tlsf/tlsf.c (3)

674-676: if 语句缺少大括号。

第 675 行是 if 语句的主体,但没有使用大括号包裹。虽然当前代码功能正确,但这种风格容易在后续维护时引入 bug(例如添加新行时忘记加大括号)。

♻️ 建议的修复
-            if(control->mem_rec.used > control->mem_rec.max_used)
-			control->mem_rec.max_used = control->mem_rec.used;
+            if (control->mem_rec.used > control->mem_rec.max_used) {
+                control->mem_rec.max_used = control->mem_rec.used;
+            }

582-588: 函数命名不一致。

rt_memory_info22 函数名中的 "22" 后缀看起来不寻常。如果这是有意的版本标识,建议添加注释说明;如果是笔误,建议修正函数名以保持 API 命名一致性。


690-691: void* 指针进行算术运算是未定义行为。

第 690 行 block_to_ptr(block) + block_size(block)void* 进行加法运算。虽然 GCC 作为扩展允许这种操作(将其视为 char*),但这在标准 C 中是未定义行为。建议显式转换为 char* 以提高可移植性。

♻️ 建议的修复
-			void *block_end = block_to_ptr(block) + block_size(block);
+			void *block_end = (char *)block_to_ptr(block) + block_size(block);
test/externalModule/tlsf/tlsf_control_functions.h (2)

24-29: 内存记录使用 int32_t 可能限制大内存池支持。

mem_record_t 结构体使用 int32_t 来存储内存大小,这将限制最大可追踪的内存为约 2GB。在 64 位系统上,如果需要支持更大的内存池,建议使用 size_tint64_t

如果 2GB 限制是有意的设计决策(例如仅用于测试目的),请添加注释说明。


620-621: 内联函数依赖外部定义的 tlsf_alloc_overhead()

block_prepare_used 是一个内联函数,但它调用了在 tlsf.c 中定义的 tlsf_alloc_overhead()。这意味着:

  1. 如果其他模块单独包含此头文件而不链接 tlsf.c,将导致链接错误
  2. 打破了头文件的自包含性

建议将 tlsf_alloc_overhead() 的定义(或声明)移到头文件中,或在此处直接使用 block_header_overhead 常量。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b97df2 and 83b338e.

📒 Files selected for processing (40)
  • .clang-format-ignore
  • Makefile
  • RyanJson/RyanJson.c
  • RyanJson/RyanJson.h
  • RyanJson/RyanJsonConfig.h
  • RyanJson/RyanJsonUtils.c
  • example/RyanJsonExample.c
  • run_base_coverage.sh
  • test/RFC8259Test/RyanJsonRFC8259JsonTest.c
  • test/RFC8259Test/RyanJsonRFC8259TestUtil.c
  • test/RFC8259Test/RyanJsonRFC8259TestUtil.h
  • test/RyanJsonMemoryFootprintTest.c
  • test/RyanJsonRFC8259JsonTest.c
  • test/RyanJsonTest.c
  • test/RyanJsonTest.h
  • test/baseTest/RyanJsonBaseTest.c
  • test/baseTest/RyanJsonBaseTest.h
  • test/baseTest/RyanJsonBaseTestChangeJson.c
  • test/baseTest/RyanJsonBaseTestCompareJson.c
  • test/baseTest/RyanJsonBaseTestForEachJson.c
  • test/baseTest/RyanJsonBaseTestLoadJson.c
  • test/baseTest/RyanJsonBaseTestUtile.c
  • test/baseTest/equality/RyanJsonBaseTestEqualityBool.c
  • test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c
  • test/baseTest/equality/RyanJsonBaseTestEqualityInt.c
  • test/baseTest/equality/RyanJsonBaseTestEqualityString.c
  • test/externalModule/cJSON/cJSON.c
  • test/externalModule/cJSON/cJSON.h
  • test/externalModule/tlsf/rtthread.h
  • test/externalModule/tlsf/tlsf.c
  • test/externalModule/tlsf/tlsf.h
  • test/externalModule/tlsf/tlsf_block_functions.h
  • test/externalModule/tlsf/tlsf_control_functions.h
  • test/externalModule/valloc/valloc.c
  • test/externalModule/valloc/valloc.h
  • test/externalModule/yyjson/yyjson.c
  • test/externalModule/yyjson/yyjson.h
  • test/fuzzer/RyanJsonFuzzer.c
  • test/fuzzer/RyanJsonFuzzer.dict
  • xmake.lua
💤 Files with no reviewable changes (1)
  • test/RyanJsonRFC8259JsonTest.c
✅ Files skipped from review due to trivial changes (1)
  • .clang-format-ignore
🚧 Files skipped from review as they are similar to previous changes (4)
  • test/RyanJsonTest.h
  • test/fuzzer/RyanJsonFuzzer.dict
  • RyanJson/RyanJsonUtils.c
  • xmake.lua
🧰 Additional context used
🧬 Code graph analysis (15)
test/baseTest/RyanJsonBaseTest.h (4)
test/baseTest/equality/RyanJsonBaseTestEqualityBool.c (1)
  • RyanJsonBaseTestEqualityBool (4-86)
test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c (1)
  • RyanJsonBaseTestEqualityDouble (132-178)
test/baseTest/equality/RyanJsonBaseTestEqualityInt.c (1)
  • RyanJsonBaseTestEqualityInt (100-144)
test/baseTest/equality/RyanJsonBaseTestEqualityString.c (1)
  • RyanJsonBaseTestEqualityString (93-171)
test/baseTest/RyanJsonBaseTestUtile.c (1)
RyanJson/RyanJson.c (3)
  • RyanJsonCompareDouble (2345-2357)
  • RyanJsonGetDoubleValue (614-621)
  • RyanJsonGetObjectByIndex (1753-1770)
test/baseTest/RyanJsonBaseTestCompareJson.c (2)
RyanJson/RyanJson.c (1)
  • RyanJsonCompare (2366-2429)
RyanJson/RyanJsonUtils.c (1)
  • RyanJsonCompareOnlyKey (117-161)
test/baseTest/equality/RyanJsonBaseTestEqualityString.c (1)
RyanJson/RyanJson.c (5)
  • RyanJsonIsString (2183-2186)
  • RyanJsonGetStringValue (584-603)
  • RyanJsonDelete (639-659)
  • RyanJsonPrint (1663-1691)
  • RyanJsonFree (666-666)
test/RFC8259Test/RyanJsonRFC8259JsonTest.c (4)
test/RyanJsonTest.c (3)
  • vallocGetUseByTlsf (50-55)
  • v_malloc_tlsf (58-62)
  • v_free_tlsf (64-67)
test/externalModule/valloc/valloc.c (2)
  • v_mcheck (78-83)
  • displayMem (99-104)
test/RFC8259Test/RyanJsonRFC8259TestUtil.c (1)
  • RyanJsonValueSemanticEqual (244-272)
RyanJson/RyanJson.c (5)
  • RyanJsonParseOptions (1283-1302)
  • RyanJsonPrint (1663-1691)
  • RyanJsonMinify (2286-2336)
  • RyanJsonFree (666-666)
  • RyanJsonDelete (639-659)
test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c (1)
RyanJson/RyanJson.c (6)
  • RyanJsonIsDouble (2196-2199)
  • RyanJsonGetDoubleValue (614-621)
  • RyanJsonCompareDouble (2345-2357)
  • RyanJsonDelete (639-659)
  • RyanJsonPrint (1663-1691)
  • RyanJsonFree (666-666)
test/RFC8259Test/RyanJsonRFC8259TestUtil.c (1)
RyanJson/RyanJson.c (4)
  • RyanJsonCompareDouble (2345-2357)
  • RyanJsonParseOptions (1283-1302)
  • RyanJsonCompare (2366-2429)
  • RyanJsonDelete (639-659)
test/fuzzer/RyanJsonFuzzer.c (2)
RyanJson/RyanJson.c (5)
  • RyanJsonPrint (1663-1691)
  • RyanJsonCreateString (2143-2149)
  • RyanJsonPrintPreallocated (1703-1721)
  • RyanJsonGetObjectByKey (1779-1797)
  • RyanJsonIsKey (2176-2176)
RyanJson/RyanJsonUtils.c (1)
  • RyanJsonGetObjectByKeys (11-31)
test/baseTest/RyanJsonBaseTestLoadJson.c (1)
RyanJson/RyanJson.c (4)
  • RyanJsonPrintPreallocated (1703-1721)
  • RyanJsonDelete (639-659)
  • RyanJsonFree (666-666)
  • RyanJsonPrint (1663-1691)
test/baseTest/RyanJsonBaseTestForEachJson.c (1)
RyanJson/RyanJson.c (3)
  • RyanJsonIsDouble (2196-2199)
  • RyanJsonCompareDouble (2345-2357)
  • RyanJsonGetDoubleValue (614-621)
test/RFC8259Test/RyanJsonRFC8259TestUtil.h (1)
test/RFC8259Test/RyanJsonRFC8259TestUtil.c (4)
  • RyanJsonExtractSingleArrayElement (214-242)
  • RyanJsonScalarSemanticEqual (155-212)
  • RyanJsonValueSemanticEqual (244-272)
  • RyanJsonNormalizeString (25-137)
test/baseTest/equality/RyanJsonBaseTestEqualityBool.c (1)
RyanJson/RyanJson.c (4)
  • RyanJsonIsBool (2178-2178)
  • RyanJsonPrint (1663-1691)
  • RyanJsonDelete (639-659)
  • RyanJsonFree (666-666)
example/RyanJsonExample.c (1)
RyanJson/RyanJson.c (13)
  • RyanJsonGetIntValue (605-612)
  • RyanJsonGetObjectByKey (1779-1797)
  • RyanJsonDelete (639-659)
  • RyanJsonGetDoubleValue (614-621)
  • RyanJsonCompareDouble (2345-2357)
  • RyanJsonGetStringValue (584-603)
  • RyanJsonIsNull (2177-2177)
  • RyanJsonChangeKey (2050-2055)
  • RyanJsonGetKey (571-582)
  • RyanJsonChangeStringValue (2057-2062)
  • RyanJsonChangeIntValue (2064-2069)
  • RyanJsonChangeDoubleValue (2071-2076)
  • RyanJsonInitHooks (561-569)
test/baseTest/RyanJsonBaseTest.c (4)
test/baseTest/RyanJsonBaseTestForEachJson.c (1)
  • RyanJsonBaseTestForEachJson (4-45)
test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c (1)
  • RyanJsonBaseTestEqualityDouble (132-178)
test/baseTest/equality/RyanJsonBaseTestEqualityInt.c (1)
  • RyanJsonBaseTestEqualityInt (100-144)
test/baseTest/equality/RyanJsonBaseTestEqualityString.c (1)
  • RyanJsonBaseTestEqualityString (93-171)
test/externalModule/tlsf/tlsf_control_functions.h (2)
test/externalModule/tlsf/tlsf_block_functions.h (17)
  • int (95-95)
  • int (97-98)
  • int (104-105)
  • size_t (86-87)
  • void (89-93)
  • void (100-100)
  • void (102-102)
  • void (107-107)
  • void (109-109)
  • void (114-115)
  • void (144-150)
  • void (152-157)
  • block_header_t (111-112)
  • block_header_t (118-119)
  • block_header_t (122-126)
  • block_header_t (129-134)
  • block_header_t (137-142)
test/externalModule/tlsf/tlsf.c (1)
  • tlsf_alloc_overhead (266-266)
🪛 checkmake (0.2.2)
Makefile

[warning] 33-33: Target "all" should be declared PHONY.

(phonydeclared)


[warning] 43-43: Missing required phony target "all"

(minphony)


[warning] 43-43: Missing required phony target "test"

(minphony)

🪛 Clang (14.0.6)
test/externalModule/tlsf/tlsf_control_functions.h

[error] 26-26: unknown type name 'int32_t'

(clang-diagnostic-error)


[error] 27-27: unknown type name 'int32_t'

(clang-diagnostic-error)


[error] 28-28: unknown type name 'int32_t'

(clang-diagnostic-error)


[error] 56-56: unknown type name 'size_t'

(clang-diagnostic-error)


[error] 111-111: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 115-115: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 239-239: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 239-239: unknown type name 'size_t'

(clang-diagnostic-error)


[error] 254-254: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 254-254: expected ';' after top level declarator

(clang-diagnostic-error)


[error] 490-490: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 490-490: expected ';' after top level declarator

(clang-diagnostic-error)

test/externalModule/tlsf/tlsf_block_functions.h

[error] 21-21: unknown type name 'ptrdiff_t'

(clang-diagnostic-error)


[error] 49-49: unknown type name 'size_t'

(clang-diagnostic-error)


[error] 86-86: unknown type name 'rt_always_inline'

(clang-diagnostic-error)


[error] 86-86: expected ';' after top level declarator

(clang-diagnostic-error)

🪛 Cppcheck (2.19.0)
test/externalModule/tlsf/tlsf.c

[error] 124-124: syntax error

(syntaxError)

🔇 Additional comments (53)
Makefile (7)

2-4: 编译器设置合理。

使用了标准的 GCC 编译标志,包括 gnu99 标准、O2 优化级别和常见的警告选项。配置适当。


6-14: 确认是否需要在主构建中包含所有测试目录。

头文件包含路径已扩展为包括测试和外部模块目录。请确认这是否是预期的配置,因为生产构建通常不需要测试相关的头文件路径。如果这是用于构建包含测试的单一可执行文件,则配置合理。


16-24: 验证将测试代码编译到主目标的设计意图。

源文件扫描现在包含了所有测试文件和外部模块。这意味着最终的可执行文件将包含所有测试代码,这可能会显著增加二进制文件的大小。

如果目标是构建一个包含所有测试的单一可执行文件(例如用于 CI/CD 或开发测试),这是合理的。但如果需要分离生产构建和测试构建,建议考虑创建单独的目标或使用条件编译。


26-30: 目标命名清晰合理。

使用标准的模式替换生成对象文件,目标名称 app 避免了与源码文件夹的命名冲突。实现正确。


35-36: 链接规则正确。

链接步骤正确地将所有对象文件与必要的标志和数学库链接在一起。实现符合标准做法。


38-40: 编译规则实现正确。

模式规则正确使用了头文件路径、编译标志和自动变量。实现符合 Makefile 最佳实践。


42-45: 清理规则安全且正确。

使用 rm -f 而不是 rm -rf 更加安全,正确声明了 PHONY 目标,并清理了所有生成的文件。实现良好。

test/externalModule/valloc/valloc.c (1)

9-9: MALLOC_HEADER_SIZE 从 12 改为 0 的变更是合理的设计选择,现有测试应不受影响。

此改动将内存报告从"用户请求大小 + 固定开销"改为仅"用户请求大小",而实际分配仍包含 HEADER_SIZE(4 字节)用于存储用户大小。这种分离是有意为之:

  • 内存泄漏检测测试RyanJsonBaseTestDuplicateJson.c 第 122-127 行)检查删除后内存是否恢复,由于 malloc/free 对平衡,此测试不受影响
  • 内存足迹测试RyanJsonMemoryFootprintTest.cRyanJsonRFC8259JsonTest.c)均使用差值测量(操作前后的增量),不依赖常数值,故不受影响

无需修改测试代码或文档,此变更与现有测试逻辑兼容。

run_base_coverage.sh (1)

4-5: 构建命令合理。

xmake 构建命令简洁明了,set -e 会确保构建失败时脚本退出。

test/baseTest/RyanJsonBaseTestUtile.c (2)

21-21: LGTM!

正确地将本地的 compare_double 函数替换为公共 API RyanJsonCompareDouble,保持了测试代码与库 API 的一致性。


81-82: LGTM!

与第21行一致,正确使用 RyanJsonCompareDouble 进行浮点数比较。

example/RyanJsonExample.c (2)

7-9: LGTM!

函数返回类型从 int 更改为 RyanJsonBool_e 是合理的改进,与库的 API 模式保持一致。


260-274: LGTM!

示例函数中的初始化和调用模式清晰,使用 RyanJsonCheckReturnFalse 统一处理错误返回,代码结构良好。

test/fuzzer/RyanJsonFuzzer.c (4)

7-10: LGTM!

__exit 重命名为 exit__ 避免了与保留标识符冲突(双下划线前缀在C中是保留的),同时将 isEnableRandomMemFail 改为 static 内部链接是正确的做法。


14-38: LGTM!

新增的边界条件测试覆盖了类型错误、NULL 参数、无 key 的 item 等多种场景,有效提高了测试覆盖率。


280-293: LGTM!

扩展了 NULL 参数检查的测试覆盖,包括新增的 RyanJsonGetObjectByKeys 相关测试用例。


399-403: LGTM!

新增了对 RyanJsonChangeIntValueRyanJsonChangeDoubleValue 的 NULL 参数测试,与 API 的防御性设计一致。

RyanJson/RyanJson.h (4)

1-2: LGTM!

头文件保护宏从 __RyanJson__ 改为 RyanJson 是正确的做法,避免了使用双下划线前缀的保留标识符。


46-130: 优秀的文档说明

节点结构和存储策略的文档非常详尽,清晰地解释了 inline/ptr 模式的设计理念、内存布局和嵌入式平台的优化考量。这对于理解和维护代码非常有帮助。


205-207: LGTM!

新增的 RyanJsonGetPayloadStrIsPtrByFlagRyanJsonSetPayloadStrIsPtrByFlag 宏与文档中描述的 bit7 存储模式标志一致。


298-302: LGTM!

新增的值访问器 (RyanJsonGetIntValue, RyanJsonGetDoubleValue, RyanJsonGetObjectValue, RyanJsonGetArrayValue) 作为显式的 extern 函数声明,替代了之前的宏实现,提供了更好的类型安全性。

test/baseTest/RyanJsonBaseTestForEachJson.c (1)

23-23: LGTM!

正确地将本地 compare_double 替换为 RyanJsonCompareDouble,与其他测试文件的更改保持一致。

test/baseTest/RyanJsonBaseTestChangeJson.c (2)

15-19: LGTM!

扩展的测试 JSON 字符串包含了多种 key/value 长度组合,用于全面测试 inline/ptr 模式的转换。变量重命名为 jsonRoot 保持了命名一致性。


34-70: 优秀的测试覆盖

新增的测试用例全面覆盖了 inline/ptr 模式的各种转换场景:

  • inline → inline(不超过阈值)
  • inline → ptr(超过阈值)
  • ptr → inline(缩短后不超过阈值)
  • ptr → ptr(仍超过阈值)

这对于验证存储策略的正确性非常重要。

test/baseTest/RyanJsonBaseTestCompareJson.c (3)

23-24: LGTM!

新增了 RyanJsonCompareOnlyKey 的自身比较测试,与 RyanJsonCompare 的自身比较测试并行。


88-95: 测试逻辑正确

当只修改值(如 inter 从 16 改为 17,或 double 从 16.89 改为 20.89)时:

  • RyanJsonCompare 返回 False(因为值不同)
  • RyanJsonCompareOnlyKey 返回 True(因为键结构相同)

这符合 key-only 比较的设计预期。


159-173: LGTM!

删除操作导致结构变化时,RyanJsonCompareRyanJsonCompareOnlyKey 都正确返回 False,验证了当键结构不同时两种比较方法的行为一致。

test/externalModule/tlsf/rtthread.h (1)

1-14: LGTM!

兼容性头文件实现简洁明了,宏定义合理。rt_always_inline 使用了正确的 GCC 属性语法,RT_ASSERTrt_memcpy 提供了合理的标准库别名。

test/baseTest/RyanJsonBaseTest.h (2)

20-21: 良好实践:避免宏重定义警告

在重新定义 jsonLog 宏之前先使用 #undef 是很好的防御性编程实践,可以避免潜在的宏重定义警告。


46-49: LGTM!

新增的四个等值测试函数声明命名清晰、签名一致,与 test/baseTest/equality/ 目录下的实现文件相匹配。这些测试涵盖了布尔值、浮点数、整数和字符串的往返等值验证。

test/baseTest/RyanJsonBaseTestLoadJson.c (2)

10-20: LGTM!

jsonstr 从固定大小数组改为指针更符合惯用法,因为该字符串字面量在代码中不会被修改。这个改动使意图更加清晰。


41-107: 全面的 Unicode 转义序列测试覆盖

这段代码提供了出色的 Unicode 解析测试覆盖:

  • ✅ 有效序列:emoji、数字 (0-9)、小写 (a-f)、大写 (A-F)、混合大小写
  • ✅ 无效序列:非十六进制字符 (G, Z, !)
  • ✅ 使用预分配缓冲区进行打印验证

测试逻辑正确地验证了解析器能够拒绝无效的 Unicode 转义序列。

test/baseTest/RyanJsonBaseTest.c (1)

49-63: LGTM! 测试注册组织良好

测试注册更新清晰、组织良好:

  • ✅ 为每个测试添加了专业的中文描述,清楚地说明了测试目的
  • ✅ 新增的四个等值测试(Bool、Double、Int、String)在第 59 行有明确的分组注释
  • ✅ 测试描述使用专业术语(如"深拷贝"、"递归内存回收"、"所属权转移"),准确传达了测试意图
  • ✅ 所有测试调用格式一致,使用统一的 runTestWithLogAndTimer

文档质量高,有助于团队理解每个测试的具体验证内容。

test/baseTest/equality/RyanJsonBaseTestEqualityString.c (2)

3-51: 测试用例覆盖全面

X-macro 模式使用正确,测试用例涵盖了空字符串、空格、特殊字符、Unicode 等多种场景,符合字符串测试的最佳实践。


53-90: 转义字符测试用例设计合理

测试用例涵盖了制表符、换行符、引号、反斜杠、Unicode 转义等常见场景,设计全面。

test/baseTest/equality/RyanJsonBaseTestEqualityInt.c (1)

3-97: 整数测试用例覆盖完整

测试用例涵盖了零值、边界值、2的幂次、各种数量级等关键场景,测试设计合理。

test/RyanJsonTest.c (2)

18-24: 时间戳实现正确

使用 CLOCK_MONOTONIC 获取单调递增时间戳是正确的做法,适合性能测试和耗时统计。


80-154: 测试主函数结构合理

TLSF 内存管理的生命周期处理正确,测试流程清晰,包含了内存信息展示和资源清理。

test/RFC8259Test/RyanJsonRFC8259TestUtil.h (1)

1-26: RFC8259 工具函数声明规范

头文件结构规范,包含了必要的 include guard,函数声明清晰,注释使用中文说明了各函数用途。

test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c (1)

3-129: 浮点数测试用例设计优秀

测试用例非常全面,涵盖了零值、边界、科学计数法、数学常量、精度测试、极值等各种场景,特别是包含了浮点数精度经典测试(如 0.1、0.2、0.3),设计非常专业。

RyanJson/RyanJsonConfig.h (1)

1-2: 包含保护宏命名改进

新的包含保护宏 RyanJsonConfig 比原来的 __RyanJsonConfig__ 更简洁,但以双下划线开头/结尾的标识符在 C 标准中是保留的,所以这个更改是正确的。

test/RFC8259Test/RyanJsonRFC8259TestUtil.c (1)

244-272: 语义比较实现良好

RyanJsonValueSemanticEqual 实现了合理的回退策略:首先尝试完整的 JSON 解析和比较,失败时回退到单元素数组提取,最后回退到标量比较。这种分层方法对测试场景很有用。

test/externalModule/tlsf/tlsf.h (1)

89-89: 弱符号声明良好

使用 __attribute__((weak)) 声明 tlsf_check_hook 允许用户在需要时提供自定义实现,这是一个好的扩展点设计。

RyanJson/RyanJson.c (4)

714-774: 自定义双精度浮点数解析器实现良好

RyanJsonInternalParseDouble 提供了一个不依赖 strtod 的内部实现,适合嵌入式环境。使用 pow() 处理指数是合理的。


1335-1367: 正确处理 Infinity 和 NaN

按照 RFC 8259 规范,JSON 不支持 Infinity 和 NaN,将它们输出为 null 是正确的做法。双精度数的格式化策略(先尝试 %.15g,再回退到 %.17g)确保了往返精度。


2345-2357: 浮点数比较实现合理

RyanJsonCompareDouble 使用相对误差和绝对误差相结合的方式进行比较,1e-12 的绝对容差对于大多数场景是合适的。这种方法能正确处理接近零的值和非常大的值。


561-569: 公共 API RyanJsonInitHooks 设计良好

允许用户注册自定义的 malloc/free/realloc 钩子函数,且 realloc 可以为 NULL(内部会用 malloc + memcpy + free 模拟)。这是嵌入式系统友好的设计。

test/RyanJsonMemoryFootprintTest.c (1)

20-36: TLSF 内存钩子集成正确

RyanJsonMemoryFootprint 正确地在解析前记录内存使用量,解析后计算差值,这样可以准确测量每个 JSON 库的内存占用。设计模式在三个库的测试中保持一致。

test/externalModule/tlsf/tlsf.c (3)

1-18: LGTM!

许可证头和包含文件设置正确。printf 被重定义为空宏以禁止调试输出,这是合理的设计选择。


33-89: LGTM!

control_construct 函数实现正确,包含了必要的大小验证和控制结构初始化。使用 __builtin_clz 是合理的选择,与其他地方的 GCC 内置函数使用一致。


106-139: LGTM!

integrity_walker 函数实现正确。静态分析工具报告的第 124 行语法错误是误报——多行三元运算符是有效的 C 语法。

test/externalModule/tlsf/tlsf_control_functions.h (2)

41-51: 位域大小需要验证。

small_block_size 使用 8 位存储,最大值为 255。根据 control_construct 中的计算 1 << fl_index_shift(其中 fl_index_shift 最大为 5 + 2 = 7),small_block_size 最大为 128,在 8 位范围内。

但如果 ALIGN_SIZE_LOG2sl_index_count_log2 的值发生变化,可能会溢出。建议添加静态断言或注释说明这些约束。


497-497: 无问题,常量已正确定义。

POISONING_AFTER_FREE 已在 tlsf_block_functions.h 第 17 行定义(#define POISONING_AFTER_FREE true),且 tlsf_control_functions.h 在第 8 行包含了该文件,因此常量在使用处完全可访问。无需修改。

Likely an incorrect or invalid review comment.

# ================================
# 1. 运行
# ================================
./build/linux/x86/release/RyanJson
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

硬编码的平台特定路径限制了可移植性。

路径 ./build/linux/x86/release/RyanJson 是硬编码的,仅适用于 Linux x86 平台。这会导致脚本在其他平台(如 macOS、Windows 或 ARM 架构)上无法工作。

♻️ 建议:使用变量并添加文件存在性检查
+# 定义二进制文件路径
+BINARY_PATH="./build/linux/x86/release/RyanJson"
+
+# 检查二进制文件是否存在
+if [ ! -f "$BINARY_PATH" ]; then
+  echo "错误: 找不到二进制文件 $BINARY_PATH"
+  exit 1
+fi
+
 # ================================
 # 1. 运行
 # ================================
-./build/linux/x86/release/RyanJson
+"$BINARY_PATH"

更好的方案是使用 xmake 的命令来获取实际的构建路径:

# 使用 xmake 获取目标路径
BINARY_PATH=$(xmake show -t RyanJson)
if [ ! -f "$BINARY_PATH" ]; then
  echo "错误: 找不到二进制文件 $BINARY_PATH"
  exit 1
fi
"$BINARY_PATH"

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @run_base_coverage.sh at line 10, The script currently uses a hardcoded
platform-specific binary path "./build/linux/x86/release/RyanJson"; replace that
with a variable obtained from xmake (e.g., use xmake show -t RyanJson to
populate BINARY_PATH), add a file-existence check for BINARY_PATH (if not found,
print an error and exit 1), and then execute "$BINARY_PATH" so the script works
across platforms and architectures.

Comment on lines 623 to 630
RyanJson_t RyanJsonGetObjectValue(RyanJson_t pJson)
{
RyanJsonCheckCodeNoReturn(NULL != pJson, { return 0; });

RyanJson_t objValue;
RyanJsonMemcpy((void *)&objValue, RyanJsonGetValue(pJson), sizeof(void *));
return objValue;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

返回值类型不一致

RyanJsonGetObjectValue 在检查失败时返回 0 而不是 NULL。虽然在大多数情况下等效,但对于指针类型应使用 NULL 以保持一致性。

🔧 建议修复
 RyanJson_t RyanJsonGetObjectValue(RyanJson_t pJson)
 {
-	RyanJsonCheckCodeNoReturn(NULL != pJson, { return 0; });
+	RyanJsonCheckCodeNoReturn(NULL != pJson, { return NULL; });

 	RyanJson_t objValue;
 	RyanJsonMemcpy((void *)&objValue, RyanJsonGetValue(pJson), sizeof(void *));
 	return objValue;
 }
🤖 Prompt for AI Agents
In @RyanJson/RyanJson.c around lines 623 - 630, The function
RyanJsonGetObjectValue returns the integer literal 0 on the NULL-check failure
which is inconsistent for a pointer return; change the failure return from 0 to
NULL inside RyanJsonGetObjectValue (the branch inside RyanJsonCheckCodeNoReturn)
so the function consistently returns a pointer (NULL) when pJson is invalid,
leaving the rest of the function (RyanJson_t objValue; RyanJsonMemcpy(...);
return objValue;) unchanged.

Comment on lines +145 to +148
RyanJsonCheckReturnFalse(RyanJsonIsDouble(RyanJsonGetObjectToKey(jsonRoot, "double")));

// 验证解析后的数值是否正确
double doubleValue = RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(jsonRoot, "double"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

缺少 NULL 指针检查可能导致段错误

RyanJsonGetObjectToKey(jsonRoot, "double") 的返回值在使用前未进行 NULL 检查。如果键不存在,会向 RyanJsonIsDoubleRyanJsonGetDoubleValue 传递 NULL 指针,可能引发段错误。

同样的问题也出现在 Line 162 和 Line 164。

🔧 建议的修复方案
 		RyanJson_t jsonRoot = RyanJsonParse(jsondoubleStr);
 		RyanJsonCheckCode(NULL != jsonRoot, {
 			jsonLog("str: %s", jsondoubleStr);
 			goto err;
 		});
 
-		RyanJsonCheckReturnFalse(NULL != jsonRoot);
-		RyanJsonCheckReturnFalse(RyanJsonIsDouble(RyanJsonGetObjectToKey(jsonRoot, "double")));
+		RyanJson_t doubleNode = RyanJsonGetObjectToKey(jsonRoot, "double");
+		RyanJsonCheckCode(NULL != doubleNode, {
+			jsonLog("double key not found\n");
+			RyanJsonDelete(jsonRoot);
+			goto err;
+		});
+		RyanJsonCheckReturnFalse(RyanJsonIsDouble(doubleNode));
 
 		// 验证解析后的数值是否正确
-		double doubleValue = RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(jsonRoot, "double"));
+		double doubleValue = RyanJsonGetDoubleValue(doubleNode);

对 Line 162、164 应用类似的修复。

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c around lines 145 -
148, The code calls RyanJsonGetObjectToKey(jsonRoot, "double") and passes its
result directly to RyanJsonIsDouble and RyanJsonGetDoubleValue without NULL
checks; store the return of RyanJsonGetObjectToKey in a local variable (e.g.,
obj = RyanJsonGetObjectToKey(...)), check if obj is NULL and handle the failure
(use RyanJsonCheckReturnFalse or return early) before calling RyanJsonIsDouble
or RyanJsonGetDoubleValue, and apply the same pattern to the other occurrences
noted (the calls around the locations corresponding to Line 162 and Line 164).

Comment on lines +111 to +114
RyanJsonCheckReturnFalse(RyanJsonIsInt(RyanJsonGetObjectToKey(jsonRoot, "int")));

// 验证解析后的数值是否正确
int32_t intValue = RyanJsonGetIntValue(RyanJsonGetObjectToKey(jsonRoot, "int"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

缺少 NULL 指针检查可能导致段错误

RyanJsonGetObjectToKey(jsonRoot, "int") 的返回值在使用前未进行 NULL 检查。如果键不存在,会向 RyanJsonIsIntRyanJsonGetIntValue 传递 NULL 指针,可能引发段错误。

同样的问题也出现在 Line 128 和 Line 130。

🔧 建议的修复方案
 		const char *jsonIntStr = IntStringTable[i];
 		RyanJson_t jsonRoot = RyanJsonParse(jsonIntStr);
 		RyanJsonCheckCode(NULL != jsonRoot, {
 			jsonLog("str: %s", jsonIntStr);
 			goto err;
 		});
-		RyanJsonCheckReturnFalse(RyanJsonIsInt(RyanJsonGetObjectToKey(jsonRoot, "int")));
+
+		RyanJson_t intNode = RyanJsonGetObjectToKey(jsonRoot, "int");
+		RyanJsonCheckCode(NULL != intNode, {
+			jsonLog("int key not found\n");
+			RyanJsonDelete(jsonRoot);
+			goto err;
+		});
+		RyanJsonCheckReturnFalse(RyanJsonIsInt(intNode));
 
 		// 验证解析后的数值是否正确
-		int32_t intValue = RyanJsonGetIntValue(RyanJsonGetObjectToKey(jsonRoot, "int"));
+		int32_t intValue = RyanJsonGetIntValue(intNode);

对 Line 128、130 应用类似的修复。

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @test/baseTest/equality/RyanJsonBaseTestEqualityInt.c around lines 111 - 114,
The code calls RyanJsonGetObjectToKey(jsonRoot, "int") and passes its result
directly to RyanJsonIsInt and RyanJsonGetIntValue without checking for NULL; add
a NULL check by storing the result of RyanJsonGetObjectToKey in a local variable
(e.g., obj = RyanJsonGetObjectToKey(jsonRoot, "int")), verify obj != NULL before
calling RyanJsonIsInt or RyanJsonGetIntValue, handle the NULL case (fail the
test or return early) and apply the same pattern for the other occurrences
referenced (the calls around Line 128 and Line 130) to avoid dereferencing NULL.

Comment on lines +101 to +103
RyanJsonCheckReturnFalse(RyanJsonIsString(RyanJsonGetObjectToKey(jsonRoot, "str")));

const char *strValue = RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "str"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

缺少 NULL 指针检查可能导致段错误

RyanJsonGetObjectToKey(jsonRoot, "str") 的返回值在使用前未进行 NULL 检查。如果 JSON 对象中不存在 "str" 键,会导致向 RyanJsonIsStringRyanJsonGetStringValue 传递 NULL 指针,可能引发段错误。

同样的问题也出现在 Line 118、Line 137、Line 139 和 Line 157。

🔧 建议的修复方案
 		const char *jsonStrInput = SimpleStringJsonTable[i];
 		RyanJson_t jsonRoot = RyanJsonParse(jsonStrInput);
 		RyanJsonCheckReturnFalse(NULL != jsonRoot);
-		RyanJsonCheckReturnFalse(RyanJsonIsString(RyanJsonGetObjectToKey(jsonRoot, "str")));
+
+		RyanJson_t strNode = RyanJsonGetObjectToKey(jsonRoot, "str");
+		RyanJsonCheckCode(NULL != strNode, {
+			jsonLog("simple str key not found\n");
+			RyanJsonDelete(jsonRoot);
+			goto err;
+		});
+		RyanJsonCheckReturnFalse(RyanJsonIsString(strNode));
 
-		const char *strValue = RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "str"));
+		const char *strValue = RyanJsonGetStringValue(strNode);

对 Line 118、137、139、157 应用类似的修复。

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @test/baseTest/equality/RyanJsonBaseTestEqualityString.c around lines 101 -
103, The code calls RyanJsonGetObjectToKey(jsonRoot, "str") and directly passes
its result to RyanJsonIsString and RyanJsonGetStringValue, risking a NULL
dereference; fix by assigning the result to a local pointer (e.g., obj =
RyanJsonGetObjectToKey(jsonRoot, "str")), check that obj is not NULL (use
RyanJsonCheckReturnFalse or equivalent) before calling RyanJsonIsString(obj) and
RyanJsonGetStringValue(obj), and apply the same pattern to the other occurrences
referenced (the calls at the locations using keys for lines 118, 137, 139, and
157).

Comment on lines +251 to +253
cJSON_Minify(data);
static uint32_t alksdjfCOunt = 0;
checkJsonSemanticEquality(data, len, str, strlen(str), &alksdjfCOunt);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

yyjson 解析器错误使用了 cJSON_Minify

yyjsonParseData 函数中使用了 cJSON_Minify 来压缩数据,这与其他两个解析器的实现不一致。应该使用一致的方法或 yyjson 自己的压缩方案。

🔧 建议修复
-	cJSON_Minify(data);
+	// 使用通用的 minify 或保持与 RyanJson 一致
+	RyanJsonMinify(data, (int32_t)len);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @test/RFC8259Test/RyanJsonRFC8259JsonTest.c around lines 251 - 253, The
yyjson parser implementation incorrectly calls cJSON_Minify in yyjsonParseData;
replace that call with the same minify approach used by the other parsers (or
use yyjson's own minify/compaction routine) so yyjsonParseData no longer depends
on cJSON_Minify. Locate yyjsonParseData and remove the cJSON_Minify(data)
invocation, then call the shared minify helper (or yyjson-specific API) on the
input buffer before parsing and ensure the buffer/length passed to
checkJsonSemanticEquality remains correct.

Comment on lines +275 to +276
int32_t result = 0;
uint32_t testRunCount = 0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

未使用的变量 result

变量 result 声明后从未被使用。

🔧 建议修复
 RyanJsonBool_e RFC8259JsonTest(void)
 {
-	int32_t result = 0;
 	uint32_t testRunCount = 0;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
int32_t result = 0;
uint32_t testRunCount = 0;
uint32_t testRunCount = 0;
🤖 Prompt for AI Agents
In @test/RFC8259Test/RyanJsonRFC8259JsonTest.c around lines 275 - 276, The local
variable `result` is declared but never used; either remove the declaration
`int32_t result = 0;` or use it to capture and check return values where
intended (e.g., assign function return codes to `result` and assert/handle
them), so eliminate the unused symbol `result` or convert its use into
meaningful checks in the test flow (keep `testRunCount` as-is if still used).

Comment on lines +284 to +286
printfJsonCompare(jsonstr);
return RyanJsonTrue;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

未检查 printfJsonCompare 的返回值

其他测试函数都返回 printfJsonCompare(jsonstr) 的结果,但 testCompressedBusinessJsonMemory 忽略了返回值并直接返回 RyanJsonTrue

🔧 建议修复
 static RyanJsonBool_e testCompressedBusinessJsonMemory(void)
 {
 	char *jsonstr =
 		"{\"0\":\"0\",\"1\":\"189774523\",...";
-	printfJsonCompare(jsonstr);
-	return RyanJsonTrue;
+	return printfJsonCompare(jsonstr);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
printfJsonCompare(jsonstr);
return RyanJsonTrue;
}
return printfJsonCompare(jsonstr);
}
🤖 Prompt for AI Agents
In @test/RyanJsonMemoryFootprintTest.c around lines 284 - 286, The
testCompressedBusinessJsonMemory function currently calls
printfJsonCompare(jsonstr) but ignores its return value and always returns
RyanJsonTrue; change it to return the result of printfJsonCompare(jsonstr) (or
store the result and return that) so the function mirrors other tests and
propagates failures, referencing printfJsonCompare and the function
testCompressedBusinessJsonMemory and removing the hardcoded RyanJsonTrue return.

Comment on lines +290 to +292
int32_t result = 0;
uint32_t testRunCount = 0;
uint64_t funcStartMs;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

未使用的变量 result

变量 result 声明后从未被使用。funcStartMs 可能被 runTestWithLogAndTimer 宏使用。

🔧 建议修复
 RyanJsonBool_e RyanJsonMemoryFootprintTest(void)
 {
-	int32_t result = 0;
 	uint32_t testRunCount = 0;
 	uint64_t funcStartMs;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
int32_t result = 0;
uint32_t testRunCount = 0;
uint64_t funcStartMs;
uint32_t testRunCount = 0;
uint64_t funcStartMs;
🤖 Prompt for AI Agents
In @test/RyanJsonMemoryFootprintTest.c around lines 290 - 292, The variable
`result` is declared but never used; remove the `int32_t result = 0;`
declaration (or if it was intended to capture a return value, assign the return
of the call site instead) to eliminate the unused variable warning; leave
`uint64_t funcStartMs` as-is if it is referenced by the `runTestWithLogAndTimer`
macro and keep `testRunCount` untouched if it’s used elsewhere.

Comment on lines +57 to +62
#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));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 查找 RyanJsonMallocHeaderSize 和 RyanJsonMallocAlign 的定义
echo "=== Searching for RyanJsonMallocHeaderSize ==="
rg -nP 'define\s+RyanJsonMallocHeaderSize' --type=c --type=h

echo ""
echo "=== Searching for RyanJsonMallocAlign (macro definition) ==="
rg -nP 'define\s+RyanJsonMallocAlign' --type=c --type=h

echo ""
echo "=== Searching for RyanJsonMallocAlign in broader context ==="
rg -nP 'RyanJsonMallocAlign\b' --type=c --type=h -A 2 -B 2

echo ""
echo "=== Searching for related memory allocation constants ==="
rg -nP 'RyanJsonMalloc|tlsf|HeaderSize|malloc' --type=h -A 1 | head -100

Repository: Ryan-CW-Code/RyanJson

Length of output: 10278


🏁 Script executed:

#!/bin/bash
# 查看 v_malloc_tlsf 函数周围的完整上下文
echo "=== Full context of v_malloc_tlsf in test/RyanJsonTest.c ==="
sed -n '55,75p' test/RyanJsonTest.c

echo ""
echo "=== Search for similar malloc patterns in the codebase ==="
rg -nP 'RyanJsonMallocHeaderSize\s*[-+*]' --type=c --type=h -A 1 -B 1

echo ""
echo "=== Check if v_malloc_tlsf is used in tests or main code ==="
rg -nP 'v_malloc_tlsf' --type=c --type=h

echo ""
echo "=== Look for RyanJsonFlagSize to understand pattern ==="
rg -nP 'define.*RyanJsonFlagSize' --type=c --type=h -A 1

Repository: Ryan-CW-Code/RyanJson

Length of output: 1951


test/RyanJsonTest.c 第 61 行的内存对齐计算存在配置不适配问题

v_malloc_tlsf 函数中使用了硬编码的 - 4,但 RyanJsonMallocHeaderSize 在不同配置下取值为 8 或 12,导致不同配置下实际减去的值不一致:

  • RyanJsonMallocHeaderSize = 12 时,实际为 12 - 4 = 8
  • RyanJsonMallocHeaderSize = 8 时,实际为 8 - 4 = 4

而主库代码(RyanJson.c 第 180-182 行)采用自适应公式 RyanJsonMallocHeaderSize / 2。建议将硬编码的 - 4 改为 - (RyanJsonMallocHeaderSize / 2) 或补充注释说明为何使用固定值 4。

🤖 Prompt for AI Agents
In @test/RyanJsonTest.c around lines 57 - 62, The alignment subtraction in
v_malloc_tlsf uses a hardcoded "- 4" which mismatches configurable
RyanJsonMallocHeaderSize (8 or 12) and should follow the library's adaptive
formula; update the call in v_malloc_tlsf to use "- (RyanJsonMallocHeaderSize /
2)" instead of "- 4" (or, if you intentionally must keep 4, add a clear comment
next to the RyanJsonAlign usage explaining why a fixed 4 is correct) so the
allocation matches the behavior of the main library; target the v_malloc_tlsf
function and the RyanJsonMallocHeaderSize constant and RyanJsonAlign macro when
applying the change.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @RyanJson/RyanJson.c:
- Around line 744-749: The loop that handles leading zeros incorrectly rejects
the single-character "0" because RyanJsonCheckReturnFalse is called with the
combined condition parseBufHasRemain(parseBuf) && (*parseBuf->currentPtr < '0'
|| *parseBuf->currentPtr > '9'), which evaluates false when EOF is reached and
leads to an error; fix it in the while ('0' == *parseBuf->currentPtr) block by
first advancing with parseBufTyrAdvanceCurrentPrt(parseBuf, 1) and then only
enforcing the "no digit after leading zero" check when
parseBufHasRemain(parseBuf) is true (i.e., change the condition passed to
RyanJsonCheckReturnFalse to something like: if (parseBufHasRemain(parseBuf))
ensure *parseBuf->currentPtr is not '0'..'9'), leaving EOF after a single '0' as
valid.
- Around line 277-289: The function RyanJsonExpandRealloc lacks a check that
newSize is larger than oldSize and currently copies oldSize bytes
unconditionally, risking overflow; update RyanJsonExpandRealloc to either assert
the expansion precondition (call RyanJsonCheckAssert(newSize > oldSize)) before
allocating and copying, or safely copy only the minimum of the two sizes by
using RyanJsonMemcpy(newBlock, block, RyanJsonMinU32(oldSize, newSize)); ensure
the chosen fix is applied before jsonMalloc/jsonFree and retains existing null
checks (RyanJsonCheckReturnNull) and custom alloc hooks
(jsonRealloc/jsonMalloc).
🧹 Nitpick comments (1)
RyanJson/RyanJson.c (1)

224-250: 密钥长度编码实现正确,但建议增强注释说明。

代码使用了与大小端无关的逐字节编码方式来存储和读取密钥长度,这是一个很好的可移植性设计。但是,keyFieldLen 的含义(即存储"密钥长度值"所需的字节数,而不是密钥本身的长度)可能不够清晰。

建议在函数开头添加注释,明确说明 keyFieldLen 表示存储长度字段所需的字节数(0-3字节),以提高代码可读性。

♻️ 建议的注释改进
 static inline void RyanJsonSetKeyLen(RyanJson_t pJson, uint32_t value)
 {
 	RyanJsonCheckAssert(NULL != pJson);
+	// keyFieldLen 表示存储"密钥长度值"所需的字节数(0-3),而非密钥本身的长度
 	uint8_t *buf = RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize;
 	uint8_t keyFieldLen = RyanJsonGetPayloadEncodeKeyLenByFlag(pJson);
 	RyanJsonCheckAssert(keyFieldLen <= RyanJsonKeyFeidLenMaxSize);
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83b338e and 06ea8f8.

📒 Files selected for processing (1)
  • RyanJson/RyanJson.c
🔇 Additional comments (2)
RyanJson/RyanJson.c (2)

561-569: 内存钩子初始化不是线程安全的。

函数 RyanJsonInitHooks 使用全局静态变量存储内存分配钩子。如果在多线程环境中多次调用此函数,或在初始化后多个线程同时使用 JSON 解析/序列化功能,可能会出现竞态条件。

建议在文档中明确说明此函数应在程序初始化时调用一次,且不应在多线程环境中重复调用。如果需要支持多线程场景,考虑使用线程本地存储或传递钩子上下文。


1298-1341: 浮点数打印逻辑设计良好。

代码实现了以下健壮的处理:

  1. 特殊值处理:将 Infinity 和 NaN 输出为 "null",符合 RFC 8259 标准
  2. 往返验证:使用 %.15g 格式化后重新解析并比较,如果精度不足则改用 %.17g,确保序列化后能准确还原原始值

这种设计提高了数值序列化的可靠性和精度保证。

Comment on lines 277 to 289
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;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find all calls to RyanJsonExpandRealloc and show context
rg -nP -B5 -A5 'RyanJsonExpandRealloc\s*\(' --type=c

Repository: Ryan-CW-Code/RyanJson

Length of output: 1244


🏁 Script executed:

# More comprehensive search including possible variations
rg 'RyanJsonExpandRealloc' --type=c -n

Repository: Ryan-CW-Code/RyanJson

Length of output: 290


需要在 RyanJsonExpandRealloc 中添加扩容条件验证,防止缓冲区溢出风险。

函数 RyanJsonExpandRealloc 名称和注释暗示其用于扩容场景,但实现中未验证 newSize > oldSize 的前置条件。若 newSize < oldSize 调用此函数,第 287 行的 RyanJsonMemcpy(newBlock, block, oldSize) 会将 oldSize 字节复制到大小为 newSize 的缓冲区,导致缓冲区溢出

建议添加断言验证扩容条件:RyanJsonCheckAssert(newSize > oldSize),或修改复制大小为 RyanJsonMemcpy(newBlock, block, RyanJsonMinU32(oldSize, newSize))

🤖 Prompt for AI Agents
In @RyanJson/RyanJson.c around lines 277 - 289, The function
RyanJsonExpandRealloc lacks a check that newSize is larger than oldSize and
currently copies oldSize bytes unconditionally, risking overflow; update
RyanJsonExpandRealloc to either assert the expansion precondition (call
RyanJsonCheckAssert(newSize > oldSize)) before allocating and copying, or safely
copy only the minimum of the two sizes by using RyanJsonMemcpy(newBlock, block,
RyanJsonMinU32(oldSize, newSize)); ensure the chosen fix is applied before
jsonMalloc/jsonFree and retains existing null checks (RyanJsonCheckReturnNull)
and custom alloc hooks (jsonRealloc/jsonMalloc).

Comment on lines +744 to 749
while ('0' == *parseBuf->currentPtr)
{
RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1));
// 前导0后面不允许跟数组,比如"0123"
// 前导0后面不允许跟数据,比如"0123"
RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf) && (*parseBuf->currentPtr < '0' || *parseBuf->currentPtr > '9'));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

数字解析逻辑会错误地拒绝 "0"。

当输入仅为 "0" 时,代码会执行以下步骤:

  1. Line 746:消费字符 '0',此时 remainSize 变为 0
  2. Line 748:检查条件 parseBufHasRemain(parseBuf) && (...)
    • parseBufHasRemain(parseBuf) 返回 false
    • 整个条件为 false
    • RyanJsonCheckReturnFalse 返回失败

但根据 RFC 8259,"0" 是有效的 JSON 数字。当前逻辑错误地拒绝了单独的 "0"。

正确的检查应该是:如果还有剩余字符,则下一个字符不能是数字;如果没有剩余字符(EOF),则应该继续。

🐛 建议的修复
 	// 前导0是非法的
 	while ('0' == *parseBuf->currentPtr)
 	{
 		RyanJsonCheckReturnFalse(RyanJsonTrue == parseBufTyrAdvanceCurrentPrt(parseBuf, 1));
-		// 前导0后面不允许跟数据,比如"0123"
-		RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf) && (*parseBuf->currentPtr < '0' || *parseBuf->currentPtr > '9'));
+		// 前导0后面不允许跟数字,比如"0123",但允许 "0" 单独存在或后跟非数字字符
+		RyanJsonCheckReturnFalse(!parseBufHasRemain(parseBuf) || (*parseBuf->currentPtr < '0' || *parseBuf->currentPtr > '9'));
 	}
🤖 Prompt for AI Agents
In @RyanJson/RyanJson.c around lines 744 - 749, The loop that handles leading
zeros incorrectly rejects the single-character "0" because
RyanJsonCheckReturnFalse is called with the combined condition
parseBufHasRemain(parseBuf) && (*parseBuf->currentPtr < '0' ||
*parseBuf->currentPtr > '9'), which evaluates false when EOF is reached and
leads to an error; fix it in the while ('0' == *parseBuf->currentPtr) block by
first advancing with parseBufTyrAdvanceCurrentPrt(parseBuf, 1) and then only
enforcing the "no digit after leading zero" check when
parseBufHasRemain(parseBuf) is true (i.e., change the condition passed to
RyanJsonCheckReturnFalse to something like: if (parseBufHasRemain(parseBuf))
ensure *parseBuf->currentPtr is not '0'..'9'), leaving EOF after a single '0' as
valid.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
RyanJson/RyanJsonConfig.h (1)

43-50: 冗余的宏定义回退逻辑。

在第 26-27 行(RT_VER_NUM 路径)和第 36-37 行(非 RT 路径)已经定义了 RyanJsonMallocAlignRyanJsonMallocHeaderSize,因此第 43-50 行的 #ifndef 回退定义永远不会被触发。

建议移除第 43-50 行的冗余代码,或者重新组织宏定义逻辑,使回退定义仅在用户未提供自定义值时生效。

♻️ 建议的重构
 #ifdef RT_VER_NUM
 #include "rtthread.h"
 #define RyanJsonMemset             rt_memset
 #define RyanJsonMemcpy             rt_memcpy
 #define RyanJsonStrlen             rt_strlen
 #define RyanJsonStrcmp             rt_strcmp
 #define RyanJsonSnprintf           rt_snprintf
 #define RyanJsonPlatformAssert(EX) RT_ASSERT(EX)
-#define RyanJsonMallocHeaderSize   12U
-#define RyanJsonMallocAlign        RT_ALIGN_SIZE
+#ifndef RyanJsonMallocHeaderSize
+#define RyanJsonMallocHeaderSize   12U
+#endif
+#ifndef RyanJsonMallocAlign
+#define RyanJsonMallocAlign        RT_ALIGN_SIZE
+#endif
 #else
 #include <assert.h>
 #define RyanJsonMemset             memset
 #define RyanJsonMemcpy             memcpy
 #define RyanJsonStrlen             strlen
 #define RyanJsonStrcmp             strcmp
 #define RyanJsonSnprintf           snprintf
 #define RyanJsonPlatformAssert(EX) assert(EX)
-#define RyanJsonMallocHeaderSize   8U
-#define RyanJsonMallocAlign        4U
+#ifndef RyanJsonMallocHeaderSize
+#define RyanJsonMallocHeaderSize   8U
+#endif
+#ifndef RyanJsonMallocAlign
+#define RyanJsonMallocAlign        4U
+#endif
 #endif
-
-#ifndef RyanJsonMallocAlign
-#define RyanJsonMallocAlign 8U
-#endif
-
-//
-#ifndef RyanJsonMallocHeaderSize
-#define RyanJsonMallocHeaderSize 8U
-#endif
RyanJson/RyanJson.c (3)

191-224: 隐藏指针访问函数存在拼写错误。

函数名拼写错误:RyanJsonGetHiddePrtRyanJsonSetHiddePrtRyanJsonGetHiddenPtrAt 中的 "Hidde" 应为 "Hidden"。

虽然这些是静态函数,但为了代码可读性和一致性,建议修正拼写。使用 memcpy 规避非对齐访问警告是正确的做法。

♻️ 修正拼写
-static inline uint8_t *RyanJsonGetHiddePrt(RyanJson_t pJson)
+static inline uint8_t *RyanJsonGetHiddenPtr(RyanJson_t pJson)
 {
 	RyanJsonCheckAssert(NULL != pJson);
 
 	// 用memcpy规避非对其警告
 	void *tmpPtr = NULL;
 	RyanJsonMemcpy((void *)&tmpPtr, (RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize), sizeof(void *));
 	return (uint8_t *)tmpPtr;
 }

-static inline void RyanJsonSetHiddePrt(RyanJson_t pJson, uint8_t *hiddePrt)
+static inline void RyanJsonSetHiddenPtr(RyanJson_t pJson, uint8_t *hiddenPtr)
 {
 	RyanJsonCheckAssert(NULL != pJson);
-	RyanJsonCheckAssert(NULL != hiddePrt);
+	RyanJsonCheckAssert(NULL != hiddenPtr);
 
 	// 用memcpy规避非对其警告
-	void *tmpPtr = hiddePrt;
+	void *tmpPtr = hiddenPtr;
 	// uint8_t是flag,uint32_t是记录key的长度空间
 	RyanJsonMemcpy((RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize), (const void *)&tmpPtr,
 		       sizeof(void *));
 }

并在所有调用处更新函数名。


254-269: GetValue 函数包含潜在的死代码。

第 259-260 行注释表明"目前的场景不会发生 RyanJsonIsKey(pJson) 为 false 的情况",但第 261 行仍然保留了对 RyanJsonIsKey(pJson) 的条件检查。

建议:

  1. 如果确实不会出现 key 为 false 的情况,添加断言并简化逻辑
  2. 如果将来可能支持,保留现有逻辑并更新注释

384-499: 字符串修改函数逻辑复杂,需确认内联存储正确性。

RyanJsonChangeString 函数实现了内联存储与指针存储的混合策略:

  • 小字符串(≤ RyanJsonGetInlineStringSize)使用内联存储
  • 大字符串分配堆内存并使用指针

关注点

  1. 第 472-477 行内联存储的复制逻辑使用了临时数组 arr,需确保 arr 的数据正确拷贝到内联缓冲区
  2. 第 429 行声明的 arr 大小依赖宏计算,需验证其在所有配置下足够大

建议添加断言或注释说明内联存储布局,以便后续维护。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06ea8f8 and 5a68fc1.

📒 Files selected for processing (2)
  • RyanJson/RyanJson.c
  • RyanJson/RyanJsonConfig.h
🔇 Additional comments (17)
RyanJson/RyanJsonConfig.h (4)

1-2: 包含守卫宏命名符合约定。

宏名称从 __RyanJsonConfig__ 简化为 RyanJsonConfig,避免了以双下划线开头的保留标识符,符合 C 标准的最佳实践。


56-56: 嵌套限制大幅减少,需验证影响。

RyanJsonNestingLimit 从 3000 降至 300U 是 10 倍的减少。虽然这对于嵌入式系统的栈空间管理有益,但可能影响现有解析深度嵌套 JSON 的用例。

请确认:

  1. 300 层嵌套是否足以满足目标应用场景
  2. 现有用户是否可能因此限制而解析失败
  3. 是否需要在文档中说明此重大变更

106-112: 编译时验证增强了配置安全性。

新增的编译时检查确保 RyanJsonMallocHeaderSizeRyanJsonMallocAlign 为 4 的倍数(或为 0),有效防止配置错误导致的对齐问题和内存访问异常。

这是一个良好的防御性编程实践。


64-87: 配置中的缓冲区大小是针对嵌入式平台的经过深思熟虑的权衡设计,不存在实际风险。

代码实现充分考虑了这一限制:

  1. 分阶段序列化策略:先用 "%lf" 尝试(空间效率优先),仅当精度不足时才回退至 "%.17g""%.17lf"
  2. 边界检查保护:每次 snprintf 调用后都检查 len < printBufRemainBytes(printfBuf),确保无实际溢出
  3. 开发者已知悉:代码注释明确指出 "%lf" 的权衡:"可能被撑爆,但是嵌入式平台可以放心"
  4. 用户可配置:配置注释邀请用户按需修改 RyanJsonDoubleBufferSize

snprintf 的大小参数保证了安全的截断而非缓冲区溢出。此设计对嵌入式系统优化合理,PC 平台用户可自行调整。

RyanJson/RyanJson.c (13)

3-41: 模糊测试配置合理。

Fuzzer 模式下的配置增强了测试覆盖:

  • 增加嵌套限制至 350U 用于更深度的测试
  • 自定义随机数生成器避免依赖全局状态
  • snprintf 随机失败注入有助于测试错误处理路径

这些是良好的测试实践。


226-252: 密钥长度编解码采用大小端无关实现。

RyanJsonSetKeyLenRyanJsonGetKeyLen 使用逐字节的大小端无关方式读写密钥长度,确保跨平台兼容性。变长编码(1-4 字节)有效节省了空间。

实现正确且具有良好的可移植性。


279-291: RyanJsonExpandRealloc 功能有限但实现合理。

函数仅支持扩展(不考虑收缩),且不处理 block 为 NULL 的情况。注释已明确说明这些限制。

在没有 realloc 时的回退逻辑(malloc + memcpy + free)是正确的。函数名"Expand"也准确反映了其用途。


314-326: 浮点数比较实现安全且符合最佳实践。

RyanJsonCompareDouble 结合了相对误差(DBL_EPSILON * maxVal)和绝对误差(RyanJsonAbsTolerance),取两者较大值作为容差。这是浮点数相等比较的标准方法,能正确处理接近零和大数值的情况。


364-376: 节点替换函数实现正确。

RyanJsonReplaceNode 正确处理链表节点替换,包括:

  • 更新前驱节点的 next 指针
  • 继承被替换节点的后继指针
  • 断开旧节点的 next 引用

实现标准且无明显问题。


594-655: 新增的公共 getter API 实现一致且安全。

新的 getter 函数族(RyanJsonGetKeyRyanJsonGetStringValueRyanJsonGetIntValue 等)正确处理了内联与指针存储的差异,使用 memcpy 避免非对齐访问问题。

API 设计清晰,类型安全。


746-840: 数字解析函数实现符合 RFC 8259 规范。

RyanJsonInternalParseDouble 替代 strtod 以提高嵌入式平台兼容性,实现了:

  • 正确的符号处理
  • 严格的前导零检查(禁止 "0123" 但允许 "0")
  • 小数和指数部分解析
  • 整数/浮点数类型判断

第 774-780 行注释掉的代码显示了宽松的前导零处理方案。当前实现符合 RFC 8259 的严格要求,这是正确的选择。


879-1040: 字符串解析函数正确处理 Unicode 和转义序列。

RyanJsonParseStringBuffer 实现了完整的 JSON 字符串解析:

  • 预扫描获取长度后分配内存,避免多次重分配
  • 正确处理 UTF-16 代理对(surrogate pairs)
  • 将 Unicode 码点编码为 UTF-8
  • 拒绝非法控制字符(0x00-0x1F)

错误处理使用 goto error__ 标签统一清理,与代码库风格一致。


1076-1193: 数组和对象解析函数使用统一的控制流模式。

RyanJsonParseArrayRyanJsonParseObject 采用一致的解析模式:

  • 空容器优化(直接跳转至结束)
  • 使用 goto error__ 统一错误处理
  • 使用 goto next__ 作为成功路径的优化

这种模式在 C 语言中是合理的资源清理策略。


1302-1416: 数字打印函数全面处理边界情况。

RyanJsonPrintNumber 的改进包括:

  1. 合规性:将 Infinity 和 NaN 输出为 null(符合 RFC 8259)
  2. 精度保证:对接近整数的浮点数使用 .1lf;通过往返解析验证精度,必要时回退至 .17g/.17lf
  3. 美化输出:移除非科学计数法表示中的尾部零(保留至少 "x.0" 格式)

逻辑复杂但考虑周全,测试平台的条件编译有助于提高分支覆盖率。


2389-2452: JSON 对象比较函数实现完整且高效。

RyanJsonCompare 实现递归的深度相等比较:

  • 利用指针相等、类型不匹配、大小不匹配等快速路径优化
  • 使用 RyanJsonCompareDouble 安全比较浮点数
  • 递归比较嵌套的数组和对象

注释已提示"此接口效率较低, 谨慎使用",这对于深度嵌套结构是合理的警告。


2108-2120: 值修改 API 实现简洁且安全。

新增的 RyanJsonChangeIntValueRyanJsonChangeDoubleValue 使用 memcpy 写入数值,确保在所有平台上的正确性(避免非对齐访问)。

API 命名清晰,与 getter 对应。


584-592: 内存钩子初始化 API 设计合理。

RyanJsonInitHooks 允许用户提供自定义内存分配器,支持 TLSF 等专用分配器集成。userRealloc 可选的设计增加了灵活性。

建议:在公共头文件或文档中明确说明:

  1. 此函数必须在任何 JSON 操作之前调用
  2. 调用后不应再次调用(或说明重复调用的行为)
  3. 不调用此函数的默认行为(当前代码中 jsonMalloc 等初始为 NULL,会导致空指针解引用)
#!/bin/bash
# 检查 jsonMalloc 等全局变量的初始化和默认值
rg -n "jsonMalloc\s*=" --type=c
rg -n "jsonFree\s*=" --type=c
rg -n "jsonRealloc\s*=" --type=c

Comment on lines +180 to +189
#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);
// }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 查找 RyanJsonGetInlineStringSize 的定义和使用
rg -n "RyanJsonGetInlineStringSize" -A3 -B3 RyanJson/RyanJson.c

# 查找相关宏的定义
rg -n "RyanJsonAlign\s*\(" --type=c -A2 -B2
rg -n "define\s+RyanJsonFlagSize" --type=c
rg -n "define\s+RyanJsonMallocHeaderSize" --type=c
rg -n "define\s+RyanJsonMallocAlign" --type=c

Repository: Ryan-CW-Code/RyanJson

Length of output: 4063


🏁 Script executed:

#!/bin/bash
# 检查是否有单元测试文件
find . -name "*test*" -o -name "*Test*" | head -20
# 查找所有的 .c 文件
git ls-files | grep -E "\.(c|h)$" | head -30

Repository: Ryan-CW-Code/RyanJson

Length of output: 1929


🏁 Script executed:

#!/bin/bash
# 在测试文件中搜索对 RyanJsonGetInlineStringSize 的测试
rg -n "RyanJsonGetInlineStringSize" test/ --type=c

# 搜索任何关于 MallocHeaderSize 的设计说明或注释
rg -n "MallocHeaderSize" -B2 -A2 --type=c --type=h

# 查看 RyanJsonConfig.h 完整内容了解平台配置
cat -n RyanJson/RyanJsonConfig.h

Repository: Ryan-CW-Code/RyanJson

Length of output: 6053


🏁 Script executed:

#!/bin/bash
# 检查头文件中是否有相关的设计文档或说明
rg -n "inline.*string\|string.*inline" -i -B3 -A3 --type=c --type=h | head -50

Repository: Ryan-CW-Code/RyanJson

Length of output: 47


补充内联字符串缓冲区大小计算的文档和单元测试。

RyanJsonGetInlineStringSize 宏的对齐计算逻辑正确,与注释掉的函数版本等价。但存在两个改进需求:

  1. 缺少设计说明:公式中 RyanJsonMallocHeaderSize / 2 的目的未在代码中说明。建议在宏定义上方或注释函数中补充说明为何除以 2(例如:"除以 2 以计入分配器元数据开销")。

  2. 缺少单元测试:当前测试套件中未找到针对此宏的专门测试。建议在 test/ 目录下添加测试,验证在不同平台配置(RyanJsonMallocHeaderSize = 8/12, RyanJsonMallocAlign = 4/8)下的计算结果。

建议操作:

  • 在宏或注释函数上方添加说明注释,解释为何计算涉及 MallocHeaderSize / 2
  • 添加单元测试确保不同配置下的结果符合预期
  • 考虑将注释掉的函数版本转换为静态内联函数(带详细注释),作为备选文档

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🤖 Fix all issues with AI agents
In @.vscode/settings.json:
- Around line 23-27: The value in C_Cpp.codeAnalysis.clangTidy.args uses an
incorrect target triple; replace the string
"--extra-arg=--target=arm-none-eabi-gcc" with
"--extra-arg=--target=arm-none-eabi" so the target triple is correct (remove the
"-gcc" compiler suffix) in the array assigned to
"C_Cpp.codeAnalysis.clangTidy.args".

In @example/RyanJsonExample.c:
- Around line 132-139: The code may dereference a NULL when calling strcmp on
strValue returned from RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot,
"string")); guard against NULL by first checking the result of
RyanJsonGetObjectByKey and/or RyanJsonGetStringValue (e.g., if object == NULL or
strValue == NULL) before calling strcmp; on NULL, call RyanJsonDelete(jsonRoot)
and return RyanJsonFalse (and optionally print the same error message) to avoid
undefined behavior.
- Around line 200-216: The strcmp calls after
RyanJsonChangeKey/RyanJsonChangeStringValue are unsafe if RyanJsonGetKey or
RyanJsonGetStringValue return NULL; before calling strcmp, check the result of
RyanJsonGetObjectByKey(...), RyanJsonGetKey(...) and RyanJsonGetStringValue(...)
for NULL and handle the error path (printf, RyanJsonDelete(jsonRoot), return
RyanJsonFalse) if any are NULL, ensuring you only call strcmp when the pointer
is non-NULL; apply this NULL-check pattern around the checks that verify the key
was changed and the string value was changed.

In @test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c:
- Around line 156-161: RyanJsonPrint can return NULL and the current
RyanJsonCheckCode path jumps to err without freeing jsonRoot, causing a memory
leak; update the error handling so that when serializedStr is NULL you call
RyanJsonDelete(jsonRoot) before jumping to err (i.e., inside the
RyanJsonCheckCode block reference serializedStr and jsonRoot and invoke
RyanJsonDelete(jsonRoot) then goto err) to ensure jsonRoot is always released on
failure.

In @test/fuzzer/RyanJsonFuzzerCreate.c:
- Line 199: The code computes idx using "uint32_t idx = RyanJsonGetSize(pJson) %
size;" which can divide by zero if size == 0; update the logic around the
computation (in the function/block where idx is set) to check if size is zero
before using the modulus, handle the zero case (e.g., skip the operation, set
idx to a safe default, or return an error) and only perform the modulus when
size > 0 to avoid undefined behavior.
- Around line 303-304: 在 RyanJsonFuzzerTestDelete 中对 size 做取模时存在除零风险;在调用
RyanJsonGetSize(pJson) % size 和计算 (0 != size % 25) 前先判断 size 是否为 0,若为 0
则跳过删除或设定安全默认行为,确保在调用 RyanJsonDeleteByIndex(pJson, ...) 之前不会进行任何以 size
为除数的操作(引用符号:size、idx、RyanJsonGetSize、RyanJsonDeleteByIndex、RyanJsonFuzzerTestDelete)。
- Around line 254-255: There is a divide-by-zero risk because the code computes
RyanJsonGetSize(pJson) % size and size % 25 without checking size==0; fix
RyanJsonFuzzerTestDetach (and the snippet using idx/RyanJsonDetachByIndex) by
guarding against size==0: compute idx only when size>0 (e.g., idx = (size>0) ?
(RyanJsonGetSize(pJson) % size) : 0) and evaluate the size%25 condition only
when size>0 (e.g., use a bool like useIdx = (size>0 && (size % 25) != 0) and
pass useIdx ? idx : 0 to RyanJsonDetachByIndex) so no modulo by zero occurs.

In @test/fuzzer/RyanJsonFuzzerModify.c:
- Around line 100-104: In the RyanJsonObjectForEach loop the same index value is
passed every iteration to RyanJsonFuzzerVerifyGet; update the loop to increment
a child index for each child element (e.g., declare a local counter like
childIndex = index and increment it inside the loop, or use childIndex++ when
calling RyanJsonFuzzerVerifyGet) so each recursive call receives a unique
incremented index while leaving the original index semantics intact.
- Line 5: The call to RyanJsonIsNull(pJson) ignores its return value; either
convert it into an assertion by calling RyanJsonAssert(pJson) if you intend to
fail fast, or capture the boolean result (e.g., bool isNull =
RyanJsonIsNull(pJson)) and use it in a conditional to handle the null case
(return error, skip processing, or log). If the intent was a simple null check,
replace the call with an explicit check (if (pJson == NULL) { ... }) and handle
the error path accordingly; update callers of RyanJsonIsNull or the surrounding
logic to use the chosen approach.

In @test/fuzzer/RyanJsonFuzzerParse.c:
- Line 49: The allocation for buf using malloc in the test/fuzzer function is
not checked for NULL; update the code where char *buf = (char
*)malloc((size_t)bufLen); to verify buf != NULL immediately after allocation and
handle allocation failure (e.g., log/error and return or exit the test case,
freeing any prior resources as needed). Ensure subsequent uses of buf (including
any free calls) are guarded by this check so you never dereference a NULL
pointer.
- Around line 93-95: The code allocates buf with malloc(size + 100) but never
checks for NULL before calling memcpy and memset, risking a NULL dereference;
update the allocation site to check the return of malloc (buf) and handle
allocation failure (e.g., log/return from the current function or skip
processing) before calling memcpy(buf, data, size) and memset(buf + size, 0,
100), ensuring no further operations occur on a NULL pointer.

In @test/RFC8259Test/RyanJsonRFC8259JsonTest.c:
- Around line 225-227: Doc comment for the function yyjsonParseData incorrectly
says "cJson测试程序"; update the comment to "yyjson测试程序" (or equivalent accurate
wording) so the function's documentation matches the implementation and function
name yyjsonParseData; ensure any nearby references in the same doc block are
consistent with "yyjson" terminology.
- Line 251: The yyjsonParseData function erroneously calls cJSON_Minify
(copy-paste leftover); replace that call with the yyjson approach: after parsing
with yyjson_read, produce a minified output via yyjson_write (using the
compact/no-pretty flags, e.g., flags=0) or the appropriate yyjson write API,
remove the cJSON_Minify invocation, and ensure memory from
yyjson_read/yyjson_write is freed as other tests do; check symbols
yyjsonParseData, yyjson_read, yyjson_write and remove cJSON_Minify.

In @test/RyanJsonTest.c:
- Around line 69-78: v_realloc_tlsf currently memcpy/memmove using the new size
which can read past the old block and cause UB; replace the manual
allocate/copy/free with the TLSF-provided realloc by returning
tlsf_realloc(tlsfHandler, block, size) (uncomment the existing call) or, if
tlsf_realloc is unavailable, obtain the old block size via the TLSF API and
memmove only min(old_size, size) before v_free_tlsf; update v_realloc_tlsf to
use tlsf_realloc or the min-size copy to avoid buffer over-read (refer to
v_realloc_tlsf, v_malloc_tlsf, v_free_tlsf, memmove, tlsf_realloc).

In @xmake.lua:
- Around line 112-114: The build flags list contains a contradiction: both
"-Wdocumentation" and "-Wno-documentation" are present; remove one so the intent
is clear (keep "-Wdocumentation" to enable docs warnings or keep
"-Wno-documentation" to disable them) and ensure only the desired flag remains
in the flags array where these entries appear.
- Around line 143-150: The add_files calls are passing two separate tables so
the second {cxflags = "-w"} is ignored; update each add_files invocation (e.g.,
add_files('./test/*.c', ...), add_files('./test/baseTest/*.c', ...),
add_files('./test/baseTest/equality/*.c', ...),
add_files('./test/RFC8259Test/*.c', ...),
add_files('./test/externalModule/valloc/*.c', ...),
add_files('./test/externalModule/tlsf/*.c', ...),
add_files('./test/externalModule/cJSON/*.c', ...),
add_files('./test/externalModule/yyjson/*.c', ...)) to pass a single options
table that merges public = true and cxflags = "-w" (i.e., {public = true,
cxflags = "-w"}) so the compiler flags are actually applied.
🧹 Nitpick comments (22)
run_coverage.sh (1)

18-19: 并行配置看起来合理。

-workers 从 5 增加到 10 可以提高模糊测试的吞吐量。-workers=10-jobs=10 的配置是有效的。

建议添加注释说明这些值的选择依据(例如基于目标机器的 CPU 核心数),以便其他开发者理解和调整。

.vscode/settings.json (1)

38-46: 抑制规则较多,建议定期审查。

当前配置抑制了多个 cppcheck 警告类型(如 constParameterunreadVariableunusedStructMember 等)。虽然这可以减少噪音,但也可能掩盖潜在的代码质量问题。建议团队定期审查这些抑制规则,确保不会漏掉重要警告。

example/RyanJsonExample.c (2)

114-156: 建议增加节点查找的空值检查。

当前验证逻辑依赖于测试值非零(16, 16.89, RyanJsonTrue)。如果 RyanJsonGetObjectByKey 返回 NULL,getter 函数会返回默认值(0 或 RyanJsonFalse),这可能与实际值冲突。

建议先验证节点是否存在,再读取值,以提高代码健壮性。


245-252: 建议验证替换操作的结果。

RyanJsonReplaceByKey 的返回值未被检查。虽然后续的打印可以间接验证,但显式检查更符合前面代码的验证风格。

README.md (2)

29-29: Markdown格式问题:强调标记内有多余空格

**RyanJson设计时借鉴了 [json](...) 和 [cJSON](...) ! ** 结尾的 ! ** 应改为 !**,否则部分Markdown渲染器无法正确解析加粗样式。

📝 建议修复
-**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)!**

217-217: 文档可读性:建议添加空格分隔

日期和括号内容之间缺少空格,建议调整格式以提高可读性。

📝 建议修复
-(20251222 **malloc头部空间4字节,内存对齐4字节**)测试代码可在本项目根目录
+(20251222 **malloc头部空间4字节,内存对齐4字节**) 测试代码可在本项目根目录
-RT-Thread平台使用最小内存算法默认 **malloc头部空间12字节,内存对齐8字节**测试代码可在本项目根目录
+RT-Thread平台使用最小内存算法默认 **malloc头部空间12字节,内存对齐8字节** 测试代码可在本项目根目录

Also applies to: 254-254

RyanJson/RyanJsonConfig.h (2)

43-50: 冗余的默认值定义

这两个 #ifndef 块定义的默认值在当前代码流程中永远不会被触发:

  • #ifdef RT_VER_NUM 分支中,RyanJsonMallocAlign 已定义为 RT_ALIGN_SIZERyanJsonMallocHeaderSize 已定义为 12U
  • #else 分支中,它们分别定义为 4U8U

这些回退定义只有在用户手动 #undef 这些宏后才会生效。如果是刻意设计用于用户覆盖场景,建议添加注释说明;否则可以考虑移除。


64-69: 浮点数容差配置

RyanJsonAbsTolerance 的设计合理,1e-8 对一般场景足够。注释中说明"容差必须大于 0.0,同时小于 1.0",但缺少对应的编译时检查。

📝 可选:添加编译时校验
#if RyanJsonAbsTolerance <= 0.0 || RyanJsonAbsTolerance >= 1.0
#error "RyanJsonAbsTolerance 必须在 (0.0, 1.0) 范围内"
#endif

注意:预处理器对浮点数的支持有限,此检查在部分编译器上可能不工作。

test/fuzzer/RyanJsonFuzzerMemory.c (2)

13-13: 不必要的类型转换

v_mallocv_realloc 返回 void*,无需转换为 char* 再隐式转回 void*

📝 建议修复
-	return (char *)v_malloc(size);
+	return v_malloc(size);
-	return (char *)v_realloc(block, size);
+	return v_realloc(block, size);

Also applies to: 25-25


3-4: 计数器溢出风险(低优先级)

mallocCountreallocCountint32_t 类型,在超长模糊测试会话中可能溢出。考虑到模糊测试的实际运行时长,这通常不是问题,但如需更严谨可使用 uint64_t 或在达到阈值时重置。

test/RyanJsonTest.h (2)

48-48: 缺少 extern 关键字

platformUptimeMs 声明缺少 extern 关键字,与上方其他函数声明风格不一致(第 42-45 行都使用了 extern)。

📝 建议修复
-uint64_t platformUptimeMs(void);
+extern uint64_t platformUptimeMs(void);

50-66: 宏依赖外部变量

runTestWithLogAndTimer 宏依赖调用作用域中存在 testRunCountfuncStartMsresult 变量。建议在注释中说明这些前置条件,或考虑将宏改为内联函数/封装更完整的接口。

test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c (1)

139-144: 冗余的 NULL 检查。

第 144 行的 RyanJsonCheckReturnFalse(NULL != jsonRoot) 是冗余的,因为第 139-142 行已经检查了 jsonRoot 是否为 NULL,如果为 NULL 会跳转到 err

♻️ 建议删除冗余检查
 		RyanJsonCheckCode(NULL != jsonRoot, {
 			jsonLog("str: %s", jsondoubleStr);
 			goto err;
 		});
 
-		RyanJsonCheckReturnFalse(NULL != jsonRoot);
 		RyanJsonCheckReturnFalse(RyanJsonIsDouble(RyanJsonGetObjectToKey(jsonRoot, "double")));
test/RyanJsonTest.c (1)

18-24: platformUptimeMs 缺少 clock_gettime 返回值检查。

虽然 CLOCK_MONOTONIC 调用失败的情况极为罕见,但为了健壮性,建议检查返回值。

♻️ 建议添加错误检查
 uint64_t platformUptimeMs(void)
 {
 	struct timespec ts;
-	// CLOCK_MONOTONIC: 单调递增,不受系统时间修改影响,适合做耗时统计
-	clock_gettime(CLOCK_MONOTONIC, &ts);
+	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
+		return 0;
+	}
 	return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
 }
test/fuzzer/RyanJsonFuzzer.c (1)

49-56: 返回值处理不一致。

RyanJsonFuzzerTestMinifyRyanJsonFuzzerTestParse(第 49-50 行)的返回值被忽略,而 RyanJsonFuzzerTestModifyRyanJsonFuzzerTestCreateRyanJsonFuzzerTestReplace(第 54-56 行)的返回值会检查并跳转到 exit__

如果这是有意为之(某些测试失败不影响后续),建议添加注释说明;否则建议统一处理方式。

♻️ 建议统一返回值处理
-		RyanJsonFuzzerTestMinify(data, size);
-		RyanJsonFuzzerTestParse(pJson, data, size);
-		RyanJsonFuzzerTestGet(pJson, size);
+		(void)RyanJsonFuzzerTestMinify(data, size);  // 失败不中断后续测试
+		(void)RyanJsonFuzzerTestParse(pJson, data, size);
+		(void)RyanJsonFuzzerTestGet(pJson, size);

或者如果需要检查失败:

-		RyanJsonFuzzerTestMinify(data, size);
-		RyanJsonFuzzerTestParse(pJson, data, size);
-		RyanJsonFuzzerTestGet(pJson, size);
+		RyanJsonCheckCode(RyanJsonFuzzerTestMinify(data, size), { goto exit__; });
+		RyanJsonCheckCode(RyanJsonFuzzerTestParse(pJson, data, size), { goto exit__; });
+		RyanJsonCheckCode(RyanJsonFuzzerTestGet(pJson, size), { goto exit__; });
test/fuzzer/RyanJsonFuzzerModify.c (2)

9-17: 多次调用 strlenRyanJsonGetKey 效率较低。

同一个字符串多次调用 strlenRyanJsonGetKey,建议缓存结果。

♻️ 建议优化
 	if (RyanJsonIsKey(pJson))
 	{
-		char *key = (char *)malloc(strlen(RyanJsonGetKey(pJson)) + 1);
+		char *origKey = RyanJsonGetKey(pJson);
+		size_t keyLen = strlen(origKey);
+		char *key = (char *)malloc(keyLen + 1);
 		if (key)
 		{
-			memcpy(key, RyanJsonGetKey(pJson), strlen(RyanJsonGetKey(pJson)));
-			key[strlen(RyanJsonGetKey(pJson))] = 0;
+			memcpy(key, origKey, keyLen);
+			key[keyLen] = '\0';
 
 			RyanJsonChangeKey(pJson, "key");
 			RyanJsonChangeKey(pJson, key);
 			free(key);
 		}
 	}

39-49: 与 key 处理类似,strlen 重复调用。

建议同样缓存 RyanJsonGetStringValue(pJson) 和其长度。

♻️ 建议优化
 	if (RyanJsonIsString(pJson))
 	{
-		char *value = (char *)malloc(strlen(RyanJsonGetStringValue(pJson)) + 1);
+		char *origValue = RyanJsonGetStringValue(pJson);
+		size_t valueLen = strlen(origValue);
+		char *value = (char *)malloc(valueLen + 1);
 		if (value)
 		{
-			memcpy(value, RyanJsonGetStringValue(pJson), strlen(RyanJsonGetStringValue(pJson)));
-			value[strlen(RyanJsonGetStringValue(pJson))] = 0;
+			memcpy(value, origValue, valueLen);
+			value[valueLen] = '\0';
 
 			RyanJsonChangeStringValue(pJson, "hello world");
 			RyanJsonChangeStringValue(pJson, "h");
 			RyanJsonChangeStringValue(pJson, value);
 
 			free(value);
 		}
 	}
test/RFC8259Test/RyanJsonRFC8259JsonTest.c (2)

170-171: 静态变量命名不规范。

alksdjfCOunt 看起来像是调试时的临时命名,建议使用更有意义的名称如 semanticErrorCount。另外,静态变量在多次测试运行时会累积,可能导致统计不准确。

-	static uint32_t alksdjfCOunt = 0;
-	checkJsonSemanticEquality(data, len, str, strLen, &alksdjfCOunt);
+	static uint32_t semanticErrorCount = 0;
+	checkJsonSemanticEquality(data, len, str, strLen, &semanticErrorCount);

200-200: sizeof("") 用法不清晰。

sizeof("") 返回 1(空字符串只包含 null 终止符)。这里 len + sizeof("") 等同于 len + 1,但后者更清晰。

-	cJSON *json = cJSON_ParseWithLengthOpts(data, len + sizeof(""), NULL, RyanJsonTrue);
+	cJSON *json = cJSON_ParseWithLengthOpts(data, len + 1, NULL, RyanJsonTrue);
test/fuzzer/RyanJsonFuzzerCreate.c (1)

14-20: 使用编译器属性标记 fallthrough。

虽然有注释说明 fallthrough,但建议使用编译器属性以消除警告并明确意图。

 	case 2:
 		count2++;
 		if (0 == count2 % 10) { return RyanJsonDuplicate(pJson); }
-		// fallthrough
+		__attribute__((fallthrough));
 	case 11:
RyanJson/RyanJson.c (2)

640-641: 返回值类型不一致。

RyanJsonGetObjectValue 返回指针类型,但失败时返回 0 而非 NULL。虽然在 C 中 0 可以隐式转换为 NULL,但为了代码清晰性和一致性,建议使用 NULL

 RyanJson_t RyanJsonGetObjectValue(RyanJson_t pJson)
 {
-	RyanJsonCheckCodeNoReturn(NULL != pJson, { return 0; });
+	RyanJsonCheckCodeNoReturn(NULL != pJson, { return NULL; });
 
 	RyanJson_t objValue;
 	RyanJsonMemcpy((void *)&objValue, RyanJsonGetValue(pJson), sizeof(void *));
 	return objValue;
 }

421-428: 内联字符串存储逻辑复杂,建议添加注释。

这段代码决定字符串是内联存储还是使用指针存储,逻辑较为复杂。建议添加注释说明阈值计算的原理。

 	char arr[RyanJsonGetInlineStringSize] = {0};
-	// keyLenField(0-3) + 1 为key的长度
+	// 判断是否可以内联存储:
+	// mallocSize = keyLen + 1 (if key) + strValueLen + 1 (if strValue)
+	// keyLenField = 编码 keyLen 所需的字节数 (0-3)
+	// 如果总大小 <= 内联存储空间,则使用内联存储
 	if ((mallocSize + keyLenField + 1) <= RyanJsonGetInlineStringSize)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a68fc1 and cbda13f.

📒 Files selected for processing (21)
  • .gitignore
  • .vscode/settings.json
  • README.md
  • RyanJson/RyanJson.c
  • RyanJson/RyanJsonConfig.h
  • example/RyanJsonExample.c
  • run_base_coverage.sh
  • run_coverage.sh
  • test/RFC8259Test/RyanJsonRFC8259JsonTest.c
  • test/RyanJsonTest.c
  • test/RyanJsonTest.h
  • test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c
  • test/fuzzer/RyanJsonFuzzer.c
  • test/fuzzer/RyanJsonFuzzer.dict
  • test/fuzzer/RyanJsonFuzzer.h
  • test/fuzzer/RyanJsonFuzzerCreate.c
  • test/fuzzer/RyanJsonFuzzerDup.c
  • test/fuzzer/RyanJsonFuzzerMemory.c
  • test/fuzzer/RyanJsonFuzzerModify.c
  • test/fuzzer/RyanJsonFuzzerParse.c
  • xmake.lua
💤 Files with no reviewable changes (1)
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (1)
  • run_base_coverage.sh
🧰 Additional context used
🧬 Code graph analysis (9)
test/fuzzer/RyanJsonFuzzerParse.c (2)
RyanJson/RyanJson.c (8)
  • RyanJsonPrint (1739-1767)
  • RyanJsonCompare (2421-2484)
  • RyanJsonInsert (1992-2029)
  • RyanJsonCreateString (2219-2225)
  • RyanJsonDelete (654-674)
  • RyanJsonPrintPreallocated (1779-1797)
  • RyanJsonMinify (2362-2412)
  • RyanJsonParseOptions (1266-1285)
RyanJson/RyanJsonUtils.c (1)
  • RyanJsonCompareOnlyKey (117-161)
test/RyanJsonTest.h (1)
test/RyanJsonTest.c (6)
  • v_malloc_tlsf (58-62)
  • v_free_tlsf (64-67)
  • v_realloc_tlsf (69-78)
  • vallocGetUseByTlsf (50-55)
  • platformUptimeMs (18-24)
  • RyanJsonTestFun (80-155)
test/fuzzer/RyanJsonFuzzerDup.c (2)
RyanJson/RyanJson.c (10)
  • RyanJsonPrint (1739-1767)
  • RyanJsonDuplicate (2283-2353)
  • RyanJsonGetSize (1805-1820)
  • RyanJsonCompare (2421-2484)
  • RyanJsonIsArray (2263-2263)
  • RyanJsonIsObject (2264-2267)
  • RyanJsonDelete (654-674)
  • RyanJsonDetachByIndex (1882-1910)
  • RyanJsonIsKey (2252-2252)
  • RyanJsonChangeKey (2126-2131)
RyanJson/RyanJsonUtils.c (1)
  • RyanJsonCompareOnlyKey (117-161)
test/fuzzer/RyanJsonFuzzerModify.c (2)
RyanJson/RyanJson.c (16)
  • RyanJsonIsKey (2252-2252)
  • RyanJsonGetKey (586-597)
  • RyanJsonChangeKey (2126-2131)
  • RyanJsonIsBool (2254-2254)
  • RyanJsonIsNumber (2255-2258)
  • RyanJsonIsInt (2268-2271)
  • RyanJsonGetIntValue (620-627)
  • RyanJsonIsDouble (2272-2275)
  • RyanJsonIsString (2259-2262)
  • RyanJsonGetStringValue (599-618)
  • RyanJsonChangeStringValue (2133-2138)
  • RyanJsonCompareDouble (306-318)
  • RyanJsonGetObjectValue (638-645)
  • RyanJsonGetArrayValue (647-647)
  • RyanJsonGetObjectByKey (1855-1873)
  • RyanJsonGetObjectByIndex (1829-1846)
RyanJson/RyanJsonUtils.c (1)
  • RyanJsonGetObjectByKeys (11-31)
example/RyanJsonExample.c (1)
RyanJson/RyanJson.c (15)
  • RyanJsonGetIntValue (620-627)
  • RyanJsonGetObjectByKey (1855-1873)
  • RyanJsonDelete (654-674)
  • RyanJsonGetDoubleValue (629-636)
  • RyanJsonCompareDouble (306-318)
  • RyanJsonGetStringValue (599-618)
  • RyanJsonIsNull (2253-2253)
  • RyanJsonPrint (1739-1767)
  • RyanJsonFree (681-681)
  • RyanJsonChangeKey (2126-2131)
  • RyanJsonGetKey (586-597)
  • RyanJsonChangeStringValue (2133-2138)
  • RyanJsonChangeIntValue (2140-2145)
  • RyanJsonChangeDoubleValue (2147-2152)
  • RyanJsonInitHooks (576-584)
test/RFC8259Test/RyanJsonRFC8259JsonTest.c (3)
test/RyanJsonTest.c (3)
  • vallocGetUseByTlsf (50-55)
  • v_malloc_tlsf (58-62)
  • v_free_tlsf (64-67)
test/externalModule/valloc/valloc.c (2)
  • v_mcheck (78-83)
  • displayMem (99-104)
RyanJson/RyanJson.c (4)
  • RyanJsonParseOptions (1266-1285)
  • RyanJsonPrint (1739-1767)
  • RyanJsonMinify (2362-2412)
  • RyanJsonDelete (654-674)
test/RyanJsonTest.c (3)
test/baseTest/RyanJsonBaseTestUtile.c (1)
  • printJsonDebug (6-11)
test/externalModule/tlsf/tlsf.c (2)
  • tlsf_alloc_overhead (266-266)
  • rt_memory_info22 (582-588)
test/externalModule/valloc/valloc.c (3)
  • v_malloc (14-27)
  • v_free (37-48)
  • displayMem (99-104)
test/fuzzer/RyanJsonFuzzerMemory.c (1)
test/externalModule/valloc/valloc.c (3)
  • v_malloc (14-27)
  • v_free (37-48)
  • v_realloc (50-76)
test/fuzzer/RyanJsonFuzzer.h (5)
test/fuzzer/RyanJsonFuzzerMemory.c (3)
  • RyanJsonFuzzerMalloc (6-14)
  • RyanJsonFuzzerFree (16-16)
  • RyanJsonFuzzerRealloc (18-26)
test/fuzzer/RyanJsonFuzzerParse.c (2)
  • RyanJsonFuzzerTestParse (3-89)
  • RyanJsonFuzzerTestMinify (91-126)
test/fuzzer/RyanJsonFuzzerDup.c (1)
  • RyanJsonFuzzerTestDuplicate (3-115)
test/fuzzer/RyanJsonFuzzerModify.c (1)
  • RyanJsonFuzzerTestModify (3-63)
test/fuzzer/RyanJsonFuzzerCreate.c (5)
  • RyanJsonFuzzerCreateRandomNode (3-30)
  • RyanJsonFuzzerTestCreate (32-127)
  • RyanJsonFuzzerTestReplace (129-209)
  • RyanJsonFuzzerTestDetach (211-260)
  • RyanJsonFuzzerTestDelete (262-307)
🪛 Clang (14.0.6)
test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c

[error] 1-1: 'RyanJsonBaseTest.h' file not found

(clang-diagnostic-error)

🪛 Cppcheck (2.19.0)
test/fuzzer/RyanJsonFuzzerParse.c

[warning] 94-94: If memory allocation fails, then there is a possible null pointer dereference

(nullPointerOutOfMemory)


[error] 95-95: If memory allocation fails

(nullPointerArithmeticOutOfMemory)

🪛 LanguageTool
README.md

[uncategorized] ~217-~217: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...参考) #### 内存占用测试 (20251222 malloc头部空间4字节,内存对齐4字节)测试代码可在本项目根目录RyanJsonExample...

(wa5)


[uncategorized] ~217-~217: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...## 内存占用测试 (20251222 malloc头部空间4字节,内存对齐4字节)测试代码可在本项目根目录RyanJsonExample文件夹查看。 ...

(wa5)


[uncategorized] ~254-~254: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ... ``` RT-Thread平台使用最小内存算法默认 malloc头部空间12字节,内存对齐8字节测试代码可在本项目根目录RyanJsonExample文...

(wa5)


[uncategorized] ~254-~254: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...Thread平台使用最小内存算法默认 malloc头部空间12字节,内存对齐8字节测试代码可在本项目根目录RyanJsonExample文件夹查看 ``...

(wa5)

🪛 markdownlint-cli2 (0.18.1)
README.md

29-29: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)


47-47: Hard tabs
Column: 1

(MD010, no-hard-tabs)


48-48: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)


50-50: Hard tabs
Column: 1

(MD010, no-hard-tabs)


51-51: Hard tabs
Column: 1

(MD010, no-hard-tabs)


52-52: Hard tabs
Column: 1

(MD010, no-hard-tabs)


53-53: Hard tabs
Column: 1

(MD010, no-hard-tabs)


54-54: Hard tabs
Column: 1

(MD010, no-hard-tabs)


55-55: Hard tabs
Column: 1

(MD010, no-hard-tabs)


56-56: Hard tabs
Column: 1

(MD010, no-hard-tabs)


57-57: Hard tabs
Column: 1

(MD010, no-hard-tabs)


58-58: Hard tabs
Column: 1

(MD010, no-hard-tabs)


59-59: Hard tabs
Column: 1

(MD010, no-hard-tabs)


60-60: Hard tabs
Column: 1

(MD010, no-hard-tabs)


69-69: Hard tabs
Column: 1

(MD010, no-hard-tabs)


70-70: Hard tabs
Column: 1

(MD010, no-hard-tabs)


71-71: Hard tabs
Column: 1

(MD010, no-hard-tabs)


72-72: Hard tabs
Column: 1

(MD010, no-hard-tabs)


73-73: Hard tabs
Column: 1

(MD010, no-hard-tabs)


74-74: Hard tabs
Column: 1

(MD010, no-hard-tabs)


75-75: Hard tabs
Column: 1

(MD010, no-hard-tabs)


76-76: Hard tabs
Column: 1

(MD010, no-hard-tabs)


77-77: Hard tabs
Column: 1

(MD010, no-hard-tabs)


78-78: Hard tabs
Column: 1

(MD010, no-hard-tabs)


79-79: Hard tabs
Column: 1

(MD010, no-hard-tabs)


80-80: Hard tabs
Column: 1

(MD010, no-hard-tabs)


81-81: Hard tabs
Column: 1

(MD010, no-hard-tabs)


82-82: Hard tabs
Column: 1

(MD010, no-hard-tabs)


83-83: Hard tabs
Column: 1

(MD010, no-hard-tabs)


84-84: Hard tabs
Column: 1

(MD010, no-hard-tabs)


114-114: Hard tabs
Column: 1

(MD010, no-hard-tabs)


115-115: Hard tabs
Column: 1

(MD010, no-hard-tabs)


116-116: Hard tabs
Column: 1

(MD010, no-hard-tabs)


131-131: Hard tabs
Column: 1

(MD010, no-hard-tabs)


151-151: Hard tabs
Column: 1

(MD010, no-hard-tabs)


219-219: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


256-256: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


295-295: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


299-299: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (21)
run_coverage.sh (2)

4-6: LGTM!

在运行 fuzzer 之前添加构建步骤是个好做法,确保二进制文件是最新的。由于脚本开头有 set -e,如果 xmake 构建失败,脚本会立即退出。


48-48: LGTM!

添加 -show-branches=count 可以在 HTML 报告中显示分支覆盖率计数,有助于识别未覆盖的代码分支,提升覆盖率分析的精细度。

.vscode/settings.json (2)

2-12: LGTM!

禁用 Lua 工具并启用 clangd 的配置变更与 PR 中向 clangd 工作流转型的目标一致。


47-48: LGTM!

Makefile 和 liveServer 端口配置合理。

example/RyanJsonExample.c (2)

7-9: 函数签名改进良好。

使用 RyanJsonBool_e 枚举类型替代魔术数字返回值,提升了代码可读性和类型安全性。


260-275: 钩子初始化和示例调用模式良好。

使用 RyanJsonCheckReturnFalse 宏统一处理返回值检查,代码风格一致。传递 NULL 作为 realloc 参数符合库的设计(基于 RyanJsonInitHooks 的实现)。

README.md (1)

1-372: 文档更新质量良好!

文档详细描述了内存布局设计、inline vs pointer存储策略、测试覆盖率等关键信息,对开发者理解库的内部机制很有帮助。整体结构清晰,技术细节完整。

RyanJson/RyanJsonConfig.h (1)

106-116: 编译时校验逻辑良好

这些静态断言确保了宏配置的合法性,能够在编译期捕获错误配置,是良好的防御性编程实践。

test/fuzzer/RyanJsonFuzzerMemory.c (1)

6-26: 内存故障注入实现合理

通过周期性返回 NULL 模拟 OOM 场景,有效测试库的错误处理路径。与 isEnableRandomMemFail 标志配合使用,便于控制测试行为。

test/RyanJsonTest.h (1)

42-45: TLSF 内存接口声明

新增的 TLSF 内存管理接口声明与 test/RyanJsonTest.c 中的实现匹配,为测试提供了可控的内存分配环境。

test/fuzzer/RyanJsonFuzzerDup.c (2)

26-29: 比较结果未检查(已知限制)

RyanJsonCompareRyanJsonCompareOnlyKey 的返回值被忽略。注释表明这是有意为之(大浮点数和重复 key 会导致比较失败),但建议至少调用这些函数以确保不会崩溃。

当前实现符合模糊测试"执行不崩溃"的目标。

Also applies to: 93-94


96-112: 资源清理逻辑正确

exit__ 标签下的清理代码正确处理了所有分配的资源,包括 jsonStrpJsonDupjsonStrDup,符合 goto-cleanup 模式的最佳实践。

test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c (1)

3-117: 测试用例覆盖全面,设计合理。

使用 X-Macro 模式生成双精度浮点数测试值表和对应的 JSON 字符串表,覆盖了零值、边界值、科学计数法、数学常量、精度测试、极端值等多种场景,有助于全面验证双精度浮点数的解析和序列化正确性。

test/fuzzer/RyanJsonFuzzer.dict (1)

1-312: 模糊测试字典设计全面且组织良好。

字典涵盖了 JSON 模糊测试的关键场景:基本关键字、结构符号、有效/无效语法、科学计数法边界、Unicode 转义、控制字符、非法字节序列等。分类清晰,有助于发现解析器中的边缘情况和潜在漏洞。

test/RyanJsonTest.c (1)

80-155: 测试入口函数结构清晰,TLSF 内存管理集成合理。

RyanJsonTestFun 正确初始化 TLSF 内存池,在示例测试后重新设置 hooks,并在测试完成后显示内存使用情况和清理资源。

test/fuzzer/RyanJsonFuzzerParse.c (1)

3-44: API 边界测试设计合理。

测试函数覆盖了多种边界情况:无效类型、NULL 参数、缺少 key 的对象插入等,有助于验证 API 的健壮性。

test/fuzzer/RyanJsonFuzzer.c (1)

11-16: Hook 初始化测试覆盖全面。

测试了各种 NULL 参数组合来验证 RyanJsonInitHooks 的健壮性,这是良好的防御性测试实践。

test/fuzzer/RyanJsonFuzzer.h (1)

1-63: 头文件结构良好。

包含守卫、C++ 兼容性声明、清晰的函数分组注释。整体结构符合最佳实践。

唯一的小问题:signal.h 的包含在此头文件中似乎未被使用,如果确实不需要可以移除。

RyanJson/RyanJson.c (3)

1376-1381: 特殊浮点值处理符合 RFC 8259。

正确地将 InfinityNaN 输出为 null,符合 JSON 规范。


306-318: 浮点数比较实现合理。

使用相对误差和绝对误差结合的方式比较浮点数,是处理浮点精度问题的标准做法。


576-584: 内存钩子初始化函数设计良好。

允许用户自定义内存分配函数,realloc 可选(为 NULL 时有回退逻辑),增加了库的灵活性。

Comment on lines +23 to +27
"C_Cpp.codeAnalysis.clangTidy.args": [
// "--extra-arg=-ferror-limit=1"
// "--extra-arg=-m32",
"--extra-arg=--target=arm-none-eabi-gcc"
],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

目标三元组格式可能不正确。

--target=arm-none-eabi-gcc 中的 -gcc 后缀是编译器可执行文件名的一部分,不是目标架构的组成部分。正确的目标三元组应该是 arm-none-eabi

🔧 建议修复
     "C_Cpp.codeAnalysis.clangTidy.args": [
         // "--extra-arg=-ferror-limit=1"
         // "--extra-arg=-m32",
-        "--extra-arg=--target=arm-none-eabi-gcc"
+        "--extra-arg=--target=arm-none-eabi"
     ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"C_Cpp.codeAnalysis.clangTidy.args": [
// "--extra-arg=-ferror-limit=1"
// "--extra-arg=-m32",
"--extra-arg=--target=arm-none-eabi-gcc"
],
"C_Cpp.codeAnalysis.clangTidy.args": [
// "--extra-arg=-ferror-limit=1"
// "--extra-arg=-m32",
"--extra-arg=--target=arm-none-eabi"
],
🤖 Prompt for AI Agents
In @.vscode/settings.json around lines 23 - 27, The value in
C_Cpp.codeAnalysis.clangTidy.args uses an incorrect target triple; replace the
string "--extra-arg=--target=arm-none-eabi-gcc" with
"--extra-arg=--target=arm-none-eabi" so the target triple is correct (remove the
"-gcc" compiler suffix) in the array assigned to
"C_Cpp.codeAnalysis.clangTidy.args".

Comment on lines +132 to +139
// 读取 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;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

潜在的空指针解引用问题。

如果 RyanJsonGetObjectByKey 返回 NULL,则 RyanJsonGetStringValue 也会返回 NULL。将 NULL 传递给 strcmp 会导致未定义行为(崩溃)。

🐛 建议的修复方案
 	// 读取 string 数据
-	const char *strValue = RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot, "string"));
-	if (0 != strcmp(strValue, "hello"))
+	RyanJson_t stringNode = RyanJsonGetObjectByKey(jsonRoot, "string");
+	const char *strValue = RyanJsonGetStringValue(stringNode);
+	if (NULL == strValue || 0 != strcmp(strValue, "hello"))
 	{
 		printf("%s:%d 读取string失败\r\n", __FILE__, __LINE__);
 		RyanJsonDelete(jsonRoot);
 		return RyanJsonFalse;
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 读取 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;
}
// 读取 string 数据
RyanJson_t stringNode = RyanJsonGetObjectByKey(jsonRoot, "string");
const char *strValue = RyanJsonGetStringValue(stringNode);
if (NULL == strValue || 0 != strcmp(strValue, "hello"))
{
printf("%s:%d 读取string失败\r\n", __FILE__, __LINE__);
RyanJsonDelete(jsonRoot);
return RyanJsonFalse;
}
🤖 Prompt for AI Agents
In @example/RyanJsonExample.c around lines 132 - 139, The code may dereference a
NULL when calling strcmp on strValue returned from
RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot, "string")); guard
against NULL by first checking the result of RyanJsonGetObjectByKey and/or
RyanJsonGetStringValue (e.g., if object == NULL or strValue == NULL) before
calling strcmp; on NULL, call RyanJsonDelete(jsonRoot) and return RyanJsonFalse
(and optionally print the same error message) to avoid undefined behavior.

Comment on lines +200 to 216
// 修改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;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

与上文相同的空指针解引用风险。

第 202 行和第 211 行的 strcmp 调用存在相同的问题:如果 RyanJsonGetKeyRyanJsonGetStringValue 返回 NULL,将导致程序崩溃。

🐛 建议的修复方案
 	// 修改key
 	RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "name"), "name2");
-	if (0 != strcmp("name2", RyanJsonGetKey(RyanJsonGetObjectByKey(jsonRoot, "name2"))))
+	RyanJson_t name2Node = RyanJsonGetObjectByKey(jsonRoot, "name2");
+	const char *keyValue = name2Node ? RyanJsonGetKey(name2Node) : NULL;
+	if (NULL == keyValue || 0 != strcmp("name2", keyValue))
 	{
 		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"))))
+	const char *strVal = RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot, "name2"));
+	if (NULL == strVal || 0 != strcmp("Ryan", strVal))
 	{
 		printf("%s:%d 修改失败\r\n", __FILE__, __LINE__);
 		RyanJsonDelete(jsonRoot);
 		return RyanJsonFalse;
 	}
🤖 Prompt for AI Agents
In @example/RyanJsonExample.c around lines 200 - 216, The strcmp calls after
RyanJsonChangeKey/RyanJsonChangeStringValue are unsafe if RyanJsonGetKey or
RyanJsonGetStringValue return NULL; before calling strcmp, check the result of
RyanJsonGetObjectByKey(...), RyanJsonGetKey(...) and RyanJsonGetStringValue(...)
for NULL and handle the error path (printf, RyanJsonDelete(jsonRoot), return
RyanJsonFalse) if any are NULL, ensuring you only call strcmp when the pointer
is non-NULL; apply this NULL-check pattern around the checks that verify the key
was changed and the string value was changed.

Comment on lines +156 to +161
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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

RyanJsonPrint 失败时 jsonRoot 未释放,存在内存泄漏。

如果 RyanJsonPrint 返回 NULL,代码会跳转到 err 标签,但此时 jsonRoot 尚未被删除。

🐛 建议修复
 		char *serializedStr = RyanJsonPrint(jsonRoot, 128, RyanJsonFalse, NULL);
 		RyanJsonCheckCode(NULL != serializedStr, {
 			printf("serializedStr: %s, doubleString: %s, doubleValue: %g\r\n", serializedStr, jsondoubleStr, doubleValue);
+			RyanJsonDelete(jsonRoot);
 			goto err;
 		});
 		RyanJsonDelete(jsonRoot);
🤖 Prompt for AI Agents
In @test/baseTest/equality/RyanJsonBaseTestEqualityDouble.c around lines 156 -
161, RyanJsonPrint can return NULL and the current RyanJsonCheckCode path jumps
to err without freeing jsonRoot, causing a memory leak; update the error
handling so that when serializedStr is NULL you call RyanJsonDelete(jsonRoot)
before jumping to err (i.e., inside the RyanJsonCheckCode block reference
serializedStr and jsonRoot and invoke RyanJsonDelete(jsonRoot) then goto err) to
ensure jsonRoot is always released on failure.


// 按 index 替换
{
uint32_t idx = RyanJsonGetSize(pJson) % size;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

潜在的除零错误。

如果 size 为 0,RyanJsonGetSize(pJson) % size 会导致未定义行为(除零)。建议添加检查。

+	if (0 == size) { return RyanJsonTrue; }
 	// 按 index 替换
 	{
 		uint32_t idx = RyanJsonGetSize(pJson) % size;
🤖 Prompt for AI Agents
In @test/fuzzer/RyanJsonFuzzerCreate.c at line 199, The code computes idx using
"uint32_t idx = RyanJsonGetSize(pJson) % size;" which can divide by zero if size
== 0; update the logic around the computation (in the function/block where idx
is set) to check if size is zero before using the modulus, handle the zero case
(e.g., skip the operation, set idx to a safe default, or return an error) and
only perform the modulus when size > 0 to avoid undefined behavior.

Comment on lines +225 to +227
/**
* @brief cJson测试程序
*
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

文档注释错误。

函数 yyjsonParseData 的文档注释写的是 "cJson测试程序",应该是 "yyjson测试程序"。

 /**
- * @brief cJson测试程序
+ * @brief yyjson测试程序
  *
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* @brief cJson测试程序
*
/**
* @brief yyjson测试程序
*
🤖 Prompt for AI Agents
In @test/RFC8259Test/RyanJsonRFC8259JsonTest.c around lines 225 - 227, Doc
comment for the function yyjsonParseData incorrectly says "cJson测试程序"; update
the comment to "yyjson测试程序" (or equivalent accurate wording) so the function's
documentation matches the implementation and function name yyjsonParseData;
ensure any nearby references in the same doc block are consistent with "yyjson"
terminology.

goto err;
}

cJSON_Minify(data);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 检查 yyjson 是否有 minify 相关函数
rg -n "minify" --type=c -i | head -30

Repository: Ryan-CW-Code/RyanJson

Length of output: 2932


🏁 Script executed:

#!/bin/bash
# 查看测试文件的上下文和导入
cat -n test/RFC8259Test/RyanJsonRFC8259JsonTest.c | head -20

Repository: Ryan-CW-Code/RyanJson

Length of output: 725


🏁 Script executed:

#!/bin/bash
# 检查第 251 行附近的代码
cat -n test/RFC8259Test/RyanJsonRFC8259JsonTest.c | sed -n '240,260p'

Repository: Ryan-CW-Code/RyanJson

Length of output: 641


🏁 Script executed:

#!/bin/bash
# 搜索 cJSON_Minify 的定义和使用
rg -n "cJSON_Minify" --type=c -B 2 -A 2

Repository: Ryan-CW-Code/RyanJson

Length of output: 1551


🏁 Script executed:

#!/bin/bash
# 查看更大范围的上下文,找到函数定义
cat -n test/RFC8259Test/RyanJsonRFC8259JsonTest.c | sed -n '160,260p'

Repository: Ryan-CW-Code/RyanJson

Length of output: 2887


🏁 Script executed:

#!/bin/bash
# 查看文件中所有函数定义的名称
grep -n "^[a-zA-Z_].*(" test/RFC8259Test/RyanJsonRFC8259JsonTest.c | head -20

Repository: Ryan-CW-Code/RyanJson

Length of output: 962


🏁 Script executed:

#!/bin/bash
# 查找是否有专门的yyjson或cJSON测试函数
grep -n "yyjson\|cJSON" test/RFC8259Test/RyanJsonRFC8259JsonTest.c | grep -E "^\s*[a-zA-Z_].*\(" | head -10

Repository: Ryan-CW-Code/RyanJson

Length of output: 47


yyjsonParseData 函数错误地使用了 cJSON_Minify 而非 yyjson 的对应函数。

这确实是复制粘贴错误。在 yyjsonParseData 函数(第 233 行)中,虽然使用了 yyjson 库的 yyjson_readyyjson_write,但第 251 行却调用了 cJSON_Minify。这与其他测试函数的模式不一致:

  • RyanJsonParseData 使用 RyanJsonMinify(第 169 行)
  • cJSONParseData 使用 cJSON_Minify(第 211 行)
  • yyjsonParseData 应当使用与 yyjson 相应的方法,而非 cJSON_Minify

需要使用 yyjson 适当的方式处理 minify,例如通过 yyjson_write 的标志参数或其他 yyjson 提供的机制。

🤖 Prompt for AI Agents
In @test/RFC8259Test/RyanJsonRFC8259JsonTest.c at line 251, The yyjsonParseData
function erroneously calls cJSON_Minify (copy-paste leftover); replace that call
with the yyjson approach: after parsing with yyjson_read, produce a minified
output via yyjson_write (using the compact/no-pretty flags, e.g., flags=0) or
the appropriate yyjson write API, remove the cJSON_Minify invocation, and ensure
memory from yyjson_read/yyjson_write is freed as other tests do; check symbols
yyjsonParseData, yyjson_read, yyjson_write and remove cJSON_Minify.

Comment on lines +69 to +78
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);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

v_realloc_tlsf 存在缓冲区越界读取风险。

memmove(newBlock, block, size) 使用新分配的大小 size 作为复制长度,但旧块的实际大小未知。如果 size 大于旧块的大小,将导致读取旧块边界之外的内存(缓冲区越界读取)。

建议使用 tlsf_realloc(已注释)或者在分配时追踪块大小。

🐛 建议修复:使用 tlsf_realloc
 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);
+	if (size == 0) {
+		v_free_tlsf(block);
+		return NULL;
+	}
+	return tlsf_realloc(tlsfHandler, block, RyanJsonAlign(size + RyanJsonMallocHeaderSize - 4, RyanJsonMallocAlign));
 }
🤖 Prompt for AI Agents
In @test/RyanJsonTest.c around lines 69 - 78, v_realloc_tlsf currently
memcpy/memmove using the new size which can read past the old block and cause
UB; replace the manual allocate/copy/free with the TLSF-provided realloc by
returning tlsf_realloc(tlsfHandler, block, size) (uncomment the existing call)
or, if tlsf_realloc is unavailable, obtain the old block size via the TLSF API
and memmove only min(old_size, size) before v_free_tlsf; update v_realloc_tlsf
to use tlsf_realloc or the min-size copy to avoid buffer over-read (refer to
v_realloc_tlsf, v_malloc_tlsf, v_free_tlsf, memmove, tlsf_realloc).

Comment on lines +112 to +114
"-Wdocumentation",
"-Wparentheses-equality",
"-Wno-documentation", -- 临时关闭文档警告
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

矛盾的警告设置。

同时启用 -Wdocumentation-Wno-documentation 是矛盾的。后者会覆盖前者,使 -Wdocumentation 无效。

-        "-Wdocumentation",
-        "-Wparentheses-equality",
-        "-Wno-documentation",      -- 临时关闭文档警告
+        "-Wparentheses-equality",
+        -- "-Wdocumentation",      -- 文档警告(已关闭)
+        "-Wno-documentation",      -- 临时关闭文档警告
🤖 Prompt for AI Agents
In @xmake.lua around lines 112 - 114, The build flags list contains a
contradiction: both "-Wdocumentation" and "-Wno-documentation" are present;
remove one so the intent is clear (keep "-Wdocumentation" to enable docs
warnings or keep "-Wno-documentation" to disable them) and ensure only the
desired flag remains in the flags array where these entries appear.

Comment on lines +143 to +150
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,关闭警告
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

xmake add_files function syntax options table documentation

💡 Result:

Summary of add_files(...) syntax and option table (from Xmake API):

Prototype

  • add_files(files: <string|array>, ..., { defines = <string|array>, languages = <string|array>, includedirs = <string|array>, rules = <string|array>, force = {$flags}, sourcekind = , $flags = <string|array> }). [1]

Option meanings

  • files: file path string or array; supports wildcards (e.g. *, **). [1][2]
  • defines: per-file macro definitions (string or array). [1]
  • languages: language standard for those files (e.g. "c99", "c++11"). [1]
  • includedirs: header-search directories for those files (string or array). [1]
  • rules: custom rule names to apply to those files (string or array). [1][4]
  • force: an object to force compilation options / disable automatic detection (use to pass flags directly). [1]
  • sourcekind: force file source kind (e.g. "cc", "cxx"). [1]
  • $flags (generic flag entry): accept strings or arrays to set specific compile flags for these files; can be used via named flag keys depending on context (see API). [1]

Usage notes

  • add_files accepts multiple file arguments and an optional final options table. Wildcards and recursive ** are supported. [1][2]
  • To attach custom build behavior, define rules (rule(...)) and reference them via rules in add_files or add_rules on the target. [4]

References

  • Xmake API — add_files (Project Targets). [1]
  • Xmake guide — syntax / add_files examples. [2]
  • Xmake quickstart (add_files examples). [5]
  • Xmake guide — custom rules (applies via add_files). [4]

🌐 Web query:

xmake add_files multiple option tables variadic arguments correct syntax

💡 Result:

Use add_files like other xmake add_* APIs: pass one or more file strings/wildcard patterns (variadic), and then an optional options table that applies to the immediately preceding file argument(s).

Syntax (conceptual):

  • add_files(files: <string|array>, ..., { defines=..., languages=..., includedirs=..., rules=..., force={...}, sourcekind=..., ... })

Examples:

  • add_files("src/*.c")
  • add_files("src/foo.c", "src/bar.c")
  • add_files("src/*.c", "src/ignore.c|test.c")
  • add_files("src/special.c", {defines = "FOO", sourcekind = "cxx"})
  • add_files("a.c", "b.c", {force = {cxflags = "-O2"}})

Reference: xmake add_files API and syntax docs. [1][2]

Sources
[1] Xmake API — add_files (project-target).
[2] Xmake guide — syntax description / examples.


🌐 Web query:

xmake add_files behavior multiple tables arguments what happens second table

💡 Result:

Short answer: add_files appends/merges file lists. Calling add_files multiple times or passing multiple tables/arrays just joins their elements into the target's source-file list (later calls/elements are appended). Internally xmake treats array arguments like concatenated tables (table.join/table.join2 style), so the second table's entries are added after the first. [1][2]

Example:

  • add_files({"a.c", "b.c"}, {"c.c"}) → results in {"a.c","b.c","c.c"}.
  • add_files("x.c"); add_files("y.c") → results in {"x.c","y.c"}. [1][2]

Sources:
[1] Xmake add_files API (function prototype & semantics).
[2] Xmake builtin table.join documentation (merging multiple tables).


🌐 Web query:

xmake add_files options table position last argument only public cxflags

💡 Result:

Short answer: pass an options table as the last argument to add_files; put cxflags in that table and set visibility with {public=true} (or {private=true}/{interface=true}). To force raw flags (no auto-check), use the force subtable.

Examples:

  • Simple per-file cxflags (private by default):
    add_files("src/a.c", {cxflags = "-DFOO"})

  • Export cxflags to dependents (public):
    add_files("src/a.c", {cxflags = "-DFOO", public = true})

  • Force raw cxflags to compiler:
    add_files("src/*.c", {force = {cxflags = "-DTEST"}})

Docs: xmake add_files options table and visibility; force for cxflags. [1][2]

Sources:
[1] Xmake — Project Targets / add_files and force.
[2] Xmake — Quickstart / add_files options table.


add_files 选项语法不正确。

xmake 的 add_files 函数接受最后一个参数作为选项表,但这里传递了两个独立的表。第二个 {cxflags = "-w"} 不会作为选项应用,应合并为单个表。

修复建议
-    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,关闭警告
+    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,关闭警告
🤖 Prompt for AI Agents
In @xmake.lua around lines 143 - 150, The add_files calls are passing two
separate tables so the second {cxflags = "-w"} is ignored; update each add_files
invocation (e.g., add_files('./test/*.c', ...), add_files('./test/baseTest/*.c',
...), add_files('./test/baseTest/equality/*.c', ...),
add_files('./test/RFC8259Test/*.c', ...),
add_files('./test/externalModule/valloc/*.c', ...),
add_files('./test/externalModule/tlsf/*.c', ...),
add_files('./test/externalModule/cJSON/*.c', ...),
add_files('./test/externalModule/yyjson/*.c', ...)) to pass a single options
table that merges public = true and cxflags = "-w" (i.e., {public = true,
cxflags = "-w"}) so the compiler flags are actually applied.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
README.md (1)

219-289: 基准测试结果展示清晰,数据对比有说服力。

内存占用测试部分提供了两种场景下的详细对比:

  • 4 字节 malloc 头部(现代分配器)
  • 12 字节 malloc 头部(RT-Thread 最小内存算法)

测试用例覆盖了混合 JSON、对象、数组等多种场景,内存节省数据(相比 cJSON 节省 50-70%)非常有说服力。

📝 可选的文档改进建议

为代码块添加语言标识符可以提升文档的专业性和可读性。对于纯文本输出,可以使用 textconsole 标识符:

Line 219:

-```
+```text

Line 256:

-```
+```text

Line 299:

-```
+```text

这样可以消除 markdownlint 的警告,同时让代码块的用途更明确。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cbda13f and b63328e.

📒 Files selected for processing (1)
  • README.md
🧰 Additional context used
🪛 LanguageTool
README.md

[uncategorized] ~217-~217: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...参考) #### 内存占用测试 (20251222 malloc头部空间4字节,内存对齐4字节)测试代码可在本项目根目录RyanJsonExample...

(wa5)


[uncategorized] ~217-~217: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...## 内存占用测试 (20251222 malloc头部空间4字节,内存对齐4字节)测试代码可在本项目根目录RyanJsonExample文件夹查看。 ...

(wa5)


[uncategorized] ~254-~254: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ... ``` RT-Thread平台使用最小内存算法默认 malloc头部空间12字节,内存对齐8字节测试代码可在本项目根目录RyanJsonExample文...

(wa5)


[uncategorized] ~254-~254: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...Thread平台使用最小内存算法默认 malloc头部空间12字节,内存对齐8字节测试代码可在本项目根目录RyanJsonExample文件夹查看 ``...

(wa5)

🪛 markdownlint-cli2 (0.18.1)
README.md

47-47: Hard tabs
Column: 1

(MD010, no-hard-tabs)


48-48: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)


50-50: Hard tabs
Column: 1

(MD010, no-hard-tabs)


51-51: Hard tabs
Column: 1

(MD010, no-hard-tabs)


52-52: Hard tabs
Column: 1

(MD010, no-hard-tabs)


53-53: Hard tabs
Column: 1

(MD010, no-hard-tabs)


54-54: Hard tabs
Column: 1

(MD010, no-hard-tabs)


55-55: Hard tabs
Column: 1

(MD010, no-hard-tabs)


56-56: Hard tabs
Column: 1

(MD010, no-hard-tabs)


57-57: Hard tabs
Column: 1

(MD010, no-hard-tabs)


58-58: Hard tabs
Column: 1

(MD010, no-hard-tabs)


59-59: Hard tabs
Column: 1

(MD010, no-hard-tabs)


60-60: Hard tabs
Column: 1

(MD010, no-hard-tabs)


69-69: Hard tabs
Column: 1

(MD010, no-hard-tabs)


70-70: Hard tabs
Column: 1

(MD010, no-hard-tabs)


71-71: Hard tabs
Column: 1

(MD010, no-hard-tabs)


72-72: Hard tabs
Column: 1

(MD010, no-hard-tabs)


73-73: Hard tabs
Column: 1

(MD010, no-hard-tabs)


74-74: Hard tabs
Column: 1

(MD010, no-hard-tabs)


75-75: Hard tabs
Column: 1

(MD010, no-hard-tabs)


76-76: Hard tabs
Column: 1

(MD010, no-hard-tabs)


77-77: Hard tabs
Column: 1

(MD010, no-hard-tabs)


78-78: Hard tabs
Column: 1

(MD010, no-hard-tabs)


79-79: Hard tabs
Column: 1

(MD010, no-hard-tabs)


80-80: Hard tabs
Column: 1

(MD010, no-hard-tabs)


81-81: Hard tabs
Column: 1

(MD010, no-hard-tabs)


82-82: Hard tabs
Column: 1

(MD010, no-hard-tabs)


83-83: Hard tabs
Column: 1

(MD010, no-hard-tabs)


84-84: Hard tabs
Column: 1

(MD010, no-hard-tabs)


114-114: Hard tabs
Column: 1

(MD010, no-hard-tabs)


115-115: Hard tabs
Column: 1

(MD010, no-hard-tabs)


116-116: Hard tabs
Column: 1

(MD010, no-hard-tabs)


131-131: Hard tabs
Column: 1

(MD010, no-hard-tabs)


151-151: Hard tabs
Column: 1

(MD010, no-hard-tabs)


219-219: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


256-256: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


295-295: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


299-299: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (5)
README.md (5)

12-26: 文档质量优秀,特性描述清晰全面。

特性亮点部分的更新很好地突出了 RyanJson 的核心优势:

  • 内存优化的具体数据(减少 50% 占用)
  • 模糊测试的量化指标(分支覆盖率 100%)
  • 测试用例的系统化分类(9 大类)
  • 工具链集成的完整性

这些量化指标和具体工具链的引用增强了文档的可信度。


37-155: 设计文档非常详尽,内存优化策略解释清晰。

核心设计部分的更新包含了:

  • Flag 位分布的精确定义(bit0-7 各位含义)
  • Inline 与 Ptr 两种存储模式的详细说明
  • 存储阈值的计算公式和具体示例
  • 对 malloc 开销的考量(嵌入式平台特性)
  • 修改场景下的策略权衡

这种深度的技术文档对于理解库的内存优化原理非常有帮助。设计考量中提到的 TLSF 算法头部仅 4 字节的优势也很有见地。

注意:静态分析工具标记的硬制表符(hard tabs)是误报,这些制表符在 C 代码注释块中用于格式化是正常的。


157-195: 测试与质量保障部分结构清晰,覆盖全面。

测试文档的更新包含:

  • 9 大类专项功能测试的详细说明(表格形式展示)
  • LLVM Fuzzer 模糊测试的量化指标
  • Sanitizer 工具链的安全验证
  • 100% 分支覆盖率的声明并提供在线查看链接

测试类别的描述准确且有针对性,例如"修改 Json 节点测试"验证存储模式的自动切换,"循环遍历删除安全性测试"验证指针安全性等。


293-358: RFC 8259 标准符合性测试结果透明,局限性说明清晰。

RFC 8259 测试部分的优点:

  • 明确列出了 5 个精度不一致的测试案例(超大数字场景)
  • 与 yyjson 和 cJSON 的对比结果提供了横向参考
  • 坦诚说明了 Unicode 字符集的局限性
  • 给出了替代方案建议(yyjson / json-c)

这种透明的局限性说明体现了文档的专业性和负责任的态度。RyanJson 在 322 个测试用例中仅有 5 个数据不一致(都是超大数字精度问题),符合性表现良好。


360-364: 局限性说明清晰,给出了实用的解决方案。

局限性部分准确描述了:

  • 数值精度问题(使用 int/double 存储)
  • 对不支持科学计数法平台的建议(使用 String 类型)
  • 重复 Key 的处理行为(返回第一个匹配项)

这些说明有助于用户在实际应用中避免潜在问题。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants