Przeglądaj źródła

feat: 消除递归代码,规范化测试(单元测试、模糊测试),qemu模拟测试更符合真实硬件环境,增加skills

RyanCW 1 dzień temu
rodzic
commit
890e29f01c
100 zmienionych plików z 7522 dodań i 5132 usunięć
  1. 10 0
      .agent/rules/gemini.md
  2. 134 106
      .clang-format
  3. 10 3
      .clang-format-ignore
  4. 99 0
      .github/workflows/ci-pr.yml
  5. 336 0
      .github/workflows/nightly-fuzz.yml
  6. 2 2
      .gitignore
  7. 31 5
      .vscode/settings.json
  8. 1 0
      Makefile
  9. 81 2237
      RyanJson/RyanJson.c
  10. 126 74
      RyanJson/RyanJson.h
  11. 94 25
      RyanJson/RyanJsonConfig.h
  12. 119 0
      RyanJson/RyanJsonInternal.h
  13. 733 0
      RyanJson/RyanJsonItem.c
  14. 791 0
      RyanJson/RyanJsonParse.c
  15. 549 0
      RyanJson/RyanJsonPrint.c
  16. 491 79
      RyanJson/RyanJsonUtils.c
  17. 0 42
      RyanJson/RyanJsonUtils.h
  18. 43 43
      example/RyanJsonExample.c
  19. 0 35
      run_base_coverage.sh
  20. 0 53
      run_coverage.sh
  21. 33 0
      run_local_base.sh
  22. 37 0
      run_local_ci.sh
  23. 89 0
      run_local_format.sh
  24. 65 0
      run_local_fuzz.sh
  25. 440 0
      run_local_qemu.sh
  26. 143 0
      run_local_skills.sh
  27. 27 0
      scripts/README.md
  28. 213 0
      scripts/ci/runBaseCoverage.sh
  29. 226 0
      scripts/ci/runCoverage.sh
  30. 183 0
      scripts/setup/install_qemu_deps.sh
  31. 55 0
      skills/ryanjson-api-usage/SKILL.md
  32. 40 0
      skills/ryanjson-api-usage/agents/gemini.md
  33. 4 0
      skills/ryanjson-api-usage/agents/openai.yaml
  34. 40 0
      skills/ryanjson-api-usage/apiPatterns.md
  35. 37 0
      skills/ryanjson-api-usage/context.md
  36. 32 0
      skills/ryanjson-api-usage/ownership.md
  37. 114 0
      skills/ryanjson-api-usage/references/apiPatterns.md
  38. 140 0
      skills/ryanjson-api-usage/references/apiReference.md
  39. 72 0
      skills/ryanjson-api-usage/references/faultInjectionPlaybook.md
  40. 31 0
      skills/ryanjson-api-usage/references/geminiCompat.md
  41. 34 0
      skills/ryanjson-api-usage/references/hooksInitPolicy.md
  42. 66 0
      skills/ryanjson-api-usage/references/integrationTemplate.md
  43. 64 0
      skills/ryanjson-api-usage/references/ownershipAndErrors.md
  44. 35 0
      skills/ryanjson-api-usage/references/pitfallsAndDebug.md
  45. 162 0
      skills/ryanjson-api-usage/references/quickstart.md
  46. 151 0
      skills/ryanjson-api-usage/references/rtThreadExamples.md
  47. 4 0
      skills/ryanjson-api-usage/references/terminology.md
  48. 30 0
      skills/ryanjson-api-usage/sop.md
  49. 58 0
      skills/ryanjson-optimization/SKILL.md
  50. 35 0
      skills/ryanjson-optimization/agents/gemini.md
  51. 4 0
      skills/ryanjson-optimization/agents/openai.yaml
  52. 32 0
      skills/ryanjson-optimization/architecture.md
  53. 34 0
      skills/ryanjson-optimization/pitfalls.md
  54. 40 0
      skills/ryanjson-optimization/references/configSwitches.md
  55. 74 0
      skills/ryanjson-optimization/references/coreArchitecture.md
  56. 61 0
      skills/ryanjson-optimization/references/coreWorkflow.md
  57. 32 0
      skills/ryanjson-optimization/references/geminiCompat.md
  58. 44 0
      skills/ryanjson-optimization/references/moduleHotspots.md
  59. 64 0
      skills/ryanjson-optimization/references/optimizationRecipes.md
  60. 34 0
      skills/ryanjson-optimization/references/optimizationTemplate.md
  61. 42 0
      skills/ryanjson-optimization/references/regressionGates.md
  62. 4 0
      skills/ryanjson-optimization/references/terminology.md
  63. 31 0
      skills/ryanjson-optimization/sop.md
  64. 56 0
      skills/ryanjson-test-engineering/SKILL.md
  65. 35 0
      skills/ryanjson-test-engineering/agents/gemini.md
  66. 4 0
      skills/ryanjson-test-engineering/agents/openai.yaml
  67. 32 0
      skills/ryanjson-test-engineering/context.md
  68. 25 0
      skills/ryanjson-test-engineering/references/assertPolicy.md
  69. 62 0
      skills/ryanjson-test-engineering/references/coreArchitectureCheckpoints.md
  70. 41 0
      skills/ryanjson-test-engineering/references/coverageTriage.md
  71. 60 0
      skills/ryanjson-test-engineering/references/fuzzerPlaybook.md
  72. 26 0
      skills/ryanjson-test-engineering/references/geminiCompat.md
  73. 44 0
      skills/ryanjson-test-engineering/references/regressionMatrix.md
  74. 4 0
      skills/ryanjson-test-engineering/references/terminology.md
  75. 34 0
      skills/ryanjson-test-engineering/references/testcaseTemplate.md
  76. 47 0
      skills/ryanjson-test-engineering/references/unityPlaybook.md
  77. 31 0
      skills/ryanjson-test-engineering/sop.md
  78. 43 0
      skills/ryanjson-test-engineering/testArchitecture.md
  79. 157 0
      skills/shared/ryanJsonCommon.md
  80. 15 0
      skills/shared/terminology.md
  81. 0 287
      test/RFC8259Test/RyanJsonRFC8259JsonTest.c
  82. 0 26
      test/RFC8259Test/RyanJsonRFC8259TestUtil.h
  83. 0 164
      test/RyanJsonTest.c
  84. 0 78
      test/RyanJsonTest.h
  85. 0 78
      test/baseTest/RyanJsonBaseTest.c
  86. 0 55
      test/baseTest/RyanJsonBaseTest.h
  87. 0 142
      test/baseTest/RyanJsonBaseTestChangeJson.c
  88. 0 183
      test/baseTest/RyanJsonBaseTestCompareJson.c
  89. 0 86
      test/baseTest/RyanJsonBaseTestCreateJson.c
  90. 0 112
      test/baseTest/RyanJsonBaseTestDeleteJson.c
  91. 0 217
      test/baseTest/RyanJsonBaseTestDetachJson.c
  92. 0 139
      test/baseTest/RyanJsonBaseTestDuplicateJson.c
  93. 0 45
      test/baseTest/RyanJsonBaseTestForEachJson.c
  94. 0 345
      test/baseTest/RyanJsonBaseTestLoadJson.c
  95. 0 222
      test/baseTest/RyanJsonBaseTestReplaceJson.c
  96. 0 163
      test/baseTest/RyanJsonBaseTestUtile.c
  97. 0 86
      test/baseTest/equality/RyanJsonBaseTestEqualityBool.c
  98. 0 0
      test/data/rfc8259/i_number_double_huge_neg_exp.json
  99. 0 0
      test/data/rfc8259/i_number_huge_exp.json
  100. 0 0
      test/data/rfc8259/i_number_neg_int_huge_exp.json

+ 10 - 0
.agent/rules/gemini.md

@@ -0,0 +1,10 @@
+---
+trigger: always_on
+---
+
+对RyanJson核心代码使用最严格的审查,其余代码审查可以不那么严格
+始终考虑嵌入式约束,有限的RAM和ROM,资源利用效率
+优化内存管理,保证高效和实时性
+关注模块解耦和可维护性
+检查 RTOS 环境下的线程安全问题
+代码风格一致性

+ 134 - 106
.clang-format

@@ -1,149 +1,185 @@
 # SPDX-License-Identifier: Apache-2.0
 # SPDX-License-Identifier: Apache-2.0
 #
 #
-# Note: The list of ForEachMacros can be obtained using:
-#
-#    git grep -h '^#define [^[:space:]]*FOR_EACH[^[:space:]]*(' include/ \
-#    | sed "s,^#define \([^[:space:]]*FOR_EACH[^[:space:]]*\)(.*$,  - '\1'," \
-#    | sort | uniq
-#
-# References:
-#   - https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+# RyanJson clang-format 基线配置
+# 说明:
+# - 注释尽量覆盖每个配置项,便于长期维护
+# - 以 LLVM 为基线,只覆盖项目明确约束
+# - 参考:https://clang.llvm.org/docs/ClangFormatStyleOptions.html
 
 
 ---
 ---
-# 基于 LLVM 的代码风格作为起点,随后覆盖指定字段
+# 基础风格模板:以 LLVM 为起点
 BasedOnStyle: LLVM
 BasedOnStyle: LLVM
 
 
-# 连续宏定义的对齐方式
-# Enabled: true         -> 启用对齐连续宏定义
-# AcrossComments: true  -> 跨注释也会对齐,适合一组宏中间穿插注释的情况
-AlignConsecutiveMacros:
-  Enabled: true
-  AcrossComments: true
+# 单行最大宽度:超过后自动换行
+ColumnLimit: 140
 
 
-# 是否允许短代码块(如 { ... })出现在单行
-AllowShortBlocksOnASingleLine: true
+# 基础缩进宽度(单位:列)
+IndentWidth: 8
 
 
-# 是否允许短 case 标签单行
-# true  -> 允许 `case X: doSomething();`
-AllowShortCaseLabelsOnASingleLine: true
+# Tab 显示宽度(单位:列)
+TabWidth: 8
 
 
-# 是否允许短枚举在一行
-# true  -> 允许短枚举如 `enum { A, B };`
-AllowShortEnumsOnASingleLine: false
+# 续行缩进宽度(函数参数折行、表达式折行等)
+ContinuationIndentWidth: 8
 
 
-# 是否允许短函数在单行
-AllowShortFunctionsOnASingleLine: true
+# 构造函数初始化列表缩进宽度(主要影响 C++,保留统一)
+ConstructorInitializerIndentWidth: 8
 
 
-AllowShortCaseExpressionOnASingleLine: true
+# Tab 使用策略:缩进和续行使用 Tab
+UseTab: ForContinuationAndIndentation
 
 
-# 短 if 语句单行显示策略
-# Always  -> 允许并尽可能保留短 if 语句为单行(包括带 else 的情况)
-# 你希望单行 + 大括号时使用这个选项
-AllowShortIfStatementsOnASingleLine: true
+# 注释重排:false 表示不自动重排注释文本
+ReflowComments: false
 
 
-# 是否允许短循环(for/while)单行显示
-AllowShortLoopsOnASingleLine: true
+# 是否允许短代码块单行(如 { return; })
+AllowShortBlocksOnASingleLine: Always
 
 
-# 属性宏列表,列出在格式化时应视为属性的宏(影响对齐、换行等)
-# 如果代码库使用自定义属性宏,把它们列在这里可以提升格式化准确性
-AttributeMacros:
-  - __aligned
-  - __deprecated
-  - __packed
-  - __printf_like
-  - __syscall
-  - __syscall_always_inline
-  - __subsystem
+# 是否允许短函数单行
+AllowShortFunctionsOnASingleLine: false
 
 
-# 位字段冒号后的空格:After 表示 `int x : 3;` 中冒号后带一个空格(风格选择)
-BitFieldColonSpacing: After
+# 是否允许短 if 单行
+AllowShortIfStatementsOnASingleLine: WithoutElse
+
+# 是否允许短循环单行
+AllowShortLoopsOnASingleLine: false
 
 
-# 大括号换行策略:使用 Custom 配合 BraceWrapping 指定细节
-# 你用了 Custom,这意味着下面的 BraceWrapping 字段决定具体行为
+# 是否允许短 case 标签单行
+AllowShortCaseLabelsOnASingleLine: true
+
+# 是否允许短 case 表达式单行
+AllowShortCaseExpressionOnASingleLine: true
+
+# 是否允许短枚举单行
+AllowShortEnumsOnASingleLine: false
+
+# 花括号总策略:自定义
 BreakBeforeBraces: Custom
 BreakBeforeBraces: Custom
+
+# case 标签后的大括号是否换行
 BraceWrapping:
 BraceWrapping:
-  AfterCaseLabel: false           # case 标签后不另起行放 {,通常 case: 仍和语句对齐
-  AfterClass: true                # class 后大括号另起行
-  AfterControlStatement: Always   # 控制语句(if/for/while)后通常将 { 放在新行(可被覆盖)
-  AfterEnum: true                 # enum 后另起行
+  AfterCaseLabel: false
+
+  # class 后的大括号是否换行
+  AfterClass: true
+
+  # 控制语句(if/for/while)后的大括号换行策略
+  AfterControlStatement: Always
+
+  # enum 后的大括号是否换行
+  AfterEnum: true
+
+  # extern block 后的大括号是否换行
   AfterExternBlock: false
   AfterExternBlock: false
-  AfterFunction: true             # 函数体大括号另起行
+
+  # 函数定义后的大括号是否换行
+  AfterFunction: true
+
+  # namespace 后的大括号是否换行
   AfterNamespace: true
   AfterNamespace: true
+
+  # ObjC 声明后的大括号是否换行
   AfterObjCDeclaration: true
   AfterObjCDeclaration: true
+
+  # struct 后的大括号是否换行
   AfterStruct: true
   AfterStruct: true
+
+  # union 后的大括号是否换行
   AfterUnion: false
   AfterUnion: false
+
+  # catch 前是否换行
   BeforeCatch: true
   BeforeCatch: true
+
+  # else 前是否换行
   BeforeElse: true
   BeforeElse: true
+
+  # lambda 体前是否换行
   BeforeLambdaBody: false
   BeforeLambdaBody: false
+
+  # do...while 中 while 前是否换行
   BeforeWhile: false
   BeforeWhile: false
-  IndentBraces: false             # 不单独缩进大括号行
+
+  # 大括号行本身是否额外缩进
+  IndentBraces: false
+
+  # 空函数是否分裂成多行
   SplitEmptyFunction: true
   SplitEmptyFunction: true
+
+  # 空记录(如空 struct)是否分裂成多行
   SplitEmptyRecord: true
   SplitEmptyRecord: true
+
+  # 空命名空间是否分裂成多行
   SplitEmptyNamespace: true
   SplitEmptyNamespace: true
 
 
-# 单行代码的最大列数(换行阈值)
-ColumnLimit: 140
+# switch 内 case 标签是否额外缩进
+IndentCaseLabels: false
 
 
-# 构造函数初始化列表的缩进宽度(可针对长列表调整可读性)
-ConstructorInitializerIndentWidth: 8
+# goto 标签是否额外缩进
+IndentGotoLabels: false
 
 
-# 折行缩进宽度(续行缩进
-ContinuationIndentWidth: 8
+# 是否强制插入花括号(单语句控制流也加花括号
+InsertBraces: true
 
 
-# ForEach 宏列表:告诉 clang-format 哪些宏应当当作循环处理(便于格式化块体)
-ForEachMacros:
-  - "ARRAY_FOR_EACH"
-  - "ARRAY_FOR_EACH_PTR"
-  - "FOR_EACH"
+# 文件末尾是否补换行
+InsertNewlineAtEOF: true
 
 
-# If 宏列表:把 CHECKIF 等宏视为 if 语句(影响括号和后续块处理)
-IfMacros:
-  - "CHECKIF"
+# 位域冒号前后空格策略
+BitFieldColonSpacing: After
+
+# 控制语句括号前空格策略(if (x))
+SpaceBeforeParens: ControlStatementsExceptControlMacros
+
+# 继承冒号前空格策略(主要影响 C++)
+SpaceBeforeInheritanceColon: false
 
 
-# include 文件的分类和排序优先级
-# Regex: 正则匹配,Priority: 数字越小优先级越高(越先放)
+# 连续宏定义对齐策略
+AlignConsecutiveMacros:
+  # 是否启用连续宏对齐
+  Enabled: true
+
+  # 是否跨注释继续对齐
+  AcrossComments: true
+
+# include 是否自动排序(Never 表示保持人工顺序)
+SortIncludes: Never
+
+# include 分类规则(数值越小优先级越高)
 IncludeCategories:
 IncludeCategories:
+  # 项目内双引号头文件
   - Regex: '^".*\.h"$'
   - Regex: '^".*\.h"$'
     Priority: 0
     Priority: 0
+
+  # C 标准库头文件
   - Regex: '^<(assert|complex|ctype|errno|fenv|float|inttypes|limits|locale|math|setjmp|signal|stdarg|stdbool|stddef|stdint|stdio|stdlib|string|tgmath|time|wchar|wctype)\.h>$'
   - Regex: '^<(assert|complex|ctype|errno|fenv|float|inttypes|limits|locale|math|setjmp|signal|stdarg|stdbool|stddef|stdint|stdio|stdlib|string|tgmath|time|wchar|wctype)\.h>$'
     Priority: 1
     Priority: 1
-  - Regex: '^\<Ryan/.*\.h\>$'
+
+  # Ryan 体系头文件
+  - Regex: '^<Ryan/.*\.h>$'
     Priority: 2
     Priority: 2
+
+  # 兜底分类
   - Regex: ".*"
   - Regex: ".*"
     Priority: 3
     Priority: 3
 
 
-# case 标签是否缩进(true 会将 case 缩进到 switch 中)
-# false -> case 与 switch 对齐(你原先设置 false)
-IndentCaseLabels: false
-
-# goto 标签是否缩进(false 表示标签在行首)
-IndentGotoLabels: false
-
-# 缩进宽度(通常与制表符策略配合使用)
-IndentWidth: 8
-
-# 自动插入大括号(即使单语句也插入 { })
-# 这可以避免单行语句因为后续添加语句而引入 bug
-InsertBraces: true
-
-# 文件末尾自动插入换行
-InsertNewlineAtEOF: true
-
-# 继承冒号前是否加空格(False 表示不加空格:"class A: public B")
-SpaceBeforeInheritanceColon: False
-
-# 控制语句后是否加空格(这个值控制 if/for/while 等的格式)
-# ControlStatementsExceptControlMacros -> 控制语句(非宏)前加空格:`if (cond)` 而非 `if(cond)`
-SpaceBeforeParens: ControlStatementsExceptControlMacros
+# 视为 foreach 语义的宏列表(用于正确缩进与换行)
+ForEachMacros:
+  - RyanJsonArrayForEach
+  - RyanJsonObjectForEach
 
 
-# 包含文件是否自动排序(Never 表示不排序)
-SortIncludes: Never
+# 视为 if 语义的宏列表
+IfMacros:
+  - CHECKIF
 
 
-# 缩进与续行使用制表符策略
-# ForContinuationAndIndentation -> 续行与缩进使用制表符,其他空格仍按规则
-UseTab: ForContinuationAndIndentation
+# 视为属性的宏列表(影响断行与对齐)
+AttributeMacros:
+  - __aligned
+  - __deprecated
+  - __packed
+  - __printf_like
+  - __syscall
+  - __syscall_always_inline
+  - __subsystem
 
 
-# 对空白敏感的宏列表(多用于预处理器宏展开格式保持)
+# 空白敏感宏:保持参数空格布局,避免被格式化破坏
 WhitespaceSensitiveMacros:
 WhitespaceSensitiveMacros:
   - COND_CODE_0
   - COND_CODE_0
   - COND_CODE_1
   - COND_CODE_1
@@ -153,11 +189,3 @@ WhitespaceSensitiveMacros:
   - STRINGIFY
   - STRINGIFY
   - Z_STRINGIFY
   - Z_STRINGIFY
   - DT_FOREACH_PROP_ELEM_SEP
   - DT_FOREACH_PROP_ELEM_SEP
-
-# --------------------------
-# 可选:降低 clang-format 拆行惩罚,使其更倾向于保留短 if/else 单行
-# 下面两个值可以帮助把格式化后的多行 if/else 更可能压缩成单行(仅在 AllowShortIfStatementsOnASingleLine: Always 有效时可用)
-# PenaltyBreakIfElse: 0
-# PenaltyBreakStatement: 0
-
-# 注:上面的 Penalty 设置是可选的,如果你发现 clang-format 依旧不把某些 if/else 压成单行,可以取消注释并试验效果。

+ 10 - 3
.clang-format-ignore

@@ -1,3 +1,10 @@
-# 忽略外部包
-/test/externalModule/cJSON/**
-/test/externalModule/yyjson/**
+# 第三方代码:不做本仓风格重排,避免升级/同步时产生大 diff
+/test/externalModule/**
+
+# 生成目录:格式化无意义
+/build/**
+/.xmake/**
+/coverage/**
+
+# Fuzz 语料:不是源码
+/test/fuzzer/corpus/**

+ 99 - 0
.github/workflows/ci-pr.yml

@@ -0,0 +1,99 @@
+name: ci-pr
+
+on:
+  # PR 触发:用于主分支合并前的快速回归
+  pull_request:
+    branches:
+      - main
+      - master
+  # 手动触发:便于在分支上临时复跑
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  unitFull:
+    name: unit-full
+    runs-on: ubuntu-latest
+    timeout-minutes: 60
+
+    steps:
+      - name: 拉取代码
+        uses: actions/checkout@v4
+
+      - name: 安装 xmake
+        uses: xmake-io/github-action-setup-xmake@v1
+        with:
+          xmake-version: latest
+
+      - name: 安装 clang/llvm(覆盖率工具)
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y clang llvm
+
+      - name: 单元测试 full 模式(8 组配置全覆盖,跳过覆盖率)
+        run: |
+          chmod +x ./scripts/ci/runBaseCoverage.sh
+          UNIT_MODE=full \
+          UNIT_SKIP_COV=1 \
+          UNIT_STOP_ON_FAIL=1 \
+          bash ./scripts/ci/runBaseCoverage.sh
+
+      # 即使 full 模式跳过覆盖率,也保留执行产物,便于失败排查
+      - name: 上传单测产物
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: unit-full-artifacts
+          path: |
+            coverage/
+            build/
+          if-no-files-found: ignore
+          retention-days: 7
+
+  fuzzQuick:
+    name: fuzz-quick-default
+    runs-on: ubuntu-latest
+    timeout-minutes: 35
+
+    steps:
+      - name: 拉取代码
+        uses: actions/checkout@v4
+
+      - name: 安装 xmake
+        uses: xmake-io/github-action-setup-xmake@v1
+        with:
+          xmake-version: latest
+
+      - name: 安装 clang/llvm(fuzzer + 覆盖率工具)
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y clang llvm
+
+      - name: Fuzz quick 模式(跳过覆盖率)
+        env:
+          # PR 只跑一组默认语义,优先保证反馈速度
+          RYANJSON_STRICT_OBJECT_KEY_CHECK: "false"
+          RYANJSON_DEFAULT_ADD_AT_HEAD: "true"
+          RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC: "true"
+        run: |
+          chmod +x ./scripts/ci/runCoverage.sh
+          FUZZ_MODE=quick \
+          FUZZ_SKIP_COV=1 \
+          FUZZ_MAX_TOTAL_TIME=45 \
+          FUZZ_WORKERS=2 \
+          FUZZ_JOBS=2 \
+          bash ./scripts/ci/runCoverage.sh
+
+      - name: 上传 fuzz 产物
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: fuzz-quick-default-artifacts
+          path: |
+            coverage/
+            build/
+            *.log
+          if-no-files-found: ignore
+          retention-days: 7

+ 336 - 0
.github/workflows/nightly-fuzz.yml

@@ -0,0 +1,336 @@
+name: nightly-fuzz
+
+on:
+  # 每天夜间巡检一次(UTC 时间)
+  schedule:
+    - cron: "0 18 * * *"
+  # 手动触发:既能跑 nightly,也能跑 full(替代原 release 工作流)
+  workflow_dispatch:
+    inputs:
+      unitSkipCov:
+        description: "单测是否跳过覆盖率(true/false)"
+        required: true
+        default: false
+        type: boolean
+      fuzzProfile:
+        description: "fuzz 档位(nightly/full)"
+        required: true
+        default: "nightly"
+        type: choice
+        options:
+          - nightly
+          - full
+      fuzzSkipCov:
+        description: "fuzz 是否跳过覆盖率(true/false)"
+        required: true
+        default: true
+        type: boolean
+      fuzzMaxTotalTime:
+        description: "fuzz 每个 job 总时长预算(秒)"
+        required: true
+        default: "300"
+        type: string
+      fuzzWorkers:
+        description: "fuzz workers"
+        required: true
+        default: "4"
+        type: string
+      fuzzJobs:
+        description: "fuzz jobs"
+        required: true
+        default: "4"
+        type: string
+      runFuzzCoverageBaseline:
+        description: "nightly 档是否额外跑 1 组覆盖率基线"
+        required: true
+        default: true
+        type: boolean
+
+permissions:
+  contents: read
+
+jobs:
+  unitSuite:
+    name: unit-suite
+    runs-on: ubuntu-latest
+    timeout-minutes: 120
+
+    steps:
+      - name: 拉取代码
+        uses: actions/checkout@v4
+
+      - name: 安装 xmake
+        uses: xmake-io/github-action-setup-xmake@v1
+        with:
+          xmake-version: latest
+
+      - name: 安装 clang/llvm(覆盖率工具)
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y clang llvm
+
+      - name: 执行单元测试 full 矩阵(8 组配置全覆盖)
+        run: |
+          chmod +x ./scripts/ci/runBaseCoverage.sh
+
+          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
+            unitMode="full"
+            if [[ "${{ inputs.unitSkipCov }}" == "true" ]]; then
+              unitSkipCov=1
+            else
+              unitSkipCov=0
+            fi
+          else
+            # schedule 固定跑 full,确保 8 组配置都覆盖
+            unitMode="full"
+            unitSkipCov=0
+          fi
+
+          UNIT_MODE="${unitMode}" \
+          UNIT_SKIP_COV="${unitSkipCov}" \
+          UNIT_STOP_ON_FAIL=0 \
+          bash ./scripts/ci/runBaseCoverage.sh
+
+      - name: 上传单测产物
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: unit-suite-artifacts
+          path: |
+            coverage/
+            build/
+          if-no-files-found: ignore
+          retention-days: 14
+
+  fuzzNightlyMatrix:
+    name: fuzz-nightly-${{ matrix.caseId }}
+    if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.fuzzProfile == 'nightly') }}
+    runs-on: ubuntu-latest
+    timeout-minutes: 100
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          # nightly:重点覆盖 strict × addAtHead 的四种组合
+          # scientific 固定为 true,减少组合爆炸
+          - caseId: s0h0
+            strict: "false"
+            head: "false"
+            scientific: "true"
+          - caseId: s0h1
+            strict: "false"
+            head: "true"
+            scientific: "true"
+          - caseId: s1h0
+            strict: "true"
+            head: "false"
+            scientific: "true"
+          - caseId: s1h1
+            strict: "true"
+            head: "true"
+            scientific: "true"
+
+    steps:
+      - name: 拉取代码
+        uses: actions/checkout@v4
+
+      - name: 安装 xmake
+        uses: xmake-io/github-action-setup-xmake@v1
+        with:
+          xmake-version: latest
+
+      - name: 安装 clang/llvm
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y clang llvm
+
+      - name: 执行 nightly fuzz 矩阵
+        env:
+          RYANJSON_STRICT_OBJECT_KEY_CHECK: ${{ matrix.strict }}
+          RYANJSON_DEFAULT_ADD_AT_HEAD: ${{ matrix.head }}
+          RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC: ${{ matrix.scientific }}
+        run: |
+          chmod +x ./scripts/ci/runCoverage.sh
+
+          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
+            if [[ "${{ inputs.fuzzSkipCov }}" == "true" ]]; then
+              fuzzSkipCov=1
+            else
+              fuzzSkipCov=0
+            fi
+            fuzzMaxTotalTime="${{ inputs.fuzzMaxTotalTime }}"
+            fuzzWorkers="${{ inputs.fuzzWorkers }}"
+            fuzzJobs="${{ inputs.fuzzJobs }}"
+          else
+            # schedule 固定 nightly 默认预算
+            fuzzSkipCov=1
+            fuzzMaxTotalTime=240
+            fuzzWorkers=4
+            fuzzJobs=4
+          fi
+
+          FUZZ_MODE=nightly \
+          FUZZ_SKIP_COV="${fuzzSkipCov}" \
+          FUZZ_MAX_TOTAL_TIME="${fuzzMaxTotalTime}" \
+          FUZZ_WORKERS="${fuzzWorkers}" \
+          FUZZ_JOBS="${fuzzJobs}" \
+          bash ./scripts/ci/runCoverage.sh
+
+      - name: 上传 nightly fuzz 产物
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: fuzz-nightly-${{ matrix.caseId }}-artifacts
+          path: |
+            coverage/
+            build/
+            *.log
+          if-no-files-found: ignore
+          retention-days: 14
+
+  fuzzNightlyCoverage:
+    name: fuzz-nightly-coverage-baseline
+    if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.fuzzProfile == 'nightly' && inputs.runFuzzCoverageBaseline) }}
+    runs-on: ubuntu-latest
+    timeout-minutes: 100
+
+    steps:
+      - name: 拉取代码
+        uses: actions/checkout@v4
+
+      - name: 安装 xmake
+        uses: xmake-io/github-action-setup-xmake@v1
+        with:
+          xmake-version: latest
+
+      - name: 安装 clang/llvm
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y clang llvm
+
+      - name: 执行 nightly 覆盖率基线
+        env:
+          RYANJSON_STRICT_OBJECT_KEY_CHECK: "false"
+          RYANJSON_DEFAULT_ADD_AT_HEAD: "true"
+          RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC: "true"
+        run: |
+          chmod +x ./scripts/ci/runCoverage.sh
+
+          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
+            fuzzMaxTotalTime="${{ inputs.fuzzMaxTotalTime }}"
+            fuzzWorkers="${{ inputs.fuzzWorkers }}"
+            fuzzJobs="${{ inputs.fuzzJobs }}"
+          else
+            fuzzMaxTotalTime=300
+            fuzzWorkers=4
+            fuzzJobs=4
+          fi
+
+          FUZZ_MODE=nightly \
+          FUZZ_SKIP_COV=0 \
+          FUZZ_MAX_TOTAL_TIME="${fuzzMaxTotalTime}" \
+          FUZZ_WORKERS="${fuzzWorkers}" \
+          FUZZ_JOBS="${fuzzJobs}" \
+          bash ./scripts/ci/runCoverage.sh
+
+      - name: 上传 nightly 覆盖率基线产物
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: fuzz-nightly-coverage-artifacts
+          path: |
+            coverage/
+            build/
+            *.log
+          if-no-files-found: ignore
+          retention-days: 14
+
+  fuzzFullMatrix:
+    name: fuzz-full-${{ matrix.caseId }}
+    if: ${{ github.event_name == 'workflow_dispatch' && inputs.fuzzProfile == 'full' }}
+    runs-on: ubuntu-latest
+    timeout-minutes: 180
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          # full:三个布尔宏全组合(2 × 2 × 2 = 8)
+          - caseId: s0h0c0
+            strict: "false"
+            head: "false"
+            scientific: "false"
+          - caseId: s0h0c1
+            strict: "false"
+            head: "false"
+            scientific: "true"
+          - caseId: s0h1c0
+            strict: "false"
+            head: "true"
+            scientific: "false"
+          - caseId: s0h1c1
+            strict: "false"
+            head: "true"
+            scientific: "true"
+          - caseId: s1h0c0
+            strict: "true"
+            head: "false"
+            scientific: "false"
+          - caseId: s1h0c1
+            strict: "true"
+            head: "false"
+            scientific: "true"
+          - caseId: s1h1c0
+            strict: "true"
+            head: "true"
+            scientific: "false"
+          - caseId: s1h1c1
+            strict: "true"
+            head: "true"
+            scientific: "true"
+
+    steps:
+      - name: 拉取代码
+        uses: actions/checkout@v4
+
+      - name: 安装 xmake
+        uses: xmake-io/github-action-setup-xmake@v1
+        with:
+          xmake-version: latest
+
+      - name: 安装 clang/llvm
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y clang llvm
+
+      - name: 执行 full fuzz 矩阵
+        env:
+          RYANJSON_STRICT_OBJECT_KEY_CHECK: ${{ matrix.strict }}
+          RYANJSON_DEFAULT_ADD_AT_HEAD: ${{ matrix.head }}
+          RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC: ${{ matrix.scientific }}
+        run: |
+          chmod +x ./scripts/ci/runCoverage.sh
+
+          if [[ "${{ inputs.fuzzSkipCov }}" == "true" ]]; then
+            fuzzSkipCov=1
+          else
+            fuzzSkipCov=0
+          fi
+
+          FUZZ_MODE=full \
+          FUZZ_SKIP_COV="${fuzzSkipCov}" \
+          FUZZ_MAX_TOTAL_TIME="${{ inputs.fuzzMaxTotalTime }}" \
+          FUZZ_WORKERS="${{ inputs.fuzzWorkers }}" \
+          FUZZ_JOBS="${{ inputs.fuzzJobs }}" \
+          bash ./scripts/ci/runCoverage.sh
+
+      - name: 上传 full fuzz 产物
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: fuzz-full-${{ matrix.caseId }}-artifacts
+          path: |
+            coverage/
+            build/
+            *.log
+          if-no-files-found: ignore
+          retention-days: 30

+ 2 - 2
.gitignore

@@ -10,7 +10,6 @@ null
 *.exe
 *.exe
 
 
 # 忽略
 # 忽略
-core
 build
 build
 .xmake
 .xmake
 .dump
 .dump
@@ -20,4 +19,5 @@ default.profdata
 default.profraw
 default.profraw
 test/fuzzer/corpus
 test/fuzzer/corpus
 
 
-fuzz-*
+fuzz-*
+RyanJson_Technical_Paper.md

+ 31 - 5
.vscode/settings.json

@@ -8,16 +8,16 @@
     "Lua.semantic.enable": false,
     "Lua.semantic.enable": false,
     "Lua.addonManager.enable": false,
     "Lua.addonManager.enable": false,
     "Lua.signatureHelp.enable": false,
     "Lua.signatureHelp.enable": false,
-    "clangd.enable": true,
+    "clangd.enable": false,
     "clangd.arguments": [],
     "clangd.arguments": [],
     "liveServer.settings.file": "${workspaceFolder}/coverage/**",
     "liveServer.settings.file": "${workspaceFolder}/coverage/**",
     "liveServer.settings.ignoreFiles": [
     "liveServer.settings.ignoreFiles": [
         "**", // 第一步:先忽略所有文件(简单粗暴)
         "**", // 第一步:先忽略所有文件(简单粗暴)
         "!coverage/**" // 第二步:用感叹号 ! 把 coverage 目录“救”回来
         "!coverage/**" // 第二步:用感叹号 ! 把 coverage 目录“救”回来
     ],
     ],
-    "C_Cpp.intelliSenseEngine": "disabled",
-    "C_Cpp.errorSquiggles": "disabled", // 关闭微软的波浪线
-    "C_Cpp.autocomplete": "disabled", // 关闭微软的自动补全
+    // "C_Cpp.intelliSenseEngine": "disabled",
+    // "C_Cpp.errorSquiggles": "disabled", // 关闭微软的波浪线
+    // "C_Cpp.autocomplete": "disabled", // 关闭微软的自动补全
     "C_Cpp.default.compileCommands": "${workspaceFolder}/.vscode/compile_commands.json",
     "C_Cpp.default.compileCommands": "${workspaceFolder}/.vscode/compile_commands.json",
     "C_Cpp.codeAnalysis.clangTidy.enabled": false,
     "C_Cpp.codeAnalysis.clangTidy.enabled": false,
     "C_Cpp.codeAnalysis.clangTidy.args": [
     "C_Cpp.codeAnalysis.clangTidy.args": [
@@ -45,5 +45,31 @@
         "--suppress=unusedStructMember",
         "--suppress=unusedStructMember",
     ],
     ],
     "makefile.configureOnOpen": false,
     "makefile.configureOnOpen": false,
-    "liveServer.settings.port": 5501
+    "liveServer.settings.port": 5501,
+    "files.associations": {
+        "*.rules": "makefile",
+        "*.vars": "makefile",
+        "optional": "cpp",
+        "istream": "cpp",
+        "ostream": "cpp",
+        "system_error": "cpp",
+        "array": "cpp",
+        "functional": "cpp",
+        "tuple": "cpp",
+        "type_traits": "cpp",
+        "utility": "cpp",
+        "string": "cpp",
+        "deque": "cpp",
+        "vector": "cpp",
+        "iterator": "cpp",
+        "string_view": "cpp",
+        "string.h": "c",
+        "testbase.h": "c",
+        "ryanjson.h": "c",
+        "chrono": "c",
+        "random": "c",
+        "limits": "c",
+        "algorithm": "c",
+        "time.h": "c"
+    }
 }
 }

+ 1 - 0
Makefile

@@ -2,6 +2,7 @@
 # 编译器设置
 # 编译器设置
 CC = gcc
 CC = gcc
 C_FLAGS = -std=gnu99 -O2 -Wall -Wextra -Wno-unused-parameter
 C_FLAGS = -std=gnu99 -O2 -Wall -Wextra -Wno-unused-parameter
+C_FLAGS += -DRyanJsonProjectRootPath=\"$(shell pwd)\"
 
 
 # 头文件包含目录
 # 头文件包含目录
 CFLAGS_INC = -I ./RyanJson
 CFLAGS_INC = -I ./RyanJson

Plik diff jest za duży
+ 81 - 2237
RyanJson/RyanJson.c


+ 126 - 74
RyanJson/RyanJson.h

@@ -8,13 +8,13 @@ extern "C" {
 #include "RyanJsonConfig.h"
 #include "RyanJsonConfig.h"
 
 
 /**
 /**
- * @brief Json 错误处理块
- *
+ * @brief 内部错误检查宏。
+ * @note 条件失败时会打印内部日志并执行调用方传入的恢复代码。
  */
  */
 #define RyanJsonCheckCodeNoReturn(EX, code)                                                                                                \
 #define RyanJsonCheckCodeNoReturn(EX, code)                                                                                                \
 	if (!(EX))                                                                                                                         \
 	if (!(EX))                                                                                                                         \
 	{                                                                                                                                  \
 	{                                                                                                                                  \
-		jsonLog("\r\n%s:%d Check failed: %s\n", __FILE__, __LINE__, #EX);                                                          \
+		jsonLog("\r\n[INTERNAL ERROR] %s:%d: Check failed (%s)\n", __FILE__, __LINE__, #EX);                                       \
 		code                                                                                                                       \
 		code                                                                                                                       \
 	}
 	}
 
 
@@ -23,17 +23,21 @@ extern "C" {
 #define RyanJsonCheckReturnFalse(EX) RyanJsonCheckCode(EX, return RyanJsonFalse;)
 #define RyanJsonCheckReturnFalse(EX) RyanJsonCheckCode(EX, return RyanJsonFalse;)
 #define RyanJsonCheckReturnNull(EX)  RyanJsonCheckCode(EX, return NULL;)
 #define RyanJsonCheckReturnNull(EX)  RyanJsonCheckCode(EX, return NULL;)
 
 
-// !没有使能assert时RyanJsonCheckAssert不执行的
+/**
+ * @brief 断言相关宏。
+ * @note 未启用 `RyanJsonEnableAssert` 时,`RyanJsonCheckAssert` 不生效。
+ */
 #ifdef RyanJsonEnableAssert
 #ifdef RyanJsonEnableAssert
 #define RyanJsonCheckAssert(EX) RyanJsonCheckCode(EX, RyanJsonAssert(NULL &&#EX);)
 #define RyanJsonCheckAssert(EX) RyanJsonCheckCode(EX, RyanJsonAssert(NULL &&#EX);)
-#define RyanJsonCheckNeverNoAssert(EX)                                                                                                     \
+#define RyanJsonAssertAlwaysEval(EX)                                                                                                       \
 	do                                                                                                                                 \
 	do                                                                                                                                 \
 	{                                                                                                                                  \
 	{                                                                                                                                  \
 		if (!(EX)) RyanJsonAssert(NULL && #EX);                                                                                    \
 		if (!(EX)) RyanJsonAssert(NULL && #EX);                                                                                    \
 	} while (0)
 	} while (0)
 #else
 #else
-#define RyanJsonCheckAssert(EX)        ((void)0)
-#define RyanJsonCheckNeverNoAssert(EX) (void)(EX)
+#define RyanJsonCheckAssert(EX)
+// 无论是否开启断言都会“求值”,但只有在开启断言时才会 assert。 保留EX的副作用
+#define RyanJsonAssertAlwaysEval(EX) ((void)(EX))
 #endif
 #endif
 
 
 // Json 的最基础节点,所有 Json 元素都由该节点表示。
 // Json 的最基础节点,所有 Json 元素都由该节点表示。
@@ -41,6 +45,7 @@ extern "C" {
 // 其余数据(flag、key、stringValue、numberValue、doubleValue 等)均通过动态内存分配管理。
 // 其余数据(flag、key、stringValue、numberValue、doubleValue 等)均通过动态内存分配管理。
 struct RyanJsonNode
 struct RyanJsonNode
 {
 {
+	// 理论上next的低2位也是可以利用起来的
 	struct RyanJsonNode *next; // 单链表节点指针
 	struct RyanJsonNode *next; // 单链表节点指针
 
 
 	/**
 	/**
@@ -65,20 +70,19 @@ struct RyanJsonNode
 	 *
 	 *
 	 * - bit3   : 扩展位
 	 * - bit3   : 扩展位
 	 *            Bool 类型:0=false, 1=true
 	 *            Bool 类型:0=false, 1=true
-	 *            Number 类型:0=int(4字节), 1=double(8字节)
+	 *            Number 类型:0=int32_t(4字节), 1=double(8字节)
 	 *
 	 *
-	 * - bit4   : 是否包含 Key
-	 *            0=无 Key(数组元素)
-	 *            1=有 Key(对象成员)
+	 * - bit4-5 : Key 长度字段字节数
+	 *            00:无key
+	 *            01:keyLen=1字节 (≤UINT8_MAX)
+	 *            10:keyLen=2字节 (≤UINT16_MAX)
+	 *            11:keyLen=4字节 (≤UINT32_MAX)
 	 *
 	 *
-	 * - bit5-6 : Key 长度字段字节数
-	 *            00=1字节 (≤UINT8_MAX)
-	 *            01=2字节 (≤UINT16_MAX)
-	 *            10=3字节 (≤UINT24_MAX)
-	 *            11=4字节 (≤UINT32_MAX)
+	 * - bit6   : 表示key / strValue 存储模式
+	 *            0:inline 模式, 1:ptr 模式
 	 *
 	 *
-	 * - bit7   : 表示key / strValue 存储模式
-	 *            0:inline 模式, 1=ptr 模式
+	 * - bit7   : 表示是否为当前链表的最后一位,是的话nexe指针会指向Parent(线索化链表)
+	 *            0:next 指向兄弟节点, 1:next 指向Parent节点
 	 *
 	 *
 	 * @brief 动态载荷存储区
 	 * @brief 动态载荷存储区
      * 目的:
      * 目的:
@@ -89,7 +93,7 @@ struct RyanJsonNode
      * 存储策略:
      * 存储策略:
      * 利用结构体内存对齐产生的 Padding(如 Flag 后的空隙)以及原本用于存储指针的空间,形成一个缓冲区
      * 利用结构体内存对齐产生的 Padding(如 Flag 后的空隙)以及原本用于存储指针的空间,形成一个缓冲区
      * 若节点包含 key / strValue,则可能有两种方案:
      * 若节点包含 key / strValue,则可能有两种方案:
-     * 1. inline 模式 (小数据优化)
+     * inline 模式 (小数据优化)
      *    - 当 (KeyLen + Key + Value) 的总长度 ≤ 阈值时,直接存储在结构体内部。
      *    - 当 (KeyLen + Key + Value) 的总长度 ≤ 阈值时,直接存储在结构体内部。
      *    - 阈值计算公式:
      *    - 阈值计算公式:
      *        阈值 = Padding + sizeof(void*) + (malloc头部空间的一半),再向上对齐到字节边界。
      *        阈值 = Padding + sizeof(void*) + (malloc头部空间的一半),再向上对齐到字节边界。
@@ -102,16 +106,16 @@ struct RyanJsonNode
      *        [ KeyLen | Key | Value ]
      *        [ KeyLen | Key | Value ]
      *      起始地址即为 flag 之后,数据紧凑排列,无需额外 malloc。
      *      起始地址即为 flag 之后,数据紧凑排列,无需额外 malloc。
      *
      *
-     * 2. ptr 模式 (大数据)
+     * ptr 模式 (大数据)
      *    - 当数据长度 > 阈值时,结构体存储一个指针,指向独立的堆区。
      *    - 当数据长度 > 阈值时,结构体存储一个指针,指向独立的堆区。
      *    - 存储布局:
      *    - 存储布局:
-     *        [ KeyLen | *ptr ] -> (ptr指向) [ Key | Value ]
+     *        [ KeyLen | *ptr | Padding ] -> (ptr指向) [ Key | Value ]
      *    - KeyLen 的大小由 flag 中的长度字段决定 (最多 4 字节)。
      *    - KeyLen 的大小由 flag 中的长度字段决定 (最多 4 字节)。
      *    - 这样保证大数据不会撑爆结构体,同时保持 API 一致性。
      *    - 这样保证大数据不会撑爆结构体,同时保持 API 一致性。
 
 
      * 其他类型的存储:
      * 其他类型的存储:
 	 * - null / bool : 由 flag 位直接表示,无需额外空间。
 	 * - null / bool : 由 flag 位直接表示,无需额外空间。
-	 * - number      : 根据 flag 扩展位决定存储 int(4字节) 或 double(8字节)。
+	 * - number      : 根据 flag 扩展位决定存储 int32_t(4字节) 或 double(8字节)。
 	 * - object      : 动态分配空间存储子节点,采用链表结构。
 	 * - object      : 动态分配空间存储子节点,采用链表结构。
      *
      *
      * 设计考量:
      * 设计考量:
@@ -161,23 +165,29 @@ typedef enum
 	RyanJsonTypeString = 4,
 	RyanJsonTypeString = 4,
 	RyanJsonTypeArray = 5,
 	RyanJsonTypeArray = 5,
 	RyanJsonTypeObject = 6,
 	RyanJsonTypeObject = 6,
-} RyanjsonType_e;
+} RyanJsonType_e;
+
+typedef RyanJsonType_e RyanjsonType_e;
 
 
 #define RyanJsonFalse (false)
 #define RyanJsonFalse (false)
 #define RyanJsonTrue  (true)
 #define RyanJsonTrue  (true)
 
 
-// !兼容之前的类型定义
+/**
+ * @brief 兼容历史版本类型定义。
+ */
 typedef bool RyanJsonBool_e;
 typedef bool RyanJsonBool_e;
 typedef RyanJsonBool_e RyanJsonBool;
 typedef RyanJsonBool_e RyanJsonBool;
 
 
-// 内存钩子函数
+/**
+ * @brief 内存钩子函数类型定义。
+ */
 typedef void *(*RyanJsonMalloc_t)(size_t size);
 typedef void *(*RyanJsonMalloc_t)(size_t size);
 typedef void (*RyanJsonFree_t)(void *block);
 typedef void (*RyanJsonFree_t)(void *block);
 typedef void *(*RyanJsonRealloc_t)(void *block, size_t size);
 typedef void *(*RyanJsonRealloc_t)(void *block, size_t size);
 
 
 /**
 /**
- * !!! 较底层接口, 不推荐用户使用,除非用户知道这些接口意义
- * !!! 一定要看这里,这里的接口不推荐使用
+ * @brief 底层访问宏(不建议业务侧直接使用)。
+ * @note 仅在明确理解内部内存布局与位字段语义时使用。
  */
  */
 #define RyanJsonGetMask(bits)                           (((1U << (bits)) - 1))
 #define RyanJsonGetMask(bits)                           (((1U << (bits)) - 1))
 #define RyanJsonGetPayloadPtr(pJson)                    ((uint8_t *)(pJson) + sizeof(struct RyanJsonNode))
 #define RyanJsonGetPayloadPtr(pJson)                    ((uint8_t *)(pJson) + sizeof(struct RyanJsonNode))
@@ -195,45 +205,62 @@ typedef void *(*RyanJsonRealloc_t)(void *block, size_t size);
 #define RyanJsonGetPayloadNumberIsDoubleByFlag(pJson)        RyanJsonGetPayloadFlagField((pJson), 3, RyanJsonGetMask(1))
 #define RyanJsonGetPayloadNumberIsDoubleByFlag(pJson)        RyanJsonGetPayloadFlagField((pJson), 3, RyanJsonGetMask(1))
 #define RyanJsonSetPayloadNumberIsDoubleByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 3, RyanJsonGetMask(1), (value))
 #define RyanJsonSetPayloadNumberIsDoubleByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 3, RyanJsonGetMask(1), (value))
 
 
-#define RyanJsonGetPayloadWhiteKeyByFlag(pJson)        RyanJsonGetPayloadFlagField((pJson), 4, RyanJsonGetMask(1))
-#define RyanJsonSetPayloadWhiteKeyByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 4, RyanJsonGetMask(1), (value))
+/**
+ * @brief key 长度字段编码访问宏。
+ * @note 该编码当前受 8bit flag 位宽限制。
+ */
+#define RyanJsonGetPayloadEncodeKeyLenByFlag(pJson)        RyanJsonGetPayloadFlagField((pJson), 4, RyanJsonGetMask(2))
+#define RyanJsonSetPayloadEncodeKeyLenByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 4, RyanJsonGetMask(2), (value))
 
 
-// ! 使用超过8字节后一定要注意 RyanJsonSetPayloadFlagField 目前限制uint8_t类型
-// flag空间不够的时候可以把这个字段弃用,用redis的listpack方法将key和keyLen一起表示,内存占用也挺好,但是复杂度高,有空间就保持现在这样
-#define RyanJsonGetPayloadEncodeKeyLenByFlag(pJson)        ((uint8_t)RyanJsonGetPayloadFlagField((pJson), 5, RyanJsonGetMask(2)) + 1)
-#define RyanJsonSetPayloadEncodeKeyLenByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 5, RyanJsonGetMask(2), (value))
+#define RyanJsonGetPayloadStrIsPtrByFlag(pJson)        RyanJsonGetPayloadFlagField((pJson), 6, RyanJsonGetMask(1))
+#define RyanJsonSetPayloadStrIsPtrByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 6, RyanJsonGetMask(1), (value))
 
 
-#define RyanJsonGetPayloadStrIsPtrByFlag(pJson)        RyanJsonGetPayloadFlagField((pJson), 7, RyanJsonGetMask(1))
-#define RyanJsonSetPayloadStrIsPtrByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 7, RyanJsonGetMask(1), (value))
+#define RyanJsonGetPayloadIsLastByFlag(pJson)        RyanJsonGetPayloadFlagField((pJson), 7, RyanJsonGetMask(1))
+#define RyanJsonSetPayloadIsLastByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 7, RyanJsonGetMask(1), (value))
 
 
 extern RyanJsonBool_e RyanJsonInsert(RyanJson_t pJson, uint32_t index, RyanJson_t item);
 extern RyanJsonBool_e RyanJsonInsert(RyanJson_t pJson, uint32_t index, RyanJson_t item);
+extern RyanJson_t RyanJsonGetNext(RyanJson_t pJson);
+
 /**
 /**
- * !!!上面的接口不推荐使用
- *
+ * @brief 上述底层宏与接口不建议业务侧直接调用。
  */
  */
 
 
 /**
 /**
- * @brief json对象函数
+ * @brief Json 对外接口
  */
  */
 extern RyanJsonBool_e RyanJsonInitHooks(RyanJsonMalloc_t userMalloc, RyanJsonFree_t userFree, RyanJsonRealloc_t userRealloc);
 extern RyanJsonBool_e RyanJsonInitHooks(RyanJsonMalloc_t userMalloc, RyanJsonFree_t userFree, RyanJsonRealloc_t userRealloc);
 extern RyanJson_t RyanJsonParseOptions(const char *text, uint32_t size, RyanJsonBool_e requireNullTerminator,
 extern RyanJson_t RyanJsonParseOptions(const char *text, uint32_t size, RyanJsonBool_e requireNullTerminator,
-				       const char **parseEndPtr);                                             // 需用户释放内存
-#define RyanJsonParse(text) RyanJsonParseOptions((text), (uint32_t)RyanJsonStrlen(text), RyanJsonFalse, NULL) // 需用户释放内存
+				       const char **parseEndPtr); // 需用户释放内存
+extern RyanJson_t RyanJsonParse(const char *text);                // 需用户释放内存
+
 extern void RyanJsonDelete(RyanJson_t pJson);
 extern void RyanJsonDelete(RyanJson_t pJson);
 extern void RyanJsonFree(void *block);
 extern void RyanJsonFree(void *block);
 
 
 /**
 /**
- * @brief 打印json对象函数
+ * @brief 打印风格配置
  */
  */
-extern char *RyanJsonPrint(RyanJson_t pJson, uint32_t preset, RyanJsonBool_e format, uint32_t *len); // 需用户释放内存
+typedef struct
+{
+	char *indent;            // 缩进字符串 (例如 "\t" 或 "  ")
+	char *newline;           // 换行字符串 (例如 "\n" 或 "\r\n")
+	uint8_t indentLen;       // 缩进字符串长度
+	uint8_t newlineLen;      // 换行字符串长度
+	uint8_t spaceAfterColon; // 冒号后空格数量
+	RyanJsonBool_e format;   // 是否启用格式化逻辑
+} RyanJsonPrintStyle;
+extern char *RyanJsonPrintWithStyle(RyanJson_t pJson, uint32_t preset, const RyanJsonPrintStyle *style, uint32_t *len);
+extern char *RyanJsonPrintPreallocatedWithStyle(RyanJson_t pJson, char *buffer, uint32_t length, const RyanJsonPrintStyle *style,
+						uint32_t *len);
+extern char *RyanJsonPrint(RyanJson_t pJson, uint32_t preset, RyanJsonBool_e format, uint32_t *len);
 extern char *RyanJsonPrintPreallocated(RyanJson_t pJson, char *buffer, uint32_t length, RyanJsonBool_e format, uint32_t *len);
 extern char *RyanJsonPrintPreallocated(RyanJson_t pJson, char *buffer, uint32_t length, RyanJsonBool_e format, uint32_t *len);
 
 
 /**
 /**
- * @brief json杂项函数
+ * @brief Json 杂项函数
  */
  */
 extern RyanJson_t RyanJsonDuplicate(RyanJson_t pJson); // 需用户释放内存
 extern RyanJson_t RyanJsonDuplicate(RyanJson_t pJson); // 需用户释放内存
 extern uint32_t RyanJsonMinify(char *text, int32_t textLen);
 extern uint32_t RyanJsonMinify(char *text, int32_t textLen);
 extern RyanJsonBool_e RyanJsonCompare(RyanJson_t leftJson, RyanJson_t rightJson);
 extern RyanJsonBool_e RyanJsonCompare(RyanJson_t leftJson, RyanJson_t rightJson);
+extern RyanJsonBool_e RyanJsonCompareOnlyKey(RyanJson_t leftJson, RyanJson_t rightJson);
 extern RyanJsonBool_e RyanJsonCompareDouble(double a, double b);
 extern RyanJsonBool_e RyanJsonCompareDouble(double a, double b);
 extern uint32_t RyanJsonGetSize(RyanJson_t pJson);
 extern uint32_t RyanJsonGetSize(RyanJson_t pJson);
 #define RyanJsonGetArraySize(pJson) RyanJsonGetSize(pJson)
 #define RyanJsonGetArraySize(pJson) RyanJsonGetSize(pJson)
@@ -241,13 +268,19 @@ extern uint32_t RyanJsonGetSize(RyanJson_t pJson);
 /**
 /**
  * @brief 添加相关函数
  * @brief 添加相关函数
  */
  */
-extern RyanJson_t RyanJsonCreateObject(void);                                  // 如果没有添加到父json, 则需释放内存
-extern RyanJson_t RyanJsonCreateNull(const char *key);                         // 如果没有添加到父json, 则需释放内存
-extern RyanJson_t RyanJsonCreateBool(const char *key, RyanJsonBool_e boolean); // 如果没有添加到父json, 则需释放内存
-extern RyanJson_t RyanJsonCreateInt(const char *key, int32_t number);          // 如果没有添加到父json, 则需释放内存
-extern RyanJson_t RyanJsonCreateDouble(const char *key, double number);        // 如果没有添加到父json, 则需释放内存
-extern RyanJson_t RyanJsonCreateString(const char *key, const char *string);   // 如果没有添加到父json, 则需释放内存
-extern RyanJson_t RyanJsonCreateArray(void);                                   // 如果没有添加到父json, 则需释放内存
+extern RyanJson_t RyanJsonCreateObject(void);                                  // 如果没有添加到父 Json,则需释放内存
+extern RyanJson_t RyanJsonCreateNull(const char *key);                         // 如果没有添加到父 Json,则需释放内存
+extern RyanJson_t RyanJsonCreateBool(const char *key, RyanJsonBool_e boolean); // 如果没有添加到父 Json,则需释放内存
+extern RyanJson_t RyanJsonCreateInt(const char *key, int32_t number);          // 如果没有添加到父 Json,则需释放内存
+extern RyanJson_t RyanJsonCreateDouble(const char *key, double number);        // 如果没有添加到父 Json,则需释放内存
+extern RyanJson_t RyanJsonCreateString(const char *key, const char *string);   // 如果没有添加到父 Json,则需释放内存
+extern RyanJson_t RyanJsonCreateArray(void);                                   // 如果没有添加到父 Json,则需释放内存
+/**
+ * @brief 语法糖
+ */
+extern RyanJson_t RyanJsonCreateIntArray(const int32_t *numbers, uint32_t count);
+extern RyanJson_t RyanJsonCreateDoubleArray(const double *numbers, uint32_t count);
+extern RyanJson_t RyanJsonCreateStringArray(const char **strings, uint32_t count);
 
 
 /**
 /**
  * @brief 分离相关函数
  * @brief 分离相关函数
@@ -267,7 +300,9 @@ extern RyanJsonBool_e RyanJsonDeleteByKey(RyanJson_t pJson, const char *key);
 extern RyanJson_t RyanJsonGetObjectByKey(RyanJson_t pJson, const char *key);
 extern RyanJson_t RyanJsonGetObjectByKey(RyanJson_t pJson, const char *key);
 extern RyanJson_t RyanJsonGetObjectByIndex(RyanJson_t pJson, uint32_t index);
 extern RyanJson_t RyanJsonGetObjectByIndex(RyanJson_t pJson, uint32_t index);
 
 
-// 工具宏
+/**
+ * @brief 工具宏
+ */
 #define RyanJsonMakeBool(ex) ((ex) ? RyanJsonTrue : RyanJsonFalse)
 #define RyanJsonMakeBool(ex) ((ex) ? RyanJsonTrue : RyanJsonFalse)
 
 
 /**
 /**
@@ -288,10 +323,11 @@ extern RyanJsonBool_e RyanJsonIsArray(RyanJson_t pJson);
 extern RyanJsonBool_e RyanJsonIsObject(RyanJson_t pJson);
 extern RyanJsonBool_e RyanJsonIsObject(RyanJson_t pJson);
 extern RyanJsonBool_e RyanJsonIsInt(RyanJson_t pJson);
 extern RyanJsonBool_e RyanJsonIsInt(RyanJson_t pJson);
 extern RyanJsonBool_e RyanJsonIsDouble(RyanJson_t pJson);
 extern RyanJsonBool_e RyanJsonIsDouble(RyanJson_t pJson);
+extern RyanJsonBool_e RyanJsonIsDetachedItem(RyanJson_t item);
 
 
 /**
 /**
- * @brief 取值宏
- * !取值宏使用前一定要RyanJsonIsXXXX类型判断函数做好判断,否则会内存访问越界
+ * @brief 节点取值接口。
+ * @note 调用前应先判空并使用 `RyanJsonIsXXX` 做类型判断。
  */
  */
 extern char *RyanJsonGetKey(RyanJson_t pJson);
 extern char *RyanJsonGetKey(RyanJson_t pJson);
 extern char *RyanJsonGetStringValue(RyanJson_t pJson);
 extern char *RyanJsonGetStringValue(RyanJson_t pJson);
@@ -299,18 +335,32 @@ extern int32_t RyanJsonGetIntValue(RyanJson_t pJson);
 extern double RyanJsonGetDoubleValue(RyanJson_t pJson);
 extern double RyanJsonGetDoubleValue(RyanJson_t pJson);
 extern RyanJson_t RyanJsonGetObjectValue(RyanJson_t pJson);
 extern RyanJson_t RyanJsonGetObjectValue(RyanJson_t pJson);
 extern RyanJson_t RyanJsonGetArrayValue(RyanJson_t pJson);
 extern RyanJson_t RyanJsonGetArrayValue(RyanJson_t pJson);
-#define RyanJsonGetBoolValue(pJson) RyanJsonGetPayloadBoolValueByFlag(pJson)
+extern RyanJsonBool_e RyanJsonGetBoolValue(RyanJson_t pJson);
 
 
 /**
 /**
- * @brief 添加相关函数
- * ! add函数使用前建议RyanJsonIsXXXX宏判断是否是对象 / 数组,否则会内存访问越界
- * ! add函数内部会处理失败情况,如果返回false,不需要用户手动释放内存
+ * @brief 变参路径查询底层接口。
+ * @note 建议优先使用 `RyanJsonGetObjectToKey/ToIndex` 与 `RyanJsonHasObjectToKey/ToIndex` 宏。
  */
  */
-#define RyanJsonAddNullToObject(pJson, key)           RyanJsonInsert(pJson, UINT32_MAX, RyanJsonCreateNull(key))
-#define RyanJsonAddBoolToObject(pJson, key, boolean)  RyanJsonInsert(pJson, UINT32_MAX, RyanJsonCreateBool(key, boolean))
-#define RyanJsonAddIntToObject(pJson, key, number)    RyanJsonInsert(pJson, UINT32_MAX, RyanJsonCreateInt(key, number))
-#define RyanJsonAddDoubleToObject(pJson, key, number) RyanJsonInsert(pJson, UINT32_MAX, RyanJsonCreateDouble(key, number))
-#define RyanJsonAddStringToObject(pJson, key, string) RyanJsonInsert(pJson, UINT32_MAX, RyanJsonCreateString(key, string))
+extern RyanJson_t RyanJsonGetObjectByIndexs(RyanJson_t pJson, uint32_t index, ...);
+extern RyanJson_t RyanJsonGetObjectByKeys(RyanJson_t pJson, const char *key, ...);
+#define RyanJsonGetObjectToKey(pJson, key, ...)     RyanJsonGetObjectByKeys(pJson, (key), ##__VA_ARGS__, NULL)
+#define RyanJsonGetObjectToIndex(pJson, index, ...) RyanJsonGetObjectByIndexs(pJson, (index), ##__VA_ARGS__, UINT32_MAX)
+#define RyanJsonHasObjectToKey(pJson, key, ...)     RyanJsonMakeBool(RyanJsonGetObjectToKey(pJson, key, ##__VA_ARGS__))
+#define RyanJsonHasObjectToIndex(pJson, index, ...) RyanJsonMakeBool(RyanJsonGetObjectToIndex(pJson, index, ##__VA_ARGS__))
+
+/**
+ * @brief Add 系列接口与便捷宏。
+ * @note 建议调用前先用 `RyanJsonIsObject/RyanJsonIsArray` 做类型校验。
+ * @note Add/Insert 在 `item` 为游离节点时失败会自动释放 `item`。
+ * @note `item` 非游离节点时失败不会释放 `item`(保护原树)。
+ * @note Object key 必须唯一,重复 key 会失败(Parse 也拒绝重复 key)。
+ * @note `AddItem` 仅接受 Array/Object 节点;标量请使用 `AddInt/AddString` 等接口。
+ */
+#define RyanJsonAddNullToObject(pJson, key)           RyanJsonInsert(pJson, RyanJsonAddPosition, RyanJsonCreateNull(key))
+#define RyanJsonAddBoolToObject(pJson, key, boolean)  RyanJsonInsert(pJson, RyanJsonAddPosition, RyanJsonCreateBool(key, boolean))
+#define RyanJsonAddIntToObject(pJson, key, number)    RyanJsonInsert(pJson, RyanJsonAddPosition, RyanJsonCreateInt(key, number))
+#define RyanJsonAddDoubleToObject(pJson, key, number) RyanJsonInsert(pJson, RyanJsonAddPosition, RyanJsonCreateDouble(key, number))
+#define RyanJsonAddStringToObject(pJson, key, string) RyanJsonInsert(pJson, RyanJsonAddPosition, RyanJsonCreateString(key, string))
 extern RyanJsonBool_e RyanJsonAddItemToObject(RyanJson_t pJson, const char *key, RyanJson_t item);
 extern RyanJsonBool_e RyanJsonAddItemToObject(RyanJson_t pJson, const char *key, RyanJson_t item);
 
 
 #define RyanJsonAddNullToArray(pJson)           RyanJsonAddNullToObject(pJson, NULL)
 #define RyanJsonAddNullToArray(pJson)           RyanJsonAddNullToObject(pJson, NULL)
@@ -320,27 +370,29 @@ extern RyanJsonBool_e RyanJsonAddItemToObject(RyanJson_t pJson, const char *key,
 #define RyanJsonAddStringToArray(pJson, string) RyanJsonAddStringToObject(pJson, NULL, string)
 #define RyanJsonAddStringToArray(pJson, string) RyanJsonAddStringToObject(pJson, NULL, string)
 #define RyanJsonAddItemToArray(pJson, item)     RyanJsonAddItemToObject(pJson, NULL, item)
 #define RyanJsonAddItemToArray(pJson, item)     RyanJsonAddItemToObject(pJson, NULL, item)
 
 
-/**
- * @brief 遍历函数
- */
-#define RyanJsonArrayForEach(pJson, item)  for ((item) = RyanJsonGetArrayValue(pJson); NULL != (item); (item) = (item)->next)
-#define RyanJsonObjectForEach(pJson, item) for ((item) = RyanJsonGetObjectValue(pJson); NULL != (item); (item) = (item)->next)
+#define RyanJsonArrayForEach(pJson, item)                                                                                                  \
+	for ((item) = RyanJsonIsArray(pJson) ? RyanJsonGetArrayValue(pJson) : NULL; NULL != (item); (item) = RyanJsonGetNext(item))
+#define RyanJsonObjectForEach(pJson, item)                                                                                                 \
+	for ((item) = RyanJsonIsObject(pJson) ? RyanJsonGetObjectValue(pJson) : NULL; NULL != (item); (item) = RyanJsonGetNext(item))
 
 
 /**
 /**
- * @brief 修改相关函数
- * !修改函数没有对入参做校验,使用前请做使用RyanJsonIsXXXX类型判断宏做好判断,否则会内存访问越界
+ * @brief 同类型值修改接口。
+ * @note 修改函数会执行基本参数/类型校验,失败返回 false。
+ * @note 仍建议调用前使用 `RyanJsonIsXXX` 做前置判断。
  */
  */
 extern RyanJsonBool_e RyanJsonChangeKey(RyanJson_t pJson, const char *key);
 extern RyanJsonBool_e RyanJsonChangeKey(RyanJson_t pJson, const char *key);
 extern RyanJsonBool_e RyanJsonChangeStringValue(RyanJson_t pJson, const char *strValue);
 extern RyanJsonBool_e RyanJsonChangeStringValue(RyanJson_t pJson, const char *strValue);
 extern RyanJsonBool_e RyanJsonChangeIntValue(RyanJson_t pJson, int32_t number);
 extern RyanJsonBool_e RyanJsonChangeIntValue(RyanJson_t pJson, int32_t number);
 extern RyanJsonBool_e RyanJsonChangeDoubleValue(RyanJson_t pJson, double number);
 extern RyanJsonBool_e RyanJsonChangeDoubleValue(RyanJson_t pJson, double number);
-#define RyanJsonChangeBoolValue(pJson, boolean) RyanJsonSetPayloadBoolValueByFlag(pJson, boolean)
+extern RyanJsonBool_e RyanJsonChangeBoolValue(RyanJson_t pJson, RyanJsonBool_e boolean);
 
 
-// 这是change方法的补充,当需要修改value类型时,使用此函数
-// 请参考 changeJsonTest 示例,严格按照规则来使用
 /**
 /**
- * @brief
- * !这是change方法的补充,当需要修改value类型时,使用此函数,请参考 RyanJsonBaseTestChangeJson 示例,严格按照规则来使用
+ * @brief 节点替换接口(用于修改 value 类型)
+ * @note 需要跨类型替换时使用 `ReplaceByKey/ReplaceByIndex`。
+ * @note 示例:`RyanJsonReplaceByKey(root, "k", RyanJsonCreateObject());`
+ * @note 示例:`RyanJsonReplaceByIndex(arr, i, RyanJsonCreateString(NULL, "v"));`
+ * @note Replace 成功后,`item` 所有权转移到目标树。
+ * @note Replace 失败后,调用方仍持有 `item`,需自行释放或复用。
  */
  */
 extern RyanJsonBool_e RyanJsonReplaceByKey(RyanJson_t pJson, const char *key, RyanJson_t item);
 extern RyanJsonBool_e RyanJsonReplaceByKey(RyanJson_t pJson, const char *key, RyanJson_t item);
 extern RyanJsonBool_e RyanJsonReplaceByIndex(RyanJson_t pJson, uint32_t index, RyanJson_t item); // object对象也可以使用,但是不推荐
 extern RyanJsonBool_e RyanJsonReplaceByIndex(RyanJson_t pJson, uint32_t index, RyanJson_t item); // object对象也可以使用,但是不推荐

+ 94 - 25
RyanJson/RyanJsonConfig.h

@@ -14,6 +14,7 @@ extern "C" {
 #include <float.h>
 #include <float.h>
 #include <math.h>
 #include <math.h>
 #include <inttypes.h>
 #include <inttypes.h>
+#include <stdarg.h>
 
 
 #ifdef RT_VER_NUM
 #ifdef RT_VER_NUM
 #include "rtthread.h"
 #include "rtthread.h"
@@ -33,49 +34,85 @@ extern "C" {
 #define RyanJsonStrcmp             strcmp
 #define RyanJsonStrcmp             strcmp
 #define RyanJsonSnprintf           snprintf
 #define RyanJsonSnprintf           snprintf
 #define RyanJsonPlatformAssert(EX) assert(EX)
 #define RyanJsonPlatformAssert(EX) assert(EX)
-#define RyanJsonMallocHeaderSize   8U
-#define RyanJsonMallocAlign        4U
+#define RyanJsonMallocHeaderSize   12U
+#define RyanJsonMallocAlign        8U
 #endif
 #endif
 
 
-// 是否启用assert
+/**
+ * @brief RyanJsonEnableAssert: 启用库内断言(RyanJsonAssert / RyanJsonCheckAssert)。
+ * @note 默认关闭(注释状态)。
+ */
 // #define RyanJsonEnableAssert
 // #define RyanJsonEnableAssert
 
 
+/**
+ * @brief RyanJsonMallocAlign: 内存对齐粒度(字节)。
+ * @note 默认值为 8(平台未提前定义时)。
+ * @note 必须是 4 的倍数。
+ */
 #ifndef RyanJsonMallocAlign
 #ifndef RyanJsonMallocAlign
 #define RyanJsonMallocAlign 8U
 #define RyanJsonMallocAlign 8U
 #endif
 #endif
 
 
-//
+/**
+ * @brief RyanJsonMallocHeaderSize: 分配器头部开销(字节),用于内联阈值估算。
+ * @note 默认值为 8(平台未提前定义时)。
+ * @note 必须是 4 的倍数。
+ */
 #ifndef RyanJsonMallocHeaderSize
 #ifndef RyanJsonMallocHeaderSize
 #define RyanJsonMallocHeaderSize 8U
 #define RyanJsonMallocHeaderSize 8U
 #endif
 #endif
 
 
-// 限制解析数组/对象中嵌套的深度
-// RyanJson使用递归 序列化/反序列化 json
-// 请根据单片机资源合理设置以防止堆栈溢出。
-#ifndef RyanJsonNestingLimit
-#define RyanJsonNestingLimit 300U
-#endif
-
-// 当 RyanJsonPrint 剩余缓冲空间不足时申请的空间大小
+/**
+ * @brief RyanJsonPrintfPreAlloSize: RyanJsonPrint 缓冲区不足时,每次扩容的预分配大小。
+ * @note 默认值为 64 字节。
+ */
 #ifndef RyanJsonPrintfPreAlloSize
 #ifndef RyanJsonPrintfPreAlloSize
 #define RyanJsonPrintfPreAlloSize (64U)
 #define RyanJsonPrintfPreAlloSize (64U)
 #endif
 #endif
 
 
-// 浮点数比较可调的绝对容差,一般场景 1e-8 足够了.
-// 可以根据自己需求进行调整
-// 容差必须大于 0.0,同时小于 1.0
+/**
+ * @brief RyanJsonAbsTolerance: 浮点比较的绝对容差。
+ * @note 默认值为 1e-8(常见场景足够)。
+ * @note 建议范围为 (0.0, 1.0)。
+ */
 #ifndef RyanJsonAbsTolerance
 #ifndef RyanJsonAbsTolerance
 #define RyanJsonAbsTolerance 1e-8
 #define RyanJsonAbsTolerance 1e-8
 #endif
 #endif
 
 
-// 用户需声明目标平台的 snprintf 是否支持科学计数法输出, (库内部是否使用 %g/%e)
+/**
+ * @brief RyanJsonStrictObjectKeyCheck: 控制 Object 是否允许重复 key。
+ * @note true 时 Parse/Insert/ReplaceByIndex 拒绝重复 key。
+ * @note false 时允许重复 key,但按 key 的 API 通常只命中第一个节点。
+ * @note 默认值为 false。
+ */
+#ifndef RyanJsonStrictObjectKeyCheck
+#define RyanJsonStrictObjectKeyCheck false
+#endif
+
+/**
+ * @brief RyanJsonDefaultAddAtHead: 控制 Add 系列接口(Array/Object)的默认插入方向。
+ * @note false 为尾插(保持业务顺序,超大链表时查尾为 O(N))。
+ * @note true 为头插(O(1),但遍历/打印时新元素会在前面)。
+ * @note 默认值为 false。
+ */
+#ifndef RyanJsonDefaultAddAtHead
+#define RyanJsonDefaultAddAtHead false
+#endif
+
+/**
+ * @brief RyanJsonSnprintfSupportScientific: 声明目标平台 snprintf 是否支持科学计数法(%g/%e)。
+ * @note 该配置会影响 double 序列化策略与 RyanJsonDoubleBufferSize 默认值。
+ * @note 默认值为 true。
+ */
 #ifndef RyanJsonSnprintfSupportScientific
 #ifndef RyanJsonSnprintfSupportScientific
-#define RyanJsonSnprintfSupportScientific false
+#define RyanJsonSnprintfSupportScientific true
 #endif
 #endif
 
 
-// 用户需声明 double 序列化时的缓冲区大小
-// 如果 snprintf 支持科学计数法,建议值 ≥ 32
-// 如果不支持科学计数法,建议值 ≥ 330
+/**
+ * @brief RyanJsonDoubleBufferSize: double 序列化临时缓冲区大小。
+ * @note 若支持科学计数法:默认 32(建议 >= 32)。
+ * @note 若不支持科学计数法:默认 64(理论上完整输出可到 330+,可按需求增大)。
+ */
 #ifndef RyanJsonDoubleBufferSize
 #ifndef RyanJsonDoubleBufferSize
 #if true == RyanJsonSnprintfSupportScientific
 #if true == RyanJsonSnprintfSupportScientific
 #define RyanJsonDoubleBufferSize 32
 #define RyanJsonDoubleBufferSize 32
@@ -87,8 +124,9 @@ extern "C" {
 #endif
 #endif
 
 
 /**
 /**
- * @brief 调试相关配置
- *
+ * @brief jsonLog: 内部调试日志钩子。
+ * @note 默认为空实现。
+ * @note 可按需取消下一行注释替换为 printf 等实现。
  */
  */
 // #define jsonLog(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
 // #define jsonLog(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
 #define jsonLog(...)
 #define jsonLog(...)
@@ -96,12 +134,11 @@ extern "C" {
 #ifdef RyanJsonEnableAssert
 #ifdef RyanJsonEnableAssert
 #define RyanJsonAssert(EX) RyanJsonPlatformAssert(EX)
 #define RyanJsonAssert(EX) RyanJsonPlatformAssert(EX)
 #else
 #else
-#define RyanJsonAssert(EX) (void)(EX)
+#define RyanJsonAssert(EX)
 #endif
 #endif
 
 
 /**
 /**
- * @brief 检查宏是否合法
- *
+ * @brief 配置合法性检查(编译期)。
  */
  */
 #if 0 != RyanJsonMallocHeaderSize && RyanJsonMallocHeaderSize % 4 != 0
 #if 0 != RyanJsonMallocHeaderSize && RyanJsonMallocHeaderSize % 4 != 0
 #error "RyanJsonMallocHeaderSize 必须是4的倍数"
 #error "RyanJsonMallocHeaderSize 必须是4的倍数"
@@ -115,6 +152,38 @@ extern "C" {
 #error "RyanJsonDoubleBufferSize 必须大于8"
 #error "RyanJsonDoubleBufferSize 必须大于8"
 #endif
 #endif
 
 
+#if true != RyanJsonStrictObjectKeyCheck && false != RyanJsonStrictObjectKeyCheck
+#error "RyanJsonStrictObjectKeyCheck 必须是 true 或 false"
+#endif
+
+#if true != RyanJsonDefaultAddAtHead && false != RyanJsonDefaultAddAtHead
+#error "RyanJsonDefaultAddAtHead 必须是 true 或 false"
+#endif
+
+/**
+ * @brief RyanJsonInlineStringSize: key/短字符串内联阈值(单位:字节)。
+ * @note 用户可在包含 RyanJson.h 前自行定义。
+ * @note C99 预处理器无法对包含 sizeof 的表达式做 #if 校验。
+ */
+#ifdef RyanJsonInlineStringSize
+#if RyanJsonInlineStringSize <= 1U
+#error "RyanJsonInlineStringSize 必须大于1"
+#endif
+#endif
+
+/**
+ * @brief RyanJsonAddPosition: Add 系列接口的默认插入索引(用于 RyanJsonInsert)。
+ * @note 0 表示头插,UINT32_MAX 表示尾插(按追加语义处理)。
+ * @note 默认值由 RyanJsonDefaultAddAtHead 自动推导。
+ */
+#ifndef RyanJsonAddPosition
+#if true == RyanJsonDefaultAddAtHead
+#define RyanJsonAddPosition 0U
+#else
+#define RyanJsonAddPosition UINT32_MAX
+#endif
+#endif
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 119 - 0
RyanJson/RyanJsonInternal.h

@@ -0,0 +1,119 @@
+#ifndef RyanJsonInternal_h
+#define RyanJsonInternal_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "RyanJson.h"
+
+#ifndef RyanJsonInternalApi
+#define RyanJsonInternalApi extern
+#endif
+
+#define RyanJsonFlagSize               sizeof(uint8_t)
+#define RyanJsonKeyFeidLenMaxSize      sizeof(uint32_t)
+#define RyanJsonAlign(size, align)     (((size) + (align) - 1) & ~((align) - 1))
+#define RyanJsonAlignDown(size, align) ((size) & ~((align) - 1))
+#define _checkType(info, type)         (RyanJsonGetType(info) == (type))
+#define RyanJsonUnused(x)              (void)(x)
+
+#ifndef RyanJsonInlineStringSize
+/**
+ * @brief RyanJsonInlineStringSize 默认值(单位:字节,与历史版本等价)。
+ * @details
+ * 该宏用于计算“节点内联字符串区可用容量”(不含 flag 字节)。
+ *
+ * 当前实现:
+ * rawSize = sizeof(void*) - RyanJsonFlagSize
+ *         + sizeof(void*)
+ *         + (RyanJsonMallocHeaderSize / 2)
+ *         + RyanJsonFlagSize
+ * inlineSize = RyanJsonAlign(rawSize, RyanJsonMallocAlign) - RyanJsonFlagSize
+ *
+ * 各项用途:
+ * - sizeof(void*) - RyanJsonFlagSize:
+ *   flag占用一个字节,32位4字节对齐情况下,减去flag 占用的 1 字节后,剩余部分正好是一个指针槽的净可用载荷。
+ * - + sizeof(void*):
+ *   char* 最少占用一个指针大小,所以再加一个指针大小
+ * - + (RyanJsonMallocHeaderSize / 2):
+ *   再加上半个 header 大小的补偿,内存占用和字节内联的一个平衡点
+ * - + RyanJsonFlagSize:
+ *   在对齐前把前面扣掉的 flag 补回,便于统一按总尺寸做 align。
+ * - RyanJsonAlign(..., RyanJsonMallocAlign):
+ *   将总尺寸按 RyanJsonMallocAlign 向上对齐。
+ * - - RyanJsonFlagSize:
+ *   对齐后再扣掉 flag,得到最终“字符串可用字节数”。
+ *
+ * 其中 rawSize 里的 `-RyanJsonFlagSize` 与 `+RyanJsonFlagSize` 会抵消,
+ * 因此与历史公式完全等价:
+ * inlineSize = RyanJsonAlign(2 * sizeof(void*) + (RyanJsonMallocHeaderSize / 2), RyanJsonMallocAlign)
+ *            - RyanJsonFlagSize
+ *
+ * 32 位示例(sizeof(void*)=4, RyanJsonFlagSize=1, RyanJsonMallocAlign=4):
+ * - RyanJsonMallocHeaderSize=8:  Align(12, 4) - 1 = 11
+ * - RyanJsonMallocHeaderSize=12: Align(14, 4) - 1 = 15
+ */
+#define RyanJsonInlineStringSize                                                                                                           \
+	(RyanJsonAlign((sizeof(void *) - RyanJsonFlagSize + sizeof(void *) + (RyanJsonMallocHeaderSize / 2) + RyanJsonFlagSize),           \
+		       RyanJsonMallocAlign) -                                                                                              \
+	 RyanJsonFlagSize)
+// static uint32_t RyanJsonInlineStringSize(void)
+// {
+// 	// Step1: 先计算“两个指针槽 + 补偿值”的基础尺寸(暂不做对齐)
+// 	uint32_t baseSize = sizeof(void *) - RyanJsonFlagSize;     // 一个指针槽,先扣掉 flag
+// 	baseSize += sizeof(void *) + RyanJsonMallocHeaderSize / 2; // 再加一个指针槽和半个 header 补偿
+// 	// Step2: 对齐前把 flag 加回,对齐后再减回,得到最终可用字节数
+// 	return (uint32_t)(RyanJsonAlign(baseSize + RyanJsonFlagSize, RyanJsonMallocAlign) - RyanJsonFlagSize);
+// }
+#endif
+
+// 该结构字段语义需与 struct RyanJsonNode 保持一致
+typedef struct
+{
+	const char *key;
+	const char *strValue;
+
+	RyanjsonType_e type;
+	RyanJsonBool_e boolIsTrueFlag;
+	RyanJsonBool_e numberIsDoubleFlag;
+} RyanJsonNodeInfo_t;
+
+RyanJsonInternalApi RyanJsonMalloc_t jsonMalloc;
+RyanJsonInternalApi RyanJsonFree_t jsonFree;
+RyanJsonInternalApi RyanJsonRealloc_t jsonRealloc;
+
+RyanJsonInternalApi uint8_t *RyanJsonInternalGetStrPtrModeBuf(RyanJson_t pJson);
+RyanJsonInternalApi void RyanJsonInternalSetStrPtrModeBuf(RyanJson_t pJson, uint8_t *heapPtr);
+RyanJsonInternalApi uint8_t *RyanJsonInternalGetStrPtrModeBufAt(RyanJson_t pJson, uint32_t index);
+RyanJsonInternalApi uint8_t RyanJsonInternalDecodeKeyLenField(uint8_t encoded);
+RyanJsonInternalApi uint8_t RyanJsonInternalCalcLenBytes(uint32_t len);
+RyanJsonInternalApi uint32_t RyanJsonInternalGetKeyLen(RyanJson_t pJson);
+RyanJsonInternalApi void *RyanJsonInternalGetValue(RyanJson_t pJson);
+
+RyanJsonInternalApi RyanJson_t RyanJsonInternalNewNode(RyanJsonNodeInfo_t *info);
+RyanJsonInternalApi void RyanJsonInternalListInsertAfter(RyanJson_t parent, RyanJson_t prev, RyanJson_t item);
+RyanJsonInternalApi RyanJson_t RyanJsonInternalGetParent(RyanJson_t pJson);
+RyanJsonInternalApi RyanJsonBool_e RyanJsonInternalChangeString(RyanJson_t pJson, RyanJsonBool_e isNew, const char *key,
+								const char *strValue);
+RyanJsonInternalApi RyanJson_t RyanJsonInternalCreateObjectAndKey(const char *key);
+RyanJsonInternalApi RyanJson_t RyanJsonInternalCreateArrayAndKey(const char *key);
+// 内部接口:仅用于容器 children 指针改写(调用方需保证 pJson 为 array/object 且非 NULL)
+RyanJsonInternalApi RyanJsonBool_e RyanJsonInternalChangeObjectValue(RyanJson_t pJson, RyanJson_t objValue);
+RyanJsonInternalApi RyanJsonBool_e RyanJsonInternalStrEq(const char *s1, const char *s2);
+RyanJsonInternalApi void *RyanJsonInternalExpandRealloc(void *block, uint32_t oldSize, uint32_t newSize); // Keep if used across modules
+
+RyanJsonInternalApi RyanJsonBool_e RyanJsonInternalParseDoubleRaw(const uint8_t *currentPtr, uint32_t remainSize, double *numberValuePtr);
+
+#ifdef RyanJsonLinuxTestEnv
+#undef RyanJsonSnprintf
+RyanJsonInternalApi RyanJsonBool_e RyanJsonFuzzerShouldFail(uint32_t probability);
+RyanJsonInternalApi int32_t RyanJsonSnprintf(char *buf, size_t size, const char *fmt, ...);
+RyanJsonInternalApi uint32_t RyanJsonRandRange(uint32_t min, uint32_t max);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 733 - 0
RyanJson/RyanJsonItem.c

@@ -0,0 +1,733 @@
+#include "RyanJsonInternal.h"
+
+/**
+ * @brief 在对象节点中按 key 查找子节点
+ *
+ * @param pJson 对象节点
+ * @param key 目标 key
+ * @param prevOut 输出前驱节点,可为 NULL
+ * @return RyanJson_t 命中节点,未命中返回 NULL
+ */
+static RyanJson_t RyanJsonFindNodeByKey(RyanJson_t pJson, const char *key, RyanJson_t *prevOut)
+{
+	RyanJsonCheckAssert(NULL != pJson && NULL != key);
+	RyanJsonCheckReturnNull(_checkType(pJson, RyanJsonTypeObject));
+
+	RyanJson_t prev = NULL;
+	RyanJson_t nextItem = RyanJsonGetObjectValue(pJson);
+
+	while (nextItem)
+	{
+		// 对象子节点按约定必须带 key,异常场景下直接返回,避免继续访问无效数据
+		RyanJsonCheckAssert(RyanJsonIsKey(nextItem));
+		if (RyanJsonTrue == RyanJsonInternalStrEq(RyanJsonGetKey(nextItem), key))
+		{
+			if (prevOut) { *prevOut = prev; }
+			return nextItem;
+		}
+		prev = nextItem;
+		nextItem = RyanJsonGetNext(nextItem);
+	}
+
+	return NULL;
+}
+
+/**
+ * @brief 用新节点替换旧节点并维护链关系
+ *
+ * @param prev 旧节点前驱,可为 NULL
+ * @param oldItem 旧节点
+ * @param newItem 新节点
+ * @return RyanJsonBool_e 替换是否成功
+ */
+static RyanJsonBool_e RyanJsonReplaceNode(RyanJson_t prev, RyanJson_t oldItem, RyanJson_t newItem)
+{
+	RyanJsonCheckAssert(NULL != oldItem && NULL != newItem);
+
+	// 复制 IsLast 标志位
+	if (RyanJsonGetPayloadIsLastByFlag(oldItem)) { RyanJsonSetPayloadIsLastByFlag(newItem, 1); }
+	else
+	{
+		RyanJsonSetPayloadIsLastByFlag(newItem, 0);
+	}
+
+	// 链接前驱节点
+	if (NULL != prev) { prev->next = newItem; }
+
+	// 链接后继节点
+	// 即使指向的是父节点(IsLast=1),我们也把指针复制过来,保持线索化结构
+	newItem->next = oldItem->next;
+
+	oldItem->next = NULL;
+	RyanJsonSetPayloadIsLastByFlag(oldItem, 0);
+	return RyanJsonTrue;
+}
+
+#if true == RyanJsonStrictObjectKeyCheck
+/**
+ * @brief 检查对象中是否存在重复 key(可忽略指定节点)
+ *
+ * @param pJson 对象节点
+ * @param key 目标 key
+ * @param skipItem 需跳过的节点
+ * @return RyanJsonBool_e 是否存在冲突
+ */
+static RyanJsonBool_e RyanJsonObjectHasKeyConflict(RyanJson_t pJson, const char *key, RyanJson_t skipItem)
+{
+	RyanJson_t item = RyanJsonGetObjectValue(pJson);
+	while (NULL != item)
+	{
+		if (item != skipItem)
+		{
+			// 对象节点理论上必须带 key,容错处理避免 release 下异常访问
+			RyanJsonCheckAssert(RyanJsonTrue == RyanJsonIsKey(item));
+			if (RyanJsonTrue == RyanJsonInternalStrEq(RyanJsonGetKey(item), key)) { return RyanJsonTrue; }
+		}
+		item = RyanJsonGetNext(item);
+	}
+
+	return RyanJsonFalse;
+}
+#endif
+
+/**
+ * @brief 为容器插入场景创建包装节点
+ */
+static RyanJson_t RyanJsonCreateItem(const char *key, RyanJson_t item)
+{
+	RyanJsonCheckAssert(NULL != item);
+
+	RyanjsonType_e type = RyanJsonGetType(item);
+	RyanJsonNodeInfo_t nodeInfo = {
+		.type = (RyanJsonTypeArray == type) ? RyanJsonTypeArray : RyanJsonTypeObject,
+		.key = key,
+	};
+
+	RyanJson_t newItem = RyanJsonInternalNewNode(&nodeInfo);
+	RyanJsonCheckReturnNull(NULL != newItem);
+
+	if (RyanJsonTypeArray == type || RyanJsonTypeObject == type)
+	{
+		// 转移子节点所有权
+		RyanJson_t children = RyanJsonGetObjectValue(item);
+		RyanJsonInternalChangeObjectValue(newItem, children);
+		RyanJsonInternalChangeObjectValue(item, NULL);
+
+		// 更新线索化链表:最后一个子节点的 next 指向新父节点 (newItem)
+		if (children)
+		{
+			RyanJson_t last = children;
+			while (RyanJsonGetNext(last))
+			{
+				last = RyanJsonGetNext(last);
+			}
+			last->next = newItem;
+		}
+
+		// 销毁旧节点(已剥离子节点)
+		// 必须切断 next 指针,防止 RyanJsonDelete 继续遍历
+		item->next = NULL;
+		RyanJsonDelete(item);
+	}
+	else
+	{
+		// 原始类型包装:将 item 作为 newItem 的唯一子节点
+		RyanJsonInternalChangeObjectValue(newItem, item);
+		item->next = newItem; // 最后一个节点指向父节点
+		RyanJsonSetPayloadIsLastByFlag(item, 1);
+	}
+
+	return newItem;
+}
+
+/**
+ * @brief 基础创建接口(语义直观,统一说明)
+ *
+ * 约定:
+ * - key 可为 NULL,表示无 key 节点(如数组元素)
+ * - 返回值为新建节点,失败返回 NULL
+ */
+RyanJson_t RyanJsonCreateNull(const char *key)
+{
+	RyanJsonNodeInfo_t nodeInfo = {.type = RyanJsonTypeNull, .key = key};
+	return RyanJsonInternalNewNode(&nodeInfo);
+}
+RyanJson_t RyanJsonCreateBool(const char *key, RyanJsonBool_e boolean)
+{
+	RyanJsonNodeInfo_t nodeInfo = {.type = RyanJsonTypeBool, .key = key, .boolIsTrueFlag = boolean};
+	return RyanJsonInternalNewNode(&nodeInfo);
+}
+RyanJson_t RyanJsonCreateInt(const char *key, int32_t number)
+{
+	RyanJsonNodeInfo_t nodeInfo = {.type = RyanJsonTypeNumber, .key = key, .numberIsDoubleFlag = RyanJsonFalse};
+
+	RyanJson_t item = RyanJsonInternalNewNode(&nodeInfo);
+	RyanJsonCheckReturnNull(NULL != item);
+
+	RyanJsonChangeIntValue(item, number);
+	return item;
+}
+RyanJson_t RyanJsonCreateDouble(const char *key, double number)
+{
+	RyanJsonNodeInfo_t nodeInfo = {.type = RyanJsonTypeNumber, .key = key, .numberIsDoubleFlag = RyanJsonTrue};
+	RyanJson_t item = RyanJsonInternalNewNode(&nodeInfo);
+	RyanJsonCheckReturnNull(NULL != item);
+
+	RyanJsonChangeDoubleValue(item, number);
+	return item;
+}
+RyanJson_t RyanJsonCreateString(const char *key, const char *string)
+{
+	RyanJsonCheckReturnNull(NULL != string);
+
+	RyanJsonNodeInfo_t nodeInfo = {.type = RyanJsonTypeString, .key = key, .strValue = string};
+	return RyanJsonInternalNewNode(&nodeInfo);
+}
+RyanJsonInternalApi RyanJson_t RyanJsonInternalCreateObjectAndKey(const char *key)
+{
+	RyanJsonNodeInfo_t nodeInfo = {.type = RyanJsonTypeObject, .key = key};
+	return RyanJsonInternalNewNode(&nodeInfo);
+}
+RyanJson_t RyanJsonCreateObject(void)
+{
+	return RyanJsonInternalCreateObjectAndKey(NULL);
+}
+RyanJsonInternalApi RyanJson_t RyanJsonInternalCreateArrayAndKey(const char *key)
+{
+	RyanJsonNodeInfo_t nodeInfo = {.type = RyanJsonTypeArray, .key = key};
+	return RyanJsonInternalNewNode(&nodeInfo);
+}
+RyanJson_t RyanJsonCreateArray(void)
+{
+	return RyanJsonInternalCreateArrayAndKey(NULL);
+}
+
+/**
+ * @brief 类型/属性判断接口(语义直观,统一说明)
+ *
+ * 约定:
+ * - 参数为 NULL 时统一返回 RyanJsonFalse
+ */
+RyanJsonBool_e RyanJsonIsKey(RyanJson_t pJson)
+{
+	return RyanJsonMakeBool(NULL != pJson && RyanJsonGetPayloadEncodeKeyLenByFlag(pJson));
+}
+RyanJsonBool_e RyanJsonIsNull(RyanJson_t pJson)
+{
+	return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeNull == RyanJsonGetType(pJson));
+}
+RyanJsonBool_e RyanJsonIsBool(RyanJson_t pJson)
+{
+	return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeBool == RyanJsonGetType(pJson));
+}
+RyanJsonBool_e RyanJsonIsNumber(RyanJson_t pJson)
+{
+	return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeNumber == RyanJsonGetType(pJson));
+}
+RyanJsonBool_e RyanJsonIsString(RyanJson_t pJson)
+{
+	return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeString == RyanJsonGetType(pJson));
+}
+RyanJsonBool_e RyanJsonIsArray(RyanJson_t pJson)
+{
+	return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeArray == RyanJsonGetType(pJson));
+}
+RyanJsonBool_e RyanJsonIsObject(RyanJson_t pJson)
+{
+	return RyanJsonMakeBool(NULL != pJson && RyanJsonTypeObject == RyanJsonGetType(pJson));
+}
+RyanJsonBool_e RyanJsonIsInt(RyanJson_t pJson)
+{
+	return RyanJsonMakeBool(RyanJsonIsNumber(pJson) && (RyanJsonFalse == RyanJsonGetPayloadNumberIsDoubleByFlag(pJson)));
+}
+RyanJsonBool_e RyanJsonIsDouble(RyanJson_t pJson)
+{
+	return RyanJsonMakeBool(RyanJsonIsNumber(pJson) && (RyanJsonTrue == RyanJsonGetPayloadNumberIsDoubleByFlag(pJson)));
+}
+
+/**
+ * @brief 检查 item 是否为游离节点(未挂到任何树)
+ *
+ * @param item 待检查节点
+ * @return RyanJsonBool_e 是否为游离节点
+ */
+RyanJsonBool_e RyanJsonIsDetachedItem(RyanJson_t item)
+{
+	RyanJsonCheckReturnFalse(NULL != item);
+	RyanJsonCheckReturnFalse(NULL == item->next);
+	RyanJsonCheckReturnFalse(!RyanJsonGetPayloadIsLastByFlag(item));
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 获取节点 key 字符串指针
+ *
+ * @param pJson 目标节点
+ * @return char* key 字符串,失败返回 NULL
+ */
+char *RyanJsonGetKey(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+	if (RyanJsonFalse == RyanJsonGetPayloadStrIsPtrByFlag(pJson))
+	{
+		uint8_t keyFieldLen = RyanJsonInternalDecodeKeyLenField(RyanJsonGetPayloadEncodeKeyLenByFlag(pJson));
+		RyanJsonCheckAssert(keyFieldLen <= RyanJsonKeyFeidLenMaxSize);
+		return (char *)(RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + keyFieldLen);
+	}
+
+	return (char *)RyanJsonInternalGetStrPtrModeBufAt(pJson, 0);
+}
+
+/**
+ * @brief 值读取接口(语义直观,统一说明)
+ *
+ * 约定:
+ * - 调用前需保证 pJson 非 NULL 且类型匹配(先用 RyanJsonIsXXXX 判断)
+ * - 非匹配类型场景不保证返回值语义
+ */
+char *RyanJsonGetStringValue(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+
+	uint32_t len = 0;
+
+	if (RyanJsonFalse == RyanJsonGetPayloadStrIsPtrByFlag(pJson))
+	{
+		uint8_t keyFieldLen = RyanJsonInternalDecodeKeyLenField(RyanJsonGetPayloadEncodeKeyLenByFlag(pJson));
+		RyanJsonCheckAssert(keyFieldLen <= RyanJsonKeyFeidLenMaxSize);
+
+		len += keyFieldLen;
+		if (RyanJsonIsKey(pJson)) { len += RyanJsonInternalGetKeyLen(pJson) + 1U; }
+		return (char *)(RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + len);
+	}
+
+	if (RyanJsonIsKey(pJson)) { len = RyanJsonInternalGetKeyLen(pJson) + 1U; }
+
+	return (char *)RyanJsonInternalGetStrPtrModeBufAt(pJson, len);
+}
+RyanJsonBool_e RyanJsonGetBoolValue(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+	return RyanJsonGetPayloadBoolValueByFlag(pJson);
+}
+int32_t RyanJsonGetIntValue(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+
+	int32_t intValue;
+	RyanJsonMemcpy(&intValue, RyanJsonInternalGetValue(pJson), sizeof(intValue));
+	return intValue;
+}
+double RyanJsonGetDoubleValue(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+
+	double doubleValue;
+	RyanJsonMemcpy(&doubleValue, RyanJsonInternalGetValue(pJson), sizeof(doubleValue));
+	return doubleValue;
+}
+RyanJson_t RyanJsonGetObjectValue(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+
+	RyanJson_t objValue;
+	RyanJsonMemcpy((void *)&objValue, RyanJsonInternalGetValue(pJson), sizeof(void *));
+	return objValue;
+}
+RyanJson_t RyanJsonGetArrayValue(RyanJson_t pJson)
+{
+	return RyanJsonGetObjectValue(pJson);
+}
+
+/**
+ * @brief 公共值修改接口(语义直观,统一说明)
+ *
+ * 约定:
+ * - 公共 Change 接口会做基础参数/类型校验,失败返回 RyanJsonFalse
+ * - 数值/布尔修改为原位写入
+ * - key/string 修改会触发字符串存储布局更新
+ */
+RyanJsonBool_e RyanJsonChangeKey(RyanJson_t pJson, const char *key)
+{
+	RyanJsonCheckReturnFalse(NULL != pJson && NULL != key);
+	RyanJsonCheckReturnFalse(RyanJsonIsKey(pJson));
+	return RyanJsonInternalChangeString(pJson, RyanJsonFalse, key, RyanJsonIsString(pJson) ? RyanJsonGetStringValue(pJson) : NULL);
+}
+
+/**
+ * @brief 修改字符串节点的 value
+ *
+ * @param pJson 字符串节点
+ * @param strValue 新 strValue
+ * @return RyanJsonBool_e 修改是否成功
+ */
+RyanJsonBool_e RyanJsonChangeStringValue(RyanJson_t pJson, const char *strValue)
+{
+	RyanJsonCheckReturnFalse(NULL != pJson && NULL != strValue);
+	RyanJsonCheckReturnFalse(RyanJsonIsString(pJson));
+	return RyanJsonInternalChangeString(pJson, RyanJsonFalse, RyanJsonIsKey(pJson) ? RyanJsonGetKey(pJson) : NULL, strValue);
+}
+RyanJsonBool_e RyanJsonChangeIntValue(RyanJson_t pJson, int32_t number)
+{
+	RyanJsonCheckReturnFalse(NULL != pJson);
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonIsInt(pJson));
+	RyanJsonMemcpy(RyanJsonInternalGetValue(pJson), &number, sizeof(number));
+	return RyanJsonTrue;
+}
+RyanJsonBool_e RyanJsonChangeDoubleValue(RyanJson_t pJson, double number)
+{
+	RyanJsonCheckReturnFalse(NULL != pJson);
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonIsDouble(pJson));
+	RyanJsonMemcpy(RyanJsonInternalGetValue(pJson), &number, sizeof(number));
+	return RyanJsonTrue;
+}
+RyanJsonBool_e RyanJsonChangeBoolValue(RyanJson_t pJson, RyanJsonBool_e boolean)
+{
+	RyanJsonCheckReturnFalse(NULL != pJson);
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonIsBool(pJson));
+	RyanJsonSetPayloadBoolValueByFlag(pJson, boolean);
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 设置容器节点的首子节点指针(内部接口)
+ *
+ * @param pJson 容器节点(Object 或 Array)
+ * @param objValue 新的首子节点,可为 NULL
+ * @return RyanJsonBool_e 设置是否成功
+ */
+RyanJsonInternalApi RyanJsonBool_e RyanJsonInternalChangeObjectValue(RyanJson_t pJson, RyanJson_t objValue)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+	RyanJsonCheckAssert(RyanJsonIsObject(pJson) || RyanJsonIsArray(pJson));
+	RyanJsonMemcpy(RyanJsonInternalGetValue(pJson), (void *)&objValue, sizeof(void *));
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 向对象追加容器节点(array/object)
+ *
+ * @param pJson 对象节点
+ * @param key 新子节点 key
+ * @param item 待追加节点(要求游离)
+ * @return RyanJsonBool_e 添加是否成功
+ */
+RyanJsonBool_e RyanJsonAddItemToObject(RyanJson_t pJson, const char *key, RyanJson_t item)
+{
+	RyanJsonCheckReturnFalse(NULL != item);
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonIsDetachedItem(item));
+
+	// AddItem 仅支持容器类型(Array/Object),标量请使用 AddInt/AddString 等接口
+	RyanjsonType_e type = RyanJsonGetType(item);
+	if (RyanJsonTypeArray != type && RyanJsonTypeObject != type)
+	{
+		RyanJsonDelete(item);
+		return RyanJsonFalse;
+	}
+
+	RyanJson_t pItem = RyanJsonCreateItem(key, item);
+	RyanJsonCheckCode(NULL != pItem, {
+		RyanJsonDelete(item);
+		return RyanJsonFalse;
+	});
+	return RyanJsonInsert(pJson, RyanJsonAddPosition, pItem);
+}
+
+/**
+ * @brief 按索引替换子节点
+ *
+ * @param pJson 父节点(数组或对象)
+ * @param index 目标索引
+ * @param item 新节点(要求游离)
+ * @return RyanJsonBool_e 替换是否成功
+ */
+RyanJsonBool_e RyanJsonReplaceByIndex(RyanJson_t pJson, uint32_t index, RyanJson_t item)
+{
+	RyanJsonCheckReturnFalse(NULL != pJson && NULL != item);
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonIsDetachedItem(item));
+
+	RyanJsonCheckReturnFalse(_checkType(pJson, RyanJsonTypeArray) || _checkType(pJson, RyanJsonTypeObject));
+	if (_checkType(pJson, RyanJsonTypeObject)) { RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonIsKey(item)); }
+
+	RyanJson_t prev = NULL;
+	RyanJson_t nextItem = RyanJsonGetObjectValue(pJson);
+	RyanJsonCheckReturnFalse(NULL != nextItem);
+
+	// 定位目标索引节点
+	while (index > 0)
+	{
+		prev = nextItem;
+		nextItem = RyanJsonGetNext(nextItem);
+		index--;
+		RyanJsonCheckReturnFalse(NULL != nextItem);
+	}
+
+	// 严格模式下:Object 不允许替换成重复 key
+#if true == RyanJsonStrictObjectKeyCheck
+	if (_checkType(pJson, RyanJsonTypeObject))
+	{
+		RyanJsonCheckReturnFalse(RyanJsonFalse == RyanJsonObjectHasKeyConflict(pJson, RyanJsonGetKey(item), nextItem));
+	}
+#endif
+
+	RyanJsonReplaceNode(prev, nextItem, item);
+	if (NULL == prev) { RyanJsonInternalChangeObjectValue(pJson, item); }
+
+	RyanJsonDelete(nextItem);
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 按 key 替换对象子节点
+ *
+ * @param pJson 对象节点
+ * @param key 目标 key
+ * @param item 新节点(要求游离)
+ * @return RyanJsonBool_e 替换是否成功
+ */
+RyanJsonBool_e RyanJsonReplaceByKey(RyanJson_t pJson, const char *key, RyanJson_t item)
+{
+	RyanJsonCheckReturnFalse(NULL != pJson && NULL != key && NULL != item);
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonIsDetachedItem(item));
+
+	RyanJson_t prev = NULL;
+	RyanJson_t nextItem = RyanJsonFindNodeByKey(pJson, key, &prev);
+	RyanJsonCheckReturnFalse(NULL != nextItem);
+
+	// 若传入节点没有 key,则构造一个带 key 的包装节点
+	if (RyanJsonFalse == RyanJsonIsKey(item))
+	{
+		item = RyanJsonCreateItem(key, item);
+		RyanJsonCheckReturnFalse(NULL != item);
+	}
+	else
+	{
+		if (RyanJsonTrue != RyanJsonInternalStrEq(RyanJsonGetKey(item), key))
+		{
+			RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonChangeKey(item, key));
+		}
+	}
+
+	RyanJsonReplaceNode(prev, nextItem, item);
+	if (NULL == prev) { RyanJsonInternalChangeObjectValue(pJson, item); }
+
+	RyanJsonDelete(nextItem);
+
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 按索引获取子节点
+ *
+ * @param pJson 父节点(数组或对象)
+ * @param index 子节点索引
+ * @return RyanJson_t 命中节点,失败返回 NULL
+ */
+RyanJson_t RyanJsonGetObjectByIndex(RyanJson_t pJson, uint32_t index)
+{
+	RyanJsonCheckReturnNull(NULL != pJson);
+
+	RyanJsonCheckReturnNull(_checkType(pJson, RyanJsonTypeArray) || _checkType(pJson, RyanJsonTypeObject));
+
+	RyanJson_t nextItem = RyanJsonGetObjectValue(pJson);
+	RyanJsonCheckReturnNull(NULL != nextItem);
+	while (index > 0)
+	{
+		index--;
+		nextItem = RyanJsonGetNext(nextItem);
+		RyanJsonCheckReturnNull(NULL != nextItem);
+	}
+
+	return nextItem;
+}
+
+/**
+ * @brief 按 key 获取对象子节点
+ *
+ * @param pJson 对象节点
+ * @param key 目标 key
+ * @return RyanJson_t 命中节点,失败返回 NULL
+ */
+RyanJson_t RyanJsonGetObjectByKey(RyanJson_t pJson, const char *key)
+{
+	RyanJsonCheckReturnNull(NULL != pJson && NULL != key);
+	return RyanJsonFindNodeByKey(pJson, key, NULL);
+}
+
+/**
+ * @brief 按索引分离子节点(不释放)
+ *
+ * @param pJson 父节点(数组或对象)
+ * @param index 子节点索引
+ * @return RyanJson_t 被分离节点,失败返回 NULL
+ */
+RyanJson_t RyanJsonDetachByIndex(RyanJson_t pJson, uint32_t index)
+{
+	RyanJsonCheckReturnNull(NULL != pJson);
+
+	RyanJsonCheckReturnNull(_checkType(pJson, RyanJsonTypeArray) || _checkType(pJson, RyanJsonTypeObject));
+
+	RyanJson_t prev = NULL;
+	RyanJson_t nextItem = RyanJsonGetObjectValue(pJson);
+	RyanJsonCheckReturnNull(NULL != nextItem);
+
+	while (index > 0)
+	{
+		prev = nextItem;
+		nextItem = RyanJsonGetNext(nextItem);
+		index--;
+		RyanJsonCheckReturnNull(NULL != nextItem);
+	}
+
+	// 维护线索化链表关系
+	RyanJson_t trueNext = RyanJsonGetNext(nextItem);
+
+	if (NULL != prev)
+	{
+		prev->next = trueNext;
+		// trueNext 为 NULL 表示 nextItem 原本是尾节点。
+		// 分离后 prev 成为新尾节点,需要回指父节点并设置 IsLast。
+		if (NULL == trueNext)
+		{
+			RyanJsonSetPayloadIsLastByFlag(prev, 1);
+			prev->next = pJson; // 指向父节点
+		}
+	}
+	else
+	{
+		RyanJsonInternalChangeObjectValue(pJson, trueNext);
+	}
+
+	nextItem->next = NULL;
+	RyanJsonSetPayloadIsLastByFlag(nextItem, 0);
+
+	return nextItem;
+}
+
+/**
+ * @brief 按 key 分离对象子节点(不释放)
+ *
+ * @param pJson 对象节点
+ * @param key 目标 key
+ * @return RyanJson_t 被分离节点,失败返回 NULL
+ */
+RyanJson_t RyanJsonDetachByKey(RyanJson_t pJson, const char *key)
+{
+	RyanJsonCheckReturnNull(NULL != pJson && NULL != key);
+
+	RyanJson_t prev = NULL;
+	RyanJson_t nextItem = RyanJsonFindNodeByKey(pJson, key, &prev);
+	RyanJsonCheckReturnNull(NULL != nextItem);
+
+	// 维护线索化链表关系
+	RyanJson_t trueNext = RyanJsonGetNext(nextItem);
+
+	if (NULL != prev)
+	{
+		prev->next = trueNext;
+		// trueNext 为 NULL 表示 nextItem 原本是尾节点。
+		// 分离后 prev 成为新尾节点,需要回指父节点并设置 IsLast。
+		if (NULL == trueNext)
+		{
+			RyanJsonSetPayloadIsLastByFlag(prev, 1);
+			prev->next = pJson; // 指向父节点
+		}
+	}
+	else // 分离的是首节点
+	{
+		RyanJsonInternalChangeObjectValue(pJson, trueNext);
+	}
+
+	nextItem->next = NULL;
+	RyanJsonSetPayloadIsLastByFlag(nextItem, 0);
+
+	return nextItem;
+}
+
+/**
+ * @brief 按索引删除子节点
+ *
+ * @param pJson 父节点(数组或对象)
+ * @param index 子节点索引
+ * @return RyanJsonBool_e 删除是否成功
+ */
+RyanJsonBool_e RyanJsonDeleteByIndex(RyanJson_t pJson, uint32_t index)
+{
+	RyanJsonCheckReturnFalse(NULL != pJson);
+
+	RyanJson_t nextItem = RyanJsonDetachByIndex(pJson, index);
+	RyanJsonCheckReturnFalse(NULL != nextItem);
+
+	RyanJsonDelete(nextItem);
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 按 key 删除对象子节点
+ *
+ * @param pJson 对象节点
+ * @param key 目标 key
+ * @return RyanJsonBool_e 删除是否成功
+ */
+RyanJsonBool_e RyanJsonDeleteByKey(RyanJson_t pJson, const char *key)
+{
+	RyanJsonCheckReturnFalse(NULL != pJson && NULL != key);
+
+	RyanJson_t nextItem = RyanJsonDetachByKey(pJson, key);
+	RyanJsonCheckReturnFalse(NULL != nextItem);
+
+	RyanJsonDelete(nextItem);
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 向容器按索引插入子节点
+ *
+ * @param pJson 父节点(数组或对象)
+ * @param index 插入位置,超范围等价尾插
+ * @param item 待插入节点(要求游离)
+ * @return RyanJsonBool_e 插入是否成功
+ */
+RyanJsonBool_e RyanJsonInsert(RyanJson_t pJson, uint32_t index, RyanJson_t item)
+{
+	RyanJson_t nextItem = NULL;
+	RyanJson_t prev = NULL;
+
+	RyanJsonCheckReturnFalse(NULL != item);
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonIsDetachedItem(item));
+	RyanJsonCheckCode(NULL != pJson, { goto error__; });
+
+	RyanJsonCheckCode(_checkType(pJson, RyanJsonTypeArray) || (_checkType(pJson, RyanJsonTypeObject) && RyanJsonIsKey(item)), {
+		jsonLog("error__ 不是正确类型 %d\r\n", index);
+		goto error__;
+	});
+
+	// 严格模式下:Object 从插入入口拒绝重复 key
+#if true == RyanJsonStrictObjectKeyCheck
+	if (_checkType(pJson, RyanJsonTypeObject))
+	{
+		RyanJsonCheckCode(RyanJsonFalse == RyanJsonObjectHasKeyConflict(pJson, RyanJsonGetKey(item), NULL), { goto error__; });
+	}
+#endif
+
+	nextItem = RyanJsonGetObjectValue(pJson);
+	while (nextItem && index > 0)
+	{
+		prev = nextItem;
+		nextItem = RyanJsonGetNext(nextItem);
+		index--;
+	}
+
+	if (NULL != prev) { RyanJsonInternalListInsertAfter(pJson, prev, item); }
+	else
+	{
+		// prev 为 NULL 表示头插,交给统一链表插入函数处理
+		RyanJsonInternalListInsertAfter(pJson, NULL, item);
+	}
+
+	return RyanJsonTrue;
+
+error__:
+	RyanJsonDelete(item);
+	return RyanJsonFalse;
+}

+ 791 - 0
RyanJson/RyanJsonParse.c

@@ -0,0 +1,791 @@
+#include "RyanJsonInternal.h"
+
+typedef struct
+{
+	const uint8_t *currentPtr; // 待解析字符串地址
+	uint32_t remainSize;       // 待解析字符串剩余长度
+} RyanJsonParseBuffer;
+
+// 相关宏定义已迁移到 RyanJsonInternal.h
+#define parseBufAdvanceCurrentPrt(parseBuf, bytesToAdvance)                                                                                \
+	do                                                                                                                                 \
+	{                                                                                                                                  \
+		(parseBuf)->currentPtr += (bytesToAdvance);                                                                                \
+		(parseBuf)->remainSize -= (bytesToAdvance);                                                                                \
+	} while (0)
+
+// 是否还有可读的待解析文本在指定索引处
+#define parseBufHasRemainAtIndex(parseBuf, index) ((index) < (parseBuf)->remainSize)
+// 是否还有可读的待解析文本
+#define parseBufHasRemainBytes(parseBuf, bytes)   ((parseBuf)->remainSize >= (bytes))
+#define parseBufHasRemain(parseBuf)               parseBufHasRemainBytes(parseBuf, 1)
+
+/**
+ * @brief 尝试向前移动解析缓冲区指针
+ */
+static inline RyanJsonBool_e RyanJsonParseBufTryAdvanceCurrentPtr(RyanJsonParseBuffer *parseBuf, uint32_t bytesToAdvance)
+{
+	RyanJsonCheckAssert(NULL != parseBuf);
+
+#ifdef isEnableFuzzer
+	if (RyanJsonFuzzerShouldFail(800)) { bytesToAdvance = UINT32_MAX; }
+#endif
+
+	if (parseBufHasRemainBytes(parseBuf, bytesToAdvance))
+	{
+		parseBufAdvanceCurrentPrt(parseBuf, bytesToAdvance);
+		return RyanJsonTrue;
+	}
+
+	return RyanJsonFalse;
+}
+
+/**
+ * @brief 跳过无意义的字符
+ */
+static inline RyanJsonBool_e RyanJsonParseBufSkipWhitespace(RyanJsonParseBuffer *parseBuf)
+{
+	RyanJsonCheckAssert(NULL != parseBuf);
+
+#ifdef isEnableFuzzer
+	RyanJsonCheckReturnFalse(!RyanJsonFuzzerShouldFail(1500));
+#endif
+
+	while (parseBufHasRemain(parseBuf))
+	{
+		uint8_t cursor = *parseBuf->currentPtr;
+		if (' ' == cursor || '\n' == cursor || '\r' == cursor || '\t' == cursor)
+		{
+			RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+		}
+		else
+		{
+			break;
+		}
+	}
+
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 解析 4 位十六进制文本(XXXX)
+ *
+ * @param text 十六进制文本起始地址
+ * @param value 解析结果输出
+ * @return RyanJsonBool_e 解析是否成功
+ */
+static RyanJsonBool_e RyanJsonParseHex(const uint8_t *text, uint32_t *value)
+{
+	RyanJsonCheckAssert(NULL != text && NULL != value);
+	uint32_t valueTemp = 0;
+
+	for (uint8_t i = 0; i < 4; ++i)
+	{
+		switch (text[i])
+		{
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9': valueTemp = (valueTemp << 4) + (text[i] - '0'); break;
+		case 'a':
+		case 'b':
+		case 'c':
+		case 'd':
+		case 'e':
+		case 'f': valueTemp = (valueTemp << 4) + 10 + (text[i] - 'a'); break;
+		case 'A':
+		case 'B':
+		case 'C':
+		case 'D':
+		case 'E':
+		case 'F': valueTemp = (valueTemp << 4) + 10 + (text[i] - 'A'); break;
+		default: return RyanJsonFalse;
+		}
+	}
+
+	*value = valueTemp;
+
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 解析 Json number(含符号/小数/指数)
+ *
+ * @param parseBuf 解析缓冲区
+ * @param numberValuePtr 输出数值
+ * @param isIntPtr 输出是否为整数
+ * @return RyanJsonBool_e 解析是否成功
+ */
+static RyanJsonBool_e RyanJsonInternalParseDouble(RyanJsonParseBuffer *parseBuf, double *numberValuePtr, RyanJsonBool_e *isIntPtr)
+{
+	RyanJsonCheckAssert(NULL != parseBuf && NULL != numberValuePtr && NULL != isIntPtr);
+
+	double number = 0;
+	int32_t scale = 0;
+	int32_t e_sign = 1;
+	int32_t e_scale = 0;
+	RyanJsonBool_e isNegative = RyanJsonFalse;
+	RyanJsonBool_e isInt = RyanJsonTrue;
+
+	// 处理符号
+	if ('-' == *parseBuf->currentPtr)
+	{
+		isNegative = RyanJsonTrue;
+		// 这个不会失败因为进来前已经判断过 parseBufHasRemain(parseBuf)
+		RyanJsonAssertAlwaysEval(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+		RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9');
+	}
+
+	// 前导0是非法的
+	if ('0' == *parseBuf->currentPtr)
+	{
+		RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+		// 前导0后面不允许跟数字,比如"0123"
+		if (parseBufHasRemain(parseBuf)) { RyanJsonCheckReturnFalse(*parseBuf->currentPtr < '0' || *parseBuf->currentPtr > '9'); }
+	}
+
+	// 整数部分
+	while (parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9')
+	{
+		number = number * 10.0 + (*parseBuf->currentPtr - '0');
+		// 数值过大导致溢出
+		RyanJsonCheckReturnFalse(isfinite(number));
+		RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+	}
+
+	// 小数部分
+	if (parseBufHasRemain(parseBuf) && '.' == *parseBuf->currentPtr)
+	{
+		RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+		RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9');
+
+		while (parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9')
+		{
+			number = number * 10.0 + (*parseBuf->currentPtr - '0');
+			RyanJsonCheckReturnFalse(isfinite(number));
+			scale--; // 每读一位小数,scale减一
+			RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+		}
+		isInt = RyanJsonFalse;
+	}
+
+	// 指数部分
+	if (parseBufHasRemain(parseBuf) && ('e' == *parseBuf->currentPtr || 'E' == *parseBuf->currentPtr))
+	{
+		RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+		RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf));
+
+		// 只有遇到 +/- 符号时才跳过
+		if ('+' == *parseBuf->currentPtr || '-' == *parseBuf->currentPtr)
+		{
+			e_sign = ('-' == *parseBuf->currentPtr) ? -1 : 1;
+			RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+		}
+
+		RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9');
+
+		while (parseBufHasRemain(parseBuf) && *parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9')
+		{
+			int32_t digit = (int32_t)(*parseBuf->currentPtr - '0');
+			// 防止指数累乘出现有符号溢出
+			RyanJsonCheckReturnFalse(e_scale <= (INT32_MAX / 10));
+			RyanJsonCheckReturnFalse(e_scale < (INT32_MAX / 10) || digit <= (INT32_MAX % 10));
+			e_scale = e_scale * 10 + digit;
+			RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+		}
+		isInt = RyanJsonFalse;
+	}
+
+	// 判断符号
+	if (RyanJsonTrue == isNegative) { number = -number; }
+
+	// 浮点数还需要处理
+	if (RyanJsonFalse == isInt)
+	{
+		// 使用更宽位数拼接指数,避免中间表达式溢出
+		int64_t exponent = (int64_t)scale + (int64_t)e_sign * (int64_t)e_scale;
+		double expFactor = pow(10.0, (double)exponent);
+		number *= expFactor;
+		RyanJsonCheckReturnFalse(isfinite(number));
+	}
+
+	*numberValuePtr = number;
+	*isIntPtr = isInt;
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 解析文本中的数字并创建 Json 节点
+ */
+static RyanJsonBool_e RyanJsonParseNumber(RyanJsonParseBuffer *parseBuf, char *key, RyanJson_t *out)
+{
+	RyanJsonCheckAssert(NULL != parseBuf && NULL != out);
+
+	double number = 0;
+	RyanJsonBool_e isInt = RyanJsonTrue;
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonInternalParseDouble(parseBuf, &number, &isInt));
+
+	// 创建 Json 节点
+	RyanJson_t newItem = NULL;
+	if (RyanJsonTrue == isInt && number >= INT32_MIN && number <= INT32_MAX) { newItem = RyanJsonCreateInt(key, (int32_t)number); }
+	else
+	{
+		newItem = RyanJsonCreateDouble(key, number);
+	}
+
+	RyanJsonCheckReturnFalse(NULL != newItem);
+
+	*out = newItem;
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 预扫描字符串长度并统计是否包含转义字符
+ *
+ * @param parseBuf 解析缓冲区(当前指向起始双引号)
+ * @param lenPtr 输出解码后的字节长度(不含 '\0')
+ * @param hasEscapePtr 输出是否包含转义字符
+ * @return RyanJsonBool_e 扫描是否成功
+ */
+static RyanJsonBool_e RyanJsonParseStringBufferGetLen(RyanJsonParseBuffer *parseBuf, uint32_t *lenPtr, RyanJsonBool_e *hasEscapePtr)
+{
+	RyanJsonCheckAssert(NULL != parseBuf && NULL != lenPtr && NULL != hasEscapePtr);
+
+	uint32_t len = 0;
+	RyanJsonBool_e hasEscape = RyanJsonFalse;
+
+	// 不是字符串
+	RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf) && '\"' == *parseBuf->currentPtr);
+
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1));
+
+	// 获取字符串解码后的实际字节长度
+	for (uint32_t i = 0;;)
+	{
+		RyanJsonCheckReturnFalse(parseBufHasRemainAtIndex(parseBuf, i));
+
+		uint8_t ch = parseBuf->currentPtr[i];
+
+		if ('\"' == ch) { break; }
+
+		// 检查非法控制字符 (ASCII 0–31)
+		RyanJsonCheckReturnFalse(ch > 0x1F);
+
+		if ('\\' != ch)
+		{
+			len++;
+			i++;
+			continue;
+		}
+
+		// 转义字符
+		hasEscape = RyanJsonTrue;
+		RyanJsonCheckReturnFalse(parseBufHasRemainAtIndex(parseBuf, i + 1));
+		uint8_t esc = parseBuf->currentPtr[i + 1];
+		switch (esc)
+		{
+		case '\"':
+		case '\\':
+		case '/':
+		case 'b':
+		case 'f':
+		case 'n':
+		case 'r':
+		case 't':
+			len++;
+			i += 2;
+			break;
+		case 'u': {
+			RyanJsonCheckReturnFalse(parseBufHasRemainAtIndex(parseBuf, i + 5));
+			// 十六进制合法性在后续真正解码时校验
+
+			// 简化:每个 \\uXXXX 预估最大 4 字节 UTF-8
+			len += 4;
+			i += 6;
+			break;
+		}
+		default:
+#ifdef isEnableFuzzer
+			if (RyanJsonFuzzerShouldFail(2))
+			{
+				len++;
+				i += 2;
+				break;
+			}
+#endif
+			return RyanJsonFalse;
+		}
+	}
+
+	*lenPtr = len;
+	*hasEscapePtr = hasEscape;
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 将 Json 字符串片段解码到目标缓冲区
+ *
+ * @note 调用前必须先执行 RyanJsonParseStringBufferGetLen 获取长度与转义信息。
+ */
+static RyanJsonBool_e RyanJsonParseStringBuffer(RyanJsonParseBuffer *parseBuf, char *buffer, uint32_t len, RyanJsonBool_e hasEscape)
+{
+	uint8_t *outCurrentPtr = (uint8_t *)buffer;
+
+	// 获取长度时已确保有结尾引号
+	if (RyanJsonFalse == hasEscape)
+	{
+		RyanJsonMemcpy(outCurrentPtr, parseBuf->currentPtr, len);
+		outCurrentPtr[len] = '\0';
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, len), { goto error__; });
+
+		// 跳过结尾引号
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1), { goto error__; });
+		return RyanJsonTrue;
+	}
+
+	// 预扫描长度阶段已确保字符串一定存在结束引号
+	while ('\"' != *parseBuf->currentPtr)
+	{
+		// 普通字符
+		if ('\\' != *parseBuf->currentPtr)
+		{
+			*outCurrentPtr++ = *parseBuf->currentPtr;
+			RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1), { goto error__; });
+			continue;
+		}
+
+		// 转义字符
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1), { goto error__; });
+		switch (*parseBuf->currentPtr)
+		{
+
+		case 'b': *outCurrentPtr++ = '\b'; break;
+		case 'f': *outCurrentPtr++ = '\f'; break;
+		case 'n': *outCurrentPtr++ = '\n'; break;
+		case 'r': *outCurrentPtr++ = '\r'; break;
+		case 't': *outCurrentPtr++ = '\t'; break;
+		case '\"':
+		case '\\':
+		case '/': *outCurrentPtr++ = *parseBuf->currentPtr; break;
+
+		case 'u': {
+			// 获取 Unicode 字符
+			uint64_t codepoint = 0;
+			RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 4), { goto error__; });
+			uint32_t firstCode = 0;
+			RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseHex(parseBuf->currentPtr - 3, &firstCode), { goto error__; });
+			// 检查是否有效
+			RyanJsonCheckCode(firstCode < 0xDC00 || firstCode > 0xDFFF, { goto error__; });
+
+			if (firstCode >= 0xD800 && firstCode <= 0xDBFF) // UTF16 代理对
+			{
+				RyanJsonCheckCode(parseBufHasRemainAtIndex(parseBuf, 2), { goto error__; });
+
+				RyanJsonCheckCode('\\' == parseBuf->currentPtr[1] && 'u' == parseBuf->currentPtr[2], {
+					goto error__; // 缺少代理的后半部分
+				});
+
+				RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 6), { goto error__; });
+				uint32_t secondCode = 0;
+				// 读取代理后半部分
+				RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseHex(parseBuf->currentPtr - 3, &secondCode),
+						  { goto error__; });
+				RyanJsonCheckCode(secondCode >= 0xDC00 && secondCode <= 0xDFFF, {
+					goto error__; // 无效的代理后半部分
+				});
+
+				codepoint = 0x10000 + (((firstCode & 0x3FF) << 10) | (secondCode & 0x3FF));
+			}
+			else
+			{
+				codepoint = firstCode;
+			}
+
+			// 将 Unicode 码点编码为 UTF-8
+			uint8_t utf8Length;
+			uint8_t firstByteMark;
+			if (codepoint < 0x80)
+			{
+				utf8Length = 1; // ASCII:0xxxxxxx
+				firstByteMark = 0;
+			}
+			else if (codepoint < 0x800)
+			{
+				utf8Length = 2;       // 双字节:110xxxxx 10xxxxxx
+				firstByteMark = 0xC0; // 11000000
+			}
+			else if (codepoint < 0x10000)
+			{
+				utf8Length = 3;       // 三字节:1110xxxx 10xxxxxx 10xxxxxx
+				firstByteMark = 0xE0; // 11100000
+			}
+			else
+			{
+				utf8Length = 4;       // 四字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+				firstByteMark = 0xF0; // 11110000
+			}
+
+			// 先从末尾写 continuation byte(10xxxxxx)
+			for (uint8_t utf8Position = (uint8_t)(utf8Length - 1); utf8Position > 0; utf8Position--)
+			{
+				outCurrentPtr[utf8Position] = (uint8_t)((codepoint | 0x80) & 0xBF); // 10xxxxxx
+				codepoint >>= 6;
+			}
+
+			// 再写首字节
+			if (utf8Length > 1) { outCurrentPtr[0] = (uint8_t)((codepoint | firstByteMark) & 0xFF); }
+			else
+			{
+				outCurrentPtr[0] = (uint8_t)(codepoint & 0x7F);
+			}
+			outCurrentPtr += utf8Length;
+			break;
+		}
+
+		default: goto error__;
+		}
+
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1), { goto error__; });
+	}
+	*outCurrentPtr = '\0';
+
+	RyanJsonCheckAssert('\"' == *parseBuf->currentPtr);
+
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufTryAdvanceCurrentPtr(parseBuf, 1), { goto error__; });
+	return RyanJsonTrue;
+
+error__:
+	return RyanJsonFalse;
+}
+
+/**
+ * @brief 解析字符串节点并创建 Json string 节点
+ */
+static RyanJsonBool_e RyanJsonParseString(RyanJsonParseBuffer *parseBuf, char *key, RyanJson_t *out)
+{
+	RyanJsonCheckAssert(NULL != parseBuf && NULL != out);
+
+	uint32_t len;
+	RyanJsonBool_e hasEscape = RyanJsonFalse;
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseStringBufferGetLen(parseBuf, &len, &hasEscape));
+
+	if (len + 1 > RyanJsonInlineStringSize)
+	{
+		char *bufferMalloc = (char *)jsonMalloc((size_t)(len + 1U));
+		RyanJsonCheckReturnFalse(NULL != bufferMalloc);
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseStringBuffer(parseBuf, bufferMalloc, len, hasEscape), {
+			jsonFree(bufferMalloc);
+			return RyanJsonFalse;
+		});
+
+		RyanJson_t newItem = RyanJsonCreateString(key, bufferMalloc);
+		RyanJsonCheckCode(NULL != newItem, {
+			jsonFree(bufferMalloc);
+			return RyanJsonFalse;
+		});
+
+		jsonFree(bufferMalloc);
+		*out = newItem;
+	}
+	else
+	{
+		char buffer[RyanJsonInlineStringSize] = {0};
+		RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseStringBuffer(parseBuf, buffer, len, hasEscape));
+		RyanJson_t newItem = RyanJsonCreateString(key, buffer);
+		RyanJsonCheckReturnFalse(NULL != newItem);
+		*out = newItem;
+	}
+
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 解析单个 Json 值(非递归,仅创建当前层节点)
+ */
+static RyanJsonBool_e RyanJsonParseValue(RyanJsonParseBuffer *parseBuf, char *key, RyanJson_t *out)
+{
+	RyanJsonCheckAssert(NULL != parseBuf && NULL != out);
+
+	RyanJsonCheckReturnFalse(parseBufHasRemain(parseBuf));
+
+	*out = NULL;
+
+	if ('\"' == *parseBuf->currentPtr) { return RyanJsonParseString(parseBuf, key, out); }
+	if ('{' == *parseBuf->currentPtr)
+	{
+		*out = RyanJsonInternalCreateObjectAndKey(key);
+		RyanJsonCheckReturnFalse(NULL != *out);
+		parseBufAdvanceCurrentPrt(parseBuf, 1); // 消费掉 '{',后续迭代解析器会处理内部
+		return RyanJsonTrue;
+	}
+	if ('-' == *parseBuf->currentPtr || (*parseBuf->currentPtr >= '0' && *parseBuf->currentPtr <= '9'))
+	{
+		return RyanJsonParseNumber(parseBuf, key, out);
+	}
+	if ('[' == *parseBuf->currentPtr)
+	{
+		*out = RyanJsonInternalCreateArrayAndKey(key);
+		RyanJsonCheckReturnFalse(NULL != *out);
+		parseBufAdvanceCurrentPrt(parseBuf, 1); // 消费掉 '[',后续迭代解析器会处理内部
+		return RyanJsonTrue;
+	}
+
+	if (parseBufHasRemainBytes(parseBuf, 4) && 0 == strncmp((const char *)parseBuf->currentPtr, "null", 4))
+	{
+		*out = RyanJsonCreateNull(key);
+		RyanJsonCheckReturnFalse(NULL != *out);
+
+		parseBufAdvanceCurrentPrt(parseBuf, 4);
+		return RyanJsonTrue;
+	}
+	if (parseBufHasRemainBytes(parseBuf, 5) && 0 == strncmp((const char *)parseBuf->currentPtr, "false", 5))
+	{
+		*out = RyanJsonCreateBool(key, RyanJsonFalse);
+		RyanJsonCheckReturnFalse(NULL != *out);
+
+		parseBufAdvanceCurrentPrt(parseBuf, 5);
+		return RyanJsonTrue;
+	}
+	if (parseBufHasRemainBytes(parseBuf, 4) && 0 == strncmp((const char *)parseBuf->currentPtr, "true", 4))
+	{
+		*out = RyanJsonCreateBool(key, RyanJsonTrue);
+		RyanJsonCheckReturnFalse(NULL != *out);
+
+		parseBufAdvanceCurrentPrt(parseBuf, 4);
+		return RyanJsonTrue;
+	}
+
+	return RyanJsonFalse;
+}
+
+/**
+ * @brief 迭代解析器 (使用线索链表维护父子关系,不使用显式栈)
+ */
+static RyanJsonBool_e RyanJsonParseIterative(RyanJsonParseBuffer *parseBuf, RyanJson_t *root)
+{
+	RyanJsonCheckAssert(NULL != parseBuf && NULL != root);
+
+	// 先解析根节点
+	RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonParseValue(parseBuf, NULL, root));
+
+	// 如果是标量 (String, Number, Bool, Null),直接返回,无需迭代
+	if (!RyanJsonIsArray(*root) && !RyanJsonIsObject(*root)) { return RyanJsonTrue; }
+
+	// 初始化迭代状态
+	RyanJson_t scopeParent = *root; // 当前容器 (父节点)
+	RyanJson_t lastSibling = NULL;  // 同级上一个节点 (用来链接 sibling->next)
+
+	char shortKey[RyanJsonInlineStringSize];       // 栈上短 key 缓存
+	char *key = NULL;                              // 指向当前使用的 key (shortKey 或 堆内存)
+	RyanJsonBool_e isKeyAllocated = RyanJsonFalse; // 标记 key 是否需要释放
+	RyanJson_t newItem = NULL;                     // 新解析出的节点
+
+	while (1)
+	{
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufSkipWhitespace(parseBuf), { goto error__; });
+		RyanJsonCheckCode(parseBufHasRemain(parseBuf), { goto error__; });
+
+		// 阶段:检查当前容器是否结束(']' 或 '}')
+		uint8_t ch = *parseBuf->currentPtr;
+		RyanJsonBool_e scopeParentIsArray = RyanJsonIsArray(scopeParent);
+
+		if ((scopeParentIsArray && ']' == ch) || (!scopeParentIsArray && '}' == ch))
+		{
+			parseBufAdvanceCurrentPrt(parseBuf, 1);
+
+			// 当前容器已经闭合,接下来回溯到父容器。
+			// 父容器指针保存在 scopeParent->next(下沉时写入的线索)。
+
+			// 如果回到根节点,说明整个 Json 解析完成
+			if (scopeParent == *root) { return RyanJsonTrue; }
+
+			// 读取当前容器的父容器
+			RyanJson_t parent = scopeParent->next;
+
+			// 更新回溯后的层级状态
+			// 回到父层后,当前容器变成上一层的 lastSibling。
+			lastSibling = scopeParent;
+			scopeParent = parent;
+
+			// 继续检查父层后续(逗号或结束符)
+			continue;
+		}
+
+		// 阶段:处理同层分隔符
+		if (lastSibling)
+		{
+			if (',' == ch)
+			{
+				parseBufAdvanceCurrentPrt(parseBuf, 1);
+				RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufSkipWhitespace(parseBuf), { goto error__; });
+			}
+			else
+			{
+				goto error__; // 缺少逗号
+			}
+		}
+
+		// 阶段:解析对象 key(仅对象)
+		if (!scopeParentIsArray)
+		{
+			uint32_t len;
+			RyanJsonBool_e hasEscape = RyanJsonFalse;
+			RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseStringBufferGetLen(parseBuf, &len, &hasEscape), { goto error__; });
+
+			// 短 key 优化:优先使用栈内存
+			if (len + 1 > RyanJsonInlineStringSize)
+			{
+				key = (char *)jsonMalloc((size_t)(len + 1U));
+				RyanJsonCheckCode(NULL != key, { goto error__; });
+				isKeyAllocated = RyanJsonTrue;
+			}
+			else
+			{
+				key = shortKey;
+				isKeyAllocated = RyanJsonFalse;
+			}
+
+			// 解析 key 到缓冲区(key 指向堆内存或栈内存)
+			RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseStringBuffer(parseBuf, key, len, hasEscape), { goto error__; });
+
+			RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufSkipWhitespace(parseBuf), { goto error__; });
+			RyanJsonCheckCode(parseBufHasRemain(parseBuf) && ':' == *parseBuf->currentPtr, { goto error__; });
+			parseBufAdvanceCurrentPrt(parseBuf, 1);
+			RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseBufSkipWhitespace(parseBuf), { goto error__; });
+
+			// 严格模式下:对象从源头拒绝重复 key,避免后续语义歧义(Get/Replace/Compare)
+#if true == RyanJsonStrictObjectKeyCheck
+			RyanJsonCheckCode(RyanJsonFalse == RyanJsonHasObjectByKey(scopeParent, key), { goto error__; });
+#endif
+		}
+		else
+		{
+			key = NULL; // 数组没有 key
+			isKeyAllocated = RyanJsonFalse;
+		}
+
+		// 阶段:解析 value
+		// 解析值 (可能是标量,也可能是新的容器)
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseValue(parseBuf, key, &newItem), { goto error__; });
+
+		// 释放动态分配的 key(Node 内部已拷贝一份)
+		if (isKeyAllocated)
+		{
+			jsonFree(key);
+			isKeyAllocated = RyanJsonFalse;
+		}
+		key = NULL; // 重置 key 指针
+
+		// 阶段:挂接到父容器
+		RyanJsonInternalListInsertAfter(scopeParent, lastSibling, newItem);
+
+		lastSibling = newItem; // 更新游标
+
+		// 阶段:遇到容器时下沉
+		if (_checkType(newItem, RyanJsonTypeArray) || _checkType(newItem, RyanJsonTypeObject))
+		{
+			// 更新下沉后的层级状态
+			scopeParent = newItem;
+			lastSibling = NULL; // 新容器初始没有子节点
+		}
+	}
+
+error__:
+	// 失败收敛路径:释放临时资源并清理已构建的树
+	if (isKeyAllocated) { jsonFree(key); }
+
+	// 删除根节点(因为已经链接好了,删除根节点会递归删除所有已解析的部分)
+	RyanJsonDelete(*root);
+	*root = NULL;
+
+	return RyanJsonFalse;
+}
+
+/**
+ * @brief 校验解析结束位置是否合法
+ *
+ * @param parseBuf 解析缓冲区
+ * @param requireNullTerminator RyanJsonTrue 时要求仅剩空白
+ * @return RyanJsonBool_e 校验是否成功
+ */
+static RyanJsonBool_e RyanJsonParseCheckNullTerminator(RyanJsonParseBuffer *parseBuf, RyanJsonBool_e requireNullTerminator)
+{
+	RyanJsonCheckAssert(NULL != parseBuf);
+
+	if (requireNullTerminator)
+	{
+		// 故意不检查,允许空白
+		(void)RyanJsonParseBufSkipWhitespace(parseBuf);
+
+		// 上面已经去掉空白,如果后面还有数据,则失败
+		RyanJsonCheckReturnFalse(!parseBufHasRemain(parseBuf));
+	}
+
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 解析 Json 文本(可配置长度与尾部校验)
+ *
+ * @param text 输入文本
+ * @param size 文本长度
+ * @param requireNullTerminator 是否要求解析后仅剩空白
+ * @param parseEndPtr 输出解析结束位置,可为 NULL
+ * @return RyanJson_t 解析成功返回根节点,失败返回 NULL
+ */
+RyanJson_t RyanJsonParseOptions(const char *text, uint32_t size, RyanJsonBool_e requireNullTerminator, const char **parseEndPtr)
+{
+	RyanJsonCheckReturnNull(NULL != text);
+
+	RyanJsonParseBuffer parseBuf = {.currentPtr = (const uint8_t *)text, .remainSize = size};
+	RyanJsonCheckReturnNull(RyanJsonTrue == RyanJsonParseBufSkipWhitespace(&parseBuf));
+
+	RyanJson_t pJson;
+	RyanJsonCheckReturnNull(RyanJsonTrue == RyanJsonParseIterative(&parseBuf, &pJson));
+
+	// 检查解析后的文本后面是否有无意义的字符
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonParseCheckNullTerminator(&parseBuf, requireNullTerminator), {
+		RyanJsonDelete(pJson);
+		return NULL;
+	});
+
+	if (parseEndPtr) { *parseEndPtr = (const char *)parseBuf.currentPtr; }
+
+	return pJson;
+}
+
+/**
+ * @brief 解析以 '\\0' 结尾的 Json 文本
+ *
+ * @param text 输入文本
+ * @return RyanJson_t 解析成功返回根节点,失败返回 NULL
+ */
+RyanJson_t RyanJsonParse(const char *text)
+{
+	RyanJsonCheckReturnNull(NULL != text);
+	return RyanJsonParseOptions(text, (uint32_t)RyanJsonStrlen(text), RyanJsonFalse, NULL);
+}
+
+/**
+ * @brief 解析原始 number 文本(打印回读校验辅助)
+ *
+ * @param currentPtr number 文本起始地址
+ * @param remainSize number 文本长度
+ * @param numberValuePtr 输出数值
+ * @return RyanJsonBool_e 解析是否成功
+ */
+RyanJsonInternalApi RyanJsonBool_e RyanJsonInternalParseDoubleRaw(const uint8_t *currentPtr, uint32_t remainSize, double *numberValuePtr)
+{
+	RyanJsonCheckAssert(NULL != currentPtr && NULL != numberValuePtr);
+	RyanJsonCheckAssert(remainSize > 0);
+
+	RyanJsonBool_e isInt = RyanJsonTrue;
+	RyanJsonParseBuffer parseBuf = {.currentPtr = currentPtr, .remainSize = remainSize};
+	return RyanJsonInternalParseDouble(&parseBuf, numberValuePtr, &isInt);
+}

+ 549 - 0
RyanJson/RyanJsonPrint.c

@@ -0,0 +1,549 @@
+#include "RyanJsonInternal.h"
+
+typedef struct
+{
+	uint8_t *bufAddress;      // 打印输出缓冲区地址
+	uint32_t cursor;          // 当前写入位置(字节偏移)
+	uint32_t size;            // 缓冲区总容量,禁止扩容时写满即返回失败
+	RyanJsonBool_e isNoAlloc; // 是否禁止动态扩容(True 表示不扩容)
+} RyanJsonPrintBuffer;
+
+#define printBufPutChar(printfBuf, char)                                                                                                   \
+	do                                                                                                                                 \
+	{                                                                                                                                  \
+		((printfBuf)->bufAddress[(printfBuf)->cursor++] = (char));                                                                 \
+	} while (0)
+#define printBufPutString(printfBuf, putStr, putStrLen)                                                                                    \
+	do                                                                                                                                 \
+	{                                                                                                                                  \
+		for (uint32_t putStrCount = 0; putStrCount < (uint32_t)(putStrLen); putStrCount++)                                         \
+		{                                                                                                                          \
+			printBufPutChar(printfBuf, (putStr)[putStrCount]);                                                                 \
+		}                                                                                                                          \
+	} while (0)
+#define printBufCurrentPtr(printfBuf)  (&((printfBuf)->bufAddress[(printfBuf)->cursor]))
+#define printBufRemainBytes(printfBuf) ((printfBuf)->size - (printfBuf)->cursor)
+
+/**
+ * @brief 检查并扩展打印缓冲区容量
+ */
+static RyanJsonBool_e RyanJsonPrintBufAppend(RyanJsonPrintBuffer *printfBuf, uint32_t needed)
+{
+	RyanJsonCheckAssert(NULL != printfBuf && NULL != printfBuf->bufAddress);
+
+	needed += printfBuf->cursor;
+
+	// 当前缓冲区空间充足
+	if (needed <= printfBuf->size) { return RyanJsonTrue; }
+
+	// 禁止动态扩容
+	RyanJsonCheckReturnFalse(RyanJsonFalse == printfBuf->isNoAlloc);
+
+	uint32_t size = needed + RyanJsonPrintfPreAlloSize;
+	char *address = (char *)RyanJsonInternalExpandRealloc(printfBuf->bufAddress, printfBuf->size, size);
+	RyanJsonCheckReturnFalse(NULL != address);
+
+	printfBuf->size = size;
+	printfBuf->bufAddress = (uint8_t *)address;
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 规范化浮点数输出:删除尾部无效的0(非科学计数法时)
+ */
+static int32_t RyanJsonTrimDoubleTrailingZeros(RyanJsonPrintBuffer *printfBuf, int32_t len)
+{
+	// Linux 测试环境:偶尔把 'e' 改为 'E',覆盖大小写兼容分支
+#ifdef RyanJsonLinuxTestEnv
+	int32_t eIndex = INT32_MIN;
+	if (RyanJsonFuzzerShouldFail(20))
+	{
+		for (int32_t i = 0; i < len; i++)
+		{
+			if ('e' == printBufCurrentPtr(printfBuf)[i])
+			{
+				printBufCurrentPtr(printfBuf)[i] = 'E';
+				eIndex = i;
+				break;
+			}
+		}
+	}
+#endif
+
+	// 检查是否使用科学计数法
+	RyanJsonBool_e isScientificNotation = RyanJsonFalse;
+	for (int32_t i = 0; i < len; i++)
+	{
+		// 有些平台会输出'E'
+		if ('e' == printBufCurrentPtr(printfBuf)[i] || 'E' == printBufCurrentPtr(printfBuf)[i])
+		{
+			isScientificNotation = RyanJsonTrue;
+			break;
+		}
+	}
+
+	// 恢复测试环境临时改写的大写 'E'
+#ifdef RyanJsonLinuxTestEnv
+	if (INT32_MIN != eIndex) { printBufCurrentPtr(printfBuf)[eIndex] = 'e'; }
+#endif
+
+	if (RyanJsonFalse == isScientificNotation)
+	{
+		// 删除小数部分中无效的 0
+		// 最小也要为"0.0"
+		while (len > 3)
+		{
+			if ('0' != printBufCurrentPtr(printfBuf)[len - 1]) { break; }
+			if ('.' == printBufCurrentPtr(printfBuf)[len - 2]) { break; }
+			len--;
+			printBufCurrentPtr(printfBuf)[len] = '\0';
+		}
+	}
+
+	return len;
+}
+
+/**
+ * @brief 打印数字节点
+ */
+static RyanJsonBool_e RyanJsonPrintNumber(RyanJson_t pJson, RyanJsonPrintBuffer *printfBuf)
+{
+	RyanJsonCheckAssert(NULL != pJson && NULL != printfBuf);
+
+	int32_t len;
+
+	// Number 节点按 int32_t 存储
+	if (RyanJsonFalse == RyanJsonGetPayloadNumberIsDoubleByFlag(pJson))
+	{
+		// INT32_MIN = -2147483648 (11 chars) + '\0'
+		RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, 12));
+
+		len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printBufRemainBytes(printfBuf), "%" PRId32,
+				       RyanJsonGetIntValue(pJson));
+		// 这里前面已保证至少 12 字节空间(INT32_MIN + '\0'),正常实现下不会截断
+		RyanJsonCheckReturnFalse(len > 0);
+		printfBuf->cursor += (uint32_t)len;
+
+		return RyanJsonTrue;
+	}
+
+	// Number 节点按 double 存储
+	RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, RyanJsonDoubleBufferSize));
+	double doubleValue = RyanJsonGetDoubleValue(pJson);
+
+	// 处理特殊值:无穷大和 NaN 输出为 null(RFC 8259 不支持 Infinity/NaN)
+	if (isinf(doubleValue) || isnan(doubleValue))
+	{
+		printBufPutString(printfBuf, (uint8_t *)"null", 4);
+		return RyanJsonTrue;
+	}
+
+	double absDoubleValue = fabs(doubleValue);
+
+	// 判断是否可按整数样式输出,并保留一位小数(例如 5.0、0.0)
+	// 注意:0 也需要特殊处理,否则会进入科学记数法分支
+	// 在有界空间内使用完全变换
+	if ((absDoubleValue < DBL_EPSILON || (absDoubleValue < 1.0e15 && absDoubleValue >= 1.0e-6)) &&
+	    fabs(floor(doubleValue) - doubleValue) <= DBL_EPSILON)
+	{
+		len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printBufRemainBytes(printfBuf), "%.1lf", doubleValue);
+		// 有外层限制 1e-6 ~ 1e15, 所以肯定不会越界
+		RyanJsonCheckReturnFalse(len > 0);
+
+		// 嵌入式场景下缓冲区可能偏小,保留额外边界检查
+#ifndef RyanJsonLinuxTestEnv
+		RyanJsonCheckReturnFalse(len < (int32_t)printBufRemainBytes(printfBuf));
+#endif
+	}
+	else
+	{
+		// Linux 测试环境:在两种格式间切换,覆盖去零与回读校验分支
+#ifdef RyanJsonLinuxTestEnv
+#undef RyanJsonSnprintfSupportScientific
+		// 基于 double 值本身选择格式(确定性),保证同一个值总是用相同格式
+		// %lf 在 [1e-6, 1e6] 范围内输出安全
+		// 极端值(>1e6 或 <1e-6)必须使用科学记数法,否则可能超出缓冲区
+		RyanJsonBool_e RyanJsonSnprintfSupportScientific = doubleValue > 1.0 ? RyanJsonTrue : RyanJsonFalse;
+
+#endif
+
+		// 极大/极小数或普通浮点数
+		// 不使用 %.15g 是因为很多嵌入式平台上 %.15g 与 %.17g 效果接近
+		// 可能出现 0.2 -> 0.200000003000000,即便去掉尾部 0 仍不美观
+		// 使用 %lf 存在缓冲区压力,但在当前嵌入式目标上可接受
+		len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printBufRemainBytes(printfBuf),
+				       RyanJsonSnprintfSupportScientific ? "%.15g" : "%lf", doubleValue);
+#ifdef isEnableFuzzer
+		// 测试环境:偶尔模拟溢出以触发防御性检查分支
+		if (RyanJsonFuzzerShouldFail(1000) && len > 0) { len = (int32_t)printBufRemainBytes(printfBuf) + 1; }
+#endif
+		RyanJsonCheckReturnFalse(len > 0 && len < (int32_t)printBufRemainBytes(printfBuf));
+
+		// 往返检查:在去0之前进行,确保原始精度足够
+		// 如果精度不够,改用 %.17g
+		double number = 0;
+		RyanJsonCheckReturnFalse(RyanJsonTrue ==
+					 RyanJsonInternalParseDoubleRaw(printBufCurrentPtr(printfBuf), (uint32_t)len, &number));
+		if (RyanJsonFalse == RyanJsonCompareDouble(number, doubleValue))
+		{
+			len = RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf), printBufRemainBytes(printfBuf), "%.17g", doubleValue);
+			RyanJsonCheckReturnFalse(len > 0);
+
+#ifndef RyanJsonLinuxTestEnv
+			// "%.17g" 也做边界检查,避免平台实现差异导致越界
+			RyanJsonCheckReturnFalse(len < (int32_t)printBufRemainBytes(printfBuf));
+#endif
+		}
+
+		// 删除尾部无效 0。理论上主要作用于 %lf,但统一处理更稳妥
+		len = RyanJsonTrimDoubleTrailingZeros(printfBuf, len);
+	}
+
+	printfBuf->cursor += (uint32_t)len;
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 打印字符串并执行转义
+ */
+static RyanJsonBool_e RyanJsonPrintStringBuffer(const uint8_t *strValue, RyanJsonPrintBuffer *printfBuf)
+{
+	RyanJsonCheckAssert(NULL != strValue && NULL != printfBuf);
+	// 获取长度
+	const uint8_t *strCurrentPtr = strValue;
+	uint32_t escapeCharCount = 0;
+	for (strCurrentPtr = strValue; *strCurrentPtr; strCurrentPtr++)
+	{
+		switch (*strCurrentPtr)
+		{
+		case '\"':
+		case '\\':
+		case '\b':
+		case '\f':
+		case '\n':
+		case '\r':
+		case '\t':
+		case '/': escapeCharCount++; break;
+
+		default:
+			// 按最坏情况预留转义空间,保证后续写入安全
+			if (*strCurrentPtr < 32) { escapeCharCount += 5; }
+			break;
+		}
+	}
+
+	RyanJsonCheckReturnFalse(
+		RyanJsonPrintBufAppend(printfBuf, (uint32_t)(strCurrentPtr - strValue) + escapeCharCount + 2U)); // 最小是\" \"
+	printBufPutChar(printfBuf, '\"');
+
+	// 没有转义字符
+	if (0 == escapeCharCount)
+	{
+		printBufPutString(printfBuf, strValue, (strCurrentPtr - strValue));
+		printBufPutChar(printfBuf, '\"');
+		return RyanJsonTrue;
+	}
+
+	strCurrentPtr = strValue;
+	while (*strCurrentPtr)
+	{
+		if ((*strCurrentPtr) >= ' ' && '\"' != *strCurrentPtr && '\\' != *strCurrentPtr)
+		{
+			printBufPutChar(printfBuf, *strCurrentPtr++);
+			continue;
+		}
+
+		// 转义和打印
+		printBufPutChar(printfBuf, '\\');
+
+		switch (*strCurrentPtr)
+		{
+		case '\\': printBufPutChar(printfBuf, '\\'); break;
+		case '\"': printBufPutChar(printfBuf, '\"'); break;
+		case '\b': printBufPutChar(printfBuf, 'b'); break;
+		case '\f': printBufPutChar(printfBuf, 'f'); break;
+		case '\n': printBufPutChar(printfBuf, 'n'); break;
+		case '\r': printBufPutChar(printfBuf, 'r'); break;
+		case '\t': printBufPutChar(printfBuf, 't'); break;
+
+		default: {
+			// 这里无需额外校验输入字节有效性,RyanJson 已保证转义序列合法
+			RyanJsonCheckReturnFalse(5 == RyanJsonSnprintf((char *)printBufCurrentPtr(printfBuf),
+								       printBufRemainBytes(printfBuf), "u%04X", *strCurrentPtr));
+			printfBuf->cursor += 5; // uXXXX 四位十六进制编码
+			break;
+		}
+		}
+		strCurrentPtr++;
+	}
+
+	printBufPutChar(printfBuf, '\"');
+
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 打印节点中的 strValue
+ *
+ * @param pJson 字符串节点
+ * @param printfBuf 打印缓冲区
+ * @return RyanJsonBool_e 打印是否成功
+ */
+static RyanJsonBool_e RyanJsonPrintString(RyanJson_t pJson, RyanJsonPrintBuffer *printfBuf)
+{
+	RyanJsonCheckAssert(NULL != pJson && NULL != printfBuf);
+	return RyanJsonPrintStringBuffer((const uint8_t *)RyanJsonGetStringValue(pJson), printfBuf);
+}
+
+/**
+ * @brief 将 Json 树打印为字符串(迭代实现)
+ */
+static RyanJsonBool_e RyanJsonPrintValue(RyanJson_t pJson, RyanJsonPrintBuffer *printfBuf, uint32_t depthStart,
+					 const RyanJsonPrintStyle *style)
+{
+	RyanJsonCheckAssert(NULL != pJson && NULL != printfBuf);
+
+	RyanJson_t curr = pJson;
+	uint32_t depth = depthStart;
+
+	// 无需显式栈:通过线索化链表与容器状态完成遍历与回溯
+	while (1)
+	{
+		// 打印 key(当前节点包含 key 时)
+		if (curr != pJson && RyanJsonIsKey(curr))
+		{
+			if (style->format)
+			{
+				uint32_t needed = depth * style->indentLen;
+				RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, needed));
+				for (uint32_t i = 0; i < depth; i++)
+				{
+					printBufPutString(printfBuf, (uint8_t *)style->indent, style->indentLen);
+				}
+			}
+
+			RyanJsonCheckReturnFalse(RyanJsonPrintStringBuffer((const uint8_t *)RyanJsonGetKey(curr), printfBuf));
+
+			// 打印冒号和空格
+			uint32_t spaceLen = style->format ? style->spaceAfterColon : 0;
+			RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, 1 + spaceLen));
+			printBufPutChar(printfBuf, ':');
+			if (style->format)
+			{
+				for (uint32_t i = 0; i < spaceLen; i++)
+				{
+					printBufPutChar(printfBuf, ' ');
+				}
+			}
+		}
+		else if (curr != pJson && style->format)
+		{
+			// 数组元素缩进(没有 key)
+			uint32_t needed = depth * style->indentLen;
+			RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, needed));
+			for (uint32_t i = 0; i < depth; i++)
+			{
+				printBufPutString(printfBuf, (uint8_t *)style->indent, style->indentLen);
+			}
+		}
+
+		// 打印 Value(标量值或容器起始符)
+		RyanJsonType_e type = RyanJsonGetType(curr);
+		switch (type)
+		{
+		case RyanJsonTypeNull:
+			RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, 4));
+			printBufPutString(printfBuf, (uint8_t *)"null", 4);
+			break;
+
+		case RyanJsonTypeBool: {
+			RyanJsonBool_e val = RyanJsonGetBoolValue(curr);
+			RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, val ? 4 : 5));
+			printBufPutString(printfBuf, val ? (uint8_t *)"true" : (uint8_t *)"false", val ? 4 : 5);
+			break;
+		}
+
+		case RyanJsonTypeNumber: RyanJsonCheckReturnFalse(RyanJsonPrintNumber(curr, printfBuf)); break;
+
+		case RyanJsonTypeString: RyanJsonCheckReturnFalse(RyanJsonPrintString(curr, printfBuf)); break;
+
+		case RyanJsonTypeArray:
+		case RyanJsonTypeObject: {
+			RyanJsonBool_e currIsObject = (type == RyanJsonTypeObject);
+			RyanJson_t currChild = RyanJsonGetObjectValue(curr);
+
+			// 空容器直接输出 [] 或 {}
+			if (NULL == currChild)
+			{
+				RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, 2));
+				printBufPutChar(printfBuf, currIsObject ? '{' : '[');
+				printBufPutChar(printfBuf, currIsObject ? '}' : ']');
+			}
+			// 非空容器进入子节点处理
+			else
+			{
+				uint32_t newlineLen = style->format ? style->newlineLen : 0;
+				RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, 1 + newlineLen)); // '[' + newline
+				printBufPutChar(printfBuf, currIsObject ? '{' : '[');
+
+				// 开启 format 后,非空容器统一走多行输出
+				if (style->format) { printBufPutString(printfBuf, (uint8_t *)style->newline, newlineLen); }
+
+				curr = currChild;
+				depth++;
+				continue; // 直接跳转到下一次循环处理 Child
+			}
+			break;
+		}
+
+		default: return RyanJsonFalse;
+		}
+
+		// 处理逗号、兄弟节点切换与回溯闭合
+		if (curr == pJson) { return RyanJsonTrue; }
+
+		do
+		{
+			// 有兄弟节点时输出逗号并切换到兄弟
+			RyanJson_t nextInfo = RyanJsonGetNext(curr); // 能够处理 IsLast
+			if (nextInfo)
+			{
+				uint32_t newlineLen = style->format ? style->newlineLen : 0;
+				RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, 1 + newlineLen)); // ',' + newline
+				printBufPutChar(printfBuf, ',');
+
+				if (style->format) { printBufPutString(printfBuf, (uint8_t *)style->newline, newlineLen); }
+
+				curr = nextInfo;
+				break; // 处理新的 curr(兄弟节点)
+			}
+
+			// 无兄弟节点时回溯到父节点并闭合容器
+			// 利用线索化特性:IsLast 节点的 next 指向父节点
+			curr = curr->next;
+			depth--;
+
+			// 打印结束括号前的缩进(仅 format 模式)
+			if (style->format)
+			{
+				uint32_t needed = style->newlineLen + depth * style->indentLen;
+
+				RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, needed));
+				printBufPutString(printfBuf, (uint8_t *)style->newline, style->newlineLen);
+				for (uint32_t i = 0; i < depth; i++)
+				{
+					printBufPutString(printfBuf, (uint8_t *)style->indent, style->indentLen);
+				}
+			}
+
+			RyanJsonCheckReturnFalse(RyanJsonPrintBufAppend(printfBuf, 1));
+			printBufPutChar(printfBuf, RyanJsonIsArray(curr) ? ']' : '}');
+
+			// 如果回溯到了起始根节点,结束打印
+			if (curr == pJson) { return RyanJsonTrue; }
+
+			// 继续循环,检查父节点是否仍有同层兄弟
+		} while (1);
+	}
+}
+
+/**
+ * @brief 按指定风格打印 Json(动态分配输出缓冲)
+ *
+ * @param pJson 待打印节点
+ * @param preset 初始缓冲大小
+ * @param style 打印风格
+ * @param len 输出长度,可为 NULL
+ * @return char* 打印结果,需调用 RyanJsonFree 释放
+ */
+char *RyanJsonPrintWithStyle(RyanJson_t pJson, uint32_t preset, const RyanJsonPrintStyle *style, uint32_t *len)
+{
+	RyanJsonCheckReturnNull(NULL != pJson && NULL != style);
+
+	RyanJsonPrintBuffer printfBuf = {
+		.isNoAlloc = RyanJsonFalse,
+		.size = preset,
+		.cursor = 0,
+	};
+
+	if (printfBuf.size < RyanJsonPrintfPreAlloSize) { printfBuf.size = RyanJsonPrintfPreAlloSize; }
+	printfBuf.bufAddress = (uint8_t *)jsonMalloc(printfBuf.size);
+	RyanJsonCheckReturnNull(NULL != printfBuf.bufAddress);
+
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonPrintValue(pJson, &printfBuf, 0, style), {
+		jsonFree(printfBuf.bufAddress);
+		return NULL;
+	});
+
+	RyanJsonCheckCode(RyanJsonPrintBufAppend(&printfBuf, 1), {
+		jsonFree(printfBuf.bufAddress);
+		return NULL;
+	});
+
+	printfBuf.bufAddress[printfBuf.cursor] = '\0';
+	if (len) { *len = printfBuf.cursor; }
+
+	return (char *)printfBuf.bufAddress;
+}
+
+/**
+ * @brief 使用默认风格打印 Json(动态分配输出缓冲)
+ *
+ * @param pJson 待打印节点
+ * @param preset 初始缓冲大小
+ * @param format 是否格式化输出
+ * @param len 输出长度,可为 NULL
+ * @return char* 打印结果,需调用 RyanJsonFree 释放
+ */
+char *RyanJsonPrint(RyanJson_t pJson, uint32_t preset, RyanJsonBool_e format, uint32_t *len)
+{
+	RyanJsonPrintStyle style = {
+		.indent = "\t", .newline = "\n", .indentLen = 1, .newlineLen = 1, .spaceAfterColon = 1, .format = format};
+	return RyanJsonPrintWithStyle(pJson, preset, &style, len);
+}
+
+/**
+ * @brief 按指定风格打印 Json(使用外部预分配缓冲)
+ *
+ * @param pJson 待打印节点
+ * @param buffer 外部缓冲区
+ * @param length 缓冲区大小
+ * @param style 打印风格
+ * @param len 输出长度,可为 NULL
+ * @return char* 成功返回 buffer,失败返回 NULL
+ */
+char *RyanJsonPrintPreallocatedWithStyle(RyanJson_t pJson, char *buffer, uint32_t length, const RyanJsonPrintStyle *style, uint32_t *len)
+{
+	RyanJsonCheckReturnNull(NULL != pJson && NULL != buffer && NULL != style && length > 0);
+
+	RyanJsonPrintBuffer printfBuf = {
+		.bufAddress = (uint8_t *)buffer,
+		.isNoAlloc = RyanJsonTrue,
+		.size = length,
+		.cursor = 0,
+	};
+
+	RyanJsonCheckReturnNull(RyanJsonTrue == RyanJsonPrintValue(pJson, &printfBuf, 0, style));
+	RyanJsonCheckReturnNull(RyanJsonPrintBufAppend(&printfBuf, 1));
+	printfBuf.bufAddress[printfBuf.cursor] = '\0';
+	if (len) { *len = printfBuf.cursor; }
+
+	return (char *)printfBuf.bufAddress;
+}
+
+/**
+ * @brief 使用默认风格打印 Json(使用外部预分配缓冲)
+ *
+ * @param pJson 待打印节点
+ * @param buffer 外部缓冲区
+ * @param length 缓冲区大小
+ * @param format 是否格式化输出
+ * @param len 输出长度,可为 NULL
+ * @return char* 成功返回 buffer,失败返回 NULL
+ */
+char *RyanJsonPrintPreallocated(RyanJson_t pJson, char *buffer, uint32_t length, RyanJsonBool_e format, uint32_t *len)
+{
+	RyanJsonPrintStyle style = {
+		.indent = "\t", .newline = "\n", .indentLen = 1, .newlineLen = 1, .spaceAfterColon = 1, .format = format};
+	return RyanJsonPrintPreallocatedWithStyle(pJson, buffer, length, &style, len);
+}

+ 491 - 79
RyanJson/RyanJsonUtils.c

@@ -1,12 +1,457 @@
-#include "RyanJsonUtils.h"
+#include "RyanJsonInternal.h"
+
+#ifdef RyanJsonLinuxTestEnv
+#include <stdio.h>
+#include <time.h>
+
+#undef RyanJsonSnprintf
+/**
+ * @brief Linux 测试环境下的 snprintf 包装(支持 fuzzer 注入)
+ */
+RyanJsonInternalApi int32_t RyanJsonSnprintf(char *buf, size_t size, const char *fmt, ...)
+{
+#ifdef isEnableFuzzer
+	// Fuzzer 模式:随机触发失败,测试错误处理路径
+	if (RyanJsonFuzzerShouldFail(500)) { return 0; }
+#endif
+
+	va_list args;
+	va_start(args, fmt);
+	int32_t ret = vsnprintf(buf, size, fmt, args);
+	va_end(args);
+	return ret;
+}
+#endif // RyanJsonLinuxTestEnv
+
+/**
+ * @brief 比较两个 C 字符串是否相等
+ *
+ * @param s1 字符串1
+ * @param s2 字符串2
+ * @return RyanJsonBool_e 是否相等
+ */
+RyanJsonInternalApi RyanJsonBool_e RyanJsonInternalStrEq(const char *s1, const char *s2)
+{
+	// NULL 检查
+	RyanJsonCheckAssert(NULL != s1 && NULL != s2);
+
+	// 地址相同
+	if (s1 == s2) { return RyanJsonTrue; }
+
+	// 首字符不同
+	if (*s1 != *s2) { return RyanJsonFalse; }
+
+	return RyanJsonMakeBool(0 == RyanJsonStrcmp(s1, s2));
+}
+
+/**
+ * @brief 安全的浮点数比较
+ */
+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 absTolerance = RyanJsonAbsTolerance; // 绝对容差
+
+	return diff <= (epsilon > absTolerance ? epsilon : absTolerance);
+}
+
+/**
+ * @brief 获取字符串指针模式的缓冲区地址
+ *
+ * @param pJson Json 节点
+ * @return uint8_t* 缓冲区首地址
+ */
+RyanJsonInternalApi uint8_t *RyanJsonInternalGetStrPtrModeBuf(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+
+	// 使用 memcpy 规避潜在的非对齐访问警告
+	void *tmpPtr = NULL;
+	RyanJsonMemcpy((void *)&tmpPtr, (RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize), sizeof(void *));
+	return (uint8_t *)tmpPtr;
+}
+
+/**
+ * @brief 设置字符串指针模式的缓冲区地址
+ *
+ * @param pJson Json 节点
+ * @param heapPtr 缓冲区首地址
+ */
+RyanJsonInternalApi void RyanJsonInternalSetStrPtrModeBuf(RyanJson_t pJson, uint8_t *heapPtr)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+	RyanJsonCheckAssert(NULL != heapPtr);
+
+	// 使用 memcpy 规避潜在的非对齐访问警告
+	void *tmpPtr = heapPtr;
+	// 前面依次是 flag 与 key 长度字段空间,这里只写入堆指针
+	RyanJsonMemcpy((RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize + RyanJsonKeyFeidLenMaxSize), (const void *)&tmpPtr,
+		       sizeof(void *));
+}
+
+/**
+ * @brief 获取字符串指针模式指定偏移处的地址
+ *
+ * @param pJson Json 节点
+ * @param index 索引
+ * @return uint8_t* 偏移后的地址
+ */
+RyanJsonInternalApi uint8_t *RyanJsonInternalGetStrPtrModeBufAt(RyanJson_t pJson, uint32_t index)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+	return (uint8_t *)(RyanJsonInternalGetStrPtrModeBuf(pJson) + (index));
+}
+
+/**
+ * @brief 计算 key 长度需要的字节数
+ *
+ * @param len key 长度
+ * @return uint8_t 需要的字节数
+ */
+RyanJsonInternalApi uint8_t RyanJsonInternalCalcLenBytes(uint32_t len)
+{
+	if (len <= UINT8_MAX) { return 1; }  // 01: 1 byte
+	if (len <= UINT16_MAX) { return 2; } // 10: 2 bytes
+	return 3;                            // 11: 4 bytes
+}
 
 
 /**
 /**
- * @brief 连续通过 key 获取json对象的子项
+ * @brief 解码 key 长度字段
  *
  *
- * @param pJson
- * @param key
- * @param ... 可变参,连续输入key,直到NULL结束
- * @return RyanJson_t
+ * @param encoded 编码后的 key 长度字段
+ * @return uint8_t 解码后的 key 长度
+ */
+RyanJsonInternalApi uint8_t RyanJsonInternalDecodeKeyLenField(uint8_t encoded)
+{
+	return 3 == encoded ? 4 : encoded;
+}
+
+/**
+ * @brief 设置 key 长度
+ *
+ * @param pJson Json 节点
+ * @param value key 长度
+ */
+static void RyanJsonSetKeyLen(RyanJson_t pJson, uint32_t value)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+	uint8_t *buf = RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize;
+	uint8_t keyFieldLen = RyanJsonInternalDecodeKeyLenField(RyanJsonGetPayloadEncodeKeyLenByFlag(pJson));
+	RyanJsonCheckAssert(keyFieldLen <= RyanJsonKeyFeidLenMaxSize);
+
+	// 使用大小端无关的方式写入
+	for (uint8_t i = 0; i < keyFieldLen; i++)
+	{
+		buf[i] = (uint8_t)(value & 0xFF);
+		value >>= 8;
+	}
+}
+
+/**
+ * @brief 获取节点 key 的长度
+ *
+ * @param pJson Json 节点
+ * @return uint32_t key 长度
+ */
+RyanJsonInternalApi uint32_t RyanJsonInternalGetKeyLen(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+	uint8_t *buf = RyanJsonGetPayloadPtr(pJson) + RyanJsonFlagSize;
+	uint8_t keyFieldLen = RyanJsonInternalDecodeKeyLenField(RyanJsonGetPayloadEncodeKeyLenByFlag(pJson));
+	RyanJsonCheckAssert(keyFieldLen <= RyanJsonKeyFeidLenMaxSize);
+
+	// 使用大小端无关的方式读取
+	uint32_t value = 0;
+	for (uint8_t i = 0; i < keyFieldLen; i++)
+	{
+		value |= ((uint32_t)buf[i]) << (i * 8);
+	}
+	return value;
+}
+
+/**
+ * @brief 获取节点 value 存储区地址
+ *
+ * @param pJson Json 节点
+ * @return void* value 地址
+ */
+RyanJsonInternalApi void *RyanJsonInternalGetValue(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+
+	uint32_t len = RyanJsonFlagSize;
+	if (RyanJsonIsKey(pJson)) { len += RyanJsonInlineStringSize; }
+	return RyanJsonGetPayloadPtr(pJson) + len;
+}
+
+/**
+ * @brief 更新 key 与 strValue
+ *
+ * @param pJson Json 节点
+ * @param isNew 是否是新创建的节点
+ * @param key key 字符串
+ * @param strValue strValue
+ * @return RyanJsonBool_e
+ */
+RyanJsonInternalApi RyanJsonBool_e RyanJsonInternalChangeString(RyanJson_t pJson, RyanJsonBool_e isNew, const char *key,
+								const char *strValue)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+
+	uint32_t keyLen = 0;      // key 字节长度
+	uint8_t keyLenField = 0;  // key 长度字段编码所需字节数
+	uint32_t strValueLen = 0; // strValue 字节长度
+
+	uint32_t mallocSize = 0;
+
+	// 计算 str 缓冲区所需的总字节数
+	if (NULL != key)
+	{
+		keyLen = RyanJsonStrlen(key);
+		keyLenField = RyanJsonInternalCalcLenBytes(keyLen);
+		mallocSize += keyLen + 1;
+	}
+
+	if (NULL != strValue)
+	{
+		strValueLen = RyanJsonStrlen(strValue);
+		mallocSize += strValueLen + 1;
+	}
+	if (0 == mallocSize) { return RyanJsonTrue; }
+
+	// 记录旧 str 缓冲区,切换成功后再释放
+	uint8_t *oldPrt = NULL;
+	if (RyanJsonFalse == isNew)
+	{
+		if (RyanJsonTrue == RyanJsonGetPayloadStrIsPtrByFlag(pJson)) { oldPrt = RyanJsonInternalGetStrPtrModeBuf(pJson); }
+	}
+
+	char arr[RyanJsonInlineStringSize];
+
+	// 若 key + value + keyLenField 编码都能放进内联区,则走内联存储
+	if ((mallocSize + RyanJsonInternalDecodeKeyLenField(keyLenField)) <= RyanJsonInlineStringSize)
+	{
+		RyanJsonSetPayloadStrIsPtrByFlag(pJson, RyanJsonFalse);
+		// 先写入临时缓冲区,避免切换内联模式时出现覆盖
+		if (keyLen) { RyanJsonMemcpy(arr, key, keyLen); }
+		if (strValueLen) { RyanJsonMemcpy(arr + keyLen, strValue, strValueLen); }
+	}
+	else
+	{
+		// 申请新的 str 缓冲区
+		uint8_t *newPtr = (uint8_t *)jsonMalloc(mallocSize);
+		RyanJsonCheckReturnFalse(NULL != newPtr);
+
+		// 先拷贝内容,再写回指针,避免指针写入后覆盖原数据
+		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';
+		}
+
+		RyanJsonInternalSetStrPtrModeBuf(pJson, newPtr);
+		RyanJsonSetPayloadStrIsPtrByFlag(pJson, RyanJsonTrue);
+	}
+
+	// 设置 key
+	if (NULL != key)
+	{
+		RyanJsonSetPayloadEncodeKeyLenByFlag(pJson, keyLenField);
+		RyanJsonSetKeyLen(pJson, keyLen);
+		if (RyanJsonFalse == RyanJsonGetPayloadStrIsPtrByFlag(pJson))
+		{
+			char *keyBuf = RyanJsonGetKey(pJson);
+			if (0 != keyLen) { RyanJsonMemcpy(keyBuf, arr, keyLen); }
+			keyBuf[keyLen] = '\0';
+		}
+	}
+	else
+	{
+		RyanJsonSetPayloadEncodeKeyLenByFlag(pJson, 0);
+	}
+
+	// 设置 strValue
+	if (NULL != strValue)
+	{
+		if (RyanJsonFalse == RyanJsonGetPayloadStrIsPtrByFlag(pJson))
+		{
+			char *strValueBuf = RyanJsonGetStringValue(pJson);
+			if (0 != strValueLen) { RyanJsonMemcpy(strValueBuf, arr + keyLen, strValueLen); }
+			strValueBuf[strValueLen] = '\0';
+		}
+	}
+
+	if (oldPrt) { jsonFree(oldPrt); }
+	return RyanJsonTrue;
+}
+
+/**
+ * @brief 创建一个节点
+ *
+ * @param info 节点信息
+ * @return RyanJson_t 节点
+ */
+RyanJsonInternalApi RyanJson_t RyanJsonInternalNewNode(RyanJsonNodeInfo_t *info)
+{
+	RyanJsonCheckAssert(NULL != info);
+
+	// 加1是flag的空间
+	uint32_t size = sizeof(struct RyanJsonNode) + RyanJsonFlagSize;
+
+	switch (info->type)
+	{
+	case RyanJsonTypeNumber:
+		if (RyanJsonFalse == info->numberIsDoubleFlag) { size += sizeof(int32_t); }
+		else
+		{
+			size += sizeof(double);
+		}
+		break;
+	case RyanJsonTypeArray:
+	case RyanJsonTypeObject: size += sizeof(RyanJson_t); break;
+
+	default: break;
+	}
+
+	// 是否内联字符串
+	if (NULL != info->key || RyanJsonTypeString == info->type) { size += RyanJsonInlineStringSize; }
+
+	RyanJson_t pJson = (RyanJson_t)jsonMalloc((size_t)size);
+	RyanJsonCheckReturnNull(NULL != pJson);
+
+	// 节点体积较小,直接整块清零即可
+	RyanJsonMemset(pJson, 0, size);
+
+	RyanJsonSetType(pJson, info->type);
+
+	// 设置 key 和 value
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonInternalChangeString(pJson, RyanJsonTrue, info->key, info->strValue), {
+		jsonFree(pJson);
+		return NULL;
+	});
+
+	// 设置 bool / number
+	if (RyanJsonTypeBool == info->type) { RyanJsonSetPayloadBoolValueByFlag(pJson, info->boolIsTrueFlag); }
+	else if (RyanJsonTypeNumber == info->type) { RyanJsonSetPayloadNumberIsDoubleByFlag(pJson, info->numberIsDoubleFlag); }
+
+	return pJson;
+}
+
+/**
+ * @brief 在父节点中插入子节点(维护线索化链表)
+ *
+ * @param parent 父节点(Object 或 Array)
+ * @param prev 前驱兄弟节点,为 NULL 表示头插
+ * @param item 待插入节点
+ */
+RyanJsonInternalApi void RyanJsonInternalListInsertAfter(RyanJson_t parent, RyanJson_t prev, RyanJson_t item)
+{
+	RyanJson_t nextItem;
+
+	if (prev)
+	{
+		// 若 prev 原本是尾节点,则 prev->next 指向 parent,此时 nextItem 应视为 NULL
+		// 通过 IsLast 标志区分“下一个兄弟节点”与“父节点线索”。
+		if (RyanJsonGetPayloadIsLastByFlag(prev)) { nextItem = NULL; }
+		else
+		{
+			nextItem = prev->next;
+		}
+	}
+	else
+	{
+		// 插入到头部。RyanJsonNode 结构保证了 Object 和 Array 的 Value 指针位置一致
+		nextItem = RyanJsonGetObjectValue(parent);
+	}
+
+	// 先链接 prev -> item
+	if (prev)
+	{
+		prev->next = item;
+		// prev 不再是最后一个,取消 IsLast 标记
+		RyanJsonSetPayloadIsLastByFlag(prev, 0);
+	}
+	else
+	{
+		// 只有 item 一个子节点,或者 item 是新的头部
+		RyanJsonInternalChangeObjectValue(parent, item);
+	}
+
+	// 再链接 item -> nextItem(或 Parent)
+	if (nextItem)
+	{
+		// 不是最后一个节点,item->next 指向下一个兄弟
+		item->next = nextItem;
+		RyanJsonSetPayloadIsLastByFlag(item, 0);
+	}
+	else
+	{
+		// 是最后一个节点,item->next 指向 Parent (线索化)
+		item->next = parent;
+		RyanJsonSetPayloadIsLastByFlag(item, 1);
+	}
+}
+
+/**
+ * @brief 获取同层下一个兄弟节点
+ *
+ * @param pJson 当前节点
+ * @return RyanJson_t 下一个兄弟节点;若当前为最后节点返回 NULL
+ */
+RyanJson_t RyanJsonGetNext(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+
+	// 尾节点(Bit7=1)的 next 保存的是父节点线索,对外应返回 NULL
+	if (RyanJsonGetPayloadIsLastByFlag(pJson)) { return NULL; }
+	return pJson->next;
+}
+
+/**
+ * @brief 获取当前节点的父节点
+ *
+ * @param pJson 当前节点
+ * @return RyanJson_t 父节点
+ */
+RyanJsonInternalApi RyanJson_t RyanJsonInternalGetParent(RyanJson_t pJson)
+{
+	RyanJsonCheckAssert(NULL != pJson);
+
+	RyanJson_t curr = pJson;
+	RyanJson_t next = RyanJsonGetNext(curr);
+
+	// 沿着兄弟链表一直走到该层级的最后一个节点
+	while (NULL != next)
+	{
+		curr = next;
+		next = RyanJsonGetNext(curr);
+	}
+
+	// 此时 curr 是当前层级的最后一个节点,其 next 指针存放的就是父节点
+	return curr->next;
+}
+
+/**
+ * @brief 按多级 key 路径获取节点
+ *
+ * @param pJson 起始节点
+ * @param key 第一级 key
+ * @return RyanJson_t 目标节点,失败返回 NULL
  */
  */
 RyanJson_t RyanJsonGetObjectByKeys(RyanJson_t pJson, const char *key, ...)
 RyanJson_t RyanJsonGetObjectByKeys(RyanJson_t pJson, const char *key, ...)
 {
 {
@@ -15,7 +460,7 @@ RyanJson_t RyanJsonGetObjectByKeys(RyanJson_t pJson, const char *key, ...)
 	const char *nextKey = key;
 	const char *nextKey = key;
 	RyanJson_t nextItem = RyanJsonGetObjectByKey(pJson, nextKey);
 	RyanJson_t nextItem = RyanJsonGetObjectByKey(pJson, nextKey);
 	RyanJsonCheckReturnNull(NULL != nextItem);
 	RyanJsonCheckReturnNull(NULL != nextItem);
-	RyanJsonCheckNeverNoAssert(RyanJsonIsKey(nextItem));
+	RyanJsonCheckAssert(RyanJsonIsKey(nextItem));
 
 
 	va_list args;
 	va_list args;
 	va_start(args, key);
 	va_start(args, key);
@@ -31,12 +476,11 @@ RyanJson_t RyanJsonGetObjectByKeys(RyanJson_t pJson, const char *key, ...)
 }
 }
 
 
 /**
 /**
- * @brief 连续通过 索引 获取json对象的子项
+ * @brief 按多级 index 路径获取节点
  *
  *
- * @param pJson
- * @param index
- * @param ... 可变参,连续输入索引,直到UINT32_MAX结束
- * @return RyanJson_t
+ * @param pJson 起始节点
+ * @param index 第一级索引
+ * @return RyanJson_t 目标节点,失败返回 NULL
  */
  */
 RyanJson_t RyanJsonGetObjectByIndexs(RyanJson_t pJson, uint32_t index, ...)
 RyanJson_t RyanJsonGetObjectByIndexs(RyanJson_t pJson, uint32_t index, ...)
 {
 {
@@ -60,102 +504,70 @@ RyanJson_t RyanJsonGetObjectByIndexs(RyanJson_t pJson, uint32_t index, ...)
 }
 }
 
 
 /**
 /**
- * @brief 创建一个int类型的数组json对象
+ * @brief 创建 int32_t 数组节点
  *
  *
- * @param numbers 数组的地址必须为int类型
- * @param count 数组的长度
- * @return RyanJson_t
+ * @param numbers 输入数组
+ * @param count 元素个
+ * @return RyanJson_t 新建数组节点,失败返回 NULL
  */
  */
 RyanJson_t RyanJsonCreateIntArray(const int32_t *numbers, uint32_t count)
 RyanJson_t RyanJsonCreateIntArray(const int32_t *numbers, uint32_t count)
 {
 {
 	RyanJsonCheckReturnNull(NULL != numbers);
 	RyanJsonCheckReturnNull(NULL != numbers);
 
 
 	RyanJson_t pJson = RyanJsonCreateArray();
 	RyanJson_t pJson = RyanJsonCreateArray();
-	for (uint32_t i = 0; pJson && i < count; i++) { RyanJsonAddIntToArray(pJson, numbers[i]); }
+	RyanJsonCheckReturnNull(NULL != pJson);
+	for (uint32_t i = 0; i < count; i++)
+	{
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonAddIntToArray(pJson, numbers[i]), {
+			RyanJsonDelete(pJson);
+			return NULL;
+		});
+	}
 	return pJson;
 	return pJson;
 }
 }
 
 
 /**
 /**
- * @brief 创建一个double类型的数组json对象
+ * @brief 创建 double 数组节点
  *
  *
- * @param numbers
- * @param count
- * @return RyanJson_t
+ * @param numbers 输入数组
+ * @param count 元素个数
+ * @return RyanJson_t 新建数组节点,失败返回 NULL
  */
  */
 RyanJson_t RyanJsonCreateDoubleArray(const double *numbers, uint32_t count)
 RyanJson_t RyanJsonCreateDoubleArray(const double *numbers, uint32_t count)
 {
 {
 	RyanJsonCheckReturnNull(NULL != numbers);
 	RyanJsonCheckReturnNull(NULL != numbers);
 
 
 	RyanJson_t pJson = RyanJsonCreateArray();
 	RyanJson_t pJson = RyanJsonCreateArray();
-	for (uint32_t i = 0; pJson && i < count; i++) { RyanJsonAddDoubleToArray(pJson, numbers[i]); }
+	RyanJsonCheckReturnNull(NULL != pJson);
+	for (uint32_t i = 0; i < count; i++)
+	{
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonAddDoubleToArray(pJson, numbers[i]), {
+			RyanJsonDelete(pJson);
+			return NULL;
+		});
+	}
 	return pJson;
 	return pJson;
 }
 }
 
 
 /**
 /**
- * @brief 创建一个string类型的数组json对象
+ * @brief 创建字符串数组节点
  *
  *
- * @param strings
- * @param count
- * @return RyanJson_t
+ * @param strings 输入字符串数组
+ * @param count 元素个数
+ * @return RyanJson_t 新建数组节点,失败返回 NULL
  */
  */
 RyanJson_t RyanJsonCreateStringArray(const char **strings, uint32_t count)
 RyanJson_t RyanJsonCreateStringArray(const char **strings, uint32_t count)
 {
 {
 	RyanJsonCheckReturnNull(NULL != strings);
 	RyanJsonCheckReturnNull(NULL != strings);
 
 
 	RyanJson_t pJson = RyanJsonCreateArray();
 	RyanJson_t pJson = RyanJsonCreateArray();
-	for (uint32_t i = 0; pJson && i < count; i++) { RyanJsonAddStringToArray(pJson, strings[i]); }
-	return pJson;
-}
-
-/**
- * @brief 递归比较两个 pJson 对象key是否相等。
- * 此接口效率较低, 谨慎使用
- * @param leftJson
- * @param rightJson
- * @return RyanJsonBool_e
- */
-RyanJsonBool_e RyanJsonCompareOnlyKey(RyanJson_t leftJson, RyanJson_t rightJson)
-{
-	RyanJsonCheckReturnFalse(NULL != leftJson && NULL != rightJson);
-
-	// 相同的对象相等
-	if (leftJson == rightJson) { return RyanJsonTrue; }
-
-	RyanJsonCheckReturnFalse(RyanJsonGetType(leftJson) == RyanJsonGetType(rightJson));
-
-	switch (RyanJsonGetType(leftJson))
+	RyanJsonCheckReturnNull(NULL != pJson);
+	for (uint32_t i = 0; i < count; i++)
 	{
 	{
-	case RyanJsonTypeBool:
-	case RyanJsonTypeNull:
-	case RyanJsonTypeNumber:
-	case RyanJsonTypeString: return RyanJsonTrue;
-
-	case RyanJsonTypeArray: {
-		RyanJsonCheckReturnFalse(RyanJsonGetSize(leftJson) == RyanJsonGetSize(rightJson));
-
-		RyanJson_t item;
-		uint32_t itemIndex = 0;
-		RyanJsonArrayForEach(leftJson, item)
-		{
-			RyanJsonCheckReturnFalse(RyanJsonTrue ==
-						 RyanJsonCompareOnlyKey(item, RyanJsonGetObjectByIndex(rightJson, itemIndex)));
-			itemIndex++;
-		}
-		return RyanJsonTrue;
+		RyanJsonCheckCode(RyanJsonTrue == RyanJsonAddStringToArray(pJson, strings[i]), {
+			RyanJsonDelete(pJson);
+			return NULL;
+		});
 	}
 	}
-
-	case RyanJsonTypeObject: {
-		RyanJsonCheckReturnFalse(RyanJsonGetSize(leftJson) == RyanJsonGetSize(rightJson));
-
-		RyanJson_t item;
-		RyanJsonObjectForEach(leftJson, item)
-		{
-			RyanJsonCheckReturnFalse(RyanJsonTrue ==
-						 RyanJsonCompareOnlyKey(item, RyanJsonGetObjectByKey(rightJson, RyanJsonGetKey(item))));
-		}
-		return RyanJsonTrue;
-	}
-	}
-
-	return RyanJsonFalse;
+	return pJson;
 }
 }

+ 0 - 42
RyanJson/RyanJsonUtils.h

@@ -1,42 +0,0 @@
-#ifndef RyanJsonUtils
-#define RyanJsonUtils
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "RyanJson.h"
-#include <stdarg.h>
-
-// 语法糖,根据传入的numbers数组创建一个int类型的数组。如果没有添加到父json, 则需释放内存
-extern RyanJson_t RyanJsonCreateIntArray(const int32_t *numbers, uint32_t count);
-// 语法糖,根据传入的numbers数组创建一个double类型的数组。如果没有添加到父json, 则需释放内存
-extern RyanJson_t RyanJsonCreateDoubleArray(const double *numbers, uint32_t count);
-// 语法糖,根据传入的strings数组创建一个string类型的数组。如果没有添加到父json, 则需释放内存
-extern RyanJson_t RyanJsonCreateStringArray(const char **strings, uint32_t count);
-
-extern RyanJsonBool_e RyanJsonCompareOnlyKey(RyanJson_t leftJson, RyanJson_t rightJson);
-
-/**
- * @brief 查询函数,此接口较为底层,请使用下发的宏定义调用
- */
-extern RyanJson_t RyanJsonGetObjectByIndexs(RyanJson_t pJson, uint32_t index, ...);
-extern RyanJson_t RyanJsonGetObjectByKeys(RyanJson_t pJson, const char *key, ...);
-
-/**
- * @brief 可使用此宏进行嵌套式查找,
- * 例如 RyanJsonGetObjectToKey(json, "test", "inter")
- * 例如 RyanJsonGetObjectToIndex(json, 0, 2)
- *
- */
-#define RyanJsonGetObjectToKey(pJson, key, ...)     RyanJsonGetObjectByKeys(pJson, (key), ##__VA_ARGS__, NULL)
-#define RyanJsonGetObjectToIndex(pJson, index, ...) RyanJsonGetObjectByIndexs(pJson, (index), ##__VA_ARGS__, UINT32_MAX)
-
-#define RyanJsonHasObjectToKey(pJson, key, ...)     RyanJsonMakeBool(RyanJsonGetObjectByKeys(pJson, key, ##__VA_ARGS__, NULL))
-#define RyanJsonHasObjectToIndex(pJson, index, ...) RyanJsonMakeBool(RyanJsonGetObjectByIndexs(pJson, index, ##__VA_ARGS__, UINT32_MAX))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif

+ 43 - 43
example/RyanJsonExample.c

@@ -1,8 +1,7 @@
 #include "RyanJson.h"
 #include "RyanJson.h"
-#include "RyanJsonUtils.h"
 
 
 /**
 /**
- * @brief 生成json示例
+ * @brief Json 构建示例
  *
  *
  * @return RyanJsonBool_e
  * @return RyanJsonBool_e
  */
  */
@@ -11,7 +10,7 @@ static RyanJsonBool_e createJsonExample(void)
 	char *str = NULL;
 	char *str = NULL;
 	RyanJson_t jsonRoot, item;
 	RyanJson_t jsonRoot, item;
 
 
-	// 对象生成测试
+	// 构建根对象
 	jsonRoot = RyanJsonCreateObject();
 	jsonRoot = RyanJsonCreateObject();
 	RyanJsonAddIntToObject(jsonRoot, "inter", 16);
 	RyanJsonAddIntToObject(jsonRoot, "inter", 16);
 	RyanJsonAddDoubleToObject(jsonRoot, "double", 16.89);
 	RyanJsonAddDoubleToObject(jsonRoot, "double", 16.89);
@@ -28,10 +27,10 @@ static RyanJsonBool_e createJsonExample(void)
 	RyanJsonAddBoolToObject(item, "boolTrue", RyanJsonTrue);
 	RyanJsonAddBoolToObject(item, "boolTrue", RyanJsonTrue);
 	RyanJsonAddBoolToObject(item, "boolFalse", RyanJsonFalse);
 	RyanJsonAddBoolToObject(item, "boolFalse", RyanJsonFalse);
 	RyanJsonAddNullToObject(item, "null");
 	RyanJsonAddNullToObject(item, "null");
-	RyanJsonAddItemToObject(jsonRoot, "item", item); // 将上面创建的item子对象添加到root父对象
+	RyanJsonAddItemToObject(jsonRoot, "item", item); // 把子对象挂到根对象
 
 
 	// 添加数字子数组
 	// 添加数字子数组
-	int arrayInt[] = {16, 16, 16, 16, 16};
+	int32_t arrayInt[] = {16, 16, 16, 16, 16};
 	RyanJsonAddItemToObject(jsonRoot, "arrayInt", RyanJsonCreateIntArray(arrayInt, sizeof(arrayInt) / sizeof(arrayInt[0])));
 	RyanJsonAddItemToObject(jsonRoot, "arrayInt", RyanJsonCreateIntArray(arrayInt, sizeof(arrayInt) / sizeof(arrayInt[0])));
 
 
 	// 添加浮点数子数组
 	// 添加浮点数子数组
@@ -52,7 +51,7 @@ static RyanJsonBool_e createJsonExample(void)
 	RyanJsonAddBoolToArray(array, RyanJsonTrue);
 	RyanJsonAddBoolToArray(array, RyanJsonTrue);
 	RyanJsonAddBoolToArray(array, RyanJsonFalse);
 	RyanJsonAddBoolToArray(array, RyanJsonFalse);
 	RyanJsonAddNullToArray(array);
 	RyanJsonAddNullToArray(array);
-	RyanJsonAddItemToObject(jsonRoot, "array", array); // 将上面创建的item子对象数组添加到root父对象
+	RyanJsonAddItemToObject(jsonRoot, "array", array); // 把混合数组挂到根对象
 
 
 	// 添加对象数组
 	// 添加对象数组
 	RyanJson_t arrayItem = RyanJsonCreateArray();
 	RyanJson_t arrayItem = RyanJsonCreateArray();
@@ -63,7 +62,7 @@ static RyanJsonBool_e createJsonExample(void)
 	RyanJsonAddBoolToObject(item, "boolTrue", RyanJsonTrue);
 	RyanJsonAddBoolToObject(item, "boolTrue", RyanJsonTrue);
 	RyanJsonAddBoolToObject(item, "boolFalse", RyanJsonFalse);
 	RyanJsonAddBoolToObject(item, "boolFalse", RyanJsonFalse);
 	RyanJsonAddNullToObject(item, "null");
 	RyanJsonAddNullToObject(item, "null");
-	RyanJsonAddItemToObject(arrayItem, "item", item); // 将item对象添加到arrayItem数组里面
+	RyanJsonAddItemToObject(arrayItem, "item", item); // 将对象加入数组
 
 
 	item = RyanJsonCreateObject();
 	item = RyanJsonCreateObject();
 	RyanJsonAddIntToObject(item, "inter", 16);
 	RyanJsonAddIntToObject(item, "inter", 16);
@@ -72,12 +71,12 @@ static RyanJsonBool_e createJsonExample(void)
 	RyanJsonAddBoolToObject(item, "boolTrue", RyanJsonTrue);
 	RyanJsonAddBoolToObject(item, "boolTrue", RyanJsonTrue);
 	RyanJsonAddBoolToObject(item, "boolFalse", RyanJsonFalse);
 	RyanJsonAddBoolToObject(item, "boolFalse", RyanJsonFalse);
 	RyanJsonAddNullToObject(item, "null");
 	RyanJsonAddNullToObject(item, "null");
-	RyanJsonAddItemToObject(arrayItem, "item", item); // 将item对象添加到arrayItem数组里面
+	RyanJsonAddItemToObject(arrayItem, "item", item); // 将对象加入数组
 
 
-	RyanJsonAddItemToObject(jsonRoot, "arrayItem", arrayItem); // 将arrayItem数组添加到root父对象
+	RyanJsonAddItemToObject(jsonRoot, "arrayItem", arrayItem); // 把对象数组挂到根对象
 
 
 	uint32_t len = 0;
 	uint32_t len = 0;
-	str = RyanJsonPrint(jsonRoot, 250, RyanJsonTrue, &len); // 以带格式方式将数据打印出来
+	str = RyanJsonPrint(jsonRoot, 250, RyanJsonTrue, &len); // 格式化打印
 	printf("strLen: %" PRIu32 ", data: %s\r\n", len, str);
 	printf("strLen: %" PRIu32 ", data: %s\r\n", len, str);
 	RyanJsonFree(str);
 	RyanJsonFree(str);
 
 
@@ -86,7 +85,7 @@ static RyanJsonBool_e createJsonExample(void)
 }
 }
 
 
 /**
 /**
- * @brief 序列化json文本示例
+ * @brief Json 解析示例
  *
  *
  * @return RyanJsonBool_e
  * @return RyanJsonBool_e
  */
  */
@@ -94,26 +93,27 @@ static RyanJsonBool_e loadJsonExample(void)
 {
 {
 	char *str = NULL;
 	char *str = NULL;
 	RyanJson_t jsonRoot = NULL;
 	RyanJson_t jsonRoot = NULL;
-	const char *jsonstr = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			      "\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\","
-			      "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,"
-			      "16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\","
-			      "\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{"
-			      "\"inter\":16,\"double\":16.89,\"string\":\"hello\","
-			      "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\","
-			      "\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]}";
-
-	// 解析json数据
+	static const char *jsonstr =
+		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
+		"\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\","
+		"\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,"
+		"16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\","
+		"\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{"
+		"\"inter\":16,\"double\":16.89,\"string\":\"hello\","
+		"\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\","
+		"\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]}";
+
+	// 解析 Json 文本
 	jsonRoot = RyanJsonParse(jsonstr);
 	jsonRoot = RyanJsonParse(jsonstr);
 	if (NULL == jsonRoot)
 	if (NULL == jsonRoot)
 	{
 	{
-		printf("%s:%d 序列化失败\r\n", __FILE__, __LINE__);
+		printf("%s:%d 解析失败\r\n", __FILE__, __LINE__);
 		return RyanJsonFalse;
 		return RyanJsonFalse;
 	}
 	}
 
 
-	// 读取 int 数据
-	int inter = RyanJsonGetIntValue(RyanJsonGetObjectByKey(jsonRoot, "inter"));
-	if (inter != 16)
+	// 读取 int32_t 数据
+	int32_t inter = RyanJsonGetIntValue(RyanJsonGetObjectByKey(jsonRoot, "inter"));
+	if (16 != inter)
 	{
 	{
 		printf("%s:%d 读取int失败\r\n", __FILE__, __LINE__);
 		printf("%s:%d 读取int失败\r\n", __FILE__, __LINE__);
 		RyanJsonDelete(jsonRoot);
 		RyanJsonDelete(jsonRoot);
@@ -155,31 +155,31 @@ static RyanJsonBool_e loadJsonExample(void)
 		return RyanJsonFalse;
 		return RyanJsonFalse;
 	}
 	}
 
 
-	// 将序列化的数据以无格式样式打印出来,并和原始数据进行对比
+	// 将解析结果以无格式重新输出,并与原始文本对比
 	str = RyanJsonPrint(jsonRoot, 250, RyanJsonFalse, NULL);
 	str = RyanJsonPrint(jsonRoot, 250, RyanJsonFalse, NULL);
-	if (strcmp(str, jsonstr) != 0)
+	if (0 != strcmp(str, jsonstr))
 	{
 	{
-		printf("%s:%d 序列化与反序列化后的数据不对应  %s\r\n", __FILE__, __LINE__, str);
+		printf("%s:%d 解析后再序列化结果不一致  %s\r\n", __FILE__, __LINE__, str);
 		RyanJsonFree(str);
 		RyanJsonFree(str);
 		RyanJsonDelete(jsonRoot);
 		RyanJsonDelete(jsonRoot);
 		return RyanJsonFalse;
 		return RyanJsonFalse;
 	}
 	}
 	RyanJsonFree(str);
 	RyanJsonFree(str);
 
 
-	// 将序列化的数据以有格式样式打印出来
+	// 将解析结果以格式化样式打印
 	uint32_t len = 0;
 	uint32_t len = 0;
 	str = RyanJsonPrint(jsonRoot, 250, RyanJsonTrue, &len);
 	str = RyanJsonPrint(jsonRoot, 250, RyanJsonTrue, &len);
 	printf("strLen: %" PRIu32 ", data: %s\r\n", len, str);
 	printf("strLen: %" PRIu32 ", data: %s\r\n", len, str);
 	RyanJsonFree(str);
 	RyanJsonFree(str);
 
 
-	// 删除json对象
+	// 释放 Json 对象
 	RyanJsonDelete(jsonRoot);
 	RyanJsonDelete(jsonRoot);
 
 
 	return RyanJsonTrue;
 	return RyanJsonTrue;
 }
 }
 
 
 /**
 /**
- * @brief 修改json示例
+ * @brief Json 修改示例
  *
  *
  * @return RyanJsonBool_e
  * @return RyanJsonBool_e
  */
  */
@@ -189,15 +189,15 @@ static RyanJsonBool_e changeJsonExample(void)
 	RyanJson_t jsonRoot;
 	RyanJson_t jsonRoot;
 	const char *jsonstr = "{\"name\":\"Mash\",\"star\":4,\"doubleKey\":4.4,\"boolKey\":true,\"hits\":[2,2,1,3]}";
 	const char *jsonstr = "{\"name\":\"Mash\",\"star\":4,\"doubleKey\":4.4,\"boolKey\":true,\"hits\":[2,2,1,3]}";
 
 
-	// 解析json数据
+	// 解析 Json 文本
 	jsonRoot = RyanJsonParse(jsonstr);
 	jsonRoot = RyanJsonParse(jsonstr);
 	if (jsonRoot == NULL)
 	if (jsonRoot == NULL)
 	{
 	{
-		printf("%s:%d 序列化失败\r\n", __FILE__, __LINE__);
+		printf("%s:%d 解析失败\r\n", __FILE__, __LINE__);
 		return RyanJsonFalse;
 		return RyanJsonFalse;
 	}
 	}
 
 
-	// 修改key
+	// 修改 key
 	RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "name"), "name2");
 	RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "name"), "name2");
 	if (0 != strcmp("name2", RyanJsonGetKey(RyanJsonGetObjectByKey(jsonRoot, "name2"))))
 	if (0 != strcmp("name2", RyanJsonGetKey(RyanJsonGetObjectByKey(jsonRoot, "name2"))))
 	{
 	{
@@ -206,7 +206,7 @@ static RyanJsonBool_e changeJsonExample(void)
 		return RyanJsonFalse;
 		return RyanJsonFalse;
 	}
 	}
 
 
-	// 修改strValue
+	// 修改 strValue
 	RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "name2"), "Ryan");
 	RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "name2"), "Ryan");
 	if (0 != strcmp("Ryan", RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot, "name2"))))
 	if (0 != strcmp("Ryan", RyanJsonGetStringValue(RyanJsonGetObjectByKey(jsonRoot, "name2"))))
 	{
 	{
@@ -215,7 +215,7 @@ static RyanJsonBool_e changeJsonExample(void)
 		return RyanJsonFalse;
 		return RyanJsonFalse;
 	}
 	}
 
 
-	// 修改intValue
+	// 修改 intValue
 	RyanJsonChangeIntValue(RyanJsonGetObjectByKey(jsonRoot, "star"), 5);
 	RyanJsonChangeIntValue(RyanJsonGetObjectByKey(jsonRoot, "star"), 5);
 	if (5 != RyanJsonGetIntValue(RyanJsonGetObjectByKey(jsonRoot, "star")))
 	if (5 != RyanJsonGetIntValue(RyanJsonGetObjectByKey(jsonRoot, "star")))
 	{
 	{
@@ -224,7 +224,7 @@ static RyanJsonBool_e changeJsonExample(void)
 		return RyanJsonFalse;
 		return RyanJsonFalse;
 	}
 	}
 
 
-	// 修改doubleValue
+	// 修改 doubleValue
 	RyanJsonChangeDoubleValue(RyanJsonGetObjectByKey(jsonRoot, "doubleKey"), 5.5);
 	RyanJsonChangeDoubleValue(RyanJsonGetObjectByKey(jsonRoot, "doubleKey"), 5.5);
 	if (RyanJsonFalse == RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectByKey(jsonRoot, "doubleKey")), 5.5))
 	if (RyanJsonFalse == RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectByKey(jsonRoot, "doubleKey")), 5.5))
 	{
 	{
@@ -233,7 +233,7 @@ static RyanJsonBool_e changeJsonExample(void)
 		return RyanJsonFalse;
 		return RyanJsonFalse;
 	}
 	}
 
 
-	// 修改boolValue
+	// 修改 boolValue
 	RyanJsonChangeBoolValue(RyanJsonGetObjectByKey(jsonRoot, "boolKey"), RyanJsonFalse);
 	RyanJsonChangeBoolValue(RyanJsonGetObjectByKey(jsonRoot, "boolKey"), RyanJsonFalse);
 	if (RyanJsonFalse != RyanJsonGetBoolValue(RyanJsonGetObjectByKey(jsonRoot, "boolKey")))
 	if (RyanJsonFalse != RyanJsonGetBoolValue(RyanJsonGetObjectByKey(jsonRoot, "boolKey")))
 	{
 	{
@@ -242,16 +242,16 @@ static RyanJsonBool_e changeJsonExample(void)
 		return RyanJsonFalse;
 		return RyanJsonFalse;
 	}
 	}
 
 
-	// 替换节点(修改节点类型)
+	// 替换节点(同时改变节点类型)
 	RyanJsonReplaceByKey(jsonRoot, "star", RyanJsonCreateString("", "123456"));
 	RyanJsonReplaceByKey(jsonRoot, "star", RyanJsonCreateString("", "123456"));
 
 
-	// 将序列化的数据以有格式样式打印出来
+	// 将修改结果以格式化样式打印
 	uint32_t len = 0;
 	uint32_t len = 0;
 	str = RyanJsonPrint(jsonRoot, 250, RyanJsonTrue, &len);
 	str = RyanJsonPrint(jsonRoot, 250, RyanJsonTrue, &len);
 	printf("strLen: %" PRIu32 ", data: %s\r\n", len, str);
 	printf("strLen: %" PRIu32 ", data: %s\r\n", len, str);
 	RyanJsonFree(str);
 	RyanJsonFree(str);
 
 
-	// 删除json对象
+	// 释放 Json 对象
 	RyanJsonDelete(jsonRoot);
 	RyanJsonDelete(jsonRoot);
 
 
 	return RyanJsonTrue;
 	return RyanJsonTrue;
@@ -264,13 +264,13 @@ RyanJsonBool_e RyanJsonExample(void)
 	printf("\r\n--------------------------- RyanJson 生成示例 --------------------------\r\n");
 	printf("\r\n--------------------------- RyanJson 生成示例 --------------------------\r\n");
 	RyanJsonCheckReturnFalse(RyanJsonTrue == createJsonExample());
 	RyanJsonCheckReturnFalse(RyanJsonTrue == createJsonExample());
 
 
-	printf("\r\n--------------------------- RyanJson 序列化json文本示例 --------------------------\r\n");
+	printf("\r\n--------------------------- RyanJson 解析json文本示例 --------------------------\r\n");
 	RyanJsonCheckReturnFalse(RyanJsonTrue == loadJsonExample());
 	RyanJsonCheckReturnFalse(RyanJsonTrue == loadJsonExample());
 
 
 	printf("\r\n--------------------------- RyanJson 修改json示例 --------------------------\r\n");
 	printf("\r\n--------------------------- RyanJson 修改json示例 --------------------------\r\n");
 	RyanJsonCheckReturnFalse(RyanJsonTrue == changeJsonExample());
 	RyanJsonCheckReturnFalse(RyanJsonTrue == changeJsonExample());
 
 
-	// 更多功能请查看 RyanJson.h 文件,不了解的可以查看 test/baseTest 下的文件
+	// 更多功能请查看 RyanJson.h 与 test 目录下的测试用例
 
 
 	return RyanJsonTrue;
 	return RyanJsonTrue;
 }
 }

+ 0 - 35
run_base_coverage.sh

@@ -1,35 +0,0 @@
-#!/bin/bash
-set -e  # 遇到错误立即退出
-
-xmake
-echo "xmake build success"
-
-# ================================
-# 1. 运行
-# ================================
-./build/linux/x86/release/RyanJson
-
-
-# ================================
-# 2. 合并 profile 数据
-# ================================
-llvm-profdata merge -sparse default.profraw -o default.profdata
-
-# ================================
-# 3. 生成覆盖率报告(文本汇总)
-# ================================
-# 注意:llvm-cov report 只支持汇总统计,不支持行级参数
-llvm-cov report ./build/linux/x86/release/RyanJson \
-  -instr-profile=default.profdata \
-  -show-mcdc-summary \
-  -sources ./RyanJson
-
-# ================================
-# 4. 生成覆盖率报告(HTML详细)
-# ================================
-llvm-cov show ./build/linux/x86/release/RyanJson \
-  -instr-profile=default.profdata \
-  -format=html \
-  -output-dir=coverage/docs \
-  -sources ./RyanJson
-#   -sources ./test/fuzzer ./RyanJson

+ 0 - 53
run_coverage.sh

@@ -1,53 +0,0 @@
-#!/bin/bash
-set -e  # 遇到错误立即退出
-
-xmake
-echo "xmake build success"
-
-# git clone -b coverage git@github.com:Ryan-CW-Code/RyanJson.git coverage
-# ================================
-# 1. 运行 fuzzer
-# ================================
-./build/linux/x86/release/RyanJson \
-  ./test/fuzzer/corpus \
-  -dict=./test/fuzzer/RyanJsonFuzzer.dict \
-  -timeout=4 \
-  -runs=99999999 \
-  -verbosity=0 \
-  -max_len=8192 \
-  -workers=10 \
-  -jobs=10
-
-
-# ================================
-# 2. 合并 profile 数据
-# ================================
-llvm-profdata merge -sparse default.profraw -o default.profdata
-
-# ================================
-# 3. 生成覆盖率报告(文本汇总)
-# ================================
-# 注意:llvm-cov report 只支持汇总统计,不支持行级参数
-# --show-functions       显示函数级覆盖率
-# --show-region-summary  显示区域覆盖率
-llvm-cov report ./build/linux/x86/release/RyanJson \
-  -instr-profile=default.profdata \
-  -show-mcdc-summary \
-  -show-functions \
-  -show-region-summary \
-  -sources ./test/fuzzer ./RyanJson
-
-# ================================
-# 4. 生成覆盖率报告(HTML详细)
-# ================================
-llvm-cov show ./build/linux/x86/release/RyanJson \
-  -instr-profile=default.profdata \
-  -format=html \
-  -output-dir=coverage/docs \
-  -show-mcdc-summary \
-  -show-branches=count \
-  -show-expansions \
-  -show-regions \
-  -show-line-counts-or-regions \
-  -sources ./RyanJson
-#   -sources ./test/fuzzer ./RyanJson

+ 33 - 0
run_local_base.sh

@@ -0,0 +1,33 @@
+#!/bin/bash
+set -euo pipefail
+
+# 本地一键 Base(单元测试矩阵)。
+# 用途:直接调用 scripts/ci/runBaseCoverage.sh,免去手工拼参数。
+# 默认值:
+#   UNIT_MODE=full(8 组配置全覆盖)
+#   UNIT_SKIP_COV=1(跳过覆盖率,提速)
+#   UNIT_STOP_ON_FAIL=1(首个失败立即退出)
+#   XMAKE_FORCE_CLEAN=0(增量配置,减少重编译)
+# 以上参数都可用同名环境变量临时覆盖。
+
+scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "${scriptDir}"
+
+unitMode="${UNIT_MODE:-full}"
+unitSkipCov="${UNIT_SKIP_COV:-1}"
+unitStopOnFail="${UNIT_STOP_ON_FAIL:-1}"
+xmakeForceClean="${XMAKE_FORCE_CLEAN:-0}"
+
+echo "===================================================="
+echo "本地 Base 启动(单元测试矩阵)"
+echo "  - UNIT_MODE=${unitMode}"
+echo "  - UNIT_SKIP_COV=${unitSkipCov}"
+echo "  - UNIT_STOP_ON_FAIL=${unitStopOnFail}"
+echo "  - XMAKE_FORCE_CLEAN=${xmakeForceClean}"
+echo "===================================================="
+
+UNIT_MODE="${unitMode}" \
+UNIT_SKIP_COV="${unitSkipCov}" \
+UNIT_STOP_ON_FAIL="${unitStopOnFail}" \
+XMAKE_FORCE_CLEAN="${xmakeForceClean}" \
+bash ./scripts/ci/runBaseCoverage.sh

+ 37 - 0
run_local_ci.sh

@@ -0,0 +1,37 @@
+#!/bin/bash
+set -euo pipefail
+
+# 本地一键 CI(模拟 ci-pr)。
+# 执行顺序:
+#   先跑 full 单元矩阵(8 组),再跑 quick fuzz(1 组默认语义)。
+# 默认参数与 ci-pr.yml 对齐:
+#   unit: UNIT_MODE=full, UNIT_SKIP_COV=1
+#   fuzz: FUZZ_MODE=quick, FUZZ_SKIP_COV=1, FUZZ_MAX_TOTAL_TIME=45, workers/jobs=2
+# fuzz 阶段参数可用同名环境变量临时覆盖。
+
+scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "${scriptDir}"
+
+echo "===================================================="
+echo "本地 CI 启动:阶段 1/2 -> Base 单元测试"
+echo "===================================================="
+bash ./run_local_base.sh
+
+echo "===================================================="
+echo "本地 CI 启动:阶段 2/2 -> Fuzz quick"
+echo "===================================================="
+
+RYANJSON_STRICT_OBJECT_KEY_CHECK="${RYANJSON_STRICT_OBJECT_KEY_CHECK:-false}" \
+RYANJSON_DEFAULT_ADD_AT_HEAD="${RYANJSON_DEFAULT_ADD_AT_HEAD:-true}" \
+RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC="${RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC:-true}" \
+FUZZ_MODE="${FUZZ_MODE:-quick}" \
+FUZZ_SKIP_COV="${FUZZ_SKIP_COV:-1}" \
+FUZZ_MAX_TOTAL_TIME="${FUZZ_MAX_TOTAL_TIME:-45}" \
+FUZZ_WORKERS="${FUZZ_WORKERS:-2}" \
+FUZZ_JOBS="${FUZZ_JOBS:-2}" \
+XMAKE_FORCE_CLEAN="${XMAKE_FORCE_CLEAN:-0}" \
+bash ./scripts/ci/runCoverage.sh
+
+echo "===================================================="
+echo "本地 CI 执行完成"
+echo "===================================================="

+ 89 - 0
run_local_format.sh

@@ -0,0 +1,89 @@
+#!/bin/bash
+set -euo pipefail
+
+# 本地一键 clang-format。
+# 默认行为:格式化仓库内受管源码(C/C++ 头源文件)。
+# 可选参数:
+#   --check    仅检查,不修改文件(不符合时返回非 0)
+#   --changed  仅处理当前改动文件(默认处理全部受管源码)
+
+scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "${scriptDir}"
+
+mode="write"   # write | check
+scope="all"    # all | changed
+
+while (($# > 0)); do
+	case "$1" in
+	--check)
+		mode="check"
+		;;
+	--changed)
+		scope="changed"
+		;;
+	-h | --help)
+		echo "用法: bash ./run_local_format.sh [--check] [--changed]"
+		exit 0
+		;;
+	*)
+		echo "[错误] 未知参数: $1"
+		echo "用法: bash ./run_local_format.sh [--check] [--changed]"
+		exit 2
+		;;
+	esac
+	shift
+done
+
+if command -v clang-format >/dev/null 2>&1; then
+	formatter="clang-format"
+elif command -v clang-format-21 >/dev/null 2>&1; then
+	formatter="clang-format-21"
+elif command -v clang-format-20 >/dev/null 2>&1; then
+	formatter="clang-format-20"
+else
+	echo "[错误] 未找到 clang-format,可执行文件名尝试过: clang-format / clang-format-21 / clang-format-20"
+	exit 127
+fi
+
+allFiles=()
+if [[ "${scope}" == "all" ]]; then
+	mapfile -d '' allFiles < <(git ls-files -z -- '*.c' '*.h' '*.cc' '*.cpp' '*.hpp')
+else
+	mapfile -d '' allFiles < <(git diff --name-only -z -- '*.c' '*.h' '*.cc' '*.cpp' '*.hpp')
+fi
+
+files=()
+for f in "${allFiles[@]}"; do
+	case "${f}" in
+	test/externalModule/* | build/* | coverage/* | .xmake/* | test/fuzzer/corpus/*) ;;
+	*)
+		files+=("${f}")
+		;;
+	esac
+done
+
+echo "===================================================="
+echo "本地格式化启动"
+echo "  - formatter=${formatter}"
+echo "  - mode=${mode}"
+echo "  - scope=${scope}"
+echo "  - files=${#files[@]}"
+echo "===================================================="
+
+if [[ ${#files[@]} -eq 0 ]]; then
+	echo "[信息] 没有可处理的源码文件"
+	exit 0
+fi
+
+if [[ "${mode}" == "check" ]]; then
+	if printf '%s\0' "${files[@]}" | xargs -0 "${formatter}" --dry-run --Werror; then
+		echo "[完成] clang-format 检查通过"
+	else
+		echo "[失败] 存在不符合 .clang-format 的文件,请执行: bash ./run_local_format.sh"
+		exit 1
+	fi
+else
+	printf '%s\0' "${files[@]}" | xargs -0 "${formatter}" -i
+	echo "[完成] clang-format 已应用到 ${#files[@]} 个文件"
+fi
+

+ 65 - 0
run_local_fuzz.sh

@@ -0,0 +1,65 @@
+#!/bin/bash
+set -euo pipefail
+
+# 本地一键 Fuzz(默认低内存并发,可通过环境变量覆盖)。
+# 用途:封装常用 fuzz 参数,避免每次手工传 FUZZ_WORKERS/FUZZ_JOBS/FUZZ_RSS_LIMIT_MB。
+# 默认行为:
+#   workers/jobs 默认 2/2(可覆盖)
+#   默认 rss_limit_mb=2048(可覆盖)
+#   按轮次执行(默认 FUZZ_RUNS=10000000,不走 max_total_time)
+#   默认模式 nightly,默认生成覆盖率
+# 可覆盖参数:
+#   FUZZ_MODE/FUZZ_SKIP_COV/FUZZ_RUNS/FUZZ_TIMEOUT/FUZZ_MAX_LEN
+#   FUZZ_WORKERS/FUZZ_JOBS/FUZZ_RSS_LIMIT_MB/FUZZ_MALLOC_LIMIT_MB
+# 三个语义宏默认值(可覆盖):
+#   RYANJSON_STRICT_OBJECT_KEY_CHECK=false
+#   RYANJSON_DEFAULT_ADD_AT_HEAD=false
+#   RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC=false
+
+scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "${scriptDir}"
+
+fuzzMode="${FUZZ_MODE:-nightly}"
+fuzzSkipCov="${FUZZ_SKIP_COV:-0}"
+fuzzRuns="${FUZZ_RUNS:-10000000}"
+fuzzTimeout="${FUZZ_TIMEOUT:-4}"
+fuzzMaxLen="${FUZZ_MAX_LEN:-8192}"
+fuzzWorkers="${FUZZ_WORKERS:-3}"
+fuzzJobs="${FUZZ_JOBS:-9}"
+fuzzRssLimitMb="${FUZZ_RSS_LIMIT_MB:-4096}"
+fuzzMallocLimitMb="${FUZZ_MALLOC_LIMIT_MB:-}"
+
+export RYANJSON_STRICT_OBJECT_KEY_CHECK="${RYANJSON_STRICT_OBJECT_KEY_CHECK:-false}"
+export RYANJSON_DEFAULT_ADD_AT_HEAD="${RYANJSON_DEFAULT_ADD_AT_HEAD:-false}"
+export RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC="${RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC:-false}"
+
+echo "===================================================="
+echo "本地 Fuzz 启动"
+echo "  - FUZZ_MODE=${fuzzMode}"
+echo "  - FUZZ_SKIP_COV=${fuzzSkipCov}"
+echo "  - FUZZ_RUNS=${fuzzRuns}"
+echo "  - FUZZ_TIMEOUT=${fuzzTimeout}"
+echo "  - FUZZ_MAX_LEN=${fuzzMaxLen}"
+echo "  - FUZZ_WORKERS=${fuzzWorkers}"
+echo "  - FUZZ_JOBS=${fuzzJobs}"
+if [[ -n "${fuzzRssLimitMb}" ]]; then
+  echo "  - FUZZ_RSS_LIMIT_MB=${fuzzRssLimitMb}"
+fi
+if [[ -n "${fuzzMallocLimitMb}" ]]; then
+  echo "  - FUZZ_MALLOC_LIMIT_MB=${fuzzMallocLimitMb}"
+fi
+echo "  - RYANJSON_STRICT_OBJECT_KEY_CHECK=${RYANJSON_STRICT_OBJECT_KEY_CHECK}"
+echo "  - RYANJSON_DEFAULT_ADD_AT_HEAD=${RYANJSON_DEFAULT_ADD_AT_HEAD}"
+echo "  - RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC=${RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC}"
+echo "===================================================="
+
+FUZZ_MODE="${fuzzMode}" \
+FUZZ_SKIP_COV="${fuzzSkipCov}" \
+FUZZ_RUNS="${fuzzRuns}" \
+FUZZ_TIMEOUT="${fuzzTimeout}" \
+FUZZ_MAX_LEN="${fuzzMaxLen}" \
+FUZZ_WORKERS="${fuzzWorkers}" \
+FUZZ_JOBS="${fuzzJobs}" \
+FUZZ_RSS_LIMIT_MB="${fuzzRssLimitMb}" \
+FUZZ_MALLOC_LIMIT_MB="${fuzzMallocLimitMb}" \
+bash ./scripts/ci/runCoverage.sh

+ 440 - 0
run_local_qemu.sh

@@ -0,0 +1,440 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "${scriptDir}"
+
+userMachineSet=0
+userCpuSet=0
+userTargetSet=0
+if [[ -n "${QEMU_MACHINE+x}" ]]; then userMachineSet=1; fi
+if [[ -n "${QEMU_CPU+x}" ]]; then userCpuSet=1; fi
+if [[ -n "${QEMU_TARGET+x}" ]]; then userTargetSet=1; fi
+
+qemuMode="${QEMU_MODE:-full}"
+qemuMachine="${QEMU_MACHINE:-mps2-an386}"
+qemuCpu="${QEMU_CPU:-cortex-m4}"
+qemuTimeoutSec="${QEMU_TIMEOUT_SEC:-120}"
+qemuTarget="${QEMU_TARGET:-RyanJsonQemu}"
+qemuForceClean="${QEMU_FORCE_CONFIG_CLEAN:-0}"
+qemuStopOnFail="${QEMU_STOP_ON_FAIL:-1}"
+qemuLogRoot="${QEMU_LOG_ROOT:-coverage/qemu}"
+qemuMemory="${QEMU_MEMORY:-64M}"
+qemuConsoleLog="${QEMU_CONSOLE_LOG:-1}"
+qemuSaveLog="${QEMU_SAVE_LOG:-0}"
+qemuMaxCases="${QEMU_MAX_CASES:-0}"
+
+require_cmd() {
+  if ! command -v "$1" >/dev/null 2>&1; then
+    echo "[错误] 缺少命令: $1"
+    echo "[提示] 可执行: bash ./scripts/setup/install_qemu_deps.sh"
+    exit 1
+  fi
+}
+
+require_cmd xmake
+require_cmd arm-none-eabi-gcc
+require_cmd arm-none-eabi-objcopy
+require_cmd qemu-system-arm
+
+qemuSemihostingMode="legacy"
+if qemu-system-arm -help 2>&1 | grep -q -- "-semihosting-config"; then
+  qemuSemihostingMode="config"
+fi
+
+machine_supported() {
+  local machineName="$1"
+  qemu-system-arm -machine help | awk '{print $1}' | grep -Fxq "${machineName}"
+}
+
+cleanQemuStream() {
+  if command -v perl >/dev/null 2>&1; then
+    # Normalize UART CRLF to LF so grep ^...$ marker checks are reliable.
+    # Keep ANSI ESC (\x1B) for colored test output; strip other non-printable noise bytes.
+    perl -ne 's/\r//g; s/[\x00-\x08\x0B\x0C\x0E-\x1A\x1C-\x1F\x7F]//g; next if length($_) > 4096; print;'
+  else
+    tr -d '\000\r'
+  fi
+}
+
+getQemuChildPid() {
+  local parentPid="$1"
+  pgrep -P "${parentPid}" qemu-system-arm | head -n 1 || true
+}
+
+stopQemuRun() {
+  local parentPid="$1"
+  local reason="$2"
+  local childPid=""
+  local waitDeadline=0
+
+  childPid="$(getQemuChildPid "${parentPid}")"
+  if [[ -n "${childPid}" ]]; then
+    kill -TERM "${childPid}" >/dev/null 2>&1 || true
+  fi
+
+  waitDeadline=$((SECONDS + 3))
+  while kill -0 "${parentPid}" >/dev/null 2>&1; do
+    if ((SECONDS >= waitDeadline)); then
+      break
+    fi
+    sleep 0.2
+  done
+
+  if kill -0 "${parentPid}" >/dev/null 2>&1; then
+    kill -TERM "${parentPid}" >/dev/null 2>&1 || true
+    sleep 1
+    kill -KILL "${parentPid}" >/dev/null 2>&1 || true
+  fi
+}
+
+# 优先 mps2-an386(CM4F),若当前 QEMU 不支持则自动回退到 mps2-an385(CM3)。
+if ! machine_supported "${qemuMachine}"; then
+  if [[ "${qemuMachine}" == "mps2-an386" ]] && machine_supported "mps2-an385"; then
+    echo "[信息] 当前 QEMU 不支持 mps2-an386,自动回退到 mps2-an385。"
+    qemuMachine="mps2-an385"
+    if [[ "${userCpuSet}" -eq 0 ]]; then
+      qemuCpu="cortex-m3"
+    fi
+    if [[ "${userTargetSet}" -eq 0 ]]; then
+      qemuTarget="RyanJsonQemuCm3"
+    fi
+  else
+    echo "[错误] QEMU 不支持 machine=${qemuMachine}"
+    echo "[提示] 可用 machine 列表:"
+    qemu-system-arm -machine help | sed -n '1,120p'
+    exit 1
+  fi
+fi
+
+# 用户直接选择 an385 且未显式指定 target/cpu 时,自动切到 CM3 构建目标。
+if [[ "${qemuMachine}" == "mps2-an385" ]]; then
+  if [[ "${userCpuSet}" -eq 0 ]]; then
+    qemuCpu="cortex-m3"
+  fi
+  if [[ "${userTargetSet}" -eq 0 ]]; then
+    qemuTarget="RyanJsonQemuCm3"
+  fi
+fi
+
+caseList=()
+add_case() {
+  caseList+=("$1 $2 $3")
+}
+
+case "${qemuMode}" in
+  quick)
+    add_case false true true
+    add_case true false true
+    ;;
+  nightly)
+    for strictKey in false true; do
+      for addAtHead in false true; do
+        add_case "${strictKey}" "${addAtHead}" true
+      done
+    done
+    ;;
+  full)
+    for strictKey in false true; do
+      for addAtHead in false true; do
+        for scientific in false true; do
+          add_case "${strictKey}" "${addAtHead}" "${scientific}"
+        done
+      done
+    done
+    ;;
+  *)
+    echo "[错误] QEMU_MODE 仅支持 quick/nightly/full,当前值:${qemuMode}"
+    exit 1
+    ;;
+esac
+
+if ! [[ "${qemuMaxCases}" =~ ^[0-9]+$ ]]; then
+  echo "[错误] QEMU_MAX_CASES 仅支持非负整数,当前值:${qemuMaxCases}"
+  exit 1
+fi
+
+if ! [[ "${qemuSaveLog}" =~ ^[01]$ ]]; then
+  echo "[错误] QEMU_SAVE_LOG 仅支持 0/1,当前值:${qemuSaveLog}"
+  exit 1
+fi
+
+if ! [[ "${qemuConsoleLog}" =~ ^[01]$ ]]; then
+  echo "[错误] QEMU_CONSOLE_LOG 仅支持 0/1,当前值:${qemuConsoleLog}"
+  exit 1
+fi
+
+if [[ "${qemuConsoleLog}" == "0" && "${qemuSaveLog}" == "0" ]]; then
+  echo "[信息] QEMU_CONSOLE_LOG=0 且 QEMU_SAVE_LOG=0 无可见输出,自动切换 QEMU_CONSOLE_LOG=1。"
+  qemuConsoleLog="1"
+fi
+
+if [[ "${qemuSaveLog}" == "1" ]]; then
+  mkdir -p "${qemuLogRoot}"
+fi
+
+if (( qemuMaxCases > 0 )) && (( qemuMaxCases < ${#caseList[@]} )); then
+  caseList=("${caseList[@]:0:qemuMaxCases}")
+fi
+
+echo "===================================================="
+echo "QEMU 本地链路启动(完整 localbase 单测 + 对齐语义)"
+echo "  - MODE=${qemuMode}"
+echo "  - TARGET=${qemuTarget}"
+echo "  - MACHINE=${qemuMachine}"
+echo "  - CPU=${qemuCpu}"
+echo "  - TIMEOUT=${qemuTimeoutSec}s"
+echo "  - LOG_ROOT=${qemuLogRoot}"
+echo "  - MEMORY=${qemuMemory}"
+echo "  - CONSOLE_LOG=${qemuConsoleLog}"
+echo "  - SAVE_LOG=${qemuSaveLog}"
+echo "  - SEMIHOSTING=${qemuSemihostingMode}"
+echo "  - MAX_CASES=${qemuMaxCases}"
+echo "===================================================="
+
+totalCases="${#caseList[@]}"
+caseIndex=0
+failedCases=0
+
+logHasRequiredMarkers() {
+  local logPath="$1"
+  if ! grep -Eq "^\\[QEMU\\]\\[RESULT\\] UNIT_PASS code=0 tick=[0-9]+\r?$" "${logPath}"; then
+    return 1
+  fi
+
+  if ! grep -Eq "^\\[QEMU\\]\\[ALIGN\\] aligned_access PASS read=0x[0-9A-Fa-f]+\r?$" "${logPath}"; then
+    return 1
+  fi
+
+  if ! grep -Eq "^\\[QEMU\\]\\[ALIGN\\] unaligned_access TRIGGER addr=0x[0-9A-Fa-f]+\r?$" "${logPath}"; then
+    return 1
+  fi
+
+  if ! grep -Eq "^\\[QEMU\\]\\[RESULT\\] EXPECTED_UNALIGNED_FAULT (cfsr|fallbackAddr)=0x[0-9A-Fa-f]+\r?$" "${logPath}"; then
+    return 1
+  fi
+
+  if ! grep -Eq "^\\[QEMU\\]\\[HARDFAULT\\] CFSR=0x[0-9A-Fa-f]+ HFSR=0x[0-9A-Fa-f]+ BFAR=0x[0-9A-Fa-f]+ MMFAR=0x[0-9A-Fa-f]+\r?$" "${logPath}" \
+     && ! grep -Eq "^\\[QEMU\\]\\[HARDFAULT\\] fallback_soft_trap_no_hw_fault addr=0x[0-9A-Fa-f]+\r?$" "${logPath}"; then
+    return 1
+  fi
+
+  return 0
+}
+
+logHasFailureMarkers() {
+  local logPath="$1"
+  if grep -Eq "^\\[QEMU\\]\\[RESULT\\] UNIT_FAIL code=-?[0-9]+\r?$" "${logPath}"; then
+    return 0
+  fi
+  return 1
+}
+
+cleanupCaseLogIfNeeded() {
+  local keepCaseLog="$1"
+  local caseLogPath="$2"
+  if [[ "${keepCaseLog}" == "0" && -n "${caseLogPath}" ]]; then
+    rm -f "${caseLogPath}" >/dev/null 2>&1 || true
+  fi
+}
+
+run_case() {
+  local strictKey="$1"
+  local addAtHead="$2"
+  local scientific="$3"
+  local caseName="strict_${strictKey}__head_${addAtHead}__sci_${scientific}"
+  local caseLogPath=""
+  local buildLogPath=""
+  local keepCaseLog="0"
+  local deadlineSec=0
+  local qemuPid=0
+  local qemuRc=0
+
+  echo "----------------------------------------------------"
+  echo "[用例] ${caseName}"
+  echo "  - RyanJsonStrictObjectKeyCheck=${strictKey}"
+  echo "  - RyanJsonDefaultAddAtHead=${addAtHead}"
+  echo "  - RyanJsonSnprintfSupportScientific=${scientific}"
+  echo "----------------------------------------------------"
+
+  export RYANJSON_STRICT_OBJECT_KEY_CHECK="${strictKey}"
+  export RYANJSON_DEFAULT_ADD_AT_HEAD="${addAtHead}"
+  export RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC="${scientific}"
+
+  if [[ "${qemuForceClean}" == "1" ]]; then
+    xmake f -c -p cross -a arm
+  else
+    xmake f -p cross -a arm
+  fi
+  # 清理旧产物,避免构建失败时误用历史 ELF。
+  find ./build -type f -name "${qemuTarget}.elf" -delete >/dev/null 2>&1 || true
+  buildLogPath="$(mktemp "/tmp/${caseName}.build.XXXX.log")"
+  if ! xmake -b "${qemuTarget}" 2>&1 | tee "${buildLogPath}"; then
+    echo "[错误] xmake 构建失败,已终止本用例并跳过 QEMU 运行。"
+    tail -n 120 "${buildLogPath}" || true
+    rm -f "${buildLogPath}" >/dev/null 2>&1 || true
+    return 1
+  fi
+  if grep -Eq '(^|[^[:alpha:]])error:' "${buildLogPath}"; then
+    echo "[错误] 构建日志检测到编译错误,已终止本用例并跳过 QEMU 运行。"
+    tail -n 120 "${buildLogPath}" || true
+    rm -f "${buildLogPath}" >/dev/null 2>&1 || true
+    return 1
+  fi
+  rm -f "${buildLogPath}" >/dev/null 2>&1 || true
+
+  local elfPath
+  elfPath="$(find ./build -type f -name "${qemuTarget}.elf" | head -n 1 || true)"
+  if [[ -z "${elfPath}" ]]; then
+    echo "[错误] 未找到 ELF 输出(${qemuTarget}.elf)"
+    cleanupCaseLogIfNeeded "${keepCaseLog}" "${caseLogPath}"
+    return 1
+  fi
+
+  if [[ "${qemuSaveLog}" == "1" ]]; then
+    caseLogPath="${qemuLogRoot}/${caseName}.log"
+    keepCaseLog="1"
+  else
+    caseLogPath="$(mktemp "/tmp/${caseName}.XXXX.log")"
+    keepCaseLog="0"
+  fi
+
+  echo "[信息] ELF: ${elfPath}"
+  if [[ "${qemuSaveLog}" == "1" ]]; then
+    echo "[阶段] 启动 QEMU 并抓取日志 -> ${caseLogPath}"
+  else
+    echo "[阶段] 启动 QEMU(终端实时输出,日志不落盘)"
+  fi
+
+  local -a qemuArgs=(
+    -M "${qemuMachine}"
+    -cpu "${qemuCpu}"
+    -nographic
+    -kernel "${elfPath}"
+  )
+  if [[ "${qemuSemihostingMode}" == "config" ]]; then
+    qemuArgs+=(-semihosting-config enable=on,target=native)
+  else
+    qemuArgs+=(-semihosting)
+  fi
+  if [[ -n "${qemuMemory}" ]]; then
+    qemuArgs+=(-m "${qemuMemory}")
+  fi
+
+  : > "${caseLogPath}"
+  deadlineSec=$((SECONDS + qemuTimeoutSec))
+
+  set +e
+  if [[ "${qemuConsoleLog}" == "1" ]]; then
+    (
+      qemu-system-arm "${qemuArgs[@]}" 2>&1 | cleanQemuStream | tee -a "${caseLogPath}"
+    ) &
+  else
+    (
+      qemu-system-arm "${qemuArgs[@]}" 2>&1 | cleanQemuStream >> "${caseLogPath}"
+    ) &
+  fi
+  qemuPid=$!
+
+  while true; do
+    if ! kill -0 "${qemuPid}" >/dev/null 2>&1; then
+      wait "${qemuPid}"
+      qemuRc=$?
+      break
+    fi
+
+    if ((SECONDS >= deadlineSec)); then
+      stopQemuRun "${qemuPid}" "timeout"
+      wait "${qemuPid}"
+      qemuRc=124
+      break
+    fi
+
+    sleep 1
+  done
+  set -e
+
+  # Give the log pipeline a tiny grace window to flush trailing bytes.
+  sleep 0.1
+
+  if [[ "${qemuRc}" -eq 124 ]]; then
+    echo "[错误] QEMU 超时(${qemuTimeoutSec}s)"
+    tail -n 120 "${caseLogPath}"
+    cleanupCaseLogIfNeeded "${keepCaseLog}" "${caseLogPath}"
+    return 1
+  fi
+
+  if logHasFailureMarkers "${caseLogPath}"; then
+    echo "[错误] 用例失败(检测到 [QEMU][RESULT] UNIT_FAIL)"
+    tail -n 120 "${caseLogPath}"
+    cleanupCaseLogIfNeeded "${keepCaseLog}" "${caseLogPath}"
+    return 1
+  fi
+
+  local missing=0
+  if ! logHasRequiredMarkers "${caseLogPath}"; then
+    if ! grep -Eq "^\\[QEMU\\]\\[RESULT\\] UNIT_PASS code=0 tick=[0-9]+\r?$" "${caseLogPath}"; then
+      echo "[错误] 日志缺少关键标记: [QEMU][RESULT] UNIT_PASS code=0 tick=<num>"
+    fi
+    if ! grep -Eq "^\\[QEMU\\]\\[ALIGN\\] aligned_access PASS read=0x[0-9A-Fa-f]+\r?$" "${caseLogPath}"; then
+      echo "[错误] 日志缺少关键标记: [QEMU][ALIGN] aligned_access PASS read=0x<hex>"
+    fi
+    if ! grep -Eq "^\\[QEMU\\]\\[ALIGN\\] unaligned_access TRIGGER addr=0x[0-9A-Fa-f]+\r?$" "${caseLogPath}"; then
+      echo "[错误] 日志缺少关键标记: [QEMU][ALIGN] unaligned_access TRIGGER addr=0x<hex>"
+    fi
+    if ! grep -Eq "^\\[QEMU\\]\\[RESULT\\] EXPECTED_UNALIGNED_FAULT (cfsr|fallbackAddr)=0x[0-9A-Fa-f]+\r?$" "${caseLogPath}"; then
+      echo "[错误] 日志缺少关键标记: [QEMU][RESULT] EXPECTED_UNALIGNED_FAULT <dynamic>"
+    fi
+    if ! grep -Eq "^\\[QEMU\\]\\[HARDFAULT\\] CFSR=0x[0-9A-Fa-f]+ HFSR=0x[0-9A-Fa-f]+ BFAR=0x[0-9A-Fa-f]+ MMFAR=0x[0-9A-Fa-f]+\r?$" "${caseLogPath}" \
+       && ! grep -Eq "^\\[QEMU\\]\\[HARDFAULT\\] fallback_soft_trap_no_hw_fault addr=0x[0-9A-Fa-f]+\r?$" "${caseLogPath}"; then
+      echo "[错误] 日志缺少 fault 现场标记(HARDFAULT cfsr/hfsr 或 fallback addr)"
+    fi
+    missing=1
+  fi
+
+  if [[ "${missing}" -ne 0 ]]; then
+    echo "[错误] 用例失败,日志尾部:"
+    tail -n 120 "${caseLogPath}"
+    cleanupCaseLogIfNeeded "${keepCaseLog}" "${caseLogPath}"
+    return 1
+  fi
+
+  cleanupCaseLogIfNeeded "${keepCaseLog}" "${caseLogPath}"
+  echo "[通过] ${caseName}"
+  return 0
+}
+
+for entry in "${caseList[@]}"; do
+  caseIndex=$((caseIndex + 1))
+  read -r strictKey addAtHead scientific <<< "${entry}"
+
+  echo
+  echo "===================================================="
+  echo "【QEMU 用例 ${caseIndex}/${totalCases}】"
+  echo "===================================================="
+
+  if run_case "${strictKey}" "${addAtHead}" "${scientific}"; then
+    :
+  else
+    failedCases=$((failedCases + 1))
+    if [[ "${qemuStopOnFail}" == "1" ]]; then
+      echo "[错误] 按 QEMU_STOP_ON_FAIL=1 提前终止。"
+      exit 1
+    fi
+  fi
+done
+
+echo
+echo "QEMU 单测矩阵执行完成。"
+echo "  - 模式: ${qemuMode}"
+echo "  - 总用例: ${totalCases}"
+echo "  - 失败用例: ${failedCases}"
+if [[ "${qemuSaveLog}" == "1" ]]; then
+  echo "  - 日志目录: ${qemuLogRoot}"
+else
+  echo "  - 日志输出: 终端实时输出(不落盘)"
+fi
+
+if [[ "${failedCases}" -gt 0 ]]; then
+  exit 1
+fi

+ 143 - 0
run_local_skills.sh

@@ -0,0 +1,143 @@
+#!/bin/bash
+set -euo pipefail
+
+# 本地一键 skills 同步与规范校验。
+# 默认行为:
+#   1) 同步各技能的 references/terminology.md 到统一占位模板
+#   2) 使用 skill-creator 的 quick_validate.py 校验每个技能
+#   3) 校验 agents/openai.yaml 关键字段与默认 prompt 的技能名引用
+#
+# 可选参数:
+#   --sync-only      仅执行同步
+#   --validate-only  仅执行校验
+
+scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "${scriptDir}"
+
+doSync=1
+doValidate=1
+
+while (($# > 0)); do
+	case "$1" in
+	--sync-only)
+		doValidate=0
+		;;
+	--validate-only)
+		doSync=0
+		;;
+	-h | --help)
+		echo "用法: bash ./run_local_skills.sh [--sync-only|--validate-only]"
+		exit 0
+		;;
+	*)
+		echo "[错误] 未知参数: $1"
+		echo "用法: bash ./run_local_skills.sh [--sync-only|--validate-only]"
+		exit 2
+		;;
+	esac
+	shift
+done
+
+validator="/root/.codex/skills/.system/skill-creator/scripts/quick_validate.py"
+if [[ ! -f "${validator}" ]]; then
+	echo "[错误] 未找到技能校验脚本: ${validator}"
+	exit 1
+fi
+
+mapfile -t skillDirs < <(find skills -mindepth 1 -maxdepth 1 -type d ! -name shared | sort)
+if [[ ${#skillDirs[@]} -eq 0 ]]; then
+	echo "[信息] 未发现可处理的技能目录(skills/*,排除 skills/shared)"
+	exit 0
+fi
+
+echo "===================================================="
+echo "本地 Skills 任务启动"
+echo "  - sync=${doSync}"
+echo "  - validate=${doValidate}"
+echo "  - skills=${#skillDirs[@]}"
+echo "===================================================="
+
+if [[ ${doSync} -eq 1 ]]; then
+	echo "[阶段] 同步术语占位文档..."
+	for skillDir in "${skillDirs[@]}"; do
+		termFile="${skillDir}/references/terminology.md"
+		mkdir -p "$(dirname "${termFile}")"
+		if [[ ! -f "${termFile}" ]]; then
+			cat > "${termFile}" <<'EOF'
+# 术语字典
+
+- 统一术语定义复用共享文档:`../../shared/terminology.md`。
+- 如出现本技能专属术语,可在本文件追加扩展,不覆盖共享定义。
+EOF
+			echo "  - synced ${termFile} (created)"
+		elif grep -Fq '../../shared/terminology.md' "${termFile}"; then
+			echo "  - synced ${termFile} (already linked)"
+		else
+			tmpFile="$(mktemp)"
+			cat > "${tmpFile}" <<'EOF'
+# 术语字典
+
+- 统一术语定义复用共享文档:`../../shared/terminology.md`。
+- 如出现本技能专属术语,可在本文件追加扩展,不覆盖共享定义。
+
+EOF
+			cat "${termFile}" >> "${tmpFile}"
+			mv "${tmpFile}" "${termFile}"
+			echo "  - synced ${termFile} (prefixed)"
+		fi
+	done
+fi
+
+if [[ ${doValidate} -eq 1 ]]; then
+	echo "[阶段] 校验技能结构与 agents 元数据..."
+	for skillDir in "${skillDirs[@]}"; do
+		skillFile="${skillDir}/SKILL.md"
+		openaiFile="${skillDir}/agents/openai.yaml"
+
+		python3 "${validator}" "${skillDir}" >/dev/null
+
+		if [[ ! -f "${openaiFile}" ]]; then
+			echo "[错误] 缺少 agents/openai.yaml: ${openaiFile}"
+			exit 1
+		fi
+
+		if ! rg -q '^[[:space:]]*display_name:' "${openaiFile}"; then
+			echo "[错误] 缺少 interface.display_name: ${openaiFile}"
+			exit 1
+		fi
+		if ! rg -q '^[[:space:]]*short_description:' "${openaiFile}"; then
+			echo "[错误] 缺少 interface.short_description: ${openaiFile}"
+			exit 1
+		fi
+		if ! rg -q '^[[:space:]]*default_prompt:' "${openaiFile}"; then
+			echo "[错误] 缺少 interface.default_prompt: ${openaiFile}"
+			exit 1
+		fi
+
+		skillName="$(awk '
+			BEGIN { inFm=0 }
+			/^---[[:space:]]*$/ { if (inFm==0) { inFm=1; next } else { exit } }
+			inFm==1 && /^name:[[:space:]]*/ {
+				sub(/^name:[[:space:]]*/, "", $0)
+				print $0
+				exit
+			}
+		' "${skillFile}")"
+
+		if [[ -z "${skillName}" ]]; then
+			echo "[错误] 未能从 ${skillFile} 读取 name"
+			exit 1
+		fi
+
+		if ! grep -Fq "\$${skillName}" "${openaiFile}"; then
+			echo "[错误] default_prompt 未引用 \$${skillName}: ${openaiFile}"
+			exit 1
+		fi
+
+		echo "  - valid ${skillDir}"
+	done
+fi
+
+echo "===================================================="
+echo "本地 Skills 任务完成"
+echo "===================================================="

+ 27 - 0
scripts/README.md

@@ -0,0 +1,27 @@
+# 脚本目录说明
+
+## 目录结构
+- `scripts/ci`:CI 与覆盖率相关核心脚本
+- `scripts/setup`:本地环境准备脚本
+
+## 脚本清单
+- `scripts/ci/runBaseCoverage.sh`
+  - 单元测试矩阵入口(`quick/nightly/full`)
+  - 支持 `UNIT_SKIP_COV`、`UNIT_STOP_ON_FAIL`、`XMAKE_FORCE_CLEAN`
+- `scripts/ci/runCoverage.sh`
+  - Fuzzer 入口(`quick/nightly/full`)
+  - 支持 `FUZZ_RUNS/FUZZ_MAX_TOTAL_TIME`、`FUZZ_WORKERS/JOBS`、`XMAKE_FORCE_CLEAN`
+- `scripts/setup/install_qemu_deps.sh`
+  - 安装 `arm-none-eabi` 工具链与 `qemu-system-arm`
+  - 支持 `--no-update` 跳过包索引刷新
+
+## 根目录本地脚本
+- `run_local_base.sh`
+  - 本地一键跑单元测试矩阵(默认 full)
+- `run_local_ci.sh`
+  - 本地一键模拟 `ci-pr`(先 unit,再 fuzz quick)
+- `run_local_fuzz.sh`
+  - 本地一键 fuzz
+- `run_local_qemu.sh`
+  - 本地一键跑 QEMU 硬件语义校验(完整 localbase 单测 + 非对齐 fault)
+  - 默认 `QEMU_MEMORY=64M`,可按需通过环境变量覆盖

+ 213 - 0
scripts/ci/runBaseCoverage.sh

@@ -0,0 +1,213 @@
+#!/bin/bash
+set -euo pipefail
+
+# 单元测试配置矩阵入口(Linux)。
+# 脚本路径:scripts/ci/runBaseCoverage.sh
+# 执行模式:UNIT_MODE=quick|nightly|full
+#   quick: 2 组(PR 快检)
+#   nightly: 4 组(strict × addAtHead,scientific 固定 true)
+#   full: 8 组(三个布尔宏全组合)
+# 常用参数:
+#   UNIT_SKIP_COV=0|1:是否跳过覆盖率
+#   UNIT_STOP_ON_FAIL=0|1:失败是否立即终止
+#   XMAKE_FORCE_CLEAN=0|1:每组前是否先清理配置
+
+unitMode="${UNIT_MODE:-full}"
+unitSkipCov="${UNIT_SKIP_COV:-0}"
+unitStopOnFail="${UNIT_STOP_ON_FAIL:-1}"
+xmakeForceClean="${XMAKE_FORCE_CLEAN:-0}"
+
+# 统一切到仓库根目录,避免从任意 cwd 启动时相对路径失效
+scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+repoRoot="$(cd "${scriptDir}/../.." && pwd)"
+cd "${repoRoot}"
+
+# 覆盖率目录固定为 coverage/unitMatrix,每次执行前清理,保证只保留最新结果
+coverageRoot="coverage/unitMatrix"
+rm -rf "${coverageRoot}"
+profileRoot="${coverageRoot}/profiles"
+mkdir -p "${profileRoot}"
+
+declare -a caseList=()
+
+addCase() {
+  local strictKey="$1"
+  local addAtHead="$2"
+  local scientific="$3"
+  caseList+=("${strictKey} ${addAtHead} ${scientific}")
+}
+
+# 根据模式生成组合列表
+case "${unitMode}" in
+  quick)
+    # PR 快检:默认组合 + 对立组合
+    addCase false true true
+    addCase true false true
+    ;;
+  nightly)
+    # 夜间:覆盖 strict × addAtHead 四种核心语义
+    for strictKey in false true; do
+      for addAtHead in false true; do
+        addCase "${strictKey}" "${addAtHead}" true
+      done
+    done
+    ;;
+  full)
+    # 全量:三个布尔宏全组合
+    for strictKey in false true; do
+      for addAtHead in false true; do
+        for scientific in false true; do
+          addCase "${strictKey}" "${addAtHead}" "${scientific}"
+        done
+      done
+    done
+    ;;
+  *)
+    echo "[错误] UNIT_MODE 仅支持 quick/nightly/full,当前值:${unitMode}"
+    exit 1
+    ;;
+esac
+
+totalCases="${#caseList[@]}"
+caseIndex=0
+failedCases=0
+
+runCase() {
+  local index="$1"
+  local total="$2"
+  local strictKey="$3"
+  local addAtHead="$4"
+  local scientific="$5"
+
+  local caseName="strict_${strictKey}__head_${addAtHead}__sci_${scientific}"
+  local profraw="${profileRoot}/${caseName}.profraw"
+
+  echo "===================================================="
+  echo "【用例 ${index}/${total}】${caseName}"
+  echo "  - RyanJsonStrictObjectKeyCheck=${strictKey}"
+  echo "  - RyanJsonDefaultAddAtHead=${addAtHead}"
+  echo "  - RyanJsonSnprintfSupportScientific=${scientific}"
+  echo "===================================================="
+
+  export RYANJSON_STRICT_OBJECT_KEY_CHECK="${strictKey}"
+  export RYANJSON_DEFAULT_ADD_AT_HEAD="${addAtHead}"
+  export RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC="${scientific}"
+
+  # 重新配置,确保宏变化进入编译命令
+  # 默认走增量配置,配合第三方静态库可减少重复编译
+  if [[ "${xmakeForceClean}" == "1" ]]; then
+    echo "[阶段] 正在执行 xmake 配置(clean 模式)..."
+    if ! xmake f -c; then
+      echo "[错误] xmake 配置失败:${caseName}"
+      return 1
+    fi
+  else
+    echo "[阶段] 正在执行 xmake 配置(增量模式)..."
+    if ! xmake f; then
+      echo "[错误] xmake 配置失败:${caseName}"
+      return 1
+    fi
+  fi
+
+  echo "[阶段] 正在执行 xmake 构建(target=RyanJson)..."
+  if ! xmake -b RyanJson; then
+    echo "[错误] xmake 构建失败:${caseName}"
+    return 1
+  fi
+
+  # 单测执行,profile 分文件隔离,避免组合间互相覆盖
+  echo "[阶段] 正在运行单元测试二进制..."
+  if ! LLVM_PROFILE_FILE="${profraw}" ./build/linux/x86/release/RyanJson; then
+    echo "[错误] 单元测试执行失败:${caseName}"
+    return 1
+  fi
+
+  # 快检模式可跳过覆盖率阶段以缩短总时长
+  if [[ "${unitSkipCov}" == "1" ]]; then
+    echo "[信息] UNIT_SKIP_COV=1,已跳过覆盖率生成。"
+    return 0
+  fi
+}
+
+# 按组合清单执行
+for entry in "${caseList[@]}"; do
+  caseIndex=$((caseIndex + 1))
+  read -r strictKey addAtHead scientific <<< "${entry}"
+
+  if runCase "${caseIndex}" "${totalCases}" "${strictKey}" "${addAtHead}" "${scientific}"; then
+    :
+  else
+    failedCases=$((failedCases + 1))
+    if [[ "${unitStopOnFail}" == "1" ]]; then
+      echo
+      echo "[错误] 用例 ${caseIndex}/${totalCases} 失败,已按 UNIT_STOP_ON_FAIL=1 提前终止。"
+      exit 1
+    fi
+  fi
+done
+
+# 若启用覆盖率,则把矩阵中所有组合的 profraw 合并后只生成一份报告
+if [[ "${unitSkipCov}" != "1" ]]; then
+  if ! command -v llvm-profdata >/dev/null 2>&1; then
+    echo "[错误] 未找到 llvm-profdata,无法生成覆盖率。"
+    exit 1
+  fi
+  if ! command -v llvm-cov >/dev/null 2>&1; then
+    echo "[错误] 未找到 llvm-cov,无法生成覆盖率。"
+    exit 1
+  fi
+
+  shopt -s nullglob
+  profrawFiles=("${profileRoot}"/*.profraw)
+  shopt -u nullglob
+  if [[ "${#profrawFiles[@]}" -eq 0 ]]; then
+    echo "[错误] 未找到可合并的 profraw 文件。"
+    exit 1
+  fi
+
+  mergedProfdata="${coverageRoot}/coverage.profdata"
+  reportTxt="${coverageRoot}/report.txt"
+  reportHtml="${coverageRoot}/html"
+
+  if ! llvm-profdata merge -sparse "${profrawFiles[@]}" -o "${mergedProfdata}"; then
+    echo "[错误] profraw 合并失败。"
+    exit 1
+  fi
+
+  if ! llvm-cov report ./build/linux/x86/release/RyanJson \
+      -instr-profile="${mergedProfdata}" \
+      -show-mcdc-summary \
+      -sources ./RyanJson > "${reportTxt}"; then
+    echo "[错误] 文本覆盖率生成失败。"
+    exit 1
+  fi
+
+  if ! llvm-cov show ./build/linux/x86/release/RyanJson \
+      -instr-profile="${mergedProfdata}" \
+      -format=html \
+      -output-dir="${reportHtml}" \
+      -show-mcdc-summary \
+      -show-branches=count \
+      -show-expansions \
+      -show-regions \
+      -show-line-counts-or-regions \
+      -sources ./RyanJson; then
+    echo "[错误] HTML 覆盖率生成失败。"
+    exit 1
+  fi
+fi
+
+echo
+echo "单元测试矩阵执行完成。"
+echo "执行模式:${unitMode}"
+echo "总用例数:${totalCases}"
+echo "失败用例数:${failedCases}"
+echo "覆盖率输出目录:${coverageRoot}"
+if [[ "${unitSkipCov}" != "1" ]]; then
+  echo "覆盖率文本报告:${coverageRoot}/report.txt"
+  echo "覆盖率HTML目录:${coverageRoot}/html"
+fi
+
+if [[ "${failedCases}" -gt 0 ]]; then
+  exit 1
+fi

+ 226 - 0
scripts/ci/runCoverage.sh

@@ -0,0 +1,226 @@
+#!/bin/bash
+set -euo pipefail
+
+# Fuzzer 入口脚本(Linux)。
+# 脚本路径:scripts/ci/runCoverage.sh
+# 执行模式:FUZZ_MODE=quick|nightly|full
+#   quick: PR 快检(默认 60s)
+#   nightly: 夜间巡检(默认 300s)
+#   full: 全量压测(默认 900s)
+# 常用参数:
+#   FUZZ_SKIP_COV=0|1:是否跳过覆盖率
+#   FUZZ_RUNS=<N>:固定迭代次数(优先于 max_total_time)
+#   FUZZ_MAX_TOTAL_TIME=<秒>:总时间预算
+#   FUZZ_WORKERS / FUZZ_JOBS:并行 worker/job 数
+#   FUZZ_TIMEOUT / FUZZ_MAX_LEN / FUZZ_VERBOSITY:透传给 libFuzzer
+#   FUZZ_RSS_LIMIT_MB / FUZZ_MALLOC_LIMIT_MB:内存预算(透传给 libFuzzer)
+#   FUZZ_CORPUS_DIR / FUZZ_DICT_PATH:corpus 与字典路径
+#   FUZZ_EXTRA_ARGS:附加 libFuzzer 参数(空格分割)
+#   XMAKE_FORCE_CLEAN=0|1:是否在配置前先清理
+
+fuzzMode="${FUZZ_MODE:-quick}"
+fuzzSkipCov="${FUZZ_SKIP_COV:-0}"
+fuzzTimeout="${FUZZ_TIMEOUT:-4}"
+fuzzMaxLen="${FUZZ_MAX_LEN:-8192}"
+fuzzVerbosity="${FUZZ_VERBOSITY:-0}"
+fuzzCorpusDir="${FUZZ_CORPUS_DIR:-./test/fuzzer/corpus}"
+fuzzDictPath="${FUZZ_DICT_PATH:-./test/fuzzer/RyanJsonFuzzer.dict}"
+fuzzRuns="${FUZZ_RUNS:-}"
+fuzzMaxTotalTime="${FUZZ_MAX_TOTAL_TIME:-}"
+fuzzWorkers="${FUZZ_WORKERS:-}"
+fuzzJobs="${FUZZ_JOBS:-}"
+fuzzRssLimitMb="${FUZZ_RSS_LIMIT_MB:-}"
+fuzzMallocLimitMb="${FUZZ_MALLOC_LIMIT_MB:-}"
+xmakeForceClean="${XMAKE_FORCE_CLEAN:-0}"
+
+# 统一切到仓库根目录,避免从任意 cwd 启动时相对路径失效
+scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+repoRoot="$(cd "${scriptDir}/../.." && pwd)"
+cd "${repoRoot}"
+
+# 按模式设置默认预算(可被环境变量覆盖)
+case "${fuzzMode}" in
+  quick)
+    defaultMaxTotalTime=60
+    defaultWorkers=2
+    defaultJobs=2
+    ;;
+  nightly)
+    defaultMaxTotalTime=300
+    defaultWorkers=4
+    defaultJobs=4
+    ;;
+  full)
+    defaultMaxTotalTime=900
+    defaultWorkers=6
+    defaultJobs=6
+    ;;
+  *)
+    echo "[错误] FUZZ_MODE 仅支持 quick/nightly/full,当前值:${fuzzMode}"
+    exit 1
+    ;;
+esac
+
+# 如果用户没显式指定并行参数,则使用模式默认值
+if [[ -z "${fuzzWorkers}" ]]; then
+  fuzzWorkers="${defaultWorkers}"
+fi
+if [[ -z "${fuzzJobs}" ]]; then
+  fuzzJobs="${defaultJobs}"
+fi
+
+# 如果没设置 FUZZ_RUNS,则走 max_total_time 模式
+if [[ -z "${fuzzRuns}" ]]; then
+  if [[ -z "${fuzzMaxTotalTime}" ]]; then
+    fuzzMaxTotalTime="${defaultMaxTotalTime}"
+  fi
+fi
+
+strictKey="${RYANJSON_STRICT_OBJECT_KEY_CHECK:-false}"
+addAtHead="${RYANJSON_DEFAULT_ADD_AT_HEAD:-true}"
+scientific="${RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC:-true}"
+caseName="strict_${strictKey}__head_${addAtHead}__sci_${scientific}"
+
+# 覆盖率目录固定为 coverage/fuzz,每次执行前清理,保证只保留最新结果
+coverageRoot="coverage/fuzz"
+rm -rf "${coverageRoot}"
+profileRoot="${coverageRoot}/profiles"
+mkdir -p "${profileRoot}"
+
+profraw="${profileRoot}/coverage.profraw"
+profdata="${coverageRoot}/coverage.profdata"
+reportTxt="${coverageRoot}/report.txt"
+reportHtml="${coverageRoot}/html"
+
+echo "===================================================="
+echo "Fuzzer 执行配置"
+echo "  - 模式: ${fuzzMode}"
+echo "  - 配置: ${caseName}"
+echo "  - Corpus: ${fuzzCorpusDir}"
+echo "  - 字典: ${fuzzDictPath}"
+echo "  - timeout: ${fuzzTimeout}"
+echo "  - max_len: ${fuzzMaxLen}"
+echo "  - workers/jobs: ${fuzzWorkers}/${fuzzJobs}"
+if [[ -n "${fuzzRssLimitMb}" ]]; then
+  echo "  - rss_limit_mb: ${fuzzRssLimitMb}"
+fi
+if [[ -n "${fuzzMallocLimitMb}" ]]; then
+  echo "  - malloc_limit_mb: ${fuzzMallocLimitMb}"
+fi
+if [[ -n "${fuzzRuns}" ]]; then
+  echo "  - runs: ${fuzzRuns}"
+else
+  echo "  - max_total_time: ${fuzzMaxTotalTime}s"
+fi
+echo "===================================================="
+
+# 重新配置,确保宏变化进入编译命令
+if [[ "${xmakeForceClean}" == "1" ]]; then
+  echo "[阶段] 正在执行 xmake 配置(clean 模式)..."
+  xmake f -c
+else
+  echo "[阶段] 正在执行 xmake 配置(增量模式)..."
+  xmake f
+fi
+echo "[阶段] 正在执行 xmake 构建(target=RyanJsonFuzz)..."
+xmake -b RyanJsonFuzz
+echo "[信息] xmake 构建完成(target=RyanJsonFuzz)"
+
+# corpus 不存在时自动创建,方便首次运行
+if [[ ! -d "${fuzzCorpusDir}" ]]; then
+  mkdir -p "${fuzzCorpusDir}"
+  echo "[信息] Corpus 目录不存在,已自动创建:${fuzzCorpusDir}"
+fi
+
+# 字典文件可选:存在就启用,不存在就跳过
+declare -a dictArgs=()
+if [[ -f "${fuzzDictPath}" ]]; then
+  dictArgs+=("-dict=${fuzzDictPath}")
+else
+  echo "[警告] 未找到字典文件 ${fuzzDictPath},已跳过 -dict 参数。"
+fi
+
+# 组装 libFuzzer 参数数组,避免字符串拼接导致转义问题
+declare -a fuzzArgs=()
+fuzzArgs+=("${fuzzCorpusDir}")
+fuzzArgs+=("${dictArgs[@]}")
+fuzzArgs+=("-timeout=${fuzzTimeout}")
+fuzzArgs+=("-verbosity=${fuzzVerbosity}")
+fuzzArgs+=("-max_len=${fuzzMaxLen}")
+fuzzArgs+=("-workers=${fuzzWorkers}")
+fuzzArgs+=("-jobs=${fuzzJobs}")
+if [[ -n "${fuzzRssLimitMb}" ]]; then
+  fuzzArgs+=("-rss_limit_mb=${fuzzRssLimitMb}")
+fi
+if [[ -n "${fuzzMallocLimitMb}" ]]; then
+  fuzzArgs+=("-malloc_limit_mb=${fuzzMallocLimitMb}")
+fi
+
+if [[ -n "${fuzzRuns}" ]]; then
+  fuzzArgs+=("-runs=${fuzzRuns}")
+else
+  fuzzArgs+=("-max_total_time=${fuzzMaxTotalTime}")
+fi
+
+# 允许 CI 透传少量临时参数,例如 -rss_limit_mb=4096
+if [[ -n "${FUZZ_EXTRA_ARGS:-}" ]]; then
+  # shellcheck disable=SC2206
+  extraArgs=( ${FUZZ_EXTRA_ARGS} )
+  fuzzArgs+=("${extraArgs[@]}")
+fi
+
+# 运行 fuzz。使用独立 profile 文件,避免多次执行互相覆盖
+echo "[阶段] 正在运行 fuzz 二进制..."
+LLVM_PROFILE_FILE="${profraw}" ./build/linux/x86/release/RyanJsonFuzz "${fuzzArgs[@]}"
+
+# 快检模式可跳过覆盖率阶段以缩短总时长
+if [[ "${fuzzSkipCov}" == "1" ]]; then
+  echo "[信息] FUZZ_SKIP_COV=1,已跳过覆盖率生成。"
+  echo "输出目录:${coverageRoot}"
+  exit 0
+fi
+
+# 覆盖率工具检查:未安装时给出明确错误
+if ! command -v llvm-profdata >/dev/null 2>&1; then
+  echo "[错误] 未找到 llvm-profdata,无法生成覆盖率。"
+  exit 1
+fi
+if ! command -v llvm-cov >/dev/null 2>&1; then
+  echo "[错误] 未找到 llvm-cov,无法生成覆盖率。"
+  exit 1
+fi
+
+# 合并 profile 数据
+llvm-profdata merge -sparse "${profraw}" -o "${profdata}"
+
+# 文本汇总覆盖率:
+# 先原样打印到终端(尽量保留颜色)
+# 再单独写入文件,便于归档
+echo "---------------- 覆盖率摘要(llvm-cov report) ----------------"
+llvm-cov report ./build/linux/x86/release/RyanJsonFuzz \
+  -instr-profile="${profdata}" \
+  -show-mcdc-summary \
+  --use-color \
+  -sources ./RyanJson
+
+llvm-cov report ./build/linux/x86/release/RyanJsonFuzz \
+  -instr-profile="${profdata}" \
+  -show-mcdc-summary \
+  -sources ./RyanJson > "${reportTxt}"
+
+# HTML 详细覆盖率(用于本地/制品追踪)
+llvm-cov show ./build/linux/x86/release/RyanJsonFuzz \
+  -instr-profile="${profdata}" \
+  -format=html \
+  -output-dir="${reportHtml}" \
+  -show-mcdc-summary \
+  -show-branches=count \
+  -show-expansions \
+  -show-regions \
+  -show-line-counts-or-regions \
+  -sources ./RyanJson
+
+echo "[完成] Fuzzer 执行结束,覆盖率已生成。"
+echo "输出目录:${coverageRoot}"
+echo "覆盖率文本报告:${reportTxt}"
+echo "覆盖率HTML目录:${reportHtml}"

+ 183 - 0
scripts/setup/install_qemu_deps.sh

@@ -0,0 +1,183 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Install ARM bare-metal toolchain + QEMU needed by RyanJsonQemu.
+# Supported package managers: apt, dnf, yum, pacman, brew.
+
+SCRIPT_NAME="$(basename "$0")"
+NO_UPDATE="0"
+
+usage() {
+  cat <<USAGE
+Usage: ${SCRIPT_NAME} [--no-update]
+
+Options:
+  --no-update   Skip package index refresh step (apt/dnf/yum/pacman).
+USAGE
+}
+
+while [[ $# -gt 0 ]]; do
+  case "$1" in
+    --no-update)
+      NO_UPDATE="1"
+      shift
+      ;;
+    -h|--help)
+      usage
+      exit 0
+      ;;
+    *)
+      echo "[ERROR] Unknown argument: $1"
+      usage
+      exit 1
+      ;;
+  esac
+done
+
+if [[ $(id -u) -eq 0 ]]; then
+  SUDO=""
+else
+  if command -v sudo >/dev/null 2>&1; then
+    SUDO="sudo"
+  else
+    echo "[ERROR] sudo is required when not running as root."
+    exit 1
+  fi
+fi
+
+log() {
+  echo "[install_qemu_deps] $*"
+}
+
+have_cmd() {
+  command -v "$1" >/dev/null 2>&1
+}
+
+install_with_apt() {
+  local -a pkgs=(
+    gcc-arm-none-eabi
+    binutils-arm-none-eabi
+    qemu-system-arm
+    qemu-utils
+  )
+
+  if [[ "${NO_UPDATE}" != "1" ]]; then
+    log "apt-get update"
+    ${SUDO} apt-get update
+  fi
+
+  log "apt-get install: ${pkgs[*]}"
+  DEBIAN_FRONTEND=noninteractive ${SUDO} apt-get install -y "${pkgs[@]}"
+}
+
+install_with_dnf() {
+  local -a pkgs=(
+    arm-none-eabi-gcc-cs
+    arm-none-eabi-binutils-cs
+    qemu-system-arm
+  )
+
+  if [[ "${NO_UPDATE}" != "1" ]]; then
+    log "dnf makecache"
+    ${SUDO} dnf -y makecache
+  fi
+
+  log "dnf install: ${pkgs[*]}"
+  ${SUDO} dnf install -y "${pkgs[@]}"
+}
+
+install_with_yum() {
+  local -a pkgs=(
+    arm-none-eabi-gcc-cs
+    arm-none-eabi-binutils-cs
+    qemu-system-arm
+  )
+
+  if [[ "${NO_UPDATE}" != "1" ]]; then
+    log "yum makecache"
+    ${SUDO} yum -y makecache
+  fi
+
+  log "yum install: ${pkgs[*]}"
+  ${SUDO} yum install -y "${pkgs[@]}"
+}
+
+install_with_pacman() {
+  local -a pkgs=(
+    arm-none-eabi-gcc
+    arm-none-eabi-binutils
+    qemu-system-arm
+  )
+
+  if [[ "${NO_UPDATE}" != "1" ]]; then
+    log "pacman -Sy"
+    ${SUDO} pacman -Sy --noconfirm
+  fi
+
+  log "pacman install: ${pkgs[*]}"
+  ${SUDO} pacman -S --noconfirm --needed "${pkgs[@]}"
+}
+
+install_with_brew() {
+  local -a pkgs=(
+    arm-none-eabi-gcc
+    qemu
+  )
+
+  log "brew install: ${pkgs[*]}"
+  brew install "${pkgs[@]}"
+}
+
+install_deps() {
+  if have_cmd apt-get; then
+    install_with_apt
+    return
+  fi
+  if have_cmd dnf; then
+    install_with_dnf
+    return
+  fi
+  if have_cmd yum; then
+    install_with_yum
+    return
+  fi
+  if have_cmd pacman; then
+    install_with_pacman
+    return
+  fi
+  if have_cmd brew; then
+    install_with_brew
+    return
+  fi
+
+  echo "[ERROR] Unsupported package manager. Please install manually:"
+  echo "  - arm-none-eabi-gcc"
+  echo "  - arm-none-eabi-objcopy (binutils)"
+  echo "  - qemu-system-arm"
+  exit 1
+}
+
+verify() {
+  local missing=0
+  for cmd in arm-none-eabi-gcc arm-none-eabi-objcopy qemu-system-arm; do
+    if have_cmd "${cmd}"; then
+      log "found ${cmd}: $(command -v "${cmd}")"
+    else
+      echo "[ERROR] missing command after install: ${cmd}"
+      missing=1
+    fi
+  done
+
+  if [[ "${missing}" -ne 0 ]]; then
+    exit 1
+  fi
+
+  arm-none-eabi-gcc --version | head -n 1
+  arm-none-eabi-objcopy --version | head -n 1
+  qemu-system-arm --version | head -n 1
+
+  log "Dependencies are ready."
+}
+
+install_deps
+verify

+ 55 - 0
skills/ryanjson-api-usage/SKILL.md

@@ -0,0 +1,55 @@
+---
+name: ryanjson-api-usage
+description: 面向 RyanJson 公开 API 的 RT-Thread 落地技能。用于 Parse/Create/Add/Change/Replace/Detach/Delete、所有权与释放语义、以及 RyanJsonInitHooks 初始化集成。用户请求“RyanJson 怎么用”“RT-Thread 怎么集成”“失败时谁释放”时使用本技能。
+---
+
+# RyanJson API 使用技能
+
+## 技能定位
+- 面向公开 API 的“怎么用”问题,输出可直接落地的调用方案与代码片段。
+- 默认面向 RT-Thread 集成场景,强调初始化、失败回退和释放责任。
+
+## 适用与切换
+- 适用:接口选型、调用顺序、所有权判断、集成模板。
+- 切换到 `ryanjson-test-engineering`:当目标是补测试、补覆盖、复现崩溃。
+- 切换到 `ryanjson-optimization`:当目标是内部实现、性能优化、回归门禁。
+
+## 必读入口
+- 共享基线:`../shared/ryanJsonCommon.md`
+- 注释规范:统一使用 Doxygen 风格,且类型名/字段语义名/API 名保持英文(见共享基线第 9 节)
+- API 快速入口:`references/quickstart.md`
+- 场景模板:`references/apiPatterns.md`
+- 所有权细则:`references/ownershipAndErrors.md`
+
+## 执行入口
+- 代码规范:先执行 `bash ./run_local_format.sh --check --changed`,提交前执行 `bash ./run_local_format.sh`。
+- 行为回归:默认先跑 `bash ./run_local_base.sh`;涉及硬件语义(对齐/异常)再跑 `bash ./run_local_qemu.sh`;需要联动 fuzz 时执行 `bash ./run_local_ci.sh` 或 `bash ./run_local_fuzz.sh`。
+
+## 执行流程
+1. 先按 `../shared/ryanJsonCommon.md` 确认宏与语义前提。
+2. 按用户场景选最小 API 组合(读取/构建/更新/替换/迁移)。
+3. 给出“成功路径 + 失败路径”的最小代码,不夹带内部实现细节。
+4. 明确分支级所有权:创建方、接管点、失败后释放责任。
+5. 给出可验证项:返回值、日志、内存统计或泄漏检查点。
+
+## API 专项约束
+- `Get*` 先判空,再 `RyanJsonIsXXX` 判型。
+- 同类型更新优先 `Change*Value`,跨类型更新使用 `ReplaceByKey/ReplaceByIndex`。
+- `Add/Insert` 仅接收游离节点,禁止重复挂载。
+- 传输压缩优先非格式化打印:`RyanJsonPrint(..., RyanJsonFalse, ...)` 或 `RyanJsonPrintPreallocated(..., RyanJsonFalse, ...)`。
+- `RyanJsonMinify` 是文本清洗工具,不作为传输输出主路径。
+
+## 输出格式
+1. 前提与结论:宏/环境前提 + API 选型。
+2. 最小代码:只用公开 API,包含失败分支。
+3. 所有权清单:逐分支说明谁负责释放。
+4. 验证建议:最小验证步骤与后续扩展方向。
+
+## 参考导航
+- 完整 API:`references/apiReference.md`
+- hooks 初始化:`references/hooksInitPolicy.md`
+- RT-Thread 示例:`references/rtThreadExamples.md`
+- 集成模板:`references/integrationTemplate.md`
+- 排障与故障注入:`references/pitfallsAndDebug.md`、`references/faultInjectionPlaybook.md`
+- 术语:`references/terminology.md`
+- 本地压缩文档:`context.md`、`apiPatterns.md`、`ownership.md`、`sop.md`

+ 40 - 0
skills/ryanjson-api-usage/agents/gemini.md

@@ -0,0 +1,40 @@
+# Gemini Skill Card
+
+名称:`ryanjson-api-usage`
+
+## 定位
+- 面向嵌入式 RT-Thread 场景的 RyanJson 公开 API 落地技能。
+
+## 适用场景
+- 询问 RyanJson 公开 API 的正确调用方式。
+- 需要 Parse/Create/Add/Change/Replace/Detach/Delete 的可运行示例。
+- 需要明确失败路径、所有权和释放顺序。
+
+## 输入建议
+- 目标操作:要实现的业务 Json 流程。
+- 平台约束:RT-Thread 线程模型、内存预算、是否固定缓冲。
+- 验收标准:返回值、日志、内存统计、协议输出。
+
+## 硬约束
+- 任意 RyanJson API 前,必须先成功调用 `RyanJsonInitHooks`。
+- 默认只讲公开 API,不展开内部实现细节。
+- `Get*` 使用前必须判空并 `RyanJsonIsXXX` 判型。
+- 语义不明确时按 `example/ -> test/unityTest/ -> test/fuzzer/` 取证。
+
+## 术语口径
+- 统一按 `../references/terminology.md`。
+- 输出必须显式区分:已验证/推断、可恢复错误/不可恢复错误、失败语义。
+
+## 默认提示词
+使用 `$ryanjson-api-usage`,输出 RT-Thread 可落地的 RyanJson 公开 API 方案:强制 hooks 前置、明确所有权/释放路径、仅使用公开 API,并在语义不明确时按 `example -> unityTest -> fuzzer` 取证。
+
+## 输出骨架
+1. 结论与接口选型(标注已验证/推断)。
+2. 最小可运行代码(RT-Thread 风格)。
+3. 失败路径与所有权说明(区分 Add/Insert 与 Replace)。
+4. 上板验证步骤与下一步建议。
+
+## 依据(仓库内)
+- `../references/apiReference.md`
+- `../references/ownershipAndErrors.md`
+- `../references/apiPatterns.md`

+ 4 - 0
skills/ryanjson-api-usage/agents/openai.yaml

@@ -0,0 +1,4 @@
+interface:
+  display_name: "RyanJson API 使用"
+  short_description: "面向 RT-Thread 的公开 API 落地与所有权语义说明"
+  default_prompt: "使用 $ryanjson-api-usage 输出 RyanJson 公开 API 的可运行方案:强制 RyanJsonInitHooks 前置,严格区分成功/失败路径与所有权释放责任;语义不清时按 example -> unityTest -> fuzzer 取证,并标注已验证与推断。"

+ 40 - 0
skills/ryanjson-api-usage/apiPatterns.md

@@ -0,0 +1,40 @@
+# RyanJson API 场景模式(压缩版)
+
+## 1. 作用
+- 按用户意图快速选公开 API 路径。
+- 回答时强制带上失败分支与所有权说明。
+- 完整版见 `references/apiPatterns.md`。
+
+## 2. 先做三件事
+1. 先确认 `RyanJsonInitHooks`。
+2. 先确认宏前提:`RyanJsonStrictObjectKeyCheck`、`RyanJsonDefaultAddAtHead`。
+3. 先判定场景再给最小 API 组合。
+
+## 3. 快速选型
+| 场景 | 推荐路径 | 关键风险 |
+|---|---|---|
+| 读取配置 | Parse + Get + IsXXX | 未判型直接取值 |
+| 周期上报 | Create + Add + PrintPreallocated(..., RyanJsonFalse, ...) | 缓冲不足、清理遗漏 |
+| 同类型更新 | Get + Change*Value | 跨类型误用 Change |
+| 跨类型更新 | Create* + ReplaceBy* | Replace 失败后泄漏 |
+| 子树迁移 | Detach* + Add/Insert | detach 后未接管 |
+| 传输压缩 | Print(format=false) | 误把 Minify 当传输主路径 |
+| 文本清洗 | Minify | `\0` 终止符假设错误 |
+
+## 4. 关键语义提醒
+- `Replace` 失败不消费 `newItem`,调用方需复用或释放。
+- `Add/Insert` 与 `Replace` 失败语义不能混用。
+- 传输压缩优先非格式化打印,不推荐“先格式化再 Minify”。
+- `RyanJsonDefaultAddAtHead=true` 时,追加顺序可能反转。
+
+## 5. 输出模板
+1. 前提:宏前提 + hooks 前提。
+2. 路径:推荐 API 顺序(成功 + 失败)。
+3. 所有权:每个失败分支谁释放。
+4. 验证:返回值/日志/内存检查点。
+
+## 6. 最小依据
+- `RyanJson/RyanJsonItem.c`:Add/Insert/Replace/Detach 失败语义
+- `RyanJson/RyanJson.c`:Minify 行为
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`
+- `test/unityTest/cases/utils/testPrint.c`、`test/unityTest/cases/utils/testUtils.c`

+ 37 - 0
skills/ryanjson-api-usage/context.md

@@ -0,0 +1,37 @@
+# RyanJson API 语境(压缩版)
+
+## 1. 作用
+- 提供 API 解答的最小安全语境,防止跨宏/跨模式误答。
+- 执行与模式共性口径见 `../shared/ryanJsonCommon.md`。
+- 术语口径见 `../shared/terminology.md`。
+
+## 2. 回答前必须检查
+- 当前源码中的:
+  - `RyanJsonStrictObjectKeyCheck`
+  - `RyanJsonDefaultAddAtHead`
+- 当前仓库默认值(仅参考):
+  - `RyanJsonStrictObjectKeyCheck=false`
+  - `RyanJsonDefaultAddAtHead=true`
+
+## 3. hooks 基线
+- `RyanJsonInitHooks` 必须在任意 RyanJson API 之前执行。
+- hooks 初始化失败时必须立即中止 Json 路径。
+
+## 4. API 安全基线
+- 读取路径:先判空,再判型,再取值。
+- 更新路径:同类型用 `Change*Value`,跨类型用 `ReplaceBy*`。
+- 结构路径:`Detach` 后必须重新挂载或显式释放。
+
+## 5. 错误级别口径
+- 可恢复错误:输入非法、key 不存在、类型不符等,返回 false/NULL。
+- 不可恢复错误:内存破坏、双重释放、结构不变量损坏;在启用 `RyanJsonEnableAssert` 时可触发 assert。
+
+## 6. 输出检查单
+1. 标明宏前提。
+2. 区分已验证/推断。
+3. 给出失败分支与释放动作。
+
+## 7. 依据(仓库内)
+- `RyanJson/RyanJsonConfig.h`:`RyanJsonStrictObjectKeyCheck`、`RyanJsonDefaultAddAtHead` 默认值
+- `RyanJson/RyanJson.c`:hooks 全局指针与 `RyanJsonInitHooks`
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`:失败所有权语义

+ 32 - 0
skills/ryanjson-api-usage/ownership.md

@@ -0,0 +1,32 @@
+# RyanJson 所有权矩阵(压缩版)
+
+## 1. 作用
+- 统一调用方与库之间的所有权口径,减少泄漏与误删。
+- 术语口径见 `../shared/terminology.md`。
+
+## 2. 核心规则
+### 2.1 Create 系列
+- `RyanJsonCreate*` 成功返回后,节点归调用方。
+- 调用方必须挂载或删除该节点。
+
+### 2.2 Detach 系列
+- `RyanJsonDetach*` 成功返回后,节点归调用方。
+- 调用方必须重新挂载或删除该节点。
+
+### 2.3 Replace 系列
+- 当前实现中,`ReplaceByKey/ByIndex` 失败不消费 `newItem`。
+- 调用方必须复用或 `RyanJsonDelete(newItem)`。
+
+### 2.4 Print 系列
+- `RyanJsonPrint` 返回动态字符串,调用方必须 `RyanJsonFree`。
+- `RyanJsonPrintPreallocated` 使用调用方缓冲,不额外释放输出缓冲。
+
+## 3. 关键提醒
+- `Add/Insert` 与 `Replace` 的失败语义不能混用。
+- 边界行为不确定时,必须回查当前头文件与测试。
+- 语义依据:`RyanJson/RyanJsonItem.c`、`test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`。
+
+## 4. 失败分支检查单
+1. 每个创建对象是否在所有分支都有明确归属。
+2. API 失败时是否说明了所有权是否转移。
+3. 每块动态内存是否“且仅”释放一次。

+ 114 - 0
skills/ryanjson-api-usage/references/apiPatterns.md

@@ -0,0 +1,114 @@
+# API 场景模式(回答优先)
+
+## 1. 用途
+- 本文用于“按用户意图快速选 API 路径”,减少回答时漏掉失败语义和释放责任。
+- 仅覆盖公开 API;内部实现问题转 `ryanjson-optimization`,测试回归问题转 `ryanjson-test-engineering`。
+
+## 2. 回答前固定动作
+1. 先确认 `RyanJsonInitHooks` 在任何 Json API 前执行。
+2. 先确认宏前提:`RyanJsonStrictObjectKeyCheck`、`RyanJsonDefaultAddAtHead`。
+3. 先判定用户意图属于哪类场景,再给最小 API 组合。
+
+## 3. 意图到路径
+| 用户意图 | 推荐路径 | 是否改结构 | 关键风险 |
+|---|---|---|---|
+| 读取配置字段 | Parse + Get + IsXXX | 否 | 未判型直接取值 |
+| 周期上报/打包发送 | Create + Add + PrintPreallocated | 是 | 缓冲不足或清理遗漏 |
+| 在线改值(同类型) | Get + Change*Value | 否 | 把跨类型更新误写成 Change |
+| 字段类型切换 | Create* + ReplaceBy* | 是 | Replace 失败后 `newItem` 泄漏 |
+| 子树迁移/重排 | Detach* + Add/Insert | 是 | detach 后未接管 |
+| 重复 key 策略切换 | 宏配置 + 业务校验同步 | 视需求 | 宏与测试预期不一致 |
+| 传输压缩输出 | Print(format=false) | 否 | 误把 Minify 当传输主路径 |
+| 历史文本清洗 | Minify | 否 | `\0` 终止符假设错误 |
+
+## 4. 场景卡
+
+### A. 读取配置(Parse + Get)
+- 触发:启动配置读取、外部报文字段抽取。
+- 调用顺序:
+  1. `RyanJsonInitHooks`
+  2. `RyanJsonParse`
+  3. `RyanJsonGetObjectByKey`
+  4. `RyanJsonIsXXX` 后再 `RyanJsonGetXXXValue`
+  5. `RyanJsonDelete(root)`
+- 失败口径:
+  - `Parse == NULL`:输入非法或资源不足,可恢复错误。
+  - key 缺失/类型不符:可恢复错误,不应走崩溃路径。
+
+### B. 周期上报(Create + Add + PrintPreallocated)
+- 触发:周期遥测、固定缓冲发送。
+- 调用顺序:
+  1. `RyanJsonCreateObject`
+  2. `RyanJsonAdd*ToObject`
+  3. `RyanJsonPrintPreallocated(..., RyanJsonFalse, ...)`
+  4. `RyanJsonDelete(root)`
+- 失败口径:
+  - 任意 Add 失败:立即清理 root 后返回。
+  - PrintPreallocated 失败:走降级路径并清理 root。
+
+### C. 同类型热更新(Get + Change)
+- 触发:仅变更 value,不变更字段类型。
+- 规则:
+  - 同类型更新使用 `Change*Value`。
+  - 跨类型更新必须切换到 `ReplaceByKey/ReplaceByIndex`。
+
+### D. 跨类型替换(Replace)
+- 触发:例如 `int -> object`、`string -> array`。
+- 调用顺序:
+  1. 构造 `newItem`
+  2. 调用 `RyanJsonReplaceByKey/ByIndex`
+  3. 失败时调用方复用或 `RyanJsonDelete(newItem)`
+- 关键提醒:
+  - `Replace` 失败不消费 `newItem`,不能套用 `Add/Insert` 失败语义。
+
+### E. 子树迁移(Detach + Add/Insert)
+- 触发:把既有子树迁移到新父节点。
+- 调用顺序:
+  1. `detached = RyanJsonDetachByKey/ByIndex`
+  2. 判空并确认可重挂载
+  3. `RyanJsonAddItem*` / `RyanJsonInsert`
+  4. 失败分支立即处理 `detached` 所有权
+- 关键提醒:
+  - detach 后必须“重挂”或“释放”,不能悬空。
+
+### F. 重复 key 策略(StrictKey 宏)
+- `RyanJsonStrictObjectKeyCheck=true`:重复 key 更严格。
+- `RyanJsonStrictObjectKeyCheck=false`:兼容性更高,但 key 查询可预测性下降。
+- 回答时必须显式写明宏前提,并提示测试预期需同步。
+
+### G. 传输压缩输出(非格式化 Print)
+- 推荐路径:
+  1. 动态输出:`RyanJsonPrint(..., RyanJsonFalse, ...)`,发送后 `RyanJsonFree(str)`。
+  2. 固定缓冲:`RyanJsonPrintPreallocated(..., RyanJsonFalse, ...)`。
+- 关键提醒:
+  - 传输场景优先 `format=false`,不要默认走 `Minify`。
+
+### H. 文本清洗(Minify)
+- 适用:已有 Json 文本去空白/注释后再解析或对比。
+- 调用顺序:
+  1. 准备可写缓冲
+  2. `ret = RyanJsonMinify(buf, textLen)`
+  3. `ret < textLen` 才可直接按 C 字符串使用
+- 关键提醒:
+  - `Minify` 是文本清洗工具,不是传输输出主路径。
+
+## 5. 输出模板(回答时)
+1. 前提:宏前提、hooks 前提、输入类型。
+2. 路径:推荐 API 顺序(成功路径 + 失败路径)。
+3. 所有权:每个失败分支由谁释放。
+4. 验证:最小可执行检查点(返回值/日志/内存)。
+
+## 6. 高频误答拦截
+1. 未判型直接 `GetXXXValue`。
+2. 把 `Replace` 失败当成库自动清理。
+3. 传输路径建议先格式化再 Minify。
+4. 忽略 `RyanJsonDefaultAddAtHead` 导致索引/遍历顺序误判。
+5. 示例只写成功路径,不写失败与释放路径。
+
+## 7. 依据(仓库内)
+- `RyanJson/RyanJsonItem.c`:Add/Insert/Replace/Detach 失败与所有权路径
+- `RyanJson/RyanJson.c`:`RyanJsonMinify` 行为
+- `test/unityTest/cases/core/testCreate.c`:Add/Insert/Detach 相关断言
+- `test/unityTest/cases/core/testReplace.c`:Replace 失败不消费 `item`
+- `test/unityTest/cases/utils/testPrint.c`:非格式化打印与 preallocated 边界
+- `test/unityTest/cases/utils/testUtils.c`、`test/unityTest/cases/utils/testRobust.c`、`test/fuzzer/cases/fuzzerMinify.c`:Minify 边界与稳健性

+ 140 - 0
skills/ryanjson-api-usage/references/apiReference.md

@@ -0,0 +1,140 @@
+# RyanJson API 说明(公开接口,使用导向)
+
+## 范围
+- 本页是公开 API 的语义速查,不展开内部实现优化细节。
+- 场景化路径优先看 `apiPatterns.md`,所有权细节看 `ownershipAndErrors.md`。
+- hooks 与平台接入看 `hooksInitPolicy.md`、`rtThreadExamples.md`。
+
+## 0. 初始化与配置
+### `RyanJsonInitHooks(malloc, free, realloc)`
+- 必须最先调用。
+- 成功返回 `RyanJsonTrue`,失败返回 `RyanJsonFalse`。
+- 失败后禁止继续调用 Parse/Create/Add/Replace/Print 等 API。
+- 依据:当前实现的 `jsonMalloc/jsonFree/jsonRealloc` 全局指针默认是 `NULL`(`RyanJson/RyanJson.c`)。
+
+### `RyanJsonStrictObjectKeyCheck`(`RyanJsonConfig.h`)
+- `true`:Object 下拒绝重复 key(Parse/Insert/ReplaceByIndex 等路径更严格)。
+- `false`:允许重复 key;按 key 查询/替换/删除通常命中第一个,语义由上层约束。
+
+### `RyanJsonFree(void *block)`
+- 用于释放 `RyanJsonPrint` 返回的动态字符串。
+
+## 1. Parse 类
+### `RyanJsonParse(const char *text)`
+- 输入 `\0` 结尾字符串。
+- 成功返回根节点,失败返回 `NULL`。
+- 成功返回值由调用方 `RyanJsonDelete`。
+- 默认等价于 `RyanJsonParseOptions(text, strlen(text), RyanJsonFalse, NULL)`。
+- **默认是非严格尾部模式**(允许尾部存在未消费数据),以当前实现为准。
+
+### `RyanJsonParseOptions(text, size, requireNullTerminator, parseEndPtr)`
+- 适合非 `\0` 缓冲区或精确控制解析终点。
+- `requireNullTerminator = RyanJsonTrue` 时,解析后仅允许尾部空白。
+
+## 2. Create 类
+### 标量创建
+- `RyanJsonCreateNull(key)`
+- `RyanJsonCreateBool(key, boolean)`
+- `RyanJsonCreateInt(key, number)`
+- `RyanJsonCreateDouble(key, number)`
+- `RyanJsonCreateString(key, string)`
+
+### 容器创建
+- `RyanJsonCreateObject()`
+- `RyanJsonCreateArray()`
+- `RyanJsonCreateIntArray(numbers, count)`
+- `RyanJsonCreateDoubleArray(numbers, count)`
+- `RyanJsonCreateStringArray(strings, count)`
+
+语义:
+- Create 成功后节点归调用者。
+- 节点未挂到父树前,异常路径必须由调用者释放。
+
+## 3. Add / Insert 类
+### 常用语法糖
+- Object:`RyanJsonAddIntToObject` / `RyanJsonAddStringToObject` ...
+- Array:`RyanJsonAddIntToArray` / `RyanJsonAddStringToArray` ...
+
+### `RyanJsonAddItemToObject(pJson, key, item)` / `RyanJsonAddItemToArray`
+- `AddItem` 仅接受 `Array/Object` 节点。
+- 标量请使用 `AddInt/AddString/...`。
+
+### `RyanJsonInsert(pJson, index, item)`
+- `index=0` 头插。
+- `index=UINT32_MAX` 或越界可视为尾插。
+- Object 场景要求 `item` 带 key。
+
+所有权:
+- 成功:`item` 转移到父节点。
+- 失败:
+  - `item` 为游离节点时,`Add/Insert` 失败路径由库侧清理。
+  - `item` 非游离节点时,直接返回 false,不接管释放(保护原树)。
+  - `AddItemToObject` 传入标量时会直接失败并删除该标量节点(当前实现语义)。
+
+## 4. Change 类(仅同类型改值)
+- `RyanJsonChangeKey(pJson, key)`
+- `RyanJsonChangeStringValue(pJson, strValue)`
+- `RyanJsonChangeIntValue(pJson, number)`
+- `RyanJsonChangeDoubleValue(pJson, number)`
+- `RyanJsonChangeBoolValue(pJson, boolean)`
+
+规则:
+- Change 会做基础入参/类型校验,失败返回 `RyanJsonFalse`。
+- Change 不做类型切换;类型切换请用 Replace。
+
+## 5. Replace 类(类型切换主入口)
+### `RyanJsonReplaceByIndex(pJson, index, item)`
+- 数组/对象都可用(对象场景更推荐 `ReplaceByKey`)。
+
+### `RyanJsonReplaceByKey(pJson, key, item)`
+- 仅 Object。
+- `item` 无 key 时会按目标 key 包装。
+- `item` 有 key 且不同于目标 key 时会尝试改 key。
+
+规则:
+- 调用前要求 `item` 为游离节点(`RyanJsonIsDetachedItem`)。
+- 成功:旧节点被删除,新节点接管到树中。
+- 失败:默认不消费 `item`,调用方负责复用或释放。
+
+## 6. Get / Has / 路径类
+- `RyanJsonGetObjectByKey` / `RyanJsonGetObjectByIndex`
+- `RyanJsonHasObjectByKey` / `RyanJsonHasObjectByIndex`
+- `RyanJsonGetObjectToKey` / `RyanJsonGetObjectToIndex`
+
+关键约束:
+- `GetKey/GetString/GetInt/GetDouble/GetBool/GetObjectValue` 这类取值前,必须先判空并用 `RyanJsonIsXXX` 判型。
+
+## 7. Detach / Delete 类
+### `RyanJsonDetachByKey/DetachByIndex`
+- 从树中摘除节点并返回。
+- 返回节点归调用者,必须手动 `RyanJsonDelete`(或再次挂树)。
+
+### `RyanJsonDeleteByKey/DeleteByIndex`
+- 直接删除目标节点,不返回。
+
+### `RyanJsonDelete(root)`
+- 删除整棵树。
+
+## 8. Print / Minify / Compare
+- `RyanJsonPrint`:动态输出,返回值用 `RyanJsonFree`。
+- `RyanJsonPrintPreallocated`:预分配输出,适合 RT-Thread 固定缓冲。
+- 传输场景优先:`Print(..., RyanJsonFalse, ...)` / `PrintPreallocated(..., RyanJsonFalse, ...)` 直接输出紧凑 Json。
+- `RyanJsonMinify`:原地文本清洗(去空白/注释),用于已有 Json 文本处理,不作为首选传输输出路径。
+- `RyanJsonMinify` 终止符规则:
+  - 返回值 `< textLen`:会写入 `\0`;
+  - 返回值 `== textLen`:不会额外写入 `\0`,调用方需自行保证字符串终止空间。
+- `RyanJsonCompare` / `RyanJsonCompareOnlyKey` / `RyanJsonCompareDouble`。
+
+## 依据(仓库内)
+- `RyanJson/RyanJson.c`(`RyanJsonInitHooks` 全局 hooks 初始化)
+- `RyanJson/RyanJsonParse.c`(`RyanJsonParse -> RyanJsonParseOptions(..., RyanJsonFalse, NULL)`)
+- `RyanJson/RyanJsonItem.c`(`RyanJsonInsert` 失败清理、`RyanJsonAddItemToObject` 标量失败删除、`RyanJsonReplaceByKey/ByIndex` 失败不消费)
+- `test/unityTest/cases/core/testCreate.c`(Insert/Add 失败语义与已挂树拒绝)
+- `test/unityTest/cases/core/testReplace.c`(Replace 失败所有权)
+- `test/unityTest/cases/utils/testPrint.c`(`format=false` 紧凑输出与 preallocated 行为)
+- `RyanJson/RyanJson.c` 中 `RyanJsonMinify` 实现
+- `test/unityTest/cases/utils/testUtils.c`、`test/unityTest/cases/utils/testRobust.c`、`test/fuzzer/cases/fuzzerMinify.c`
+
+## 9. 对外/对内边界
+- `RyanJsonChangeObjectValue` 属于内部实现接口,不作为公开 API 使用。
+- 对外业务修改对象/数组内容,请通过 Add/Insert/Detach/Replace 组合完成。

+ 72 - 0
skills/ryanjson-api-usage/references/faultInjectionPlaybook.md

@@ -0,0 +1,72 @@
+# 故障注入与稳定性验证(主机侧可选)
+
+## 范围
+- 用于主机侧或实验环境验证失败路径。
+- RT-Thread 板端资源紧张时可不执行本流程。
+- 所有权口径见 `ownershipAndErrors.md`。
+
+## 目标
+- 验证分配失败场景下的可恢复性。
+- 覆盖 Parse/Create/Add/Insert/Replace/Print 的失败分支。
+
+## 注入策略
+- 固定第 N 次分配失败(稳定复现)。
+- 按概率失败(压力回归)。
+- 按调用点标签失败(精确定位)。
+
+## 包装器示例
+```c
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include "RyanJson.h"
+
+typedef struct
+{
+    uint32_t allocCount;
+    uint32_t failAt;
+} jsonFaultState_t;
+
+static jsonFaultState_t gFaultState = {0U, 0U};
+
+static void *faultMalloc(size_t size)
+{
+    gFaultState.allocCount++;
+    if ((0U != gFaultState.failAt) && (gFaultState.allocCount == gFaultState.failAt))
+    {
+        return NULL;
+    }
+    return malloc(size);
+}
+
+static void faultFree(void *ptr)
+{
+    free(ptr);
+}
+
+static void *faultRealloc(void *ptr, size_t size)
+{
+    gFaultState.allocCount++;
+    if ((0U != gFaultState.failAt) && (gFaultState.allocCount == gFaultState.failAt))
+    {
+        return NULL;
+    }
+    return realloc(ptr, size);
+}
+```
+
+## 建议检查点
+- Parse 失败:返回 `NULL`,无泄漏。
+- Add/Insert 失败:返回 false;游离 item 由库侧清理,非游离 item 失败不消费。
+- Replace 失败:返回 false,item 仍由调用方持有(可继续复用或释放)。
+- Print 失败:返回 `NULL`,原 Json 树仍可正常释放。
+
+## 结果判定
+- 无崩溃。
+- 无泄漏。
+- 返回值与所有权语义与头文件一致。
+
+## 依据(仓库内)
+- `RyanJson/RyanJsonItem.c`:`RyanJsonInsert`、`RyanJsonReplaceByKey/ByIndex`
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`
+- `test/fuzzer/cases/fuzzerCreate.c`、`test/fuzzer/cases/fuzzerReplace.c`

+ 31 - 0
skills/ryanjson-api-usage/references/geminiCompat.md

@@ -0,0 +1,31 @@
+# Gemini 兼容说明(API 使用类)
+
+## 范围
+- 本页只定义 Gemini 在 API 使用类任务的输入/输出结构。
+- 公开 API 语义以 `apiReference.md`、`apiPatterns.md` 为准。
+
+## 推荐输入结构
+- 目标:要实现的 Json 业务操作。
+- 平台:RT-Thread(线程模型、内存约束)。
+- 约束:是否允许额外内存、是否启用严格 key。
+- 验收:返回值、日志输出、内存统计。
+
+## 推荐输出结构
+1. API 选型说明(仅公开 API)。
+2. RT-Thread 可运行示例代码(含 hooks 初始化)。
+3. 失败路径与所有权说明。
+4. 上板验证步骤(日志/返回值/内存统计)。
+
+## 对齐原则
+- 必须把 `RyanJsonInitHooks` 作为前置条件写清楚。
+- 不默认要求用户在 RT-Thread 板端运行 unity/fuzzer。
+- API 语义不确定时,AI 可按 `example -> test/unityTest -> test/fuzzer` 取证。
+- API 技能默认不展开 RyanJson 内部实现细节。
+
+## 术语字典(统一)
+- 本文术语统一以 `terminology.md` 为准。
+
+## 依据(仓库内)
+- `apiReference.md`:公开 API 语义基线
+- `ownershipAndErrors.md`:失败所有权口径
+- `apiPatterns.md`:场景化调用顺序

+ 34 - 0
skills/ryanjson-api-usage/references/hooksInitPolicy.md

@@ -0,0 +1,34 @@
+# RyanJsonInitHooks 初始化规范(RT-Thread 导向)
+
+## 范围
+- 本页只定义 hooks 初始化策略与上板检查点。
+- 通用 API 调用顺序见 `quickstart.md`。
+
+## 规则
+- `RyanJsonInitHooks` 是 RyanJson 前置条件,必须在任何 API 前调用。
+- 建议在系统启动阶段只初始化一次。
+- 初始化失败必须可观测(日志/错误码),并阻断后续 Json 逻辑。
+
+## RT-Thread 推荐位置
+- 应用初始化阶段(如 `INIT_APP_EXPORT`)。
+- 或 Json owner 线程启动阶段(需保证先于业务调用)。
+
+## 实现建议
+- 映射 `rt_malloc/rt_free/rt_realloc` 或自定义内存池封装。
+- 保证 `realloc` 失败返回 `NULL` 且不破坏原指针语义。
+- 建议记录分配失败次数和峰值占用,便于板端排障。
+
+## 线程模型建议
+- 库本身不承诺线程安全。
+- 推荐单 owner 线程独占 Json 树。
+- 多线程共享时,用外层锁保护完整事务。
+
+## 上板检查清单
+- hooks 是否在首次 Parse/Create 前成功执行。
+- hooks 失败时是否正确阻断业务路径。
+- 常用 API 成功/失败路径是否都能返回并正确释放。
+
+## 依据(仓库内)
+- `RyanJson/RyanJson.c`:`jsonMalloc/jsonFree/jsonRealloc` 为全局 hooks,默认 `NULL`
+- `test/unityTest/common/testCommon.c`:测试入口先 `RyanJsonInitHooks(...)`
+- `test/fuzzer/entry.c`:`LLVMFuzzerTestOneInput` 内先校验并初始化 hooks

+ 66 - 0
skills/ryanjson-api-usage/references/integrationTemplate.md

@@ -0,0 +1,66 @@
+# 集成模板(业务流程版)
+
+## 范围
+- 这是 API 技能的 **基线模板**:初始化 -> 解析/修改 -> 输出 -> 释放。
+- `quickstart.md` 与 `rtThreadExamples.md` 都可基于本模板裁剪。
+- 共性口径见 `../../shared/ryanJsonCommon.md`。
+
+## 基线伪代码
+```c
+#include <stdlib.h>
+#include "RyanJson.h"
+
+static RyanJsonBool_e appEnsureJsonHooks(void)
+{
+    static RyanJsonBool_e inited = RyanJsonFalse;
+    if (RyanJsonTrue == inited) { return RyanJsonTrue; }
+
+    inited = RyanJsonInitHooks(malloc, free, realloc);
+    return inited;
+}
+
+RyanJsonBool_e appProcessJson(const char *input)
+{
+    RyanJson_t root = NULL;
+    char *output = NULL;
+    RyanJsonBool_e ok = RyanJsonFalse;
+
+    if (RyanJsonFalse == appEnsureJsonHooks()) { goto done; }
+
+    root = RyanJsonParse(input);
+    if (NULL == root) { goto done; }
+
+    // modify/read json here
+
+    output = RyanJsonPrint(root, 0, RyanJsonFalse, NULL);
+    if (NULL == output) { goto done; }
+
+    ok = RyanJsonTrue;
+
+done:
+    if (NULL != output) { RyanJsonFree(output); }
+    if (NULL != root) { RyanJsonDelete(root); }
+    return ok;
+}
+```
+
+## 裁剪建议
+- RTOS:把 `malloc/free/realloc` 替换为内存池包装器。
+- 高可靠:补错误码与日志等级;把 `goto done` 统一接入故障统计。
+- 高吞吐:优先 `RyanJsonPrintPreallocated`,减少动态分配。
+
+## 线程模型建议
+- 推荐单 owner 任务独占 Json 树。
+- 若必须多任务访问,外层用互斥锁包住完整事务(Parse->Modify->Print->Delete)。
+
+## 最小检查单
+1. hooks 初始化失败时是否中止事务。
+2. Parse/Print 失败分支是否都回收资源。
+3. 动态输出是否统一配对 `RyanJsonFree`。
+4. 事务是否有单一出口清理点。
+
+## 依据(仓库内)
+- `RyanJson/RyanJson.c`:hooks 全局指针与 `RyanJsonInitHooks`
+- `RyanJson/RyanJsonPrint.c`:`RyanJsonPrint`/`RyanJsonPrintPreallocated`
+- `test/unityTest/cases/utils/testPrint.c`:打印与预分配边界行为
+- `test/unityTest/cases/core/testReplace.c`:Replace 失败由调用方处理新节点

+ 64 - 0
skills/ryanjson-api-usage/references/ownershipAndErrors.md

@@ -0,0 +1,64 @@
+# 所有权与错误处理
+
+## 范围
+- 本页只定义公开 API 的所有权与失败语义。
+- 术语口径见 `../../shared/terminology.md`。
+
+## 所有权基线
+- `RyanJsonCreate*`:成功后归调用者。
+- `Add/Insert` 成功:`item` 转移给父节点。
+- `Replace` 成功:新 `item` 转移给父节点,旧节点由库删除。
+- `DetachByKey/DetachByIndex`:返回节点归调用者。
+- `RyanJsonPrint` 返回缓冲:调用者用 `RyanJsonFree` 释放。
+
+## 失败语义(重点)
+- `RyanJsonInsert` 失败:
+  - `item` 是游离节点时:失败路径由库删除该 `item`。
+  - `item` 非游离节点时:直接返回 false,不删除(避免破坏原树)。
+- `RyanJsonAddItemToObject` 失败(当前实现):
+  - `item` 非游离:返回 false,不删除。
+  - `item` 为标量:返回 false,并删除该 `item`。
+  - `item` 为容器且后续插入失败:失败路径由库侧清理(包含包装节点与其子树)。
+- `ReplaceByKey/ReplaceByIndex` 失败:不消费 `item`,调用者决定复用或释放。
+
+## 建议失败处理模板
+### Add/Insert
+```c
+RyanJson_t item = RyanJsonCreateObject();
+if ((NULL == item) || (RyanJsonFalse == RyanJsonIsDetachedItem(item))) { return RyanJsonFalse; }
+
+if (RyanJsonFalse == RyanJsonInsert(parent, UINT32_MAX, item))
+{
+    // Insert 失败时,游离 item 会由库侧清理;不要盲目二次释放
+    return RyanJsonFalse;
+}
+```
+
+### Replace
+```c
+RyanJson_t newItem = RyanJsonCreateObject();
+if ((NULL == newItem) || (RyanJsonFalse == RyanJsonIsDetachedItem(newItem))) { return RyanJsonFalse; }
+
+if (RyanJsonFalse == RyanJsonReplaceByKey(parent, "k", newItem))
+{
+    // Replace 失败不消费 item,调用者负责释放或复用
+    RyanJsonDelete(newItem);
+    return RyanJsonFalse;
+}
+```
+
+## 游离节点防误用
+- 复用节点前先 `RyanJsonIsDetachedItem(item)`。
+- 已挂树节点禁止再次 Add/Insert/Replace 到其它位置。
+
+## 常见误区
+- 把 Add/Insert 与 Replace 的失败释放语义混为一套。
+- Detach 后未释放也未重新挂树。
+- hooks 未初始化就开始 Parse/Create。
+
+## 依据(仓库内)
+- `RyanJson/RyanJsonItem.c`:`RyanJsonInsert` 失败 `error__` 删除 `item`
+- `RyanJson/RyanJsonItem.c`:`RyanJsonAddItemToObject` 对非游离/标量/包装失败路径分支
+- `RyanJson/RyanJsonItem.c`:`RyanJsonReplaceByKey/ByIndex` 失败路径返回 false,不删除新 `item`
+- `test/unityTest/cases/core/testCreate.c`:Insert/AddItem 失败语义(含已挂树拒绝、标量失败)
+- `test/unityTest/cases/core/testReplace.c`:Replace 失败后 `item` 仍游离并可复用/需调用方释放

+ 35 - 0
skills/ryanjson-api-usage/references/pitfallsAndDebug.md

@@ -0,0 +1,35 @@
+# 常见坑与排障(RT-Thread)
+
+## 范围
+- 本页是公开 API 视角排障,不展开内部实现优化细节。
+- 共性语义口径见 `../../shared/ryanJsonCommon.md`。
+
+## 高发误用
+- 未先调用 `RyanJsonInitHooks` 就 Parse/Create。
+- `Get*` 前不做判空和 `RyanJsonIsXXX` 判型。
+- 把 `Change*Value` 当类型切换接口使用。
+- 已挂树节点重复 Add/Insert/Replace。
+- 忽略 `RyanJsonStrictObjectKeyCheck` 导致重复 key 语义误判。
+- Replace 失败后误判 `item` 所有权。
+- 误以为 `RyanJsonParse` 默认会严格校验尾部文本。
+- 把 `RyanJsonMinify` 输出无条件当作 `\0` 结尾字符串使用。
+
+## 推荐排障顺序
+1. 检查 hooks 初始化是否先于任何 RyanJson API。
+2. 确认 `RyanJsonConfig.h` 关键宏(尤其严格 key)与预期一致。
+3. 还原最小输入,复现并记录返回值/日志。
+4. 核对失败分支的释放顺序(Create/Add/Replace/Detach/Print)。
+5. 需要语义取证时,按 `example -> unityTest -> fuzzer` 查证。
+
+## 典型症状速查
+- `strcmp` 越界:优先排查 key 是否为合法终止字符串。
+- leak:优先排查 Replace/Detach 失败后的调用方释放逻辑。
+- 打印返回 NULL:优先检查预分配长度边界与输入树合法性。
+- Parse 明明“看起来成功”但后续仍有脏数据:检查是否需要 `RyanJsonParseOptions(..., RyanJsonTrue, ...)`。
+- Minify 后字符串拼接异常:检查返回值是否等于 `textLen`(此时不会自动补 `\0`)。
+
+## 依据(仓库内)
+- `RyanJson/RyanJsonParse.c`:Parse 默认非严格尾部语义
+- `RyanJson/RyanJsonItem.c`:Insert/Replace 失败所有权路径
+- `RyanJson/RyanJson.c`:`RyanJsonMinify` 终止符写入条件
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`、`test/unityTest/cases/utils/testUtils.c`

+ 162 - 0
skills/ryanjson-api-usage/references/quickstart.md

@@ -0,0 +1,162 @@
+# RyanJson 快速上手(RT-Thread/C)
+
+## 范围
+- 本文提供“最小可跑通”的公开 API 路径。
+- 完整业务模板见 `integrationTemplate.md`。
+- 平台差异实现见 `rtThreadExamples.md`。
+- 失败所有权细则见 `ownershipAndErrors.md`。
+
+## 入口条件(必须)
+1. 任何 Json API 前先完成 `RyanJsonInitHooks`。
+2. 回答或示例中标明宏前提:
+   - `RyanJsonStrictObjectKeyCheck`
+   - `RyanJsonDefaultAddAtHead`
+3. 每条失败分支都要显式释放(`RyanJsonDelete` / `RyanJsonFree`)。
+
+## 示例 1:初始化 hooks(一次性)
+```c
+#include <stdlib.h>
+#include "RyanJson.h"
+
+static RyanJsonBool_e appEnsureJsonHooks(void)
+{
+    static RyanJsonBool_e inited = RyanJsonFalse;
+    if (RyanJsonTrue == inited) { return RyanJsonTrue; }
+
+    inited = RyanJsonInitHooks(malloc, free, realloc);
+    return inited;
+}
+```
+
+## 示例 2:Parse + Get(安全读取)
+```c
+#include <stdint.h>
+#include "RyanJson.h"
+
+static RyanJsonBool_e readAge(const char *jsonText, int32_t *outAge)
+{
+    RyanJson_t root = NULL;
+    RyanJson_t ageItem = NULL;
+
+    if ((NULL == jsonText) || (NULL == outAge)) { return RyanJsonFalse; }
+    if (RyanJsonFalse == appEnsureJsonHooks()) { return RyanJsonFalse; }
+
+    root = RyanJsonParse(jsonText);
+    if (NULL == root) { return RyanJsonFalse; }
+
+    ageItem = RyanJsonGetObjectByKey(root, "age");
+    if ((NULL == ageItem) || (RyanJsonFalse == RyanJsonIsInt(ageItem)))
+    {
+        RyanJsonDelete(root);
+        return RyanJsonFalse;
+    }
+
+    *outAge = RyanJsonGetIntValue(ageItem);
+    RyanJsonDelete(root);
+    return RyanJsonTrue;
+}
+```
+
+## 示例 3:Create + Add + PrintPreallocated(非格式化输出)
+```c
+#include <stdint.h>
+#include "RyanJson.h"
+
+static RyanJsonBool_e buildReport(char *out, uint32_t outCap, uint32_t *outLen)
+{
+    RyanJson_t root = NULL;
+
+    if ((NULL == out) || (NULL == outLen) || (0U == outCap)) { return RyanJsonFalse; }
+
+    root = RyanJsonCreateObject();
+    if (NULL == root) { return RyanJsonFalse; }
+
+    if ((RyanJsonFalse == RyanJsonAddStringToObject(root, "name", "ryan")) ||
+        (RyanJsonFalse == RyanJsonAddIntToObject(root, "age", 18)))
+    {
+        RyanJsonDelete(root);
+        return RyanJsonFalse;
+    }
+
+    if (NULL == RyanJsonPrintPreallocated(root, out, outCap, RyanJsonFalse, outLen))
+    {
+        RyanJsonDelete(root);
+        return RyanJsonFalse;
+    }
+
+    RyanJsonDelete(root);
+    return RyanJsonTrue;
+}
+```
+
+## 示例 4:Replace 跨类型切换(失败不消费 newItem)
+```c
+#include "RyanJson.h"
+
+static RyanJsonBool_e replaceFreqAsObject(RyanJson_t root)
+{
+    RyanJson_t newFreq = NULL;
+
+    if (NULL == root) { return RyanJsonFalse; }
+
+    newFreq = RyanJsonCreateObject();
+    if (NULL == newFreq) { return RyanJsonFalse; }
+    if (RyanJsonFalse == RyanJsonIsDetachedItem(newFreq))
+    {
+        RyanJsonDelete(newFreq);
+        return RyanJsonFalse;
+    }
+
+    if ((RyanJsonFalse == RyanJsonAddIntToObject(newFreq, "value", 100)) ||
+        (RyanJsonFalse == RyanJsonAddStringToObject(newFreq, "unit", "Hz")))
+    {
+        RyanJsonDelete(newFreq);
+        return RyanJsonFalse;
+    }
+
+    if (RyanJsonFalse == RyanJsonReplaceByKey(root, "freq", newFreq))
+    {
+        RyanJsonDelete(newFreq); // Replace 失败:调用方负责释放
+        return RyanJsonFalse;
+    }
+
+    return RyanJsonTrue;
+}
+```
+
+## 示例 5:Detach 后重挂或释放
+```c
+#include "RyanJson.h"
+
+static RyanJsonBool_e movePayload(RyanJson_t src, RyanJson_t dst)
+{
+    RyanJson_t detached = NULL;
+
+    if ((NULL == src) || (NULL == dst)) { return RyanJsonFalse; }
+
+    detached = RyanJsonDetachByKey(src, "payload");
+    if (NULL == detached) { return RyanJsonFalse; }
+
+    if (RyanJsonFalse == RyanJsonAddItemToObject(dst, "payload", detached))
+    {
+        // 当前实现下,对游离节点 AddItem 失败路径由库侧清理;不要二次释放 detached
+        return RyanJsonFalse;
+    }
+
+    return RyanJsonTrue;
+}
+```
+
+## 最小检查单
+1. hooks 初始化是否先于 Parse/Create。
+2. Get 前是否判空 + 判型。
+3. Replace 失败后是否由调用方处理 `newItem`。
+4. 动态打印返回值是否配对 `RyanJsonFree`。
+5. 退出分支是否都做了 `RyanJsonDelete`。
+
+## 依据(仓库内)
+- `RyanJson/RyanJson.c`:`RyanJsonInitHooks`、`RyanJsonDelete`
+- `RyanJson/RyanJsonItem.c`:`RyanJsonReplaceByKey`、`RyanJsonDetachByKey`、`RyanJsonAddItemToObject`
+- `test/unityTest/cases/core/testReplace.c`:Replace 失败不消费 `item`
+- `test/unityTest/cases/core/testDetach.c`、`test/unityTest/cases/core/testCreate.c`:Detach/AddItem 失败路径
+- `test/unityTest/cases/utils/testPrint.c`:非格式化打印与 preallocated 边界

+ 151 - 0
skills/ryanjson-api-usage/references/rtThreadExamples.md

@@ -0,0 +1,151 @@
+# RT-Thread 平台示例(RyanJson)
+
+## 范围
+- 本页只放 RT-Thread 平台差异:hooks 映射、任务入口、日志与并发建议。
+- 通用 API 路径见 `quickstart.md` / `integrationTemplate.md`。
+- 失败语义与释放规则见 `ownershipAndErrors.md`。
+
+## 接入建议
+1. 应用初始化阶段完成 hooks 初始化(如 `INIT_APP_EXPORT`)。
+2. 业务线程执行完整 Json 事务(Create/Parse -> Modify -> Print -> Delete)。
+3. 推荐单 owner 线程;多线程共享时在外层加锁保护完整事务。
+
+## 示例 1:hooks 初始化(RT-Thread 映射)
+```c
+#include <rtthread.h>
+#include "RyanJson.h"
+
+static void *rtJsonMalloc(size_t size) { return rt_malloc(size); }
+static void rtJsonFree(void *ptr)
+{
+    if (RT_NULL != ptr) { rt_free(ptr); }
+}
+static void *rtJsonRealloc(void *ptr, size_t size) { return rt_realloc(ptr, size); }
+
+static int32_t rtJsonInit(void)
+{
+    if (RyanJsonFalse == RyanJsonInitHooks(rtJsonMalloc, rtJsonFree, rtJsonRealloc))
+    {
+        rt_kprintf("RyanJsonInitHooks failed\n");
+        return -RT_ERROR;
+    }
+
+    rt_kprintf("RyanJson hooks ready\n");
+    return RT_EOK;
+}
+INIT_APP_EXPORT(rtJsonInit);
+```
+
+## 示例 2:线程内完整事务(Create -> Replace -> Print -> Delete)
+```c
+#include <stdint.h>
+#include <rtthread.h>
+#include "RyanJson.h"
+
+static RyanJsonBool_e buildFreqObject(RyanJson_t *outFreqObj)
+{
+    RyanJson_t freqObj = RT_NULL;
+
+    if (RT_NULL == outFreqObj)
+    {
+        return RyanJsonFalse;
+    }
+
+    freqObj = RyanJsonCreateObject();
+    if (RT_NULL == freqObj)
+    {
+        return RyanJsonFalse;
+    }
+    if (RyanJsonFalse == RyanJsonIsDetachedItem(freqObj))
+    {
+        RyanJsonDelete(freqObj);
+        return RyanJsonFalse;
+    }
+
+    if ((RyanJsonFalse == RyanJsonAddIntToObject(freqObj, "value", 100)) ||
+        (RyanJsonFalse == RyanJsonAddStringToObject(freqObj, "unit", "Hz")))
+    {
+        RyanJsonDelete(freqObj);
+        return RyanJsonFalse;
+    }
+
+    *outFreqObj = freqObj;
+    return RyanJsonTrue;
+}
+
+static void jsonWorkerEntry(void *parameter)
+{
+    RyanJson_t root = RyanJsonCreateObject();
+    RyanJson_t freqObj = RT_NULL;
+    char outBuf[192];
+    uint32_t outLen = 0U;
+    (void)parameter;
+
+    if (RT_NULL == root) { return; }
+
+    if ((RyanJsonFalse == RyanJsonAddStringToObject(root, "dev", "imu")) ||
+        (RyanJsonFalse == RyanJsonAddIntToObject(root, "freq", 100)))
+    {
+        goto done;
+    }
+
+    if (RyanJsonTrue == buildFreqObject(&freqObj))
+    {
+        if (RyanJsonFalse == RyanJsonReplaceByKey(root, "freq", freqObj))
+        {
+            RyanJsonDelete(freqObj); // Replace 失败:调用方负责释放
+        }
+    }
+
+    if (RT_NULL != RyanJsonPrintPreallocated(root, outBuf, (uint32_t)sizeof(outBuf), RyanJsonFalse, &outLen))
+    {
+        rt_kprintf("json=%s\n", outBuf);
+    }
+    else
+    {
+        rt_kprintf("print failed\n");
+    }
+
+done:
+    RyanJsonDelete(root);
+}
+```
+
+## 示例 3:Detach 迁移子树
+```c
+#include <rtthread.h>
+#include "RyanJson.h"
+
+static RyanJsonBool_e detachMovePayload(RyanJson_t src, RyanJson_t dst)
+{
+    RyanJson_t detached = RT_NULL;
+
+    if ((RT_NULL == src) || (RT_NULL == dst)) { return RyanJsonFalse; }
+
+    detached = RyanJsonDetachByKey(src, "payload");
+    if (RT_NULL == detached) { return RyanJsonFalse; }
+
+    if (RyanJsonFalse == RyanJsonAddItemToObject(dst, "payload", detached))
+    {
+        // 当前实现下,游离节点 AddItem 失败路径由库侧清理;不做二次释放
+        return RyanJsonFalse;
+    }
+
+    return RyanJsonTrue;
+}
+```
+
+## 上板检查单(不依赖 unity/fuzzer)
+1. hooks 初始化日志是否先于业务 Json 调用。
+2. Parse/Create/Add/Replace/Print 的返回值是否都处理。
+3. Replace 失败后是否没有泄漏 `newItem`。
+4. 事务退出点是否统一清理(`RyanJsonDelete`)。
+5. 异常输入是否返回 false/NULL 且不崩溃。
+
+## 依据(仓库内)
+- `RyanJson/RyanJson.c`:hooks 全局指针与 `RyanJsonInitHooks`
+- `RyanJson/RyanJsonItem.c`:`RyanJsonAddItemToObject` / `RyanJsonReplaceByKey` / `RyanJsonDetachByKey`
+- `RyanJson/RyanJsonPrint.c`:`RyanJsonPrintPreallocated`
+- `test/unityTest/cases/core/testCreate.c`:AddItem/Insert 失败行为
+- `test/unityTest/cases/core/testReplace.c`:Replace 失败不消费新节点
+- `test/unityTest/cases/utils/testPrint.c`:预分配打印边界

+ 4 - 0
skills/ryanjson-api-usage/references/terminology.md

@@ -0,0 +1,4 @@
+# 术语字典
+
+- 统一术语定义复用共享文档:`../../shared/terminology.md`。
+- 如出现本技能专属术语,可在本文件追加扩展,不覆盖共享定义。

+ 30 - 0
skills/ryanjson-api-usage/sop.md

@@ -0,0 +1,30 @@
+# RyanJson API 工作流(压缩版)
+
+## 1. 先定边界
+1. 只使用公开 API,不展开内部实现细节。
+2. 先确认宏前提与 hooks 初始化方案。
+3. 明确用户目标类型:读取、构建、更新、替换、迁移。
+
+## 2. 再给方案
+1. 选择最小 API 组合。
+2. 给出“成功 + 失败”双路径代码。
+3. 明确所有权转移与释放责任。
+4. 给出 RT-Thread 可执行验证点。
+
+## 3. 最后交付
+1. 方案结论与前提。
+2. 最小代码。
+3. 所有权/释放清单。
+4. 验证步骤与扩展建议。
+
+## 4. 常见漏项
+- 未初始化 hooks。
+- `Get*` 之前缺少判空/判型。
+- 把 Replace 失败语义误当 Add/Insert。
+- 未标注宏前提,导致预期错位。
+
+## 5. 依据(仓库内)
+- `RyanJson/RyanJson.c`:hooks 初始化与释放路径
+- `RyanJson/RyanJsonItem.c`:Insert/Replace 失败语义
+- `RyanJson/RyanJsonConfig.h`:宏前提(StrictKey / AddAtHead)
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`

+ 58 - 0
skills/ryanjson-optimization/SKILL.md

@@ -0,0 +1,58 @@
+---
+name: ryanjson-optimization
+description: 面向 RyanJson 核心代码的正确性优先优化技能。用于 Parse/Print/Item/Compare 优化、宏语义设计、内存与性能权衡、以及 crash/覆盖率回归闭环。用户请求“核心优化”“军工级稳定性”“最小内存提效”“不改 API 前提下优化”时使用本技能。
+---
+
+# RyanJson 核心优化技能
+
+## 技能定位
+- 面向核心实现层的“正确性优先”优化:Parse/Print/Item/Compare。
+- 目标是在不破坏契约的前提下提升性能、内存或稳定性。
+
+## 输入要求
+- 明确优先级:性能、内存、稳定性、代码尺寸。
+- 明确边界:是否允许行为变化、是否允许新增宏、RAM/ROM 预算。
+- 明确门禁:必须通过的测试与覆盖回归范围。
+
+## 必读入口
+- 共享基线:`../shared/ryanJsonCommon.md`
+- 注释规范:统一使用 Doxygen 风格,且类型名/字段语义名/API 名保持英文(见共享基线第 9 节)
+- 工作流:`references/coreWorkflow.md`
+- 门禁:`references/regressionGates.md`
+- 输出模板:`references/optimizationTemplate.md`
+
+## 执行入口
+- 代码规范:先执行 `bash ./run_local_format.sh --check --changed`,提交前执行 `bash ./run_local_format.sh`。
+- 本地常规回归:优先双链路 `run_local_*`(`run_local_base.sh` + `run_local_qemu.sh`)。
+- 特殊验证(矩阵细调/并发细调/覆盖率)再直调:
+  - `scripts/ci/runBaseCoverage.sh`
+  - `scripts/ci/runCoverage.sh`
+
+## 执行流程
+1. 先按 `../shared/ryanJsonCommon.md` 锁定宏前提和语义边界。
+2. 建立基线:正确性、耗时、内存峰值、崩溃样本、覆盖率。
+3. 设计最小改动:只改目标路径,避免顺手重构。
+4. 增量验证:每次改动先跑定向回归,再扩到全量门禁。
+5. 输出结论:收益、代价、兼容影响、剩余风险和回滚条件。
+
+## 优化专项约束
+- 未授权不得改变公开 API 语义或默认行为。
+- 禁止引入依赖常驻额外节点开销的侵入式提速。
+- `Add/Insert/Replace` 失败语义必须与头文件和测试一致。
+- 宏语义变更必须同步代码、测试和文档。
+- 涉及对齐/异常路径时,必须补跑 QEMU 链路确认 `UNALIGN_TRP + HardFault` 行为。
+
+## 输出格式
+1. 结论:优先级与目标达成情况(已验证/推断)。
+2. 方案:文件/函数级最小改动点与原因。
+3. 证据:unit、fuzz、覆盖、内存与性能数据。
+4. 风险:未覆盖边界、回滚触发条件、下一步建议。
+
+## 参考导航
+- 核心结构:`references/coreArchitecture.md`
+- 模块热点:`references/moduleHotspots.md`
+- 配置开关:`references/configSwitches.md`
+- 优化配方:`references/optimizationRecipes.md`
+- 术语:`references/terminology.md`
+- Gemini 对齐:`references/geminiCompat.md`
+- 本地压缩文档:`architecture.md`、`pitfalls.md`、`sop.md`

+ 35 - 0
skills/ryanjson-optimization/agents/gemini.md

@@ -0,0 +1,35 @@
+# Gemini Skill Card
+
+名称:`ryanjson-optimization`
+
+## 定位
+- 面向 RyanJson 核心代码的正确性优先优化技能。
+
+## 适用场景
+- 优化 Parse/Print/Item/Compare 等核心路径。
+- 在嵌入式约束下平衡稳定性、性能、内存、代码尺寸。
+- 需要“先修正确性再提效”的工程化方案。
+
+## 输入建议
+- 问题证据:崩溃日志、泄漏、覆盖率或性能瓶颈。
+- 约束条件:是否允许新增内存、是否允许行为变化。
+- 验收标准:需要通过的测试、覆盖、内存门禁。
+
+## 硬约束
+- 正确性优先于性能,先修 P0/P1 风险再提效。
+- 保持公开 API 语义稳定,最小化改动范围。
+- 宏语义变更需同步代码、单测、fuzzer 与文档。
+- 语义不明确时按 `example/ -> test/unityTest/ -> test/fuzzer/` 取证。
+
+## 术语口径
+- 统一按 `../references/terminology.md`。
+- 输出必须显式区分:已验证/推断、可恢复错误/不可恢复错误、失败语义。
+
+## 默认提示词
+使用 `$ryanjson-optimization`,先给正确性风险分级,再给最小改动优化方案;在嵌入式 RAM/ROM 约束下保持 API 语义稳定,并基于 `example -> unityTest -> fuzzer` 取证给出可复现证据。
+
+## 输出骨架
+1. 结论与风险分级(P0/P1/P2,标注已验证/推断)。
+2. 最小改动方案(文件 + 函数 + 原因 + 兼容影响)。
+3. 验证证据(unity/fuzzer/覆盖/内存)。
+4. 剩余风险、回滚条件、下一步建议。

+ 4 - 0
skills/ryanjson-optimization/agents/openai.yaml

@@ -0,0 +1,4 @@
+interface:
+  display_name: "RyanJson 核心优化"
+  short_description: "正确性优先的核心优化与可复现回归证据"
+  default_prompt: "使用 $ryanjson-optimization 先修正确性再做性能/内存优化:保持公开 API 语义稳定,最小化改动范围,区分可恢复与不可恢复错误;语义不清时按 example -> unityTest -> fuzzer 取证并给出可复现证据。"

+ 32 - 0
skills/ryanjson-optimization/architecture.md

@@ -0,0 +1,32 @@
+# RyanJson 核心架构(压缩版)
+
+## 1. 作用
+- 提供优化前必须掌握的结构心智,避免“改对性能、改坏正确性”。
+- 详细结构说明见 `references/coreArchitecture.md`。
+
+## 2. 节点与 Payload 心智
+- RyanJson 节点是紧凑模型,运行行为依赖内部负载布局。
+- 优化时不能按“传统大而全节点结构”做假设。
+- 未经授权不要引入会增加常驻节点开销的结构改动。
+
+## 3. 链接与遍历约束
+- 结构编辑(`Detach/Replace/Delete`)必须保持遍历不变量。
+- 任何“简化链接重接”的改动都必须配套回归覆盖。
+
+## 4. 字符串路径约束
+- 字符串处理存在短路径与动态路径切换。
+- 短长路径切换的更新逻辑必须保证无泄漏。
+
+## 5. 分配器基线
+- 内部分配依赖 hooks;未对齐 hooks 的优化验证结果无效。
+
+## 6. 优化守则
+1. 先正确再提速。
+2. 只做最小必要改动。
+3. 先验证失败路径,再验证热点路径。
+
+## 7. 依据(仓库内)
+- `RyanJson/RyanJson.c`:hooks 全局指针与初始化
+- `RyanJson/RyanJsonItem.c`:Detach/Replace/Delete 链与所有权路径
+- `RyanJson/RyanJsonConfig.h`:宏前提(StrictKey / AddAtHead)
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`

+ 34 - 0
skills/ryanjson-optimization/pitfalls.md

@@ -0,0 +1,34 @@
+# RyanJson 优化雷区(压缩版)
+
+## 1. Parse 路径雷区
+- 非法输入下的数字/Unicode 边界最容易引发崩溃。
+- 回滚路径必须能清理“半构建”结构。
+
+## 2. Print 路径雷区
+- 预分配输出路径对边界计数极其敏感。
+- 终止符与长度边界要重点防 off-by-one。
+
+## 3. 所有权雷区
+- `Add/Insert` 与 `Replace` 失败语义不同。
+- 错把两者当同一规则会导致泄漏或双重释放。
+
+## 4. Compare/遍历雷区
+- 大对象比较会进入高成本路径。
+- 改动比较逻辑必须覆盖深层与乱序结构回归。
+
+## 5. 宏相关雷区
+- `RyanJsonDefaultAddAtHead` 会影响追加顺序与索引语义。
+- `RyanJsonStrictObjectKeyCheck` 会影响重复 key 预期。
+
+## 6. 审查检查单
+1. 是否改变了公开语义。
+2. 是否覆盖了失败路径。
+3. 宏敏感断言是否同步。
+4. 相关模块是否复跑 fuzz 回归。
+
+## 7. 依据(仓库内)
+- `RyanJson/RyanJsonParse.c`:Parse 回滚与尾部语义
+- `RyanJson/RyanJsonPrint.c`:预分配边界与失败路径
+- `RyanJson/RyanJsonItem.c`:Add/Insert/Replace 失败语义差异
+- `test/fuzzer/entry.c`:fuzzer 入口与稳定性回归链路
+- `../shared/ryanJsonCommon.md`:统一执行入口与覆盖约定

+ 40 - 0
skills/ryanjson-optimization/references/configSwitches.md

@@ -0,0 +1,40 @@
+# 配置宏设计规范
+
+## 范围
+- 本页只讲“宏语义设计”和“宏变更验收点”。
+- 完整优化任务交付骨架见 `optimizationTemplate.md`。
+
+## 宏新增/修改基本规则
+- 只为“可明确切换的行为”加宏,不为临时调试加宏。
+- 宏默认值优先兼容历史行为。
+- 注释必须写清:启用后收益、代价、兼容影响、测试影响。
+
+## 变更流程
+1. 定义宏语义和默认值。
+2. 在 `RyanJsonConfig.h` 写清行为差异。
+3. 同步核心代码分支。
+4. 同步 unity + fuzzer 条件断言。
+5. 在变更说明中列出风险与回滚条件。
+
+## 典型宏:`RyanJsonStrictObjectKeyCheck`
+- `true`:拒绝重复 key,输入更严格,早发现脏数据。
+- `false`:允许重复 key,通常首匹配,语义依赖调用方约束。
+
+## 宏级别验收清单
+- Parse 是否符合宏期望。
+- Add/Insert/Replace 是否符合宏期望。
+- Compare/读取行为是否在文档中声明。
+- fuzzer 断言是否按宏分支。
+
+## 提交说明模板
+- 变更宏:
+- 默认行为变化:
+- 受影响 API:
+- 测试覆盖:
+- 兼容风险:
+- 回滚策略:
+
+## 依据(仓库内)
+- `RyanJson/RyanJsonConfig.h`:严格 key / AddAtHead 宏定义与默认值
+- `RyanJson/RyanJson.h`:宏影响的公开 API 语义注释
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`:宏分支断言

+ 74 - 0
skills/ryanjson-optimization/references/coreArchitecture.md

@@ -0,0 +1,74 @@
+# RyanJson 核心架构速图(优化视角)
+
+## 范围
+- 本页说明优化决策需要的结构心智,不包含具体改动步骤。
+- 执行流程见 `coreWorkflow.md`,门禁见 `regressionGates.md`。
+
+## 1. 数据结构基线
+- `RyanJson_t` = `struct RyanJsonNode *`。
+- `struct RyanJsonNode` 公开只有 `next` 字段;其余元数据与值在 payload 区。
+- payload 起始 1 字节是 flag,随后是类型相关数据区。
+
+## 2. 节点分配模型(`RyanJsonInternalNewNode`)
+分配大小 = 基础头 + 类型值区 + 可选 inline 区:
+- 基础:`sizeof(node) + 1B flag`
+- number:+4 或 +8
+- array/object:+`sizeof(RyanJson_t)`(children 指针)
+- 有 key 或类型是 string:+`RyanJsonInlineStringSize`
+
+优化含义:
+- 只有“可能存 key/string”的节点才有 inline 区。
+- object/array 的 children 指针在 payload 区,不在 struct 字段里。
+
+## 3. 字符串存储模式
+- inline:短 key/value 放在节点内,减少堆分配。
+- ptr:长字符串放外部堆块,payload 存指针。
+- `RyanJsonInternalChangeString` 负责模式切换和旧堆块释放。
+
+优化红线:
+- 不能破坏模式切换后的所有权与释放顺序。
+
+## 4. 线索化链(优化常踩坑)
+- `IsLast=0`:`next` 指向兄弟。
+- `IsLast=1`:`next` 指向父节点(不是兄弟)。
+
+影响:
+- `RyanJsonGetNext` 会屏蔽 `IsLast` 并返回 NULL。
+- 内部遍历/删除经常直接用 `node->next` 做回溯。
+- 优化遍历逻辑时,不能把 `next` 一律当兄弟指针。
+
+## 5. Delete/Duplicate 都是非递归
+- `RyanJsonDelete`:下沉子树,叶子释放后通过线索回溯。
+- `RyanJsonDuplicate`:同步遍历源树与目标树,依赖 `IsLast` 维护层级。
+
+优化建议:
+- 若要改遍历路径,优先补深层结构回归和栈风险回归。
+
+## 6. Hooks 是全局运行前置
+- `jsonMalloc/jsonFree/jsonRealloc` 为全局函数指针。
+- 必须由 `RyanJsonInitHooks` 先初始化。
+- 优化结论必须在 hooks 已初始化条件下给出(否则数据不可比)。
+- 当前实现中 hooks 默认值为 `NULL`,未初始化状态不可作为有效基线。
+
+## 7. 对外/对内 API 边界
+- 对外 API 用 `RyanJsonXxx`(头文件公开声明)。
+- 跨文件内部 API 用 `RyanJsonInternalApi` + `RyanJsonInternalXxx`。
+- 优化时不要把内部辅助函数误暴露为公开契约。
+
+## 8. 配置宏对行为的影响
+- `RyanJsonStrictObjectKeyCheck` 改变 parse/insert/replace 的重复 key 行为。
+- `RyanJsonInlineStringSize`、`RyanJsonMallocHeaderSize`、`RyanJsonMallocAlign` 影响节点内存布局边界。
+
+优化动作前,先锁定这些宏值。
+
+## 9. 优化取证最小顺序
+1. 头文件语义(`RyanJson.h` / `RyanJsonConfig.h`)
+2. 核心实现(`RyanJson*.c`)
+3. 样例与测试(`example/` -> `test/unityTest/` -> `test/fuzzer/`)
+
+若三者冲突,先修文档和测试,再收敛实现。
+
+## 10. 关键依据(仓库内)
+- `RyanJson/RyanJson.c`:hooks 全局变量与 `RyanJsonInitHooks`
+- `RyanJson/RyanJsonUtils.c`、`RyanJson/RyanJsonParse.c`、`RyanJson/RyanJsonPrint.c`:`jsonMalloc/jsonFree` 调用路径
+- `test/unityTest/common/testCommon.c`、`test/fuzzer/entry.c`:测试入口 hooks 初始化

+ 61 - 0
skills/ryanjson-optimization/references/coreWorkflow.md

@@ -0,0 +1,61 @@
+# RyanJson 核心优化工作流
+
+## 范围
+- 本页仅覆盖“优化执行流程”。
+- 交付结构见 `optimizationTemplate.md`。
+- 模块风险细节见 `moduleHotspots.md`。
+- 执行入口、覆盖目录、脚本参数基线见 `../../shared/ryanJsonCommon.md`。
+
+## 0) 前置条件(必须)
+- 在任何业务入口、测试入口、fuzzer 入口先调用 `RyanJsonInitHooks`。
+- 默认建议使用统一封装分配器(即便最终仍转发到 `malloc/free/realloc`),便于统计和故障注入。
+- 未初始化 hooks 时,不执行优化结论对比(避免基线不可比)。
+- 当前实现没有默认 hooks 回退(全局 hooks 初始为 `NULL`),因此该前置条件是硬约束。
+
+## 1) 定义问题边界
+- 明确是“行为错误修复”还是“性能/内存优化”。
+- 明确是否允许改变历史行为(默认不允许)。
+- 明确目标平台(32 位/64 位、是否 RTOS、是否启用 ASan/UBSan)。
+
+## 2) 建立可比较基线
+- 功能:跑与改动模块相关的单元测试。
+- 资源:记录内存峰值、分配次数、失败路径泄漏结果。
+- 稳定性:保留至少一个历史 crash 样本回归。
+- 覆盖:确认目标分支 true/false 双向是否可达。
+
+## 2.1) 回归入口策略
+- 本地常规回归:先跑 `run_local_*`。
+- 需要细调矩阵/并发/覆盖时,再直调 `scripts/ci/*`。
+
+## 3) 聚焦热点模块
+- `RyanJsonParse.c`:数字解析、字符串转义、Unicode 代理对、状态推进。
+- `RyanJsonPrint.c`:预分配边界、append 失败路径、double 打印策略。
+- `RyanJsonItem.c`:Add/Insert/Replace/Detach/Delete 的所有权与链安全。
+- `RyanJson.c`:Compare 有序/无序路径、深度与回溯。
+
+## 4) 设计最小改动
+- 优先修复内存安全与语义一致性,再做微优化。
+- 新防御逻辑要区分可恢复错误(返回 false/NULL)与不可恢复错误(启用 `RyanJsonEnableAssert` 时可 assert)。
+- 避免引入额外常驻内存;若必须引入,先得到明确授权。
+
+## 5) 同步测试策略
+- 不新增散文件,补到现有分类测试。
+- 单元测试覆盖确定性语义,fuzzer 覆盖组合路径与未知输入。
+- 新增每个分支至少一个触发样本,必要时固化到 corpus。
+
+## 6) 交付与复核
+- 给出改动原因、收益、代价、兼容影响。
+- 给出“已验证/未验证”边界。
+- 给出剩余风险和下一轮优化建议。
+
+## 常见反模式
+- 只看覆盖率,不看语义断言。
+- 通过 assert 处理用户输入错误。
+- 为了覆盖率引入会改变行为的冗余逻辑。
+
+## 依据(仓库内)
+- `RyanJson/RyanJson.c`:`RyanJsonInitHooks` 与全局 hooks 初始状态
+- `RyanJson/RyanJson.h`:`Add/Insert`、`Replace` 失败语义注释
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`:失败所有权与宏分支断言
+- `test/fuzzer/entry.c`:fuzzer 入口与稳定性回归链路
+- `../../shared/ryanJsonCommon.md`:统一执行入口与覆盖口径

+ 32 - 0
skills/ryanjson-optimization/references/geminiCompat.md

@@ -0,0 +1,32 @@
+# Gemini 兼容说明(优化类任务)
+
+## 范围
+- 本页只定义 Gemini 在优化任务中的输入/输出结构。
+- 优化流程与门禁口径以 `coreWorkflow.md`、`regressionGates.md` 为准。
+
+## 推荐输入模板
+- 背景:当前失败日志或性能瓶颈。
+- 目标:先修正确性/再做性能 或 先性能/再验证。
+- 约束:不新增额外内存、不改 API、测试文件归档规则。
+- 验收:需要通过的测试与覆盖率证据。
+
+## 推荐输出结构
+1. 风险分级(P0/P1/P2)。
+2. 最小改动方案(文件 + 函数 + 预期行为)。
+3. 验证步骤(命令 + 预期)。
+4. 残余风险与下一步。
+
+字段组织可直接复用 `optimizationTemplate.md`。
+
+## 禁止输出模式
+- 只给“建议”不给落地步骤。
+- 忽略失败路径与释放语义。
+- 不区分“已验证”与“推断”。
+
+## 跨模型对齐真值
+- API 与头文件注释优先于模型记忆。
+- 覆盖率必须看分支双向触达。
+- assert 只能用于不可恢复内部破坏场景。
+
+## 术语字典(统一)
+- 本文术语统一以 `terminology.md` 为准。

+ 44 - 0
skills/ryanjson-optimization/references/moduleHotspots.md

@@ -0,0 +1,44 @@
+# RyanJson 模块热点与审查清单
+
+## 范围
+- 本页只列“风险热点”和“审查项”。
+- 改动步骤见 `coreWorkflow.md`。
+- 输出结构见 `optimizationTemplate.md`。
+
+## RyanJsonParse.c(高风险)
+- 数字路径:整数、小数、指数累计时的溢出保护。
+- 状态推进:`RyanJsonParseBufTryAdvanceCurrentPtr` 的成功/失败路径是否一致。
+- 字符串路径:转义、Unicode 解码、代理对有效性。
+- 错误回滚:失败后当前节点与解析状态是否残留脏数据。
+
+## RyanJsonPrint.c(高风险)
+- 预分配模式:长度刚好够用时的边界是否判定正确。
+- append 失败:是否释放内部缓冲、是否返回 NULL。
+- double 打印:`isinf`/`isnan` 的 RFC8259 策略是否一致。
+- style/preset:用户自定义样式与默认行为是否冲突。
+
+## RyanJsonItem.c(高风险)
+- 所有权语义:分别核对 Add/Insert 与 Replace 的失败路径是否符合既定契约(不可混用)。
+- 游离节点保护:`RyanJsonIsDetachedItem` 防御分支是否覆盖。
+- 重复 key 语义:严格/非严格模式行为与注释一致。
+- 链完整性:next/last 标志在插入、替换、分离后的一致性。
+
+## RyanJson.c Compare(高风险)
+- 同序快路径:是否会误判 key 对齐。
+- 乱序路径:对象查找是否稳定、数组比较是否出现回溯爆炸。
+- 深度与栈:深层嵌套下是否触发栈风险。
+
+## RyanJsonInternal.h / 跨文件内部 API(中高风险)
+- 仅内部接口使用 `RyanJsonInternalApi` + `RyanJsonInternalXxx` 命名。
+- 禁止把内部函数直接暴露为公开 API。
+- 跨模块重命名后要同步 callsite、注释与技能文档。
+
+## RyanJsonConfig.h(中高风险)
+- 宏新增必须写“收益/代价/兼容影响”。
+- 默认值必须兼容历史行为。
+- 任何宏语义变化都要同步 unity/fuzzer 预期。
+
+## 依据(仓库内)
+- `RyanJson/RyanJsonItem.c`:`RyanJsonInsert`、`RyanJsonAddItemToObject`、`RyanJsonReplaceByKey/ByIndex`
+- `RyanJson/RyanJsonConfig.h`:`RyanJsonStrictObjectKeyCheck`、`RyanJsonDefaultAddAtHead`
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`

+ 64 - 0
skills/ryanjson-optimization/references/optimizationRecipes.md

@@ -0,0 +1,64 @@
+# 优化策略库(可直接复用)
+
+## 范围
+- 本页只保留“可复用配方”。
+- 执行顺序见 `coreWorkflow.md`。
+- 交付结构见 `optimizationTemplate.md`。
+
+## 配方 1:先修语义再提速
+适用:崩溃、泄漏、误判与性能问题同时存在。
+
+步骤:
+1. 先修内存安全/语义一致性。
+2. 用最小输入补单测回归。
+3. 再做性能微调(减少重复查找、减少无效分支)。
+
+## 配方 2:零额外内存优化
+适用:用户明确“不能增加额外内存”。
+
+优先手段:
+- 减少重复计算与重复遍历。
+- 改善分支顺序,提高常见路径命中。
+- 缩短失败路径,减少临时对象生命周期。
+
+避免:
+- 引入缓存表或哈希索引(除非授权)。
+
+## 配方 3:Compare 逻辑优化
+适用:对象比较高频、深层结构较多。
+
+优先检查:
+- 同序快路径是否正确。
+- 乱序路径是否 fallback 正确。
+- 深度上限和回溯开销是否可控。
+
+## 配方 4:Print 失败路径稳健化
+适用:预分配失败、append 失败、double 特殊值场景。
+
+要点:
+- 刚好够用边界(`<=`)必须有单测。
+- 内部缓冲失败必须释放并返回 NULL。
+- `inf/nan` 输出策略需与规范一致并有断言。
+
+## 配方 5:Parse 数值边界防护
+适用:覆盖指数溢出、超长小数、非法格式。
+
+要点:
+- 分阶段防溢出(整数、小数、指数分别保护)。
+- 避免前置判断吞掉目标分支。
+- 用单测 + fuzz 双路径验证。
+
+## 当前项目冻结项(默认不改)
+以下是当前已确认的项目决策,除非用户明确要求,否则不要主动推动改动:
+- `RyanJsonParse` 默认保持非严格尾部语义(不改为 strict default)。
+- Compare 相关“纯性能优化”(不改变行为)当前优先级低,默认只做正确性修复。
+- 字符串 `\uXXXX` 预扫长度的进一步内存微优化(更复杂的精算)当前不做。
+
+补充约束:
+- Compare 的结构破坏路径按“内部不变量损坏”处理;启用 `RyanJsonEnableAssert` 时可走 assert。
+
+## 依据(仓库内)
+- `RyanJson/RyanJsonParse.c`:Parse 默认尾部语义与数值路径
+- `RyanJson/RyanJsonPrint.c`、`test/unityTest/cases/utils/testPrint.c`:Print 预分配边界与失败路径
+- `RyanJson/RyanJson.c`:Minify 行为与 Compare 入口
+- `test/unityTest/cases/core/testReplace.c`、`test/unityTest/cases/core/testCreate.c`:核心失败语义回归

+ 34 - 0
skills/ryanjson-optimization/references/optimizationTemplate.md

@@ -0,0 +1,34 @@
+# 优化任务模板(复用)
+
+## 范围
+- 适用于 RyanJson 核心优化任务的统一交付结构。
+- 本模板负责“输入/方案/验证/风险”骨架;模块细节见其他 references。
+- 执行入口与覆盖口径见 `../../shared/ryanJsonCommon.md`。
+
+## 1) 输入快照
+- 目标:
+- 约束(RAM/ROM/是否可新增内存):
+- 兼容要求(是否允许行为变化):
+- 验收标准(测试/覆盖/泄漏):
+
+## 2) 风险分级(P0/P1/P2)
+- P0:
+- P1:
+- P2:
+
+## 3) 最小改动方案
+- 文件:
+- 函数:
+- 改动点:
+- 兼容影响:
+
+## 4) 验证证据
+- 已执行验证:
+- 结果摘要:
+- 失败项与原因:
+
+## 5) 结论与后续
+- 结论(已验证/推断):
+- 剩余风险:
+- 回滚条件:
+- 下一步建议:

+ 42 - 0
skills/ryanjson-optimization/references/regressionGates.md

@@ -0,0 +1,42 @@
+# 回归门禁与验收模板
+
+## 范围
+- 本页只定义门禁标准与阻塞条件。
+- 任务结论写法见 `optimizationTemplate.md`。
+- 执行入口与脚本参数基线见 `../../shared/ryanJsonCommon.md`。
+
+## 门禁级别
+- Gate-0:改动模块相关单测全部通过。
+- Gate-1:全量 unity 通过且无内存泄漏。
+- Gate-2:fuzzer 冒烟通过(含历史 crash 样本)。
+- Gate-3:关键覆盖分支双向触达。
+
+## 执行入口约定
+- 本地常规门禁:优先 `run_local_*`。
+- 特殊门禁场景(覆盖率、矩阵细调、并发细调)再直调 `scripts/ci/*`。
+
+## 必验项清单
+- Parse:非法输入返回 NULL,无崩溃。
+- Item:Add/Insert/Replace/Detach/Delete 所有权一致。
+- Print:动态与预分配模式行为一致。
+- Compare:有序/乱序路径符合预期。
+- 宏开关:严格/非严格语义与文档一致。
+
+## 结果记录模板
+- 改动范围:
+- 通过的门禁:
+- 失败的门禁:
+- 是否阻塞合入:
+- 临时豁免原因:
+- 后续修复计划:
+
+## 回滚触发条件
+- 出现 P0 崩溃或内存破坏。
+- 语义回归影响现有 API 合同。
+- 无法在限制周期内补齐测试证据。
+
+## 依据(仓库内)
+- `test/unityTest/runner/main.c`、`test/unityTest/common/testCommon.c`:unity 入口与泄漏检测门禁
+- `test/fuzzer/entry.c`:fuzzer 冒烟与回归链路
+- `../../shared/ryanJsonCommon.md`:统一执行入口与覆盖口径
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`:Item 所有权回归基础用例

+ 4 - 0
skills/ryanjson-optimization/references/terminology.md

@@ -0,0 +1,4 @@
+# 术语字典
+
+- 统一术语定义复用共享文档:`../../shared/terminology.md`。
+- 如出现本技能专属术语,可在本文件追加扩展,不覆盖共享定义。

+ 31 - 0
skills/ryanjson-optimization/sop.md

@@ -0,0 +1,31 @@
+# RyanJson 优化工作流(压缩版)
+
+## 1. 先建基线
+1. 用当前代码复现问题(正确性/性能/内存)。
+2. 采集基线证据(行为+指标)。
+3. 先定义验收标准再开始改动。
+
+## 2. 实施循环
+1. 做最小局部改动。
+2. 先跑定向回归。
+3. 再校验失败路径和宏敏感行为。
+4. 迭代到目标达成为止。
+
+## 3. 回归顺序
+1. 本地常规先跑 `run_local_*`。
+2. 需要细调矩阵/并发/覆盖时,再直调 `scripts/ci/*`。
+3. 最终门禁覆盖 unit + fuzz + 历史崩溃样本。
+
+## 4. 交付结构
+1. 改了什么,为什么改。
+2. 收益与代价。
+3. 已验证证据与剩余风险。
+4. 回滚条件与后续建议。
+
+## 5. 依据(仓库内)
+- `xmake.lua`:`RyanJson` / `RyanJsonFuzz` 模式 target
+- `run_local_base.sh`、`run_local_ci.sh`、`run_local_fuzz.sh`:本地常规入口
+- `scripts/ci/runBaseCoverage.sh`:unit 特殊矩阵/覆盖执行链
+- `scripts/ci/runCoverage.sh`:fuzz 特殊参数/覆盖执行链
+- `RyanJson/RyanJsonConfig.h`:宏前提
+- `test/unityTest/runner/main.c`:unit 入口模式隔离

+ 56 - 0
skills/ryanjson-test-engineering/SKILL.md

@@ -0,0 +1,56 @@
+---
+name: ryanjson-test-engineering
+description: 面向 RyanJson 单元测试与模糊测试的覆盖工程技能。用于按现有分类补充边界/失败路径/所有权断言、修复泄漏与崩溃回归、并提升关键分支触达质量。用户请求“扩展单测”“补覆盖率”“fuzzer 崩溃回归”时使用本技能。
+---
+
+# RyanJson 测试工程技能
+
+## 技能定位
+- 面向 unit/fuzz 的测试补强与回归闭环。
+- 重点是失败路径、边界条件、所有权语义和覆盖率触达质量。
+
+## 必读入口
+- 共享基线:`../shared/ryanJsonCommon.md`
+- 注释规范:统一使用 Doxygen 风格,且类型名/字段语义名/API 名保持英文(见共享基线第 9 节)
+- 断言与分层:`references/unityPlaybook.md`、`references/fuzzerPlaybook.md`
+- 分诊与回归:`references/coverageTriage.md`、`references/regressionMatrix.md`
+- 输出模板:`references/testcaseTemplate.md`
+
+## 执行入口
+- 代码规范:先执行 `bash ./run_local_format.sh --check --changed`,提交前执行 `bash ./run_local_format.sh`。
+- 本地常规回归:优先双链路 `run_local_*`。
+  - `run_local_base.sh`:默认 full unit(跳过覆盖)
+  - `run_local_qemu.sh`:默认 full QEMU 矩阵(完整 localbase 单测 + 非对齐 fault 校验)
+  - `run_local_ci.sh`:默认 full unit + quick fuzz
+  - `run_local_fuzz.sh`:默认 6 并发 + 固定轮次
+- 特殊测试再直调:
+  - unit:`scripts/ci/runBaseCoverage.sh`
+  - fuzz:`scripts/ci/runCoverage.sh`
+
+## 执行流程
+1. 先按 `../shared/ryanJsonCommon.md` 确认宏前提和模式边界。
+2. 从日志判定问题类型:崩溃、泄漏、断言失败、分支未命中。
+3. 先补最小确定性 unit 用例,再补 fuzz 触达样本。
+4. 回归时先走本地入口,必要时用 `scripts/ci/*` 细调参数。
+5. 输出证据:分支触达、泄漏状态、崩溃状态、覆盖率路径。
+
+## 测试专项约束
+- 不新建零散文件,优先补到现有分类。
+- unit/fuzz 必须隔离,禁止把两种模式写成同一条混合建议。
+- `Add/Insert` 与 `Replace` 的失败所有权必须分开断言。
+- 覆盖率目录为固定路径且每次会清理旧结果,不假设保留历史目录。
+- QEMU 链路不再有 basic scope;默认与 localbase 用例集一致(RFC8259 仍留 Linux 快速链路)。
+- RFC8259 文件集通过目录扫描(`readdir`)获取,不依赖 `rfc8259_filelist.inc`。
+
+## 输出格式
+1. 问题与目标:失败现象 + 目标分支。
+2. 改动清单:文件、用例函数、场景意图。
+3. 验证证据:执行模式、命中分支、泄漏/崩溃结果、覆盖率产物。
+4. 风险与下一步:未覆盖边界与后续补测建议。
+
+## 参考导航
+- 架构检查点:`references/coreArchitectureCheckpoints.md`
+- 断言策略:`references/assertPolicy.md`
+- 术语:`references/terminology.md`
+- Gemini 对齐:`references/geminiCompat.md`
+- 本地压缩文档:`testArchitecture.md`、`context.md`、`sop.md`

+ 35 - 0
skills/ryanjson-test-engineering/agents/gemini.md

@@ -0,0 +1,35 @@
+# Gemini Skill Card
+
+名称:`ryanjson-test-engineering`
+
+## 定位
+- 面向 RyanJson 单测与模糊测试的覆盖率和稳定性工程技能。
+
+## 适用场景
+- 扩展单元测试与模糊测试的边界覆盖。
+- 修复泄漏、崩溃、断言误用与失败路径缺失。
+- 覆盖率报告出现关键分支未触达。
+
+## 输入建议
+- 失败证据:报错日志、未覆盖分支、崩溃样本。
+- 约束条件:是否禁止新建文件、宏配置、内存限制。
+- 验收标准:目标分支、泄漏标准、回归命令。
+
+## 硬约束
+- 默认不新建测试文件,优先补到现有分类。
+- 断言语义必须区分 Add/Insert 与 Replace 的失败所有权规则。
+- 不用业务 assert 代替可恢复错误断言。
+- 语义不明确时按 `example/ -> test/unityTest/ -> test/fuzzer/` 取证。
+
+## 术语口径
+- 统一按 `../references/terminology.md`。
+- 输出必须显式区分:已验证/推断、可恢复错误/不可恢复错误、失败语义。
+
+## 默认提示词
+使用 `$ryanjson-test-engineering`,在现有测试分类中扩展 RyanJson 单测与 fuzzer:优先覆盖失败路径和边界分支,明确所有权断言,并基于 `example -> unityTest -> fuzzer` 取证提供可复现回归证据。
+
+## 输出骨架
+1. 失败根因与目标分支(标注已验证/推断)。
+2. 测试改动清单(文件 + 函数 + 场景)。
+3. 覆盖与稳定性证据(分支触达/泄漏/崩溃)。
+4. 回归边界与下一步建议。

+ 4 - 0
skills/ryanjson-test-engineering/agents/openai.yaml

@@ -0,0 +1,4 @@
+interface:
+  display_name: "RyanJson 测试工程"
+  short_description: "基于现有分类的 unit/fuzz 覆盖补强与所有权断言"
+  default_prompt: "使用 $ryanjson-test-engineering 在现有分类中扩展 RyanJson 单测与模糊测试:优先补失败路径、边界分支和所有权断言(区分 Add/Insert 与 Replace 失败语义);语义不清时按 example -> unityTest -> fuzzer 取证并给出回归证据。"

+ 32 - 0
skills/ryanjson-test-engineering/context.md

@@ -0,0 +1,32 @@
+# RyanJson 测试语境(压缩版)
+
+## 1. 作用
+- 统一测试侧断言边界与宏敏感规则,减少假阳性。
+
+## 2. 测试前必查
+1. 当前宏值:
+   - `RyanJsonStrictObjectKeyCheck`
+   - `RyanJsonDefaultAddAtHead`
+2. 当前模式:unit 或 fuzz。
+3. 执行入口与覆盖目录口径:见 `../shared/ryanJsonCommon.md`。
+
+## 3. 断言边界
+- 业务/输入错误:断言可恢复结果(false/NULL)。
+- 内存破坏/不变量损坏:可视为致命行为;启用 `RyanJsonEnableAssert` 时可进入 assert 路径。
+
+## 4. 高风险覆盖区
+- Parse 失败回滚。
+- Replace 失败所有权清理。
+- AddAtHead 相关索引断言。
+- 字符串边界切换。
+
+## 5. 质量检查单
+1. true/false 分支是否双向覆盖。
+2. 失败分支是否断言了所有权。
+3. 测试说明是否标明模式前提。
+
+## 6. 依据(仓库内)
+- `xmake.lua`:`RyanJson`(unit)与 `RyanJsonFuzz`(fuzz)目标分离
+- `test/unityTest/runner/main.c`:`#ifndef isEnableFuzzer` 的 unit `main` 路径
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`:失败分支所有权断言
+- `../shared/ryanJsonCommon.md`:统一执行入口与覆盖口径

+ 25 - 0
skills/ryanjson-test-engineering/references/assertPolicy.md

@@ -0,0 +1,25 @@
+# Assert 使用策略
+
+## 范围
+- 仅用于不可恢复的内部不变量破坏。
+- 不用于用户可构造输入导致的常规错误。
+- 术语口径见 `../../shared/terminology.md`。
+
+## 判定规则
+- 输入错误可恢复:返回 false/NULL,并保证资源回收。
+- 内存破坏或结构破坏不可恢复:允许 assert(启用 `RyanJsonEnableAssert` 时)。
+- Compare 内部对象链/Key 不变量属于结构破坏范畴,可按 assert 路径处理,不要求业务输入可达覆盖。
+
+## 测试策略
+- 单元测试验证“可恢复错误”返回路径。
+- fuzzer 避免把业务预期写成 assert。
+- 对 assert 路径,重点确认是否误分类。
+
+## 常见误用
+- 用 assert 拦截重复 key、非法数字、非法字符串输入。
+- 以 assert 代替失败路径释放逻辑。
+
+## 依据(仓库内)
+- `RyanJson/RyanJson.h`:断言宏与公开语义注释
+- `RyanJson/RyanJsonItem.c`:失败路径与可恢复错误返回
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`

+ 62 - 0
skills/ryanjson-test-engineering/references/coreArchitectureCheckpoints.md

@@ -0,0 +1,62 @@
+# 核心架构测试检查点
+
+## 范围
+- 把 RyanJson 核心结构映射为“必须覆盖的测试断言点”。
+- 本页强调“测什么”,执行与覆盖入口见 `../../shared/ryanJsonCommon.md`。
+
+## 1. `RyanJson_t` / payload 布局相关
+检查点:
+- 类型读取是否与 flag 一致(Null/Bool/Number/String/Array/Object)。
+- number 的 int/double 分支都要有断言。
+- object/array 的 children 指针读取应只在容器类型下进行。
+
+建议:
+- 使用前必须使用 `RyanJsonIsXXX` 判断类型。
+
+## 2. 字符串 inline/ptr 模式切换
+检查点:
+- 短字符串走 inline,长字符串走 ptr。
+- ChangeString 后模式切换时不泄漏旧堆块。
+- key/value 边界长度(刚好临界值)必须有测试。
+
+建议:
+- 单测做临界值;fuzzer 做随机长度扰动。
+
+## 3. 线索化链结构(IsLast + next)
+检查点:
+- 尾节点 `IsLast=1` 时 `RyanJsonGetNext` 返回 NULL。
+- 内部操作后链是否保持可遍历(Insert/Replace/Detach/Delete 组合)。
+- 错误使用非游离节点插入时能否被防御性拒绝。
+
+建议:
+- 多层对象+数组混合结构下验证 Delete 无崩溃无泄漏。
+
+## 4. 所有权与释放语义
+检查点:
+- Create 后未挂树节点是否可安全释放。
+- Add/Insert/Replace 成功后所有权转移是否成立。
+- Detach 返回节点是否需要调用者释放。
+- Add/Insert 失败时:游离 item 由库侧清理,非游离 item 不释放。
+- Replace 失败时:item 仍由调用者持有(可复用/可释放)。
+
+建议:
+- 每个接口至少 1 个“失败后内存计数归零”用例。
+
+## 5. hooks 前置条件
+检查点:
+- hooks 初始化前调用核心 API 的行为(当前实现无默认 hooks,测试侧应视为禁止路径)。
+- hooks 初始化失败是否被正确处理。
+- OOM 注入下失败路径是否可恢复且无泄漏。
+
+## 6. 语义不明确时的取证顺序
+1. `example/`
+2. `test/unityTest/`
+3. `test/fuzzer/`
+
+若仍不明确,回到头文件注释与核心实现,避免靠经验推断。
+
+## 依据(仓库内)
+- `RyanJson/RyanJson.h`:类型判断公开接口 `RyanJsonIsNull/Bool/Number/...`
+- `RyanJson/RyanJson.c`:hooks 全局指针初始化与 `RyanJsonInitHooks`
+- `test/unityTest/common/testCommon.c`:测试入口 hooks 初始化
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`:所有权失败语义断言

+ 41 - 0
skills/ryanjson-test-engineering/references/coverageTriage.md

@@ -0,0 +1,41 @@
+# 覆盖率与回归分诊
+
+## 范围
+- 本页仅覆盖“覆盖率诊断方法”。
+- 用例写法与证据结构见 `testcaseTemplate.md`。
+- 执行入口、模式隔离、覆盖目录口径见 `../../shared/ryanJsonCommon.md`。
+
+## 分诊常用命令
+- unit 覆盖(全矩阵):`UNIT_MODE=full UNIT_SKIP_COV=0 bash scripts/ci/runBaseCoverage.sh`
+- fuzz 覆盖(夜间预算):`FUZZ_MODE=nightly FUZZ_SKIP_COV=0 bash scripts/ci/runCoverage.sh`
+
+## 解读原则
+- 行覆盖高不代表质量高;优先看关键分支的 true/false 是否都触发。
+- 对 `RyanJsonCheckReturnFalse` 这类宏,重点看失败分支是否有证据。
+- 覆盖率不能替代语义断言与泄漏检查。
+
+## 定位流程
+1. 锁定未覆盖行对应函数和前置条件。
+2. 判断是否被更早分支拦截(前置校验/类型判断/宏条件)。
+3. 先写确定性单测触达,再用 fuzz 扩展组合路径。
+4. 复跑覆盖率,验证 true/false 双向都触发。
+
+## 典型问题定位
+- 分支只走 false:通常是输入构造不到位,或前置判断提前拦截。
+- 分支只走 true:可能是防御过强或路径被随机策略短路。
+- 覆盖下降但测试通过:检查是否引入不可达代码或常真/常假条件。
+
+## 回归闭环
+1. 从失败日志还原最小输入。
+2. 先加确定性单测,再加 fuzzer 变体触发。
+3. 复跑覆盖率,确认目标分支双向触发。
+4. 固化 crash 样本到 corpus,防止回归。
+
+## 定位技巧(当前脚本行为)
+- unit 报告是多组 `profraw` 合并后的总览。
+- fuzz 会先在终端彩色打印 `llvm-cov report`,再写入文本报告。
+- 如果只做快速真假分支定位,可先 `UNIT_MODE=quick` 或 `FUZZ_MODE=quick` 缩短反馈周期,再切 full/nightly 做最终证据。
+
+## 不应强求覆盖的路径
+- 仅在内存破坏下可达的 assert 路径。
+- 平台相关、编译选项关闭的不可达分支。

+ 60 - 0
skills/ryanjson-test-engineering/references/fuzzerPlaybook.md

@@ -0,0 +1,60 @@
+# Fuzzer 测试补充策略
+
+## 范围
+- 本页只保留 Fuzzer 特有策略(输入扰动、种子、崩溃处理)。
+- 通用交付结构见 `testcaseTemplate.md`。
+- 单元测试差异规则见 `unityPlaybook.md`。
+- 执行入口、覆盖目录、脚本默认值口径见 `../../shared/ryanJsonCommon.md`。
+
+## 前置初始化(必须)
+- 进入 `LLVMFuzzerTestOneInput` 后,先确保 `RyanJsonInitHooks` 已初始化。
+- 所有随机创建/替换/打印路径都要走 hooks 分配器。
+- 需要故障注入时,通过 hooks 驱动,不直接改核心 API 逻辑。
+- fuzz 构建使用专用 target:`xmake -b RyanJsonFuzz`(该目标内置 `isEnableFuzzer` + `-fsanitize=fuzzer`)。
+- 覆盖链路统一使用 `scripts/ci/runCoverage.sh`(fuzzer 执行 + `llvm-profdata` + `llvm-cov`)。
+- 本地常规优先 `./run_local_fuzz.sh`;只有特殊测试(自定义参数)才直调 `scripts/ci/runCoverage.sh`。
+
+## 脚本参数约定(`scripts/ci/runCoverage.sh`)
+- 模式预算:`quick/nightly/full` 分别对应短/中/长预算(具体默认值以 `scripts/ci/runCoverage.sh` 为准)。
+- 次数优先级:
+  - 设置 `FUZZ_RUNS` 时按固定轮次执行(覆盖 `FUZZ_MAX_TOTAL_TIME`)
+  - 未设置 `FUZZ_RUNS` 时按 `FUZZ_MAX_TOTAL_TIME`(或模式默认值)执行
+- 关键透传参数:
+  - `FUZZ_TIMEOUT`、`FUZZ_MAX_LEN`、`FUZZ_VERBOSITY`
+  - `FUZZ_CORPUS_DIR`、`FUZZ_DICT_PATH`
+  - `FUZZ_EXTRA_ARGS`
+- 构建策略:
+  - 默认增量:`XMAKE_FORCE_CLEAN=0`
+  - 强制清理:`XMAKE_FORCE_CLEAN=1`(仅在怀疑缓存污染时使用)
+
+## 目标
+- 让随机输入触达防御分支,不只跑通 happy path。
+- 保证 fuzzer 断言不与可配置语义冲突。
+
+## 输入策略
+- 结构扰动:对象/数组嵌套、重复 key、空 key、混合类型。
+- 数值扰动:超长整数、小数尾长、指数边界、非法符号。
+- 字符串扰动:转义截断、非法 Unicode、代理对不完整。
+- 状态扰动:parse 成功后执行 create/replace/detach/delete 组合。
+
+## 断言策略
+- 避免对“可能合法也可能非法”的输入写硬断言。
+- 对宏控制行为使用条件断言。
+- 崩溃优先级高于语义偏差;先修内存安全,再修预期一致性。
+- 所有权断言要区分:Add/Insert 失败(游离 item 由库侧清理)与 Replace 失败(item 不消费)。
+
+## 覆盖策略
+- 为目标分支准备定向种子(不仅依赖随机变异)。
+- 将历史 crash 文件固化到 corpus。
+- 对新增防御分支记录触达证据(日志或覆盖率)。
+
+## 与单元测试分工
+- 单元测试负责精确语义与回归基准。
+- fuzzer 负责发现未知组合路径与内存错误。
+
+## 依据(仓库内)
+- `test/fuzzer/entry.c`:`LLVMFuzzerTestOneInput` 中 hooks 初始化与断言
+- `xmake.lua`:`target("RyanJsonFuzz")` 的 fuzz 宏与 `-fsanitize=fuzzer` 配置
+- `scripts/ci/runCoverage.sh`:fuzz 覆盖执行链与参数默认值
+- `test/fuzzer/cases/fuzzerCreate.c`:Add/Insert 失败与游离态分支
+- `test/fuzzer/cases/fuzzerReplace.c`:Replace 失败不消费 `item`

+ 26 - 0
skills/ryanjson-test-engineering/references/geminiCompat.md

@@ -0,0 +1,26 @@
+# Gemini 兼容说明(测试类任务)
+
+## 范围
+- 本页只定义 Gemini 在测试任务的输入/输出结构。
+- 测试执行口径以 `../../shared/ryanJsonCommon.md` 为准。
+
+## 推荐请求格式
+- 任务:扩展哪一类测试(core/equality/performance/utils/fuzzer)。
+- 约束:不新建文件、是否允许额外内存、当前宏配置。
+- 验收:期望覆盖的行/分支、泄漏标准、回归命令。
+
+## 推荐输出格式
+1. 失败根因(1-2 行)。
+2. 测试改动清单(文件 + 函数 + 目标分支)。
+3. 覆盖到的分支与风险。
+4. 剩余未覆盖项与下一步。
+
+字段组织可直接复用 `testcaseTemplate.md`。
+
+## 对齐策略
+- 与 Codex 保持同一断言语义与文件归档规则。
+- 明确区分“单测断言失败”和“fuzzer 崩溃”。
+- 对宏开关语义必须显式说明。
+
+## 术语字典(统一)
+- 本文术语统一以 `terminology.md` 为准。

+ 44 - 0
skills/ryanjson-test-engineering/references/regressionMatrix.md

@@ -0,0 +1,44 @@
+# 回归矩阵(测试工程)
+
+## 范围
+- 本页只定义回归维度与门禁。
+- 问题描述、改动清单、证据输出见 `testcaseTemplate.md`。
+- 执行口径与脚本参数基线见 `../../shared/ryanJsonCommon.md`。
+
+## 语义回归
+- Parse 成功/失败语义。
+- Add/Insert/Replace/Detach/Delete 所有权语义。
+- Compare 有序/乱序语义。
+- Parse 默认非严格尾部语义(`RyanJsonParse`)与 strict 选项语义(`RyanJsonParseOptions`)。
+- Minify 终止符语义:`ret < textLen` 写 `\0`,`ret == textLen` 不写 `\0`。
+- Replace 失败语义:调用方负责复用或释放 `item`(库不消费)。
+
+## 稳定性回归
+- ASan 崩溃是否消失。
+- 泄漏检测是否归零。
+- 历史 crash 样本是否稳定通过。
+
+## 覆盖率回归
+- 目标分支 true/false 是否都有触达。
+- 新增防御分支是否有用例覆盖。
+- `RyanJsonInternalCalcLenBytes` 边界:255/256、65535/65536。
+
+## 配置回归
+- 严格/非严格宏下断言是否一致。
+- hooks 初始化路径在各测试入口是否生效。
+
+## 执行策略
+- 本地常规:优先 `run_local_*`。
+- 特殊回归:按需直调 `scripts/ci/runBaseCoverage.sh` / `scripts/ci/runCoverage.sh`。
+
+## 推荐验收门禁
+1. 改动模块相关测试全部通过。
+2. 全量 unity 通过且无泄漏。
+3. fuzzer 冒烟通过并包含历史 crash 样本。
+4. 覆盖审阅时保留 unit/fuzz 报告路径(见 shared 约定)。
+
+## 依据(仓库内)
+- `RyanJson/RyanJsonParse.c`:`RyanJsonParse` 默认 `requireNullTerminator=RyanJsonFalse`
+- `RyanJson/RyanJson.c`:`RyanJsonMinify` 终止符写入条件
+- `RyanJson/RyanJsonItem.c`:Insert/Replace 失败语义
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`、`test/unityTest/cases/utils/testUtils.c`

+ 4 - 0
skills/ryanjson-test-engineering/references/terminology.md

@@ -0,0 +1,4 @@
+# 术语字典
+
+- 统一术语定义复用共享文档:`../../shared/terminology.md`。
+- 如出现本技能专属术语,可在本文件追加扩展,不覆盖共享定义。

+ 34 - 0
skills/ryanjson-test-engineering/references/testcaseTemplate.md

@@ -0,0 +1,34 @@
+# 测试任务模板(复用)
+
+## 范围
+- 适用于 RyanJson 单元测试与模糊测试的统一交付结构。
+- 本模板负责“目标分支/改动清单/证据/回归”骨架;框架差异见 unity/fuzzer playbook。
+- 执行入口与覆盖目录口径见 `../../shared/ryanJsonCommon.md`。
+
+## 1) 问题与目标
+- 失败现象:
+- 目标分支:
+- 预期语义(可恢复错误/不可恢复错误):
+
+## 2) 测试改动清单
+- 文件:
+- 用例函数:
+- 场景意图:
+- 所有权断言(Add/Insert vs Replace):
+
+## 3) 覆盖与稳定性证据
+- 执行入口(unit/fuzz + 脚本):
+- 覆盖触达(true/false):
+- 泄漏结果:
+- 崩溃结果:
+- 覆盖率报告路径:
+
+## 4) 回归与下一步
+- 已验证边界:
+- 未验证项(推断):
+- 下一批补测建议:
+
+## 依据(仓库内)
+- `test/unityTest/cases/core/testCreate.c`:Insert/AddItem 失败断言模板
+- `test/unityTest/cases/core/testReplace.c`:Replace 失败不消费断言模板
+- `test/fuzzer/cases/fuzzerReplace.c`:fuzz 失败语义复核模板

+ 47 - 0
skills/ryanjson-test-engineering/references/unityPlaybook.md

@@ -0,0 +1,47 @@
+# Unity 测试补充策略
+
+## 范围
+- 本页只保留 Unity 特有编写规范。
+- 通用交付结构见 `testcaseTemplate.md`。
+- Fuzzer 差异规则见 `fuzzerPlaybook.md`。
+- 执行入口与覆盖目录基线见 `../../shared/ryanJsonCommon.md`。
+
+## 前置初始化(必须)
+- 在测试 runner 或每组 setup 中先调用 `RyanJsonInitHooks`。
+- hooks 初始化失败时应立即终止该组测试。
+- 内存统计、泄漏判定、OOM 注入都应通过 hooks 统一实现。
+
+## 分类归档规则
+- 核心 API 行为:`test/unityTest/cases/core/`
+- 相等性与往返:`test/unityTest/cases/equality/`
+- 工具/鲁棒性:`test/unityTest/cases/utils/`
+- 压力/资源:`test/unityTest/cases/performance/`
+
+## 编写顺序
+1. 成功路径:确认主语义。
+2. 失败路径:参数非法、插入失败、替换失败。
+3. 边界路径:空对象、空数组、极短/极长字符串、索引 0/中间/末尾。
+4. 资源路径:失败后是否自动释放,是否留下脏链。
+
+## 典型断言模板
+- 返回值:`TEST_ASSERT_TRUE/FALSE`。
+- 指针语义:`TEST_ASSERT_NULL/NOT_NULL`。
+- 结构语义:`RyanJsonCompare` 或字段级验证。
+- 泄漏语义:结合测试内存统计钩子。
+
+## 用例结构建议
+- 一个用例验证一个核心语义,避免“多目标混测”。
+- 测试名称包含场景 + 期望(例如 `testReplaceByKeyRejectDuplicateKeyStrict`)。
+- 失败路径应同时验证返回值与对象结构未损坏。
+
+## 容易漏掉的点
+- “刚好够用”边界(长度 == 容量)。
+- 错误路径下 item 所有权转移(Add/Insert 与 Replace 失败语义不同)。
+- 宏开关导致的双语义(严格/非严格)。
+- Detach 后节点再用时的游离状态。
+
+## 依据(仓库内)
+- `test/unityTest/runner/main.c`、`test/unityTest/common/testCommon.c`:runner/setup hooks 初始化
+- `test/unityTest/cases/core/testCreate.c`:Insert/AddItem 失败与游离态断言
+- `test/unityTest/cases/core/testReplace.c`:Replace 失败不消费语义
+- `test/unityTest/cases/utils/testPrint.c`:preallocated 刚好够用/不足边界

+ 31 - 0
skills/ryanjson-test-engineering/sop.md

@@ -0,0 +1,31 @@
+# RyanJson 测试工作流(压缩版)
+
+## 1. 入口与模式
+1. 先确认模式(unit/fuzz)和宏前提。
+2. 本地常规回归优先 `run_local_*`。
+3. 特殊测试(矩阵/并发/轮次/覆盖)再直调 `scripts/ci/*`。
+
+## 2. 补测循环
+1. 锁定未覆盖分支或崩溃信号。
+2. 找到阻断分支可达性的 guard。
+3. 先加最小确定性 unit 用例。
+4. 需要时补 fuzz 样本和触达路径。
+5. 复跑并确认双向分支触达。
+
+## 3. 泄漏/崩溃分诊
+1. 按所有权、边界、结构不变量分类。
+2. 关联到 API 家族(Create/Add/Replace/Detach/Change)。
+3. 在失败路径补释放断言。
+
+## 4. 交付格式
+1. 根因摘要。
+2. 修改的测试文件与函数。
+3. 证据(模式、分支、泄漏/崩溃状态、覆盖率报告路径)。
+4. 未覆盖边界与下一步补测建议。
+
+## 5. 依据(仓库内)
+- `xmake.lua`:`RyanJson` / `RyanJsonFuzz` 模式 target
+- `test/unityTest/runner/main.c`:unit 模式入口隔离
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`:失败路径断言样例
+- `scripts/ci/runBaseCoverage.sh`:unit 矩阵与合并覆盖率(`coverage/unitMatrix`)
+- `scripts/ci/runCoverage.sh`:fuzz 覆盖率复核链路(`coverage/fuzz`)

+ 43 - 0
skills/ryanjson-test-engineering/testArchitecture.md

@@ -0,0 +1,43 @@
+# RyanJson 测试架构(压缩版)
+
+## 1. 分层
+### 1.1 Unity 层(`test/unityTest/`)
+- 验证可确定契约行为。
+- 使用显式断言和分支目标用例。
+
+### 1.2 Fuzzer 层(`test/fuzzer/`)
+- 验证随机/异常输入下的稳健性。
+- 核心目标是无崩溃/无泄漏/无 UB,而非固定值断言。
+
+## 2. 放置规则
+- 优先扩展现有分类文件。
+- 非必要不新增零散测试文件。
+- 分类保持稳定:core/equality/utils/performance/fuzzer。
+
+## 2.1 执行层(脚本职责)
+- 脚本入口、模式参数、覆盖率目录口径统一见 `../shared/ryanJsonCommon.md`。
+- 本页仅强调职责差异:unit 负责确定性语义,fuzz 负责随机路径稳健性。
+
+## 3. 新增测试流程
+1. 选定目标分类文件。
+2. 添加最小可复现用例。
+3. 必要时注册 runner 入口。
+4. 在正确模式下验证。
+5. 复核覆盖率与所有权断言。
+
+## 4. Fuzzer 流程
+1. 增加可复现崩溃种子/corpus。
+2. 扩展对应 fuzz case 路径。
+3. 复跑确认历史崩溃不再复现。
+4. 若有分支目标,再同步覆盖率复核。
+
+## 5. 模式隔离规则
+- unit 与 fuzz 的入口和假设不同。
+- 禁止把两种模式写成同一条混合执行建议。
+- 推荐构建命令:unit 用 `xmake -b RyanJson`,fuzz 用 `xmake -b RyanJsonFuzz`。
+
+## 6. 依据(仓库内)
+- `test/unityTest/runner/main.c`:`#ifndef isEnableFuzzer` 的 unit 入口
+- `test/fuzzer/entry.c`:`LLVMFuzzerTestOneInput` 入口
+- `xmake.lua`:`RyanJson` / `RyanJsonFuzz` 目标配置
+- `../shared/ryanJsonCommon.md`:统一执行入口与覆盖口径

+ 157 - 0
skills/shared/ryanJsonCommon.md

@@ -0,0 +1,157 @@
+# RyanJson 共性规则
+
+## 1. 范围
+- 本文件是以下三个技能的共性约束基线:
+  - `skills/ryanjson-api-usage`
+  - `skills/ryanjson-optimization`
+  - `skills/ryanjson-test-engineering`
+- 各技能的领域细节仍以各自 `references/` 文档为准。
+- 统一术语字典:`terminology.md`。
+
+## 2. 仓库事实(必须遵守)
+- 主机侧主入口是 `xmake`。
+- 当前仓库通过 target 区分模式:
+  - `RyanJson`:默认 unit 目标(不注入 libFuzzer main)
+  - `RyanJsonFuzz`:专用 fuzz 目标(启 `isEnableFuzzer` + `-fsanitize=fuzzer`)
+  - `RyanJsonQemu` / `RyanJsonQemuCm3`:QEMU 目标(FreeRTOS Cortex-M,含非对齐访问 fault 校验)
+- QEMU 非对齐基线(默认约束):
+  - 运行时开启 `SCB->CCR.UNALIGN_TRP=1`,以硬件语义捕获非对齐访问。
+  - 不在 QEMU 目标上启用 `-mno-unaligned-access`,避免编译器辅助对齐掩盖真实语义。
+  - `YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS=1` 作为第三方库局部防御开关保留,除非用户明确要求调整。
+- 覆盖脚本分工:
+  - `scripts/ci/runBaseCoverage.sh`:单元测试矩阵(`quick=2` / `nightly=4` / `full=8`)
+  - `scripts/ci/runCoverage.sh`:fuzzer 执行与覆盖率生成
+- 本地便捷入口在仓库根目录:
+  - `run_local_base.sh`:本地一键 unit 矩阵
+  - `run_local_qemu.sh`:本地一键 QEMU 矩阵(默认 full,覆盖 localbase 用例并校验对齐异常)
+  - `run_local_ci.sh`:本地模拟 `ci-pr`(unit + quick fuzz)
+  - `run_local_fuzz.sh`:本地固定 6 并发 fuzz
+  - 默认值摘要:
+    - `run_local_base.sh`:`UNIT_MODE=full`、`UNIT_SKIP_COV=1`
+    - `run_local_qemu.sh`:`QEMU_MODE=full`、`QEMU_STOP_ON_FAIL=1`
+    - `run_local_ci.sh`:full unit + quick fuzz(`FUZZ_SKIP_COV=1`)
+    - `run_local_fuzz.sh`:`FUZZ_RUNS=10000`、`FUZZ_WORKERS/JOBS=6`
+  - `run_local_qemu.sh` 默认保留 ANSI 颜色输出;仅在用户明确要求“去色/净化日志”时再剥离控制符。
+- 覆盖率目录固定且每次执行前清理(仅保留最新结果):
+  - unit:`coverage/unitMatrix`(`report.txt` + `html/`)
+  - fuzz:`coverage/fuzz`(`report.txt` + `html/`)
+- `Makefile` 为历史辅助,不是当前主流程。
+- `SConscript` 主要用于 RT-Thread 软件包集成。
+
+### 2.1 依据(仓库内)
+- `xmake.lua`:`target("RyanJson")` 与 `target("RyanJsonFuzz")` 的模式分离
+- `xmake.lua`:`RyanJsonFuzz` 中 `add_defines("isEnableFuzzer")` 与 `-fsanitize=fuzzer`
+- `xmake.lua`:`RyanJson` 为链接兼容补入 `test/fuzzer/utils/fuzzerDriver.c`、`fuzzerMemory.c`(不启用 fuzz sanitizer)
+- `test/unityTest/runner/main.c`:`#ifndef isEnableFuzzer` 包裹 Unity `main`
+- `scripts/ci/runBaseCoverage.sh`:unit 矩阵执行与合并覆盖率
+- `scripts/ci/runCoverage.sh`:构建/执行 `RyanJsonFuzz` + `llvm-cov`
+- `run_local_base.sh`、`run_local_qemu.sh`、`run_local_ci.sh`、`run_local_fuzz.sh`:本地入口封装
+- `test/qemu/platform/qemuStartup.c`:`SCB->CCR.UNALIGN_TRP=1` 的运行时设置与启动日志
+- `xmake.lua`:QEMU target 保留 `YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS=1`,不启用 `-mno-unaligned-access`
+- `run_local_qemu.sh`:QEMU 流清洗默认保留 ANSI `ESC`(颜色输出)
+
+## 3. 语义取证链(不确定时必须执行)
+1. `example/`
+2. `test/unityTest/`
+3. `test/fuzzer/`
+- 取证不完整时,结论必须标注为“推断”。
+
+## 3.1 证据优先(回答必须可追溯)
+- 涉及语义细节时(如 `Print/Minify`、`Add/Insert/Replace` 失败所有权、宏分支行为),优先依据当前源码与单元测试。
+- 默认证据顺序:
+  1. 头文件与实现(`RyanJson/*.h`、`RyanJson/*.c`)
+  2. 单元测试(`test/unityTest/`)
+  3. 模糊测试(`test/fuzzer/`)
+- 输出建议中应给出至少一个可追溯依据路径;没有依据时必须明确写“推断”。
+
+## 4. 宏基线检查(回答前必须做)
+- 必查当前源码中的:
+  - `RyanJsonStrictObjectKeyCheck`
+  - `RyanJsonDefaultAddAtHead`
+- 当前仓库默认值(仅作初始参考,最终以当前源码为准):
+  - `RyanJsonStrictObjectKeyCheck=false`
+  - `RyanJsonDefaultAddAtHead=false`
+
+## 5. 所有权共识(统一口径)
+- `Create*` 成功:节点归调用方,直到挂载或显式释放。
+- `Detach*` 成功:返回节点归调用方。
+- `Add/Insert` 与 `Replace` 的失败语义不得混用。
+- 当前实现中:
+  - `Add/Insert` 失败时,游离 `item` 走库侧清理;非游离 `item` 返回失败但不释放(保护原树)。
+  - `Replace` 失败不消费新节点,调用方需复用或释放。
+- 对边界语义有疑问时,必须回查当前头文件与测试。
+
+### 5.1 依据(仓库内)
+- `RyanJson/RyanJsonItem.c`:`RyanJsonInsert` 失败语义(游离 `item` 走 `error__` 删除,非游离 `item` 早返回不删除)
+- `RyanJson/RyanJsonItem.c`:`RyanJsonReplaceByKey/ByIndex` 失败路径返回 false,不删除新 `item`
+- `test/unityTest/cases/core/testCreate.c`:已挂树节点被拒绝插入
+- `test/unityTest/cases/core/testReplace.c`:Replace 失败后 `item` 仍可复用并由调用方释放
+
+## 6. 测试模式契约
+- unit:走 Unity `main` 入口,不允许 fuzz sanitizer 注入 `main`。
+- qemu:走 `RyanJsonQemu*` 目标,校验 FreeRTOS 调度与非对齐访问 fault 语义。
+- fuzz:按 fuzz 宏/编译参数构建,使用 `scripts/ci/runCoverage.sh` 进行覆盖链路。
+- 禁止在同一条执行建议中混写 unit/qemu/fuzz 的前提。
+- 推荐命令:
+  - unit:`xmake -b RyanJson`
+  - qemu:`xmake -b RyanJsonQemu`
+  - fuzz:`xmake -b RyanJsonFuzz`
+
+### 6.1 脚本调用建议(优先)
+- 本地优先:直接运行根目录入口
+  - `./run_local_base.sh`
+  - `./run_local_qemu.sh`
+  - `./run_local_ci.sh`
+  - `./run_local_fuzz.sh`
+- CI/细粒度调参时再直调 `scripts/ci/*`
+  - unit 快检:`UNIT_MODE=quick UNIT_SKIP_COV=1 bash scripts/ci/runBaseCoverage.sh`
+  - unit 全矩阵:`UNIT_MODE=full bash scripts/ci/runBaseCoverage.sh`
+  - fuzz 快检:`FUZZ_MODE=quick FUZZ_SKIP_COV=1 bash scripts/ci/runCoverage.sh`
+  - fuzz 覆盖:`FUZZ_MODE=nightly FUZZ_SKIP_COV=0 bash scripts/ci/runCoverage.sh`
+- `XMAKE_FORCE_CLEAN=1` 仅在怀疑配置缓存污染时启用;默认增量模式更快。
+- `scripts/ci/runCoverage.sh` 会先把 `llvm-cov report --use-color` 输出到终端,再写入 `coverage/fuzz/report.txt`。
+- RFC8259 用例列表通过目录扫描(`readdir`)获取,不再维护 `rfc8259_filelist.inc`。
+
+## 7. 输出契约
+- 输出必须显式区分:
+  - 已验证事实
+  - 推断结论
+  - 可恢复错误/不可恢复错误
+- 代码建议必须包含:
+  - 前置条件
+  - 成功路径
+  - 失败路径
+  - 所有权与释放责任
+
+## 8. 代码与审查基线
+- 类型统一 `stdint`,命名统一小驼峰。
+- 示例中避免魔法数字,优先使用命名常量。
+- 三目运算符(`?:`)可用于“无副作用、单行、明显更清晰”的场景;复杂分支统一使用 `if/else`。
+- 禁止把未验证内部行为当作确定事实输出。
+- API 使用类问题默认限制在公开接口,除非用户明确要求内部实现。
+
+## 9. 注释规范(Doxygen)
+- 配置宏、公开接口、关键内部函数的新增/改动注释,统一使用 Doxygen 风格。
+- 首行必须使用 `@brief` 描述“这是什么 + 做什么”。
+- 存在边界或默认行为时,补 `@note`;涉及原理或公式时,补 `@details`。
+- 禁止使用序号式注释前缀:如 `1.`、`2)`、`3.1`、`A.`、`Stage 1:`;改用语义化短句标题。
+- 参数/返回值注释按需使用:
+  - 函数参数:`@param`
+  - 返回值:`@return`
+- 允许短注释使用 `//`;关键配置、公开接口、复杂逻辑优先使用 Doxygen 块注释。
+- Doxygen 注释至少三行,不使用单行 `/** @brief ... */` 形式。
+- 注释语言可中文优先,但“类型名 / 字段语义名 / API 名”保持英文原样,不做中文化。
+- 强制保留英文示例:
+  - 类型名:`Array`、`Object`、`Null`、`Bool`、`Int`、`Double`
+  - 字段语义名:`strValue`、`intValue`、`doubleValue`、`boolValue`、`objValue`
+  - API/宏名:`Add/Insert/Replace/Detach/Delete`、`RyanJsonAddPosition`、`RyanJsonInlineStringSize`
+- 若需中文解释,采用“英文术语 + 中文说明”形式,例如:`strValue(字符串载荷)`。
+- 推荐模板:
+
+```c
+/**
+ * @brief 说明对象与作用。
+ * @note 说明默认行为或限制条件。
+ * @details 说明推导过程、公式或实现约束(可选)。
+ */
+```

+ 15 - 0
skills/shared/terminology.md

@@ -0,0 +1,15 @@
+# RyanJson 术语字典(共享)
+
+- **已验证**:有直接证据支撑(代码、测试、日志、覆盖率、回归结果)。
+- **推断**:暂无直接证据,仅基于已有信息推理,输出时必须显式标注。
+- **未验证**:尚未执行验证;通常等同“推断”或“待验证项”。
+- **可恢复错误**:应返回 `false/NULL` 且完成回滚/释放,不应用 assert 终止流程。
+- **不可恢复错误**:内部不变量或结构被破坏;启用 `RyanJsonEnableAssert` 时可触发 assert。
+- **失败语义**:失败时的所有权与资源处理规则;`Add/Insert` 与 `Replace` 必须分开描述。
+- **语义取证链**:`example/ -> test/unityTest/ -> test/fuzzer/`。
+- **术语保留英文**:注释可用中文,但类型名/字段语义名/API 名保持英文(如 `Array`、`Object`、`strValue`、`objValue`)。
+
+## 依据(仓库内)
+- `RyanJson/RyanJson.h`:公开 API 与失败语义注释
+- `RyanJson/RyanJsonItem.c`:Add/Insert/Replace 失败路径
+- `test/unityTest/cases/core/testCreate.c`、`test/unityTest/cases/core/testReplace.c`

+ 0 - 287
test/RFC8259Test/RyanJsonRFC8259JsonTest.c

@@ -1,287 +0,0 @@
-#include "RyanJsonTest.h"
-#include "valloc.h"
-
-#define PrintfStrCmpEnable
-#define TEST_FILE_PATH "./test/RFC8259JsonData"
-
-typedef RyanJsonBool_e (*jsonParseData)(char *fileName, char *data, uint32_t len);
-
-/* Read a file, parse, render back, etc. */
-static RyanJsonBool_e testFile(const char *path, jsonParseData jsonParseDataHandle)
-{
-	DIR *dir = opendir(path);
-	RyanJsonCheckReturnFalse(NULL != dir);
-
-	struct dirent *entry = NULL;
-	uint32_t count = 0;
-	uint32_t used_count = 0;
-
-	// 初始缓冲区
-	uint32_t bufferCap = 4096;
-	char *data = (char *)malloc(bufferCap);
-	if (NULL == data)
-	{
-		(void)closedir(dir);
-		return RyanJsonFalse;
-	}
-
-	while (NULL != (entry = readdir(dir))) // NOLINT(concurrency-mt-unsafe)
-	{
-		char *name = (char *)entry->d_name;
-		if (NULL == name || 0 == strlen(name)) { continue; }
-		if (0 == strcmp(name, ".") || 0 == strcmp(name, "..")) { continue; }
-
-		char fullPath[512] = {0};
-		int ret = snprintf(fullPath, sizeof(fullPath), "%s/%s", path, name);
-		if (ret < 0 || ret >= (int)sizeof(fullPath)) { continue; }
-
-		FILE *f = fopen(fullPath, "rb");
-		if (NULL == f)
-		{
-			(void)printf("打开文件失败: %s\n", fullPath);
-			continue;
-		}
-
-		if (0 != fseek(f, 0, SEEK_END))
-		{
-			(void)fclose(f);
-			continue;
-		}
-
-		long fileSize = ftell(f);
-		if (fileSize < 0)
-		{
-			(void)fclose(f);
-			continue;
-		}
-		uint32_t len = (uint32_t)fileSize;
-
-		if (0 != fseek(f, 0, SEEK_SET))
-		{
-			(void)fclose(f);
-			continue;
-		}
-
-		// 必要时自动扩容
-		if (len + 1 > bufferCap)
-		{
-			bufferCap = len + 128; // 预留一点空间
-			char *newData = (char *)realloc(data, bufferCap);
-			if (NULL == newData)
-			{
-				(void)fclose(f);
-				break;
-			}
-			data = newData;
-		}
-
-		if (len != fread(data, 1, len, f))
-		{
-			(void)fclose(f);
-			continue;
-		}
-		data[len] = '\0';
-		(void)fclose(f);
-
-		int32_t startUse = vallocGetUseByTlsf();
-		RyanJsonBool_e status = jsonParseDataHandle(name, data, len);
-		used_count++;
-
-		// 判定逻辑优化
-		if (0 == strncmp("y_", name, 2))
-		{
-			if (RyanJsonTrue == status) { count++; }
-			else
-			{
-				(void)printf("应该成功,但是失败: %s, len: %u\n", data, len);
-			}
-		}
-		else if (0 == strncmp("n_", name, 2))
-		{
-			if (RyanJsonFalse == status) { count++; }
-			else
-			{
-				(void)printf("应该失败,但是成功: %s, len: %u\n", data, len);
-			}
-		}
-		else if (0 == strncmp("i_", name, 2)) { count++; }
-
-		if (startUse != vallocGetUseByTlsf())
-		{
-			int area = 0, use = 0;
-			v_mcheck(&area, &use);
-			(void)printf("内存泄漏 %s len: %u\r\n", data, len);
-			(void)printf("|||----------->>> area = %d, size = %d\r\n", area, use);
-			displayMem();
-			break;
-		}
-	}
-
-	free(data);
-	(void)closedir(dir);
-
-	(void)printf("RFC 8259 JSON: (%u/%u)\r\n", count, used_count);
-	return RyanJsonTrue;
-}
-
-#include "RyanJsonRFC8259TestUtil.h"
-
-static void checkJsonSemanticEquality(char *data, uint32_t len, char *str, uint32_t strLen, uint32_t *errorCount)
-{
-	if (0 != strcmp(data, str))
-	{
-		if (!RyanJsonValueSemanticEqual(data, len, str, strLen))
-		{
-			(*errorCount)++;
-			(void)printf("%d 数据不完全一致 -- 原始: %s -- 序列化: %s\n", *errorCount, data, str);
-		}
-	}
-}
-
-/**
- * @brief RyanJson 测试程序
- *
- * @param fileName
- * @param data
- * @param len
- * @return int
- */
-static RyanJsonBool_e RyanJsonParseData(char *fileName, char *data, uint32_t len)
-{
-
-	if (0 == strcmp(fileName, "n_structure_100000_opening_arrays.json") || 0 == strcmp(fileName, "n_structure_open_array_object.json"))
-	{
-		return RyanJsonFalse;
-	}
-	// printf("开始解析: %s\r\n", fileName);
-	RyanJson_t json = RyanJsonParseOptions(data, len, RyanJsonTrue, NULL);
-	RyanJsonCheckReturnFalse(NULL != json);
-
-#ifdef PrintfStrCmpEnable
-	int32_t strLen = 0;
-	char *str = RyanJsonPrint(json, 60, RyanJsonFalse, &strLen);
-	if (NULL == str)
-	{
-		printf("反序列化失败: [%s]\n", data);
-		goto err;
-	}
-
-	RyanJsonMinify(data, (int32_t)len);
-	static uint32_t alksdjfCOunt = 0;
-	checkJsonSemanticEquality(data, len, str, strLen, &alksdjfCOunt);
-
-	RyanJsonFree(str);
-#endif
-
-	(void)RyanJsonDelete(json);
-	return RyanJsonTrue;
-
-err:
-	(void)RyanJsonDelete(json);
-	return RyanJsonFalse;
-}
-
-/**
- * @brief cJson测试程序
- *
- * @param fileName
- * @param data
- * @param len
- * @return int
- */
-static RyanJsonBool_e cJSONParseData(char *fileName, char *data, uint32_t len)
-{
-
-	if (0 == strcmp(fileName, "n_structure_100000_opening_arrays.json") || 0 == strcmp(fileName, "n_structure_open_array_object.json"))
-	{
-		return RyanJsonFalse;
-	}
-
-	cJSON *json = cJSON_ParseWithLengthOpts(data, len + sizeof(""), NULL, RyanJsonTrue);
-	RyanJsonCheckReturnFalse(NULL != json);
-
-#ifdef PrintfStrCmpEnable
-	char *str = cJSON_PrintBuffered(json, 60, RyanJsonFalse);
-	if (NULL == str)
-	{
-		printf("反序列化失败: [%s]\n", data);
-		goto err;
-	}
-
-	cJSON_Minify(data);
-	static uint32_t alksdjfCOunt = 0;
-	checkJsonSemanticEquality(data, len, str, strlen(str), &alksdjfCOunt);
-
-	cJSON_free(str);
-#endif
-
-	(void)cJSON_Delete(json);
-	return RyanJsonTrue;
-err:
-	(void)cJSON_Delete(json);
-	return RyanJsonFalse;
-}
-
-/**
- * @brief cJson测试程序
- *
- * @param fileName
- * @param data
- * @param len
- * @return int
- */
-static RyanJsonBool_e yyjsonParseData(char *fileName, char *data, uint32_t len)
-{
-	if (0 == strcmp(fileName, "n_structure_100000_opening_arrays.json") || 0 == strcmp(fileName, "n_structure_open_array_object.json"))
-	{
-		return RyanJsonFalse;
-	}
-
-	yyjson_doc *doc = yyjson_read(data, len, 0);
-	RyanJsonCheckReturnFalse(NULL != doc);
-
-#ifdef PrintfStrCmpEnable
-	char *str = yyjson_write(doc, 0, NULL);
-	if (NULL == str)
-	{
-		printf("反序列化失败: [%s]\n", data);
-		goto err;
-	}
-
-	cJSON_Minify(data);
-	static uint32_t alksdjfCOunt = 0;
-	checkJsonSemanticEquality(data, len, str, strlen(str), &alksdjfCOunt);
-
-	free(str);
-#endif
-
-	(void)yyjson_doc_free(doc);
-	return RyanJsonTrue;
-err:
-	(void)yyjson_doc_free(doc);
-	return RyanJsonFalse;
-}
-
-// RFC 8259 JSON Test Suite
-// https://github.com/nst/JSONTestSuite
-static RyanJsonBool_e testRFC8259RyanJson(void) { return testFile(TEST_FILE_PATH, RyanJsonParseData); }
-
-static RyanJsonBool_e testRFC8259cJSON(void) { return testFile(TEST_FILE_PATH, cJSONParseData); }
-
-static RyanJsonBool_e testRFC8259yyjson(void) { return testFile(TEST_FILE_PATH, yyjsonParseData); }
-
-RyanJsonBool_e RFC8259JsonTest(void)
-{
-	int32_t result = 0;
-	uint32_t testRunCount = 0;
-	uint64_t funcStartMs;
-
-	cJSON_Hooks hooks = {.malloc_fn = v_malloc_tlsf, .free_fn = v_free_tlsf};
-	(void)cJSON_InitHooks(&hooks);
-
-	runTestWithLogAndTimer(testRFC8259RyanJson);
-	runTestWithLogAndTimer(testRFC8259yyjson);
-	runTestWithLogAndTimer(testRFC8259cJSON);
-
-	return RyanJsonTrue;
-}

+ 0 - 26
test/RFC8259Test/RyanJsonRFC8259TestUtil.h

@@ -1,26 +0,0 @@
-#ifndef RYAN_JSON_RFC8259_TEST_UTIL_H
-#define RYAN_JSON_RFC8259_TEST_UTIL_H
-
-#include <stdint.h>
-
-/**
- * @brief 提取一元素数组的唯一元素;若不是一元素数组返回 0
- */
-int RyanJsonExtractSingleArrayElement(const char *s, uint32_t len, const char **elem, uint32_t *elemLen);
-
-/**
- * @brief 值级语义比较:字符串(去引号并 normalize)、数字(含科学计数法)、布尔、null
- */
-int RyanJsonScalarSemanticEqual(const char *a, uint32_t aLen, const char *b, uint32_t bLen);
-
-/**
- * @brief JSON 语义比较:支持单元素数组剥离后进行标量比较
- */
-int RyanJsonValueSemanticEqual(const char *a, uint32_t aLen, const char *b, uint32_t bLen);
-
-/**
- * @brief 将 JSON 字符串规范化为 UTF-8 字节序列
- */
-int RyanJsonNormalizeString(const char *in, uint32_t inLen, unsigned char **out, uint32_t *outLen);
-
-#endif // RYAN_JSON_RFC8259_TEST_UTIL_H

+ 0 - 164
test/RyanJsonTest.c

@@ -1,164 +0,0 @@
-#include "RyanJson.h"
-#include "RyanJsonTest.h"
-#include "tlsf.h"
-
-extern void printJsonDebug(RyanJson_t json);
-#define jsonLogByTest(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
-#define LV_MEM_SIZE             (1024 * 1024)
-
-static void printfTitle(char *title)
-{
-	printf("\r\n");
-	printf("\r\n");
-	printf("*****************************************************************************\r\n");
-	printf("*************************** %s **************************\r\n", title);
-	printf("*****************************************************************************\r\n");
-}
-
-uint64_t platformUptimeMs(void)
-{
-	struct timespec ts;
-	// CLOCK_MONOTONIC: 单调递增,不受系统时间修改影响,适合做耗时统计
-	clock_gettime(CLOCK_MONOTONIC, &ts);
-	return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
-}
-
-static tlsf_t tlsfHandler;
-
-static size_t total2 = LV_MEM_SIZE, used2 = 0, available = 0;
-bool tlsf_walker_callback(void *ptr, size_t size, int used, void *user)
-{
-	(void)ptr;
-	(void)user; // suppress unused warnings
-	if (1 == used) { used2 += size + tlsf_alloc_overhead(); }
-	return true;
-}
-
-void showMemoryInfo(void)
-{
-	size_t total = 0, used = 0, max_used = 0;
-	rt_memory_info22(tlsfHandler, &total, &used, &max_used);
-	jsonLogByTest("total: %zu, used: %zu, max_used: %zu, available: %zu\r\n", total, used, max_used, total - used);
-
-	used2 = 0;
-	available = 0;
-	total2 = total;
-	tlsf_walk_pool(tlsf_get_pool(tlsfHandler), tlsf_walker_callback, NULL);
-	jsonLogByTest("total2: %zu, used2: %zu, max_used2: %d, available2: %zu\r\n", total2, used2, 0, total2 - used2);
-}
-
-int32_t vallocGetUseByTlsf(void)
-{
-	size_t total = 0, used = 0, max_used = 0;
-	rt_memory_info22(tlsfHandler, &total, &used, &max_used);
-	return (int32_t)used;
-}
-
-#define RyanJsonAlign(size, align) (((size) + (align) - 1) & ~((align) - 1))
-void *v_malloc_tlsf(size_t size)
-{
-	if (size == 0) { return NULL; }
-	return tlsf_malloc(tlsfHandler, RyanJsonAlign(size + RyanJsonMallocHeaderSize - 4, RyanJsonMallocAlign));
-}
-
-void v_free_tlsf(void *block)
-{
-	if (block) { tlsf_free(tlsfHandler, block); }
-}
-
-void *v_realloc_tlsf(void *block, size_t size)
-{
-	void *newBlock = v_malloc_tlsf(size);
-	if (newBlock) { memmove(newBlock, block, size); }
-
-	v_free_tlsf(block);
-	return newBlock;
-
-	// return tlsf_realloc(tlsfHandler, block, size);
-}
-
-RyanJsonBool_e RyanJsonTestFun(void)
-{
-	char *tlsfMemBuf = v_malloc(LV_MEM_SIZE);
-	tlsfHandler = tlsf_create_with_pool((void *)tlsfMemBuf, LV_MEM_SIZE, LV_MEM_SIZE);
-	jsonLogByTest("tlsf_size: %d\r\n", tlsf_size(tlsfHandler));
-
-	RyanJsonBool_e result = RyanJsonFalse;
-	RyanJsonInitHooks(v_malloc_tlsf, v_free_tlsf, v_realloc_tlsf);
-
-	for (uint32_t i = 0; i < 1; i++)
-	{
-		char *str = NULL;
-		RyanJson_t jsonRoot, item;
-
-		// const char *jsonstr = "{\"emoji\":\"\\uD83D\\uDE00\"} ";
-		const char *jsonstr = "{\"name\":\"Mash\",\"star\":0.2,\"hits\":[2,2,1,3]}";
-		// const char *jsonstr = "{\"star\":4}";
-		// const char *jsonstr = "\"name\"";
-		// const char *jsonstr =
-		// "{\"n\":0,\"q\":1,\"e\":1,\"p\":1,\"1u\":0,\"r\":0,\"w\":0.0,\"1w\":0,\"1x\":0,\"1y\":100.0,\"23\":0,"
-		// 		      "\"29\":0,\"2h\":0,\"o\":30,\"1v\":12000,\"2c\":0}";
-
-		// extern int LLVMFuzzerTestOneInput(const char *data, int32_t size);
-		// LLVMFuzzerTestOneInput(jsonstr, strlen(jsonstr));
-
-		// 解析json数据
-		jsonRoot = RyanJsonParse(jsonstr);
-		if (jsonRoot == NULL) { printf("%s:%d 序列化失败\r\n", __FILE__, __LINE__); }
-		else
-		{
-			uint32_t len = 0;
-			str = RyanJsonPrint(jsonRoot, 10, RyanJsonFalse, &len); // 以带格式方式将数据打印出来
-			printf("strLen: %d, data: %s\r\n", len, str);
-
-			RyanJsonFree(str);
-			RyanJsonDelete(jsonRoot);
-		}
-	}
-
-	showMemoryInfo();
-
-	// example内部会替换hooks,所以需要重新设置
-	result = RyanJsonExample();
-	RyanJsonInitHooks(v_malloc_tlsf, v_free_tlsf, v_realloc_tlsf);
-	// RyanJsonInitHooks(v_malloc, v_free, v_realloc);
-	if (RyanJsonTrue != result)
-	{
-		printf("%s:%d RyanJsonExample fail\r\n", __FILE__, __LINE__);
-		return RyanJsonFalse;
-	}
-
-	result = RyanJsonBaseTest();
-	if (RyanJsonTrue != result)
-	{
-		printf("%s:%d RyanJsonBaseTest fail\r\n", __FILE__, __LINE__);
-		return RyanJsonFalse;
-	}
-
-	printfTitle("RyanJson / cJSON / yyjson RFC8259标准测试");
-	result = RFC8259JsonTest();
-	if (RyanJsonTrue != result)
-	{
-		printf("%s:%d RFC8259JsonTest fail\r\n", __FILE__, __LINE__);
-		return RyanJsonFalse;
-	}
-
-	printfTitle("RyanJson / cJSON / yyjson 内存对比程序");
-	RyanJsonMemoryFootprintTest();
-	printf("\r\nok\r\n");
-
-	showMemoryInfo();
-	v_free(tlsfMemBuf);
-	displayMem();
-
-	return RyanJsonTrue;
-}
-
-#ifndef isEnableFuzzer
-int main(void)
-{
-	RyanJsonTestFun();
-	return 0;
-}
-
-#endif // isEnableFuzzer

+ 0 - 78
test/RyanJsonTest.h

@@ -1,78 +0,0 @@
-#ifndef RyanJsonTest
-#define RyanJsonTest
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <math.h>
-#include <time.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <inttypes.h>
-#include "valloc.h"
-#include "RyanJson.h"
-#include "RyanJsonUtils.h"
-#include "cJSON.h"
-#include "yyjson.h"
-
-#define getArraySize(arr) ((int32_t)(sizeof(arr) / sizeof((arr)[0])))
-#define checkMemory                                                                                                                        \
-	do                                                                                                                                 \
-	{                                                                                                                                  \
-		int area = 0, use = 0;                                                                                                     \
-		v_mcheck(&area, &use);                                                                                                     \
-		if (area != 0 || use != 0)                                                                                                 \
-		{                                                                                                                          \
-			printf("内存泄漏\r\n");                                                                                            \
-			while (1)                                                                                                          \
-			{                                                                                                                  \
-				v_mcheck(&area, &use);                                                                                     \
-				printf("|||----------->>> area = %d, size = %d\r\n", area, use);                                           \
-				delay(3000);                                                                                               \
-			}                                                                                                                  \
-		}                                                                                                                          \
-	} while (0)
-
-// 定义枚举类型
-extern void *v_malloc_tlsf(size_t size);
-extern void v_free_tlsf(void *block);
-extern void *v_realloc_tlsf(void *block, size_t size);
-extern int32_t vallocGetUseByTlsf(void);
-
-// 定义结构体类型
-uint64_t platformUptimeMs(void);
-
-#define runTestWithLogAndTimer(fun)                                                                                                        \
-	do                                                                                                                                 \
-	{                                                                                                                                  \
-		testRunCount++;                                                                                                            \
-		/* 开始执行:绿色高亮,文件名放在 [TEST n] 后面 */                                                                         \
-		printf("\x1b[32m┌── [TEST %d | %s:%d] 开始执行: %s()\x1b[0m\r\n", testRunCount, __FILE__, __LINE__, #fun);                 \
-                                                                                                                                           \
-		funcStartMs = platformUptimeMs();                                                                                          \
-		result = fun();                                                                                                            \
-                                                                                                                                           \
-		/* 结束执行:根据结果显示绿色或红色,文件名放在 [TEST n] 后面 */                                                           \
-		printf("%s└── [TEST %" PRIu32 " | %s:%d] 结束执行: 结果 %s | 耗时: %" PRIu64 " ms\x1b[0m\r\n\r\n",                         \
-		       (result == RyanJsonTrue) ? "\x1b[32m" : "\x1b[31m", testRunCount, __FILE__, __LINE__,                               \
-		       (result == RyanJsonTrue) ? "✅" : "❌", (platformUptimeMs() - funcStartMs));                                        \
-                                                                                                                                           \
-		RyanJsonCheckCodeNoReturn(RyanJsonTrue == result, { return RyanJsonFalse; });                                              \
-	} while (0)
-
-/* extern variables-----------------------------------------------------------*/
-extern RyanJsonBool_e RyanJsonExample(void);
-extern RyanJsonBool_e RyanJsonBaseTest(void);
-extern RyanJsonBool_e RFC8259JsonTest(void);
-extern RyanJsonBool_e RyanJsonMemoryFootprintTest(void);
-extern RyanJsonBool_e RyanJsonTestFun(void);
-#ifdef __cplusplus
-}
-#endif
-
-#endif

+ 0 - 78
test/baseTest/RyanJsonBaseTest.c

@@ -1,78 +0,0 @@
-#include "RyanJsonBaseTest.h"
-
-static RyanJsonBool_e likeReferenceTest()
-{
-
-	// char *str = NULL;
-	// char jsonstr[] =
-	// "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]}";
-	// RyanJson_t json = RyanJsonParse(jsonstr);
-	// RyanJson_t item = NULL;
-
-	// // RyanJson_t adfasdf = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "item"));
-
-	// // RyanJsonAddItemToObject(json, "test", adfasdf);
-
-	// // 这里做你想做的事,这里我选择打印出来
-	// // str = RyanJsonPrint(json, 50, RyanJsonTrue, NULL);
-	// // printf("item %s \r\n", str);
-	// // RyanJsonFree(str);
-
-	// for (int i = 0; i < 1; i++)
-	// {
-	//     // 分离test对象
-	//     item = RyanJsonDetachByKey(json, "item");
-
-	//     // if (RyanJsonIsKey(item))
-	//     //     RyanJsonFree(RyanJsonGetKey(item));
-
-	//     // RyanJsonFree(item);
-	// }
-
-	// RyanJsonAddItemToObject(json, "item", item);
-
-	// str = RyanJsonPrint(json, 50, RyanJsonTrue, NULL);
-	// printf("item %s \r\n", str);
-	// RyanJsonFree(str);
-
-	// RyanJsonDelete(json);
-
-	return 0;
-}
-
-RyanJsonBool_e RyanJsonBaseTest(void)
-{
-	int32_t result = 0;
-	uint32_t testRunCount = 0;
-	uint64_t funcStartMs;
-
-	runTestWithLogAndTimer(RyanJsonBaseTestChangeJson);    // 验证 JSON 动态更新及存储模式切换逻辑
-	runTestWithLogAndTimer(RyanJsonBaseTestCompareJson);   // 验证节点及其属性的深度一致性比较逻辑
-	runTestWithLogAndTimer(RyanJsonBaseTestCreateJson);    // 验证全类型节点的构造与初始化逻辑
-	runTestWithLogAndTimer(RyanJsonBaseTestDeleteJson);    // 验证节点及其子项的递归内存回收逻辑
-	runTestWithLogAndTimer(RyanJsonBaseTestDetachJson);    // 验证节点的分离操作及其所属权转移逻辑
-	runTestWithLogAndTimer(RyanJsonBaseTestDuplicateJson); // 验证对象的深拷贝 (Deep Copy) 完整性逻辑
-	runTestWithLogAndTimer(RyanJsonBaseTestForEachJson);   // 验证数组与对象迭代器的稳定性与边界情况
-	runTestWithLogAndTimer(RyanJsonBaseTestLoadJson);      // 验证复杂 JSON 文本解析与内存映射的健壮性
-	runTestWithLogAndTimer(RyanJsonBaseTestReplaceJson);   // 验证节点就地替换与成员管理机制的有效性
-
-	// 验证节点属性一致性
-	runTestWithLogAndTimer(RyanJsonBaseTestEqualityBool);   // 验证布尔值一致性
-	runTestWithLogAndTimer(RyanJsonBaseTestEqualityDouble); // 验证浮点数一致性
-	runTestWithLogAndTimer(RyanJsonBaseTestEqualityInt);    // 验证整数一致性
-	runTestWithLogAndTimer(RyanJsonBaseTestEqualityString); // 验证字符串一致性
-
-	// result = likeReferenceTest(); // 模仿 引用类型实现 示例
-	// if (0 != result)
-	// {
-	// 	printf("%s:%d loadJsonTest fail\r\n", __FILE__, __LINE__);
-	// 	return RyanJsonFalse;
-	// }
-
-	displayMem();
-	return RyanJsonTrue;
-
-__exit:
-	displayMem();
-	return RyanJsonFalse;
-}

+ 0 - 55
test/baseTest/RyanJsonBaseTest.h

@@ -1,55 +0,0 @@
-#ifndef __RyanJsonBaseTest__
-#define __RyanJsonBaseTest__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <math.h>
-#include <time.h>
-
-#include "RyanJson.h"
-#include "RyanJsonUtils.h"
-#include "cJSON.h"
-#include "valloc.h"
-#include "RyanJsonTest.h"
-
-#undef jsonLog
-#define jsonLog(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
-
-// 定义枚举类型
-
-// 定义结构体类型
-
-/* extern variables-----------------------------------------------------------*/
-
-extern void printJsonDebug(RyanJson_t json);
-extern RyanJsonBool_e rootNodeCheckTest(RyanJson_t json);
-extern RyanJsonBool_e itemNodeCheckTest(RyanJson_t json);
-extern RyanJsonBool_e arrayNodeCheckTest(RyanJson_t json);
-extern RyanJsonBool_e arrayItemNodeCheckTest(RyanJson_t json);
-extern RyanJsonBool_e RyanJsonBaseTestCheckRoot(RyanJson_t pJson);
-
-extern RyanJsonBool_e RyanJsonBaseTestChangeJson(void);
-extern RyanJsonBool_e RyanJsonBaseTestCompareJson(void);
-extern RyanJsonBool_e RyanJsonBaseTestCreateJson(void);
-extern RyanJsonBool_e RyanJsonBaseTestDeleteJson(void);
-extern RyanJsonBool_e RyanJsonBaseTestDetachJson(void);
-extern RyanJsonBool_e RyanJsonBaseTestDuplicateJson(void);
-extern RyanJsonBool_e RyanJsonBaseTestForEachJson(void);
-extern RyanJsonBool_e RyanJsonBaseTestLoadJson(void);
-extern RyanJsonBool_e RyanJsonBaseTestReplaceJson(void);
-
-extern RyanJsonBool_e RyanJsonBaseTestEqualityBool(void);
-extern RyanJsonBool_e RyanJsonBaseTestEqualityDouble(void);
-extern RyanJsonBool_e RyanJsonBaseTestEqualityInt(void);
-extern RyanJsonBool_e RyanJsonBaseTestEqualityString(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif

+ 0 - 142
test/baseTest/RyanJsonBaseTestChangeJson.c

@@ -1,142 +0,0 @@
-#include "RyanJsonBaseTest.h"
-
-/* --------------------------------------------------------------------- */
-
-RyanJsonBool_e RyanJsonBaseTestChangeJson(void)
-{
-	char jsonstr[] =
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-		"\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},"
-		"\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89],"
-		"\"arrayString\":[\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],"
-		"\"array\":[16,16.89,\"hello\",true,false,null],"
-		"\"arrayItem\":[{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},"
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}],"
-		"\"string2222\":\"hello\",\"0\":\"1\",\"nameaaaaaaaaaaaaaaaaaaaaaaaaaaaa\":\"Mash\",\"2\":\"3\",\"name\":"
-		"\"Mashaaaaaaaaaaaaaaaaaaaaaaaa\"}";
-
-	RyanJson_t jsonRoot = RyanJsonParse(jsonstr);
-	RyanJsonCheckReturnFalse(NULL != jsonRoot);
-
-	/**
-	 * @brief 修改基本类型
-	 */
-	RyanJsonChangeIntValue(RyanJsonGetObjectToKey(jsonRoot, "inter"), 20);
-	RyanJsonCheckCode(RyanJsonIsInt(RyanJsonGetObjectToKey(jsonRoot, "inter")) &&
-				  20 == RyanJsonGetIntValue(RyanJsonGetObjectToKey(jsonRoot, "inter")),
-			  { goto err; });
-
-	RyanJsonChangeDoubleValue(RyanJsonGetObjectToKey(jsonRoot, "double"), 20.89);
-	RyanJsonCheckCode(RyanJsonIsDouble(RyanJsonGetObjectToKey(jsonRoot, "double")) &&
-				  RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(jsonRoot, "double")), 20.89),
-			  { goto err; });
-
-	// inline模式只修改key,并且不超过inline长度
-	RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "0"), "type");
-	RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "type")), "type") == 0 &&
-				  strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "type")), "1") == 0,
-			  { goto err; });
-
-	// inline模式修改key,并且超过inline长度,进入ptr模式
-	RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "type"), "type000000000000000");
-	RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "type000000000000000")), "type000000000000000") == 0 &&
-				  strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "type000000000000000")), "1") == 0,
-			  { goto err; });
-
-	// ptr模式只修改key,不超过inline长度,进入inline模式
-	RyanJsonChangeKey(RyanJsonGetObjectByKey(jsonRoot, "nameaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), "na");
-	RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "na")), "na") == 0 &&
-				  strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "na")), "Mash") == 0,
-			  { goto err; });
-
-	// inline模式只修改Value,并且不超过inline长度
-	RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "2"), "type");
-	RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "2")), "2") == 0 &&
-				  strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "2")), "type") == 0,
-			  { goto err; });
-
-	// ptr模式只修改Value,不超过inline长度,进入inline模式
-	RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "name"), "Ma");
-	RyanJsonCheckCode(strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "name")), "name") == 0 &&
-				  strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "name")), "Ma") == 0,
-			  { goto err; });
-
-	// ptr模式只修改Value,超过inline长度,进入ptr模式
-	RyanJsonChangeStringValue(RyanJsonGetObjectByKey(jsonRoot, "name"), "Mashaaaaaaaaaaaaaaaaaaaaaaaa");
-	RyanJsonCheckCode(
-		strcmp(RyanJsonGetKey(RyanJsonGetObjectToKey(jsonRoot, "name")), "name") == 0 &&
-			strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "name")), "Mashaaaaaaaaaaaaaaaaaaaaaaaa") == 0,
-		{ goto err; });
-
-	RyanJsonChangeStringValue(RyanJsonGetObjectToKey(jsonRoot, "string"), "world");
-	RyanJsonCheckCode(RyanJsonIsString(RyanJsonGetObjectToKey(jsonRoot, "string")) &&
-				  strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(jsonRoot, "string")), "world") == 0,
-			  { goto err; });
-
-	RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(jsonRoot, "boolTrue"), RyanJsonFalse);
-	RyanJsonCheckCode(RyanJsonIsBool(RyanJsonGetObjectToKey(jsonRoot, "boolTrue")) &&
-				  RyanJsonGetBoolValue(RyanJsonGetObjectToKey(jsonRoot, "boolTrue")) == RyanJsonFalse,
-			  { goto err; });
-
-	RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(jsonRoot, "boolFalse"), RyanJsonTrue);
-	RyanJsonCheckCode(RyanJsonIsBool(RyanJsonGetObjectToKey(jsonRoot, "boolFalse")) &&
-				  RyanJsonGetBoolValue(RyanJsonGetObjectToKey(jsonRoot, "boolFalse")) == RyanJsonTrue,
-			  { goto err; });
-
-	/**
-	 * @brief 修改数组元素 (arrayInt)
-	 */
-	RyanJsonChangeIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayInt"), 0), 99);
-	RyanJsonCheckCode(RyanJsonIsInt(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayInt"), 0)) &&
-				  RyanJsonGetIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayInt"), 0)) == 99,
-			  { goto err; });
-
-	/**
-	 * @brief 修改数组元素 (arrayDouble)
-	 */
-	RyanJsonChangeDoubleValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayDouble"), 1), 99.99);
-	RyanJsonCheckCode(RyanJsonIsDouble(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayDouble"), 1)) &&
-				  RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectToIndex(
-								RyanJsonGetObjectToKey(jsonRoot, "arrayDouble"), 1)),
-							99.99),
-			  { goto err; });
-
-	/**
-	 * @brief 修改数组元素 (arrayString)
-	 */
-	RyanJsonChangeStringValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayString"), 2), "changedString");
-	RyanJsonCheckCode(
-		RyanJsonIsString(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayString"), 2)) &&
-			strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayString"), 2)),
-			       "changedString") == 0,
-		{ goto err; });
-
-	/**
-	 * @brief 修改嵌套对象
-	 */
-	RyanJsonChangeStringValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(jsonRoot, "item"), "string"), "nestedWorld");
-	RyanJsonCheckCode(RyanJsonIsString(RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(jsonRoot, "item"), "string")) &&
-				  strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(jsonRoot, "item"), "string")),
-					 "nestedWorld") == 0,
-			  { goto err; });
-
-	/**
-	 * @brief 修改数组对象中的字段 (arrayItem[0].inter -> 123)
-	 */
-	RyanJsonChangeIntValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayItem"), 0), "inter"),
-			       123);
-	RyanJsonCheckCode(RyanJsonIsInt(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayItem"), 0),
-							       "inter")) &&
-				  RyanJsonGetIntValue(RyanJsonGetObjectToKey(
-					  RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(jsonRoot, "arrayItem"), 0), "inter")) == 123,
-			  { goto err; });
-
-	char *str = RyanJsonPrint(jsonRoot, 1024, RyanJsonTrue, NULL);
-	RyanJsonFree(str);
-	RyanJsonDelete(jsonRoot);
-	return RyanJsonTrue;
-
-err:
-	RyanJsonDelete(jsonRoot);
-	return RyanJsonFalse;
-}

+ 0 - 183
test/baseTest/RyanJsonBaseTestCompareJson.c

@@ -1,183 +0,0 @@
-#include "RyanJsonBaseTest.h"
-/* --------------------------------------------------------------------- */
-
-RyanJsonBool_e RyanJsonBaseTestCompareJson(void)
-{
-	char jsonstr[] = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":"
-			 "{\"inter\":16,\"double\":16."
-			 "89,\"string\":\"hello\","
-			 "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,"
-			 "16.89,16.89,16.89],"
-			 "\"arrayString\":[\"hello\",\"hello\","
-			 "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			 "\"double\":16.89,\"string\":"
-			 "\"hello\",\"boolTrue\":true,"
-			 "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			 "\"boolFalse\":false,\"null\":null}]}";
-
-	RyanJson_t json = RyanJsonParse(jsonstr);
-	RyanJson_t json2 = RyanJsonParse(jsonstr);
-
-	// 比较函数
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompare(json, json), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonAddStringToObject(json2, "test", "hello");
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonAddIntToObject(json2, "test", 1);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonAddDoubleToObject(json2, "test", 2.0);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonAddBoolToObject(json2, "test", RyanJsonTrue);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonAddNullToObject(json2, "test");
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonAddIntToArray(RyanJsonGetObjectToKey(json2, "arrayInt"), 2);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonAddDoubleToArray(RyanJsonGetObjectToKey(json2, "arrayDouble"), 2.0);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonAddStringToArray(RyanJsonGetObjectToKey(json2, "arrayString"), "hello");
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonAddItemToArray(RyanJsonGetObjectToKey(json2, "arrayItem"), RyanJsonCreateString("test", "hello"));
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeKey(RyanJsonGetObjectToKey(json2, "inter"), "int2");
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeIntValue(RyanJsonGetObjectToKey(json2, "inter"), 17);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeDoubleValue(RyanJsonGetObjectToKey(json2, "double"), 20.89);
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-	if (RyanJsonFalse != RyanJsonCompare(json, json2))
-	{
-		printf("%s:%d 解析失败\r\n", __FILE__, __LINE__);
-		goto err;
-	}
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonDelete(RyanJsonDetachByKey(json2, "double"));
-	RyanJsonAddIntToObject(json2, "double", 20); // 改为int
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeStringValue(RyanJsonGetObjectToKey(json2, "string"), "49");
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(json2, "boolTrue"), RyanJsonFalse);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(json2, "item", "boolTrue"), RyanJsonFalse);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayInt"), 0), 17);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeDoubleValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayDouble"), 0), 20.89);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeStringValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayString"), 0), "20.89");
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "array"), 0), 17);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonChangeIntValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayItem"), 0), "inter"),
-			       17);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonDeleteByKey(json2, "arrayItem");
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonDeleteByIndex(RyanJsonGetObjectToKey(json2, "arrayInt"), 2);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json2);
-	json2 = RyanJsonParse(jsonstr);
-	RyanJsonDeleteByIndex(RyanJsonGetObjectToKey(json2, "arrayItem"), 0);
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
-	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
-
-	RyanJsonDelete(json);
-	RyanJsonDelete(json2);
-	return RyanJsonTrue;
-
-err:
-	RyanJsonDelete(json);
-	RyanJsonDelete(json2);
-	return RyanJsonFalse;
-}

+ 0 - 86
test/baseTest/RyanJsonBaseTestCreateJson.c

@@ -1,86 +0,0 @@
-
-#include "RyanJsonBaseTest.h"
-
-RyanJsonBool_e RyanJsonBaseTestCreateJson(void)
-{
-	RyanJson_t jsonRoot, item;
-
-	// 对象生成测试
-	jsonRoot = RyanJsonCreateObject();
-	RyanJsonAddIntToObject(jsonRoot, "inter", 16);
-	RyanJsonAddDoubleToObject(jsonRoot, "double", 16.89);
-	RyanJsonAddStringToObject(jsonRoot, "string", "hello");
-	RyanJsonAddBoolToObject(jsonRoot, "boolTrue", RyanJsonTrue);
-	RyanJsonAddBoolToObject(jsonRoot, "boolFalse", RyanJsonFalse);
-	RyanJsonAddNullToObject(jsonRoot, "null");
-
-	/**
-	 * @brief 对象添加测试
-	 *
-	 */
-	item = RyanJsonCreateObject();
-	RyanJsonAddIntToObject(item, "inter", 16);
-	RyanJsonAddDoubleToObject(item, "double", 16.89);
-	RyanJsonAddStringToObject(item, "string", "hello");
-	RyanJsonAddBoolToObject(item, "boolTrue", RyanJsonTrue);
-	RyanJsonAddBoolToObject(item, "boolFalse", RyanJsonFalse);
-	RyanJsonAddNullToObject(item, "null");
-	RyanJsonAddItemToObject(jsonRoot, "item", item);
-
-	/**
-	 * @brief 数组添加测试
-	 *
-	 */
-	int arrayInt[] = {16, 16, 16, 16, 16};
-	RyanJsonAddItemToObject(jsonRoot, "arrayInt", RyanJsonCreateIntArray(arrayInt, sizeof(arrayInt) / sizeof(arrayInt[0])));
-
-	double arrayDouble[] = {16.89, 16.89, 16.89, 16.89, 16.89};
-	RyanJsonAddItemToObject(jsonRoot, "arrayDouble",
-				RyanJsonCreateDoubleArray(arrayDouble, sizeof(arrayDouble) / sizeof(arrayDouble[0])));
-
-	const char *arrayString[] = {"hello", "hello", "hello", "hello", "hello"};
-	RyanJsonAddItemToObject(jsonRoot, "arrayString",
-				RyanJsonCreateStringArray(arrayString, sizeof(arrayString) / sizeof(arrayString[0])));
-
-	RyanJson_t array = RyanJsonCreateArray();
-	RyanJsonAddIntToArray(array, 16);
-	RyanJsonAddDoubleToArray(array, 16.89);
-	RyanJsonAddStringToArray(array, "hello");
-	RyanJsonAddBoolToArray(array, RyanJsonTrue);
-	RyanJsonAddBoolToArray(array, RyanJsonFalse);
-	RyanJsonAddNullToArray(array);
-	RyanJsonAddItemToObject(jsonRoot, "array", array);
-
-	/**
-	 * @brief 对象数组测试
-	 *
-	 */
-	RyanJson_t arrayItem = RyanJsonCreateArray();
-	item = RyanJsonCreateObject();
-	RyanJsonAddIntToObject(item, "inter", 16);
-	RyanJsonAddDoubleToObject(item, "double", 16.89);
-	RyanJsonAddStringToObject(item, "string", "hello");
-	RyanJsonAddBoolToObject(item, "boolTrue", RyanJsonTrue);
-	RyanJsonAddBoolToObject(item, "boolFalse", RyanJsonFalse);
-	RyanJsonAddNullToObject(item, "null");
-	RyanJsonAddItemToObject(arrayItem, "item", item);
-
-	item = RyanJsonCreateObject();
-	RyanJsonAddIntToObject(item, "inter", 16);
-	RyanJsonAddDoubleToObject(item, "double", 16.89);
-	RyanJsonAddStringToObject(item, "string", "hello");
-	RyanJsonAddBoolToObject(item, "boolTrue", RyanJsonTrue);
-	RyanJsonAddBoolToObject(item, "boolFalse", RyanJsonFalse);
-	RyanJsonAddNullToObject(item, "null");
-	RyanJsonAddItemToObject(arrayItem, "item", item);
-
-	RyanJsonAddItemToObject(jsonRoot, "arrayItem", arrayItem);
-
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonBaseTestCheckRoot(jsonRoot), {
-		RyanJsonDelete(jsonRoot);
-		return RyanJsonFalse;
-	});
-	RyanJsonDelete(jsonRoot);
-
-	return RyanJsonTrue;
-}

+ 0 - 112
test/baseTest/RyanJsonBaseTestDeleteJson.c

@@ -1,112 +0,0 @@
-#include "RyanJsonBaseTest.h"
-
-/* --------------------------------------------------------------------- */
-
-RyanJsonBool_e RyanJsonBaseTestDeleteJson(void)
-{
-	// 保持原始 jsonStr,不做修改
-	char jsonstr[] =
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":"
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},"
-		"\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89],"
-		"\"arrayString\":[\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],"
-		"\"array\":[16,16.89,\"hello\",true,false,null],"
-		"\"arrayItem\":[{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},"
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}],"
-		"\"string2222\":\"hello\"}";
-
-	RyanJson_t json = RyanJsonParse(jsonstr);
-	RyanJsonCheckReturnFalse(NULL != json);
-
-	/**
-	 * @brief 场景 1:删除对象中的节点(头、中、尾)
-	 */
-	{
-		// 删除中间节点 (double)
-		RyanJsonDeleteByKey(json, "double");
-		RyanJsonCheckCode(NULL == RyanJsonGetObjectToKey(json, "double"), { goto err; });
-
-		// 删除头部节点 (inter)
-		RyanJsonDeleteByIndex(json, 0);
-		RyanJsonCheckCode(NULL == RyanJsonGetObjectToKey(json, "inter"), { goto err; });
-
-		// 删除尾部节点 (string2222)
-		uint32_t lastIndex = RyanJsonGetSize(json) - 1;
-		RyanJsonDeleteByIndex(json, lastIndex);
-		RyanJsonCheckCode(NULL == RyanJsonGetObjectToKey(json, "string2222"), { goto err; });
-	}
-
-	/**
-	 * @brief 场景 2:删除数组中的元素(arrayInt)
-	 */
-	{
-		RyanJson_t array = RyanJsonGetObjectToKey(json, "arrayInt");
-
-		// 删除数组首位
-		RyanJsonDeleteByIndex(array, 0);
-		RyanJsonCheckCode(RyanJsonGetSize(array) == 4, { goto err; });
-
-		// 删除数组中间元素
-		RyanJsonDeleteByIndex(array, 1);
-		RyanJsonCheckCode(RyanJsonGetSize(array) == 3, { goto err; });
-
-		// 删除数组尾部元素
-		uint32_t lastIndex = RyanJsonGetSize(array) - 1;
-		RyanJsonDeleteByIndex(array, lastIndex);
-		RyanJsonCheckCode(RyanJsonGetSize(array) == 2, { goto err; });
-	}
-
-	/**
-	 * @brief 场景 3:深层嵌套删除(item)
-	 */
-	{
-		RyanJsonDeleteByKey(json, "item");
-		RyanJsonCheckCode(NULL == RyanJsonGetObjectToKey(json, "item"), { goto err; });
-	}
-
-	/**
-	 * @brief 场景 4:数组对象元素删除(arrayItem)
-	 */
-	{
-		RyanJson_t arrObj = RyanJsonGetObjectToKey(json, "arrayItem");
-
-		// 删除第一个对象
-		RyanJsonDeleteByIndex(arrObj, 0);
-		RyanJsonCheckCode(RyanJsonGetSize(arrObj) == 1, { goto err; });
-
-		// 删除最后一个对象
-		RyanJsonDeleteByIndex(arrObj, 0);
-		RyanJsonCheckCode(RyanJsonGetSize(arrObj) == 0, { goto err; });
-	}
-
-	/**
-	 * @brief 场景 5:特殊类型删除(null / bool)
-	 */
-	{
-		RyanJsonDeleteByKey(json, "null");
-		RyanJsonCheckCode(NULL == RyanJsonGetObjectToKey(json, "null"), { goto err; });
-
-		RyanJsonDeleteByKey(json, "boolTrue");
-		RyanJsonCheckCode(NULL == RyanJsonGetObjectToKey(json, "boolTrue"), { goto err; });
-
-		RyanJsonDeleteByKey(json, "boolFalse");
-		RyanJsonCheckCode(NULL == RyanJsonGetObjectToKey(json, "boolFalse"), { goto err; });
-	}
-
-	/**
-	 * @brief 场景 6:异常路径覆盖(健壮性)
-	 */
-	{
-		RyanJsonDeleteByKey(json, "non_exist"); // 删除不存在的 Key
-		RyanJsonDeleteByIndex(NULL, 0);         // 在 NULL 上操作
-	}
-
-	char *str = RyanJsonPrint(json, 1024, RyanJsonTrue, NULL);
-	RyanJsonFree(str);
-	RyanJsonDelete(json);
-	return RyanJsonTrue;
-
-err:
-	RyanJsonDelete(json);
-	return RyanJsonFalse;
-}

+ 0 - 217
test/baseTest/RyanJsonBaseTestDetachJson.c

@@ -1,217 +0,0 @@
-#include "RyanJsonBaseTest.h"
-
-/* --------------------------------------------------------------------- */
-
-RyanJsonBool_e RyanJsonBaseTestDetachJson(void)
-{
-	char jsonstr[] =
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":"
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},"
-		"\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89],"
-		"\"arrayString\":[\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],"
-		"\"array\":[16,16.89,\"hello\",true,false,null],"
-		"\"arrayItem\":[{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{"
-		"\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,"
-		"\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,"
-		"\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},"
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}],"
-		"\"string2222\":\"hello\"}";
-
-	RyanJson_t json = RyanJsonParse(jsonstr);
-	RyanJsonCheckReturnFalse(NULL != json);
-
-	/**
-	 * @brief 对象子项分离测试(头、中、尾)
-	 */
-	{
-		RyanJson_t json2 = RyanJsonParse(jsonstr);
-
-		// 头部 (第一个 key: inter)
-		RyanJsonDelete(RyanJsonDetachByIndex(json, 0));
-		if (RyanJsonGetObjectToKey(json, "inter"))
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		// 中间 (double)
-		RyanJsonDelete(RyanJsonDetachByKey(json, "double"));
-		if (RyanJsonGetObjectToKey(json, "double"))
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		// 尾部 (最后一个 key: string2222)
-		uint32_t lastIndex = RyanJsonGetSize(json) - 1;
-		RyanJsonDelete(RyanJsonDetachByIndex(json, lastIndex));
-		if (RyanJsonGetObjectToKey(json, "string2222"))
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(json2);
-	}
-
-	/**
-	 * @brief 数组元素分离测试 (arrayInt 头、中、尾)
-	 */
-	{
-		RyanJson_t arr = RyanJsonGetObjectByKey(json, "arrayInt");
-		RyanJson_t json2 = RyanJsonParse(jsonstr);
-
-        uint32_t jsonSize =  RyanJsonGetSize(arr);
-		// 头部
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, 0));
-		if (jsonSize == RyanJsonGetSize(arr))
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		// 中间
-         jsonSize =  RyanJsonGetSize(arr);
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, 2));
-		if (jsonSize == RyanJsonGetSize(arr))
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		// 尾部
-		uint32_t lastIndex = RyanJsonGetSize(arr) - 1;
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, lastIndex));
-		if (NULL != RyanJsonGetObjectToIndex(arr, lastIndex))
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(json2);
-	}
-
-	/**
-	 * @brief 数组元素分离测试 (arrayDouble 头、中、尾)
-	 */
-	{
-		RyanJson_t arr = RyanJsonGetObjectByKey(json, "arrayDouble");
-		RyanJson_t json2 = RyanJsonParse(jsonstr);
-
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, 0));
-		if (RyanJsonGetObjectToIndex(arr, 0) == NULL)
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, 2));
-		if (RyanJsonGetObjectToIndex(arr, 2) == NULL)
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		uint32_t lastIndex = RyanJsonGetSize(arr) - 1;
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, lastIndex));
-		if (RyanJsonGetObjectToIndex(arr, lastIndex) != NULL)
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(json2);
-	}
-
-	/**
-	 * @brief 数组元素分离测试 (arrayString 头、中、尾)
-	 */
-	{
-		RyanJson_t arr = RyanJsonGetObjectByKey(json, "arrayString");
-		RyanJson_t json2 = RyanJsonParse(jsonstr);
-
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, 0));
-		if (RyanJsonGetObjectToIndex(arr, 0) == NULL)
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, 2));
-		if (RyanJsonGetObjectToIndex(arr, 2) == NULL)
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		uint32_t lastIndex = RyanJsonGetSize(arr) - 1;
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, lastIndex));
-		if (RyanJsonGetObjectToIndex(arr, lastIndex) != NULL)
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(json2);
-	}
-
-	/**
-	 * @brief 嵌套对象分离测试 (item)
-	 */
-	{
-		RyanJson_t json2 = RyanJsonParse(jsonstr);
-		RyanJsonDelete(RyanJsonDetachByKey(json, "item"));
-		if (RyanJsonGetObjectToKey(json, "item"))
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-		RyanJsonDelete(json2);
-	}
-
-	/**
-	 * @brief 数组对象元素分离测试 (arrayItem 头、中、尾)
-	 */
-	{
-		RyanJson_t arr = RyanJsonGetObjectByKey(json, "arrayItem");
-		RyanJson_t json2 = RyanJsonParse(jsonstr);
-
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, 0));
-		if (RyanJsonGetObjectToIndex(arr, 0) == NULL)
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, 1));
-		if (RyanJsonGetObjectToIndex(arr, 1) == NULL)
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		uint32_t lastIndex = RyanJsonGetSize(arr) - 1;
-		RyanJsonDelete(RyanJsonDetachByIndex(arr, lastIndex));
-		if (RyanJsonGetObjectToIndex(arr, lastIndex) != NULL)
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(json2);
-	}
-
-	/**
-	 * @brief 特殊类型分离测试(null / bool)
-	 */
-	{
-		RyanJson_t json2 = RyanJsonParse(jsonstr);
-
-		RyanJsonDelete(RyanJsonDetachByKey(json, "null"));
-		if (RyanJsonGetObjectToKey(json, "null"))
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(RyanJsonDetachByKey(json, "boolTrue"));
-		if (RyanJsonGetObjectToKey(json, "boolTrue"))
-		{
-			RyanJsonCheckCode(NULL, { goto err; });
-		}
-
-		RyanJsonDelete(json2);
-	}
-
-	char *str = RyanJsonPrint(json, 1024, RyanJsonTrue, NULL);
-	RyanJsonFree(str);
-	RyanJsonDelete(json);
-	return RyanJsonTrue;
-
-err:
-	RyanJsonDelete(json);
-	return RyanJsonFalse;
-}

+ 0 - 139
test/baseTest/RyanJsonBaseTestDuplicateJson.c

@@ -1,139 +0,0 @@
-
-#include "RyanJsonBaseTest.h"
-
-RyanJsonBool_e RyanJsonBaseTestDuplicateJson(void)
-{
-	RyanJson_t json, dupItem, jsonRoot = NULL;
-	char jsonstr[] = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":"
-			 "{\"inter\":16,\"double\":16."
-			 "89,\"string\":\"hello\","
-			 "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,"
-			 "16.89,16.89,16.89],"
-			 "\"arrayString\":[\"hello\",\"hello\","
-			 "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			 "\"double\":16.89,\"string\":"
-			 "\"hello\",\"boolTrue\":true,"
-			 "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			 "\"boolFalse\":false,\"null\":null}]}";
-
-	/**
-	 * @brief 普通类型
-	 *
-	 */
-	json = RyanJsonParse(jsonstr);
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "inter"));
-	if (RyanJsonFalse == RyanJsonCompare(dupItem, RyanJsonGetObjectToKey(json, "inter"))) { goto err; }
-	RyanJsonDelete(dupItem);
-
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "inter"));
-	RyanJsonAddItemToObject(json, "test", dupItem);
-	if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "test", "inter"), RyanJsonGetObjectToKey(json, "inter")))
-	{
-		goto err;
-	}
-	RyanJsonDelete(RyanJsonDetachByKey(json, "test"));
-
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "inter"));
-	RyanJsonAddItemToObject(json, "test", dupItem);
-	if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "test", "inter"), RyanJsonGetObjectToKey(json, "inter")))
-	{
-		goto err;
-	}
-	RyanJsonDelete(json);
-
-	json = RyanJsonParse(jsonstr);
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "inter"));
-	RyanJsonAddItemToObject(json, "test", dupItem);
-	if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "test", "inter"), RyanJsonGetObjectToKey(json, "inter")))
-	{
-		goto err;
-	}
-	RyanJsonDelete(RyanJsonDetachByKey(json, "test"));
-	RyanJsonDelete(json);
-
-	/**
-	 * @brief 对象类型
-	 *
-	 */
-	json = RyanJsonParse(jsonstr);
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "item"));
-	if (RyanJsonFalse == RyanJsonCompare(dupItem, RyanJsonGetObjectToKey(json, "item"))) { goto err; }
-	RyanJsonDelete(dupItem);
-
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "item"));
-	RyanJsonAddItemToObject(json, "test", dupItem);
-	if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "test"), RyanJsonGetObjectToKey(json, "item"))) { goto err; }
-	RyanJsonDelete(RyanJsonDetachByKey(json, "test"));
-
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "item"));
-	RyanJsonAddItemToObject(json, "test", dupItem);
-	if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "test"), RyanJsonGetObjectToKey(json, "item"))) { goto err; }
-	RyanJsonDelete(json);
-
-	json = RyanJsonParse(jsonstr);
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "item"));
-	RyanJsonAddItemToObject(json, "test", dupItem);
-	if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "test"), RyanJsonGetObjectToKey(json, "item"))) { goto err; }
-	RyanJsonDelete(RyanJsonDetachByKey(json, "test"));
-	RyanJsonDelete(json);
-
-	/**
-	 * @brief 数组类型
-	 *
-	 */
-	json = RyanJsonParse(jsonstr);
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "arrayItem"));
-	if (RyanJsonFalse == RyanJsonCompare(dupItem, RyanJsonGetObjectToKey(json, "arrayItem"))) { goto err; }
-	RyanJsonDelete(dupItem);
-
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "arrayItem"));
-	RyanJsonAddItemToObject(json, "test", dupItem);
-	if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "test"), RyanJsonGetObjectToKey(json, "arrayItem"))) { goto err; }
-	RyanJsonDelete(RyanJsonDetachByKey(json, "test"));
-
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "arrayItem"));
-	RyanJsonAddItemToObject(json, "test", dupItem);
-	if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "test"), RyanJsonGetObjectToKey(json, "arrayItem"))) { goto err; }
-	RyanJsonDelete(json);
-
-	json = RyanJsonParse(jsonstr);
-	dupItem = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "arrayItem"));
-	RyanJsonAddItemToObject(json, "test", dupItem);
-	if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "test"), RyanJsonGetObjectToKey(json, "arrayItem"))) { goto err; }
-	RyanJsonDelete(RyanJsonDetachByKey(json, "test"));
-	RyanJsonDelete(json);
-
-	json = RyanJsonParse(jsonstr);
-	jsonRoot = RyanJsonCreateObject();
-	RyanJsonAddBoolToObject(jsonRoot, "arrayItem", RyanJsonTrue);
-	int use = 0;
-	for (uint8_t i = 0; i < 10; i++)
-	{
-		dupItem = RyanJsonParse(jsonstr);
-		RyanJsonReplaceByKey(jsonRoot, "arrayItem", RyanJsonDuplicate(dupItem));
-		if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(jsonRoot, "arrayItem"), dupItem)) { goto err; }
-		RyanJsonReplaceByKey(json, "arrayItem", RyanJsonDuplicate(RyanJsonGetObjectByKey(dupItem, "item")));
-		if (RyanJsonFalse == RyanJsonCompare(RyanJsonGetObjectToKey(json, "arrayItem"), RyanJsonGetObjectByKey(dupItem, "item")))
-		{
-			goto err;
-		}
-		RyanJsonDelete(dupItem);
-
-		int newuse = vallocGetUse();
-		if (i != 0 && newuse != use)
-		{
-			printf("%s:%d 内存泄漏\r\n", __FILE__, __LINE__);
-			goto err;
-		}
-		use = newuse;
-	}
-
-	RyanJsonDelete(json);
-	RyanJsonDelete(jsonRoot);
-	return RyanJsonTrue;
-
-err:
-	RyanJsonDelete(json);
-	RyanJsonDelete(jsonRoot);
-	return RyanJsonFalse;
-}

+ 0 - 45
test/baseTest/RyanJsonBaseTestForEachJson.c

@@ -1,45 +0,0 @@
-
-#include "RyanJsonBaseTest.h"
-
-RyanJsonBool_e RyanJsonBaseTestForEachJson(void)
-{
-	char *str = NULL;
-	char jsonstr[] = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":"
-			 "{\"inter\":16,\"double\":16."
-			 "89,\"string\":\"hello\","
-			 "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,"
-			 "16.89,16.89,16.89],"
-			 "\"arrayString\":[\"hello\",\"hello\","
-			 "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			 "\"double\":16.89,\"string\":"
-			 "\"hello\",\"boolTrue\":true,"
-			 "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			 "\"boolFalse\":false,\"null\":null}]}";
-
-	RyanJson_t json = RyanJsonParse(jsonstr);
-	RyanJson_t item = NULL;
-	RyanJsonArrayForEach(RyanJsonGetObjectToKey(json, "arrayDouble"), item)
-	{
-		if (!RyanJsonIsDouble(item) || !RyanJsonCompareDouble(16.89, RyanJsonGetDoubleValue(item))) { goto err; }
-	}
-
-	RyanJsonArrayForEach(RyanJsonGetObjectToKey(json, "arrayInt"), item)
-	{
-		if (!RyanJsonIsInt(item) || 16 != RyanJsonGetIntValue(item)) { goto err; }
-	}
-
-	int32_t strLen;
-	RyanJsonObjectForEach(RyanJsonGetObjectToKey(json, "item"), item)
-	{
-		str = RyanJsonPrint(item, 50, RyanJsonTrue, &strLen);
-		printf("item { %s : %s }  %d\r\n", RyanJsonGetKey(item), str, strLen);
-		RyanJsonFree(str);
-	}
-
-	RyanJsonDelete(json);
-	return RyanJsonTrue;
-
-err:
-	RyanJsonDelete(json);
-	return RyanJsonFalse;
-}

+ 0 - 345
test/baseTest/RyanJsonBaseTestLoadJson.c

@@ -1,345 +0,0 @@
-
-#include "RyanJsonBaseTest.h"
-
-/* --------------------------------------------------------------------- */
-
-RyanJsonBool_e RyanJsonBaseTestLoadJson(void)
-{
-	char *str = NULL;
-	RyanJson_t json;
-	char *jsonstr = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":"
-			"{\"inter\":16,\"double\":16."
-			"89,\"string\":\"hello\","
-			"\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,"
-			"16.89,16.89,16.89],"
-			"\"arrayString\":[\"hello\",\"hello\","
-			"\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			"\"double\":16.89,\"string\":"
-			"\"hello\",\"boolTrue\":true,"
-			"\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			"\"boolFalse\":false,\"null\":null}],\"unicode\":\"😀\"}";
-
-	json = RyanJsonParse(jsonstr);
-	RyanJsonCheckReturnFalse(NULL != json);
-
-	str = RyanJsonPrint(json, 250, RyanJsonFalse, NULL);
-	RyanJsonCheckCode(0 == strcmp(str, jsonstr), {
-		RyanJsonFree(str);
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	RyanJsonFree(str);
-
-	RyanJsonCheckCode(RyanJsonTrue == RyanJsonBaseTestCheckRoot(json), {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	RyanJsonDelete(json);
-
-	/**
-	 * @brief 测试 Unicode
-	 *
-	 */
-	char printfBuf[1024] = {0};
-	json = RyanJsonParse("{\"emoji\":\"\\uD83D\\uDE00\"}");
-	RyanJsonCheckReturnFalse(NULL != json);
-	str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL);
-	RyanJsonDelete(json);
-
-	// 测试数字 0-9 分支: \u0030 = '0', \u0039 = '9'
-	json = RyanJsonParse("{\"num\":\"\\u0030\\u0039\"}");
-	RyanJsonCheckReturnFalse(NULL != json);
-	str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL);
-	RyanJsonCheckCode(0 == strcmp(str, "{\"num\":\"09\"}"), {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-	RyanJsonDelete(json);
-
-	// 测试小写 a-f 分支: \u0061 = 'a', \u0066 = 'f'
-	json = RyanJsonParse("{\"lower\":\"\\u0061\\u0062\\u0063\\u0064\\u0065\\u0066\"}");
-	RyanJsonCheckReturnFalse(NULL != json);
-	str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL);
-	RyanJsonCheckCode(0 == strcmp(str, "{\"lower\":\"abcdef\"}"), {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-	RyanJsonDelete(json);
-
-	// 测试大写 A-F 分支: \u0041 = 'A', \u0046 = 'F'
-	json = RyanJsonParse("{\"upper\":\"\\u0041\\u0042\\u0043\\u0044\\u0045\\u0046\"}");
-	RyanJsonCheckReturnFalse(NULL != json);
-	str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL);
-	RyanJsonCheckCode(0 == strcmp(str, "{\"upper\":\"ABCDEF\"}"), {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-	RyanJsonDelete(json);
-
-	// 测试混合大小写: \uAbCd (混合大小写十六进制)
-	json = RyanJsonParse("{\"mixed\":\"\\uAbCd\"}");
-	RyanJsonCheckReturnFalse(NULL != json);
-	RyanJsonFree(RyanJsonPrint(json, 50, RyanJsonFalse, NULL));
-	RyanJsonDelete(json);
-
-	// 测试 default 分支 (非法十六进制字符 'G')
-	json = RyanJsonParse("{\"invalid\":\"\\uGGGG\"}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// 测试 default 分支 (非法十六进制字符 'Z')
-	json = RyanJsonParse("{\"invalid\":\"\\u00ZZ\"}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// 测试 default 分支 (非法十六进制字符 '!')
-	json = RyanJsonParse("{\"invalid\":\"\\u00!!\"}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	/**
-	 * @brief 测试序列化错误json结构
-	 *
-	 */
-	// \"inter\":16poi,  无效数字
-	json = RyanJsonParse("{\"inter\":16poi,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			     "\"item\":{\"inter\":"
-			     "16,\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// \"double\":16.8yu9,,  无效浮点数
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.8yu9,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			     "\"item\":{\"inter\":16,"
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// boolTrue 设置为 tru
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":tru,\"boolFalse\":false,\"null\":null,"
-			     "\"item\":{\"inter\":16,"
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// boolFalse 设置为 fale
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":fale,\"null\":null,"
-			     "\"item\":{\"inter\":16,"
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// null 设置为 nul
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":nul,"
-			     "\"item\":{\"inter\":16,"
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// null 设置为 NULL
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":NULL,"
-			     "\"item\":{\"inter\":16,"
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// \"inter\":16后面少个,
-	json = RyanJsonParse("{\"inter\":16\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			     "\"item\":{\"inter\":16,"
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-	// array数组项少一个,
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			     "\"item\":{\"inter\":16,"
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// \"item:{\"inter\":16,\"  少一个"
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			     "\"item:{\"inter\":16,"
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// \"item\":{\"inter\":16,double\"  少一个"
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			     "\"item\":{\"inter\":16,"
-			     "double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// \"item\":{\"inter\":16,\"\"double\"  多一个"
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			     "\"item\":{\"inter\":16,"
-			     "\"\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// \"item\":{\"inter\":16\",\"double\"  多一个"
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			     "\"item\":{\"inter\":16\","
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16."
-			     "89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-
-	// \"arrayInt\":[16,16,16m,16,16]  无效数组数字
-	json = RyanJsonParse("{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			     "\"item\":{\"inter\":16,"
-			     "\"double\":16.89,\"string\":\"hello\","
-			     "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16m,16,16],\"arrayDouble\":[16.89,"
-			     "16.89,16.89,16.89,16."
-			     "89],\"arrayString\":[\"hello\",\"hello\","
-			     "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			     "\"double\":16.89,"
-			     "\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			     "\"boolFalse\":false,"
-			     "\"null\":null}]}");
-	RyanJsonCheckCode(NULL == json, {
-		RyanJsonDelete(json);
-		return RyanJsonFalse;
-	});
-	return RyanJsonTrue;
-}

+ 0 - 222
test/baseTest/RyanJsonBaseTestReplaceJson.c

@@ -1,222 +0,0 @@
-#include "RyanJsonBaseTest.h"
-
-/* --------------------------------------------------------------------- */
-
-RyanJsonBool_e RyanJsonBaseTestReplaceJson(void)
-{
-	char jsonstr[] =
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":"
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},"
-		"\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,16.89,16.89,16.89],"
-		"\"arrayString\":[\"hello\",\"hello\",\"hello\",\"hello\",\"hello\"],"
-		"\"array\":[16,16.89,\"hello\",true,false,null],"
-		"\"arrayItem\":[{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null},"
-		"{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null}],"
-		"\"string2222\":\"hello\"}";
-
-	RyanJson_t json = RyanJsonParse(jsonstr);
-	RyanJsonCheckReturnFalse(NULL != json);
-
-	/* ---------------- 保留原有测试(并补充校验) ---------------- */
-
-	// 数组替换测试:arrayInt 头
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "arrayInt"), 0, RyanJsonCreateString(NULL, "arrayInt"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayInt"), 0);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "arrayInt") == 0, { goto err; });
-	}
-
-	// 数组替换测试:arrayInt 尾
-	{
-		RyanJson_t arr = RyanJsonGetObjectToKey(json, "arrayInt");
-		uint32_t last = RyanJsonGetSize(arr) - 1;
-		RyanJsonReplaceByIndex(arr, last, RyanJsonCreateString(NULL, "arrayInt"));
-		RyanJson_t v = RyanJsonGetObjectToIndex(arr, last);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "arrayInt") == 0, { goto err; });
-	}
-
-	// 数组对象替换测试:arrayItem[0]
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "arrayItem"), 0, RyanJsonCreateString(NULL, "arrayItem"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayItem"), 0);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "arrayItem") == 0, { goto err; });
-	}
-
-	// 数组对象替换测试:arrayItem[1]
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "arrayItem"), 1, RyanJsonCreateString(NULL, "arrayItem"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayItem"), 1);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "arrayItem") == 0, { goto err; });
-	}
-
-	// 对象字段替换:inter -> 999
-	RyanJsonReplaceByKey(json, "inter", RyanJsonCreateInt("inter", 999));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(json, "inter");
-		RyanJsonCheckCode(RyanJsonIsInt(v), { goto err; });
-		RyanJsonCheckCode(RyanJsonGetIntValue(v) == 999, { goto err; });
-	}
-
-	// 对象字段替换:double -> 123.45
-	RyanJsonReplaceByKey(json, "double", RyanJsonCreateDouble("double", 123.45));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(json, "double");
-		RyanJsonCheckCode(RyanJsonIsDouble(v), { goto err; });
-		RyanJsonCheckCode(RyanJsonGetDoubleValue(v) == 123.45, { goto err; });
-	}
-
-	// 对象字段替换:string -> "newString"
-	RyanJsonReplaceByKey(json, "string", RyanJsonCreateString("string", "newString"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(json, "string");
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "newString") == 0, { goto err; });
-	}
-
-	// 对象字段替换:boolFalse -> true
-	RyanJsonReplaceByKey(json, "boolFalse", RyanJsonCreateBool("boolFalse", RyanJsonTrue));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(json, "boolFalse");
-		RyanJsonCheckCode(RyanJsonIsBool(v), { goto err; });
-		RyanJsonCheckCode(RyanJsonGetBoolValue(v) == RyanJsonTrue, { goto err; });
-	}
-
-	// 数组替换:arrayInt 中间元素 -> "midInt"
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "arrayInt"), 2, RyanJsonCreateString(NULL, "midInt"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayInt"), 2);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "midInt") == 0, { goto err; });
-	}
-
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "arrayString"), 1, RyanJsonCreateString(NULL, "headString"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayString"), 1);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "headString") == 0, { goto err; });
-	}
-	{
-		RyanJson_t arr = RyanJsonGetObjectToKey(json, "arrayString");
-		uint32_t last = RyanJsonGetSize(arr) - 1;
-		RyanJsonReplaceByIndex(arr, last, RyanJsonCreateString(NULL, "tailString"));
-		RyanJson_t v = RyanJsonGetObjectToIndex(arr, last);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "tailString") == 0, { goto err; });
-	}
-
-	// 数组对象替换:arrayItem 尾部 -> "arrayItemTail"
-	{
-		RyanJson_t arr = RyanJsonGetObjectToKey(json, "arrayItem");
-		uint32_t last = RyanJsonGetSize(arr) - 1;
-		RyanJsonReplaceByIndex(arr, last, RyanJsonCreateString(NULL, "arrayItemTail"));
-		RyanJson_t v = RyanJsonGetObjectToIndex(arr, last);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "arrayItemTail") == 0, { goto err; });
-	}
-
-	// 嵌套对象替换:item.inter -> 111
-	RyanJsonReplaceByKey(RyanJsonGetObjectToKey(json, "item"), "inter", RyanJsonCreateInt("inter", 111));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(json, "item"), "inter");
-		RyanJsonCheckCode(RyanJsonIsInt(v), { goto err; });
-		RyanJsonCheckCode(RyanJsonGetIntValue(v) == 111, { goto err; });
-	}
-
-	// 嵌套对象替换:item.string -> "nestedReplace"
-	RyanJsonReplaceByKey(RyanJsonGetObjectToKey(json, "item"), "string", RyanJsonCreateString("string", "nestedReplace"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(RyanJsonGetObjectToKey(json, "item"), "string");
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "nestedReplace") == 0, { goto err; });
-	}
-
-	// 混合数组替换:各类型位置
-	// 0:int -> "intReplaced"
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "array"), 0, RyanJsonCreateString(NULL, "intReplaced"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "array"), 0);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "intReplaced") == 0, { goto err; });
-	}
-	// 1:double -> "doubleReplaced"
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "array"), 1, RyanJsonCreateString(NULL, "doubleReplaced"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "array"), 1);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "doubleReplaced") == 0, { goto err; });
-	}
-	// 2:string -> "stringReplaced"
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "array"), 2, RyanJsonCreateString(NULL, "stringReplaced"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "array"), 2);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "stringReplaced") == 0, { goto err; });
-	}
-	// 3:bool -> "boolReplaced"
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "array"), 3, RyanJsonCreateString(NULL, "boolReplaced"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "array"), 3);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "boolReplaced") == 0, { goto err; });
-	}
-	// 5:null -> "nullReplaced"
-	RyanJsonReplaceByIndex(RyanJsonGetObjectToKey(json, "array"), 5, RyanJsonCreateString(NULL, "nullReplaced"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "array"), 5);
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "nullReplaced") == 0, { goto err; });
-	}
-
-	// 对象替换测试:arrayString -> "arrayString2222"
-	RyanJsonReplaceByKey(json, "arrayString", RyanJsonCreateString("", "arrayString2222"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(json, "arrayString");
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "arrayString2222") == 0, { goto err; });
-	}
-
-	// 修改数组节点为对象节点:arrayDouble -> duplicate(item)
-	RyanJson_t duplicateJson = RyanJsonDuplicate(RyanJsonGetObjectToKey(json, "item"));
-	RyanJsonReplaceByKey(json, "arrayDouble", duplicateJson);
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(json, "arrayDouble");
-		RyanJsonCheckCode(RyanJsonIsObject(v), { goto err; });
-	}
-
-	// 替换普通 key 的值:string2222 -> "world"
-	RyanJsonReplaceByKey(json, "string2222", RyanJsonCreateString("string2222", "world"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(json, "string2222");
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "world") == 0, { goto err; });
-	}
-
-	// 替换布尔值:boolTrue -> false
-	RyanJsonReplaceByKey(json, "boolTrue", RyanJsonCreateBool("boolTrue", RyanJsonFalse));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(json, "boolTrue");
-		RyanJsonCheckCode(RyanJsonIsBool(v), { goto err; });
-		RyanJsonCheckCode(RyanJsonGetBoolValue(v) == RyanJsonFalse, { goto err; });
-	}
-
-	// 替换 null:null -> "notNull"
-	RyanJsonReplaceByKey(json, "null", RyanJsonCreateString("null", "notNull"));
-	{
-		RyanJson_t v = RyanJsonGetObjectToKey(json, "null");
-		RyanJsonCheckCode(RyanJsonIsString(v), { goto err; });
-		RyanJsonCheckCode(strcmp(RyanJsonGetStringValue(v), "notNull") == 0, { goto err; });
-	}
-
-	char *str = RyanJsonPrint(json, 1024, RyanJsonTrue, NULL);
-	RyanJsonFree(str);
-	RyanJsonDelete(json);
-	return RyanJsonTrue;
-
-err:
-	RyanJsonDelete(json);
-	return RyanJsonFalse;
-}

+ 0 - 163
test/baseTest/RyanJsonBaseTestUtile.c

@@ -1,163 +0,0 @@
-
-#include "RyanJsonBaseTest.h"
-
-/* --------------------------------------- jsonTest ------------------------------------------- */
-
-void printJsonDebug(RyanJson_t json)
-{
-	char *str = RyanJsonPrint(json, 1024, RyanJsonTrue, NULL);
-	printf("aa %s\r\n", str);
-	RyanJsonFree(str);
-}
-
-RyanJsonBool_e rootNodeCheckTest(RyanJson_t json)
-{
-	if (!RyanJsonIsInt(RyanJsonGetObjectToKey(json, "inter")) || 16 != RyanJsonGetIntValue(RyanJsonGetObjectToKey(json, "inter")))
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsDouble(RyanJsonGetObjectToKey(json, "double")) ||
-	    !RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(json, "double")), 16.89))
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsString(RyanJsonGetObjectToKey(json, "string")) ||
-	    strcmp(RyanJsonGetStringValue(RyanJsonGetObjectToKey(json, "string")), "hello"))
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsBool(RyanJsonGetObjectToKey(json, "boolTrue")) ||
-	    RyanJsonGetBoolValue(RyanJsonGetObjectToKey(json, "boolTrue")) != RyanJsonTrue)
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsBool(RyanJsonGetObjectToKey(json, "boolFalse")) ||
-	    RyanJsonGetBoolValue(RyanJsonGetObjectToKey(json, "boolFalse")) != RyanJsonFalse)
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsNull(RyanJsonGetObjectToKey(json, "null"))) { RyanJsonCheckReturnFalse(NULL); }
-
-	return RyanJsonTrue;
-}
-
-RyanJsonBool_e itemNodeCheckTest(RyanJson_t json)
-{
-	RyanJson_t item = RyanJsonGetObjectToKey(json, "item");
-	if (RyanJsonTrue != rootNodeCheckTest(item)) { RyanJsonCheckReturnFalse(NULL); }
-
-	return RyanJsonTrue;
-}
-
-RyanJsonBool_e arrayNodeCheckTest(RyanJson_t json)
-{
-	RyanJson_t item = NULL;
-
-	// 判断是不是数组类型
-	if (!RyanJsonIsArray(RyanJsonGetObjectToKey(json, "arrayInt"))) { RyanJsonCheckReturnFalse(NULL); }
-
-	if (!RyanJsonIsArray(RyanJsonGetObjectToKey(json, "arrayDouble"))) { RyanJsonCheckReturnFalse(NULL); }
-
-	if (!RyanJsonIsArray(RyanJsonGetObjectToKey(json, "arrayString"))) { RyanJsonCheckReturnFalse(NULL); }
-
-	if (!RyanJsonIsArray(RyanJsonGetObjectToKey(json, "array"))) { RyanJsonCheckReturnFalse(NULL); }
-
-	/**
-	 * @brief 检查弱类型数组
-	 *
-	 */
-	//   array: [16, 16.89, "hello", true, false, null],
-	if (!RyanJsonIsInt(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 0)) ||
-	    16 != RyanJsonGetIntValue(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 0)))
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsDouble(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 1)) ||
-	    !RyanJsonCompareDouble(RyanJsonGetDoubleValue(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 1)), 16.89))
-	{
-		printf("%s:%d 解析失败 %f\r\n", __FILE__, __LINE__,
-		       RyanJsonGetDoubleValue(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 1)));
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsString(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 2)) ||
-	    0 != strcmp(RyanJsonGetStringValue(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 2)), "hello"))
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsBool(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 3)) ||
-	    RyanJsonGetBoolValue(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 3)) != RyanJsonTrue)
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsBool(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 4)) ||
-	    RyanJsonGetBoolValue(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 4)) != RyanJsonFalse)
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (!RyanJsonIsNull(RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "array"), 5))) { RyanJsonCheckReturnFalse(NULL); }
-
-	/**
-	 * @brief 检查强类型数组
-	 *
-	 */
-	for (int32_t count = 0; count < RyanJsonGetSize(RyanJsonGetObjectToKey(json, "arrayInt")); count++)
-	{
-		item = RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "arrayInt"), count);
-		if (!RyanJsonIsInt(item) || 16 != RyanJsonGetIntValue(item)) { RyanJsonCheckReturnFalse(NULL); }
-	}
-
-	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); }
-	}
-
-	for (int32_t count = 0; count < RyanJsonGetSize(RyanJsonGetObjectToKey(json, "arrayString")); count++)
-	{
-		item = RyanJsonGetObjectByIndex(RyanJsonGetObjectToKey(json, "arrayString"), count);
-		if (!RyanJsonIsString(item) || strcmp(RyanJsonGetStringValue(item), "hello")) { RyanJsonCheckReturnFalse(NULL); }
-	}
-
-	if (6 != RyanJsonGetSize(RyanJsonGetObjectToKey(json, "array"))) { RyanJsonCheckReturnFalse(NULL); }
-
-	return RyanJsonTrue;
-}
-
-RyanJsonBool_e arrayItemNodeCheckTest(RyanJson_t json)
-{
-	if (!RyanJsonIsArray(RyanJsonGetObjectToKey(json, "arrayItem"))) { RyanJsonCheckReturnFalse(NULL); }
-
-	if (RyanJsonTrue != rootNodeCheckTest(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayItem"), 0)))
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-
-	if (RyanJsonTrue != rootNodeCheckTest(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json, "arrayItem"), 1)))
-	{
-		RyanJsonCheckReturnFalse(NULL);
-	}
-	return RyanJsonTrue;
-}
-
-RyanJsonBool_e RyanJsonBaseTestCheckRoot(RyanJson_t pJson)
-{
-	RyanJsonCheckReturnFalse(RyanJsonTrue == rootNodeCheckTest(pJson));
-
-	RyanJsonCheckReturnFalse(RyanJsonTrue == itemNodeCheckTest(pJson));
-
-	RyanJsonCheckReturnFalse(RyanJsonTrue == arrayNodeCheckTest(pJson));
-
-	RyanJsonCheckReturnFalse(RyanJsonTrue == arrayItemNodeCheckTest(pJson));
-
-	return RyanJsonTrue;
-}

+ 0 - 86
test/baseTest/equality/RyanJsonBaseTestEqualityBool.c

@@ -1,86 +0,0 @@
-#include "RyanJsonBaseTest.h"
-
-// 布尔值一致性测试
-RyanJsonBool_e RyanJsonBaseTestEqualityBool(void)
-{
-	// 测试 true
-	{
-		const char *jsonBoolStr = "{\"bool\":true}";
-		RyanJson_t jsonRoot = RyanJsonParse(jsonBoolStr);
-		RyanJsonCheckReturnFalse(NULL != jsonRoot);
-		RyanJsonCheckReturnFalse(RyanJsonIsBool(RyanJsonGetObjectToKey(jsonRoot, "bool")));
-		RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonGetBoolValue(RyanJsonGetObjectToKey(jsonRoot, "bool")));
-
-		// 往返测试
-		char *serializedStr = RyanJsonPrint(jsonRoot, 64, RyanJsonFalse, NULL);
-		RyanJsonDelete(jsonRoot);
-
-		RyanJson_t roundtripJson = RyanJsonParse(serializedStr);
-		RyanJsonFree(serializedStr);
-		RyanJsonCheckReturnFalse(NULL != roundtripJson);
-		RyanJsonCheckReturnFalse(RyanJsonIsBool(RyanJsonGetObjectToKey(roundtripJson, "bool")));
-		RyanJsonCheckReturnFalse(RyanJsonTrue == RyanJsonGetBoolValue(RyanJsonGetObjectToKey(roundtripJson, "bool")));
-
-		RyanJsonDelete(roundtripJson);
-	}
-
-	// 测试 false
-	{
-		const char *jsonBoolStr = "{\"bool\":false}";
-		RyanJson_t jsonRoot = RyanJsonParse(jsonBoolStr);
-		RyanJsonCheckReturnFalse(NULL != jsonRoot);
-		RyanJsonCheckReturnFalse(RyanJsonIsBool(RyanJsonGetObjectToKey(jsonRoot, "bool")));
-		RyanJsonCheckReturnFalse(RyanJsonFalse == RyanJsonGetBoolValue(RyanJsonGetObjectToKey(jsonRoot, "bool")));
-
-		// 往返测试
-		char *serializedStr = RyanJsonPrint(jsonRoot, 64, RyanJsonFalse, NULL);
-		RyanJsonDelete(jsonRoot);
-
-		RyanJson_t roundtripJson = RyanJsonParse(serializedStr);
-		RyanJsonFree(serializedStr);
-		RyanJsonCheckReturnFalse(NULL != roundtripJson);
-		RyanJsonCheckReturnFalse(RyanJsonIsBool(RyanJsonGetObjectToKey(roundtripJson, "bool")));
-		RyanJsonCheckReturnFalse(RyanJsonFalse == RyanJsonGetBoolValue(RyanJsonGetObjectToKey(roundtripJson, "bool")));
-
-		RyanJsonDelete(roundtripJson);
-	}
-
-	// 测试数组中的布尔值
-	{
-		const char *jsonArrayStr = "[true, false, true, false]";
-		RyanJson_t jsonRoot = RyanJsonParse(jsonArrayStr);
-		RyanJsonCheckReturnFalse(NULL != jsonRoot);
-		RyanJsonCheckReturnFalse(4 == RyanJsonGetArraySize(jsonRoot));
-
-		RyanJsonBool_e expected[] = {RyanJsonTrue, RyanJsonFalse, RyanJsonTrue, RyanJsonFalse};
-		int idx = 0;
-		RyanJson_t item = NULL;
-		RyanJsonArrayForEach(jsonRoot, item)
-		{
-			RyanJsonCheckReturnFalse(RyanJsonIsBool(item));
-			RyanJsonCheckReturnFalse(expected[idx] == RyanJsonGetBoolValue(item));
-			idx++;
-		}
-
-		// 往返测试
-		char *serializedStr = RyanJsonPrint(jsonRoot, 64, RyanJsonFalse, NULL);
-		RyanJsonDelete(jsonRoot);
-
-		RyanJson_t roundtripJson = RyanJsonParse(serializedStr);
-		RyanJsonFree(serializedStr);
-		RyanJsonCheckReturnFalse(NULL != roundtripJson);
-		RyanJsonCheckReturnFalse(4 == RyanJsonGetArraySize(roundtripJson));
-
-		idx = 0;
-		RyanJsonArrayForEach(roundtripJson, item)
-		{
-			RyanJsonCheckReturnFalse(RyanJsonIsBool(item));
-			RyanJsonCheckReturnFalse(expected[idx] == RyanJsonGetBoolValue(item));
-			idx++;
-		}
-
-		RyanJsonDelete(roundtripJson);
-	}
-
-	return RyanJsonTrue;
-}

+ 0 - 0
test/RFC8259JsonData/i_number_double_huge_neg_exp.json → test/data/rfc8259/i_number_double_huge_neg_exp.json


+ 0 - 0
test/RFC8259JsonData/i_number_huge_exp.json → test/data/rfc8259/i_number_huge_exp.json


+ 0 - 0
test/RFC8259JsonData/i_number_neg_int_huge_exp.json → test/data/rfc8259/i_number_neg_int_huge_exp.json


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików