Skip to content

Commit 5a67d66

Browse files
committed
refactor: 注释规范、细节优化
1 parent 818e6fb commit 5a67d66

113 files changed

Lines changed: 5389 additions & 2662 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ default.profdata
1919
default.profraw
2020
test/fuzzer/corpus
2121
./coverage/**
22+
local_logs/
23+
localLogs/
2224

2325
fuzz-*
24-
RyanJson_Technical_Paper.md
26+
RyanJson_Technical_Paper.md

README.md

Lines changed: 282 additions & 283 deletions
Large diffs are not rendered by default.

RyanJson/RyanJson.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ RyanJsonRealloc_t jsonRealloc = NULL;
1515
* @param userFree 用户自定义 free
1616
* @param userRealloc 用户自定义 realloc,可为 NULL
1717
* @return RyanJsonBool_e 初始化是否成功
18+
* @note 除编译期配置外,其他 API 调用前必须先完成初始化。
1819
*/
1920
RyanJsonBool_e RyanJsonInitHooks(RyanJsonMalloc_t userMalloc, RyanJsonFree_t userFree, RyanJsonRealloc_t userRealloc)
2021
{
@@ -30,6 +31,7 @@ RyanJsonBool_e RyanJsonInitHooks(RyanJsonMalloc_t userMalloc, RyanJsonFree_t use
3031
* @brief 释放 RyanJson 动态分配的内存块
3132
*
3233
* @param block 待释放内存
34+
* @note 需已通过 RyanJsonInitHooks 注册内存钩子。
3335
*/
3436
void RyanJsonFree(void *block)
3537
{
@@ -97,7 +99,7 @@ void RyanJsonDelete(RyanJson_t pJson)
9799
}
98100

99101
/**
100-
* @brief 获取节点规模(标量为1,容器为子节点个数)
102+
* @brief 获取节点规模(标量为 1,容器为子节点个数)
101103
*
102104
* @param pJson 待查询节点
103105
* @return uint32_t 元素数量;参数非法返回 0
@@ -135,15 +137,15 @@ static RyanJson_t RyanJsonDuplicateNode(RyanJson_t pJson)
135137
switch (RyanJsonGetType(pJson))
136138
{
137139
case RyanJsonTypeNull: newItem = RyanJsonCreateNull(key); break;
138-
case RyanJsonTypeBool: // 创建节点时已写入 bool
140+
case RyanJsonTypeBool: // 创建节点时已写入 Bool
139141
newItem = RyanJsonCreateBool(key, RyanJsonGetBoolValue(pJson));
140142
break;
141143

142144
case RyanJsonTypeNumber:
143145
if (RyanJsonIsInt(pJson)) { newItem = RyanJsonCreateInt(key, RyanJsonGetIntValue(pJson)); }
144146
else
145147
{
146-
// Number 节点除了 int32_t 只可能是 double
148+
// Number 节点除 int32_t 外只可能是 Double
147149
RyanJsonCheckAssert(RyanJsonIsDouble(pJson));
148150
newItem = RyanJsonCreateDouble(key, RyanJsonGetDoubleValue(pJson));
149151
}
@@ -249,12 +251,14 @@ RyanJson_t RyanJsonDuplicate(RyanJson_t pJson)
249251
}
250252

251253
/**
252-
* @brief 原地压缩 Json 字符串(移除空白与注释)
254+
* @brief 原地压缩 Json 文本(移除空白与注释)
253255
*
254256
* @param text 可写缓冲区
255257
* @param textLen 缓冲区可写长度
256258
* @return uint32_t 压缩后字符数(不含终止符)
257-
* @note 仅当返回值小于 textLen 时写入 '\0'
259+
* @note 支持移除 `//` 与 `/* ... * /` 形式的注释(非标准 JSON)。
260+
* @note 仅当返回值小于 textLen 时写入 '\0'。
261+
* @note 若缓冲区中存在 '\0',会在该处提前停止处理。
258262
*/
259263
uint32_t RyanJsonMinify(char *text, int32_t textLen)
260264
{
@@ -417,9 +421,9 @@ static RyanJsonBool_e RyanJsonInternalCompare(RyanJson_t leftJson, RyanJson_t ri
417421

418422
// 走到这里说明当前节点对已经比较完成,且若它们是容器,则其子树也已经比较完毕。
419423
// 接下来要在“不使用递归栈”的前提下,找到 DFS 意义上的下一个待比较节点对:
420-
// 1) 优先尝试同父层的下一个兄弟;
421-
// 2) 当前层没有兄弟时,沿内部 parent 回链逐层回溯;
422-
// 3) 一旦回溯到入口 root,说明整棵树已经完全比较结束。
424+
// - 优先尝试同父层的下一个兄弟;
425+
// - 当前层没有兄弟时,沿内部 parent 回链逐层回溯;
426+
// - 回溯到入口 root,说明整棵树已经完全比较结束。
423427
while (1)
424428
{
425429
// 进入本循环时,leftCurrent/rightCurrent 表示“刚完成比较的节点对”。
@@ -435,7 +439,7 @@ static RyanJsonBool_e RyanJsonInternalCompare(RyanJson_t leftJson, RyanJson_t ri
435439
RyanJson_t rightNext = NULL;
436440
if (RyanJsonFalse == RyanJsonIsKey(leftNext))
437441
{
438-
// 无 key 节点只会出现在数组路径上,数组比较是严格按顺序进行的
442+
// 无 key 节点只会出现在 Array 路径上,Array 比较是严格按顺序进行的
439443
// 因而右侧可以直接取当前节点的 next 兄弟,不需要额外查找。
440444
rightNext = RyanJsonGetNext(rightCurrent);
441445
}
@@ -445,7 +449,7 @@ static RyanJsonBool_e RyanJsonInternalCompare(RyanJson_t leftJson, RyanJson_t ri
445449
const char *leftNextKey = RyanJsonGetKey(leftNext);
446450
RyanJson_t rightParent = RyanJsonInternalGetParent(rightCurrent);
447451

448-
// 对象比较是“同层无序、同 key 对齐”语义,不能像数组那样直接依赖 rightCurrent->next:
452+
// Object 比较是“同层无序、同 key 对齐”语义,不能像 Array 那样直接依赖 rightCurrent->next:
449453
// - strict 模式下 key 唯一,按 key 找即可;
450454
// - non-strict 模式下允许重复 key,必须继续保证“同 key 的第 N 次出现”彼此对齐,
451455
// 否则会把后一个重复 key 错配到右侧第一个同名节点,导致 Compare/CompareOnlyKey 语义漂移。
@@ -510,8 +514,8 @@ static RyanJsonBool_e RyanJsonInternalCompare(RyanJson_t leftJson, RyanJson_t ri
510514
leftCurrent = leftCurrent->next;
511515

512516
// leftCurrent 此时已经被抬回父节点,所以这里按“父节点类型”决定右侧如何回溯。
513-
// - array:左右两边始终按相同 sibling 顺序推进,rightCurrent->next 正好也挂回父节点;
514-
// - object:右侧当前节点可能是按 key/序号匹配得到的任意兄弟,未必处在链表尾部,
517+
// - Array:左右两边始终按相同 sibling 顺序推进,rightCurrent->next 正好也挂回父节点;
518+
// - Object:右侧当前节点可能是按 key/序号匹配得到的任意兄弟,未必处在链表尾部,
515519
// 这时不能假设 rightCurrent->next 是父节点,只能显式调用 RyanJsonInternalGetParent。
516520
if (RyanJsonIsArray(leftCurrent)) { rightCurrent = rightCurrent->next; }
517521
else

RyanJson/RyanJson.h

Lines changed: 96 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -36,129 +36,123 @@ extern "C" {
3636
} while (0)
3737
#else
3838
#define RyanJsonCheckAssert(EX)
39-
// 无论是否开启断言都会“求值”,但只有在开启断言时才会 assert。 保留EX的副作用
39+
// 无论是否开启断言都会“求值”,但只有在开启断言时才会 assert。保留 EX 的副作用
4040
#define RyanJsonAssertAlwaysEval(EX) ((void)(EX))
4141
#endif
4242

43-
// Json 的最基础节点,所有 Json 元素都由该节点表示。
44-
// 结构体中仅包含固定的 next 指针,用于单向链表串联。
45-
// 其余数据(flagkey、stringValue、numberValue、doubleValue 等)均通过动态内存分配管理。
43+
// Json 最基础节点,所有 Json 元素都由该节点表示。
44+
// 结构体仅包含固定的 next 指针,用于单向链表串联。
45+
// 其余数据(flag/key/strValue/intValue/doubleValue/objValue 等)均通过动态内存分配管理。
4646
struct RyanJsonNode
4747
{
48-
// 理论上next的低2位也是可以利用起来的
48+
// 理论上 next 的低 2 位也可复用
4949
struct RyanJsonNode *next; // 单链表节点指针
5050

5151
/**
52-
* @brief RyanJson 节点结构体
53-
* 每个节点由链表连接,包含元数据标识 (Flag) 与动态载荷存储区。
52+
* @brief RyanJson 节点结构与载荷布局说明(面向使用者的实现约定)。
53+
* @details
54+
* `struct RyanJsonNode` 本体仅保存 `next` 指针,所有元数据与真实载荷都放在结构体后面的
55+
* 动态区域(payload)。该 payload 的第一个字节就是 flag,紧跟其后的区域根据 flag 语义切分。
56+
* 这种布局能缩小节点本体,并让 key/strValue 的存储策略可切换。
5457
*
55-
* 内存布局:
56-
* [ next指针 | flag(1字节) | padding/指针空间 | 动态载荷区 ]
58+
* Layout(逻辑示意):
59+
* [ next | flag(1B) | keyLenField(0/1/2/4B) | inline/ptr payload ... | value ]
5760
*
58-
* @brief 节点元数据标识 (Flag)
59-
* 紧跟 next 指针后,利用 1 字节位域描述节点类型及存储状态。
61+
* Flag Bits(bit7..bit0):
62+
* - bit0-2: Type(Null/Bool/Number/String/Array/Object)
63+
* - bit3 : Bool/Number 扩展位(Bool: true/false;Number: Int/Double)
64+
* - bit4-5: keyLenField 编码(0/1/2/4 字节)
65+
* - bit6 : strMode(inline/ptr)
66+
* - bit7 : IsLast(1 表示 next 指向 Parent 线索)
6067
*
61-
* flag 位分布定义:
62-
* bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
63-
* -----------------------------------------------------
64-
* strMode KeyLen KeyLen HasKey NumExt Type2 Type1 Type0
68+
* keyLenField(key 长度字段):
69+
* - 位于 flag 之后,长度由 bit4-5 编码决定。
70+
* - 记录 key 的字节长度(不含 '\\0'),按低字节在前写入。
71+
* - 编码值 3 表示字段宽度 4 字节(不是 3 字节)。
72+
* 这是为了用 2 bit 表达 0/1/2/4 四种宽度,详见 RyanJsonInternalDecodeKeyLenField。
6573
*
66-
* 各位含义:
67-
* - bit0-2 : 节点类型
68-
* 000=Unknown, 001=Null, 010=Bool, 011=Number,
69-
* 100=String, 101=Array, 110=Object, 111=Reserved
74+
* Payload(key/strValue 存储策略):
75+
* - 固定字符串区(仅当节点有 key 或类型为 String 时存在):
76+
* 位置:flag 后固定长度 `RyanJsonInlineStringSize`。
77+
* 起点:先写 keyLenField(宽度由 flag 编码 0/1/2/4 字节)。
7078
*
71-
* - bit3 : 扩展位
72-
* Bool 类型:0=false, 1=true
73-
* Number 类型:0=int32_t(4字节), 1=double(8字节)
79+
* - inline 模式:
80+
* 内容:keyLenField 后顺序写 key\\0 与 strValue\\0。
81+
* 变体:String 节点有 strValue;key 为空则仅 strValue\\0;非 String 节点仅 key\\0。
7482
*
75-
* - bit4-5 : Key 长度字段字节数
76-
* 00:无key
77-
* 01:keyLen=1字节 (≤UINT8_MAX)
78-
* 10:keyLen=2字节 (≤UINT16_MAX)
79-
* 11:keyLen=4字节 (≤UINT32_MAX)
83+
* - ptr 模式:
84+
* 指针槽:固定在 flag + RyanJsonKeyFeidLenMaxSize,不随 keyLenField 宽度变化。
85+
* 说明:读写指针用 memcpy,规避潜在非对齐访问。
86+
* 堆区:有 key 则 [key\\0];String 节点再追加 [strValue\\0];无 key 则仅 [strValue\\0]。
8087
*
81-
* - bit6 : 表示key / strValue 存储模式
82-
* 0:inline 模式, 1:ptr 模式
88+
* - 内联判定:
89+
* 条件:key/strValue 字节总和 + keyLenField 宽度 <= `RyanJsonInlineStringSize`。
90+
* 说明:无 key 时 keyLenField 宽度为 0。
8391
*
84-
* - bit7 : 表示是否为当前链表的最后一位,是的话nexe指针会指向Parent(线索化链表)
85-
* 0:next 指向兄弟节点, 1:next 指向Parent节点
92+
* Value 存储位置(与 key 是否存在相关):
93+
* - Number/Array/Object 的 value 位于 payload 中固定偏移处。
94+
* - String 的 value 存在于 key/strValue 区域,不使用 value 偏移。
95+
* - 如果节点带 key,则 value 放在 flag + RyanJsonInlineStringSize 之后;
96+
* 这样无论 inline/ptr 模式,value 偏移都稳定。
97+
* - 若节点无 key,则 value 紧跟 flag。
98+
* - Null/Bool 仅使用 flag 位表达,无额外 payload。
99+
* - 实际偏移以 RyanJsonInternalGetValue 的计算为准。
86100
*
87-
* @brief 动态载荷存储区
88-
* 目的:
89-
* - 在保持 API 易用性和稳定性的同时,最大限度减少 malloc 调用次数。
90-
* - 尤其在嵌入式平台,malloc 代价高昂:不仅有堆头部空间浪费,还会产生内存碎片。
91-
* - 通过利用结构体内的对齐填充 (Padding) 和指针空间,形成一个灵活的缓冲区。
92-
*
93-
* 存储策略:
94-
* 利用结构体内存对齐产生的 Padding(如 Flag 后的空隙)以及原本用于存储指针的空间,形成一个缓冲区
95-
* 若节点包含 key / strValue,则可能有两种方案:
96-
* inline 模式 (小数据优化)
97-
* - 当 (KeyLen + Key + Value) 的总长度 ≤ 阈值时,直接存储在结构体内部。
98-
* - 阈值计算公式:
99-
* 阈值 = Padding + sizeof(void*) + (malloc头部空间的一半),再向上对齐到字节边界。
100-
* 举例:
101-
* - 内存对齐:4字节
102-
* - malloc头部空间:8字节
103-
* - 可用空间 = 3 (flag后padding) + 4 (指针空间) + 4 (malloc头部一半)
104-
* - 向上对齐后得到阈值12字节
105-
* - 存储布局:
106-
* [ KeyLen | Key | Value ]
107-
* 起始地址即为 flag 之后,数据紧凑排列,无需额外 malloc。
108-
*
109-
* ptr 模式 (大数据)
110-
* - 当数据长度 > 阈值时,结构体存储一个指针,指向独立的堆区。
111-
* - 存储布局:
112-
* [ KeyLen | *ptr | Padding ] -> (ptr指向) [ Key | Value ]
113-
* - KeyLen 的大小由 flag 中的长度字段决定 (最多 4 字节)。
114-
* - 这样保证大数据不会撑爆结构体,同时保持 API 一致性。
115-
116-
* 其他类型的存储:
117-
* - null / bool : 由 flag 位直接表示,无需额外空间。
118-
* - number : 根据 flag 扩展位决定存储 int32_t(4字节) 或 double(8字节)。
119-
* - object : 动态分配空间存储子节点,采用链表结构。
120-
*
121-
* 设计考量:
122-
* - malloc 在嵌入式平台的开销:
123-
* * RTT 最小内存管理算法中,malloc 头部约 12 字节(可以考虑tlsf算法头部空间仅4字节,内存碎片也控制的很好,适合物联网应用)。
124-
* * 一个 RyanJson 节点本身可能只有个位数字节,头部空间就让内存占用翻倍。
125-
* - 因此:
126-
* * 小数据尽量 inline 存储,避免二次 malloc。
127-
* * 大数据 fallback 到 ptr 模式,保证灵活性。
128-
* - 修改场景:
129-
* * 理想情况:节点结构体后面直接跟 key/strValue,修改时释放并重新申请节点。
130-
* * 但这样 changKey/changStrValue 接口改动太大,用户层需要修改指针,代价高。
131-
* * 实际策略:提供就地修改接口。
132-
* - 若新值长度 ≤ 原有 inline 缓冲区,直接覆盖。
133-
* - 若超过阈值,自动切换到 ptr 模式,用户层无需关心。
101+
* inline / ptr 简化示意(payload 仅示意,不含 value 区):
102+
* - inline 示例:
103+
* key + strValue: [ flag | keyLenField | key\\0 | strValue\\0 | ... ]
104+
* key only (非 String): [ flag | keyLenField | key\\0 | ... ]
105+
* strValue only (key 为空): [ flag | keyLenField | strValue\\0 | ... ]
106+
* - ptr 示例:
107+
* key + strValue: [ flag | keyLenField | (pad) | ptr | ... ] ptr -> [ key\\0 | strValue\\0 ]
108+
* key only (非 String): [ flag | keyLenField | (pad) | ptr | ... ] ptr -> [ key\\0 ]
109+
* strValue only (key 为空): [ flag | keyLenField | (pad) | ptr | ... ] ptr -> [ strValue\\0 ]
110+
* padding 表示内联区未使用的剩余空间或对齐填充。
134111
*
135-
* 链表结构示例:
136-
* {
137-
* "name": "RyanJson",
138-
* next (
139-
* "version": "xxx",
140-
* next (
141-
* "repository": "https://github.com/Ryan-CW-Code/RyanJson",
142-
* next (
143-
* "keywords": [
144-
* "json",
145-
* next (
146-
* "streamlined",
147-
* next (
148-
* "parser"
149-
* ))
150-
* ],
151-
* next (
152-
* "others": { ... }
153-
* }
112+
* Threaded List(线索化链表):
113+
* - 同层兄弟节点通过 `next` 串联。
114+
* - 最后一个兄弟节点的 `next` 指向父节点,并设置 IsLast=1。
115+
* - 对外遍历必须使用 `RyanJsonGetNext`,它会屏蔽父节点线索。
116+
* - 因此 `next` 不是“永远指向兄弟”,IsLast=1 时它是父节点线索。
117+
*
118+
* Example(Object 子节点链表示意):
119+
* root(Object)
120+
* |
121+
* +-- "a":1 -> "b":2 -> (IsLast=1, next=root)
122+
* RyanJsonGetNext("b") == NULL
123+
*
124+
* Array/Object 子节点与父节点线索示意:
125+
* parent(Object)
126+
* |
127+
* +-- child0 -> child1(Last, next=parent)
128+
* |
129+
* +-- grandChild0 -> grandChild1(Last, next=child1)
130+
*
131+
* Offset 快速对照(从 payload 起点算起,用于理解访问宏偏移):
132+
* - flag: 0
133+
* - keyLenField: 1
134+
* - inline/ptr payload: 1 + keyLenField 宽度
135+
* - value(无 key): 1
136+
* - value(有 key): 1 + RyanJsonInlineStringSize
137+
*
138+
* 修改影响范围提示:
139+
* - 改动 flag 位语义或 keyLenField 编码时,需同步 RyanJsonGetKey/RyanJsonGetStringValue。
140+
* - 改动 payload 布局或内联阈值时,需同步 RyanJsonInternalGetValue 与相关测试。
141+
*
142+
* @note 该布局依赖 flag 位语义与 keyLenField 编码规则。
143+
* @note 常见误解提示:
144+
* - IsLast=1 的节点其 next 不是兄弟,而是父节点线索。
145+
* - 遍历同层必须使用 RyanJsonGetNext,不能直接读 next。
146+
* - value 偏移与是否有 key 强相关,不能用固定结构体偏移理解。
147+
* @note 修改内联阈值或 payload 布局时需同步更新注释与测试。
154148
*/
155149
};
156150

157151
typedef struct RyanJsonNode *RyanJson_t;
158152

159153
typedef enum
160154
{
161-
// 类型标志 占用8字节,剩余一个备用
155+
// 类型标志占用 3 bit(共 8 种,1 个保留)
162156
RyanJsonTypeNull = 1,
163157
RyanJsonTypeBool = 2,
164158
RyanJsonTypeNumber = 3,
@@ -199,7 +193,7 @@ typedef void *(*RyanJsonRealloc_t)(void *block, size_t size);
199193
#define RyanJsonGetType(pJson) ((RyanjsonType_e)RyanJsonGetPayloadFlagField((pJson), 0, RyanJsonGetMask(3)))
200194
#define RyanJsonSetType(pJson, type) (RyanJsonSetPayloadFlagField((pJson), 0, RyanJsonGetMask(3), (RyanjsonType_e)(type)))
201195

202-
// bool跟number用一个字段,因为bool和number类型不会同时存在
196+
// Bool 跟 Number 共用一个字段,因为 Bool 和 Number 类型不会同时存在
203197
#define RyanJsonGetPayloadBoolValueByFlag(pJson) RyanJsonGetPayloadFlagField((pJson), 3, RyanJsonGetMask(1))
204198
#define RyanJsonSetPayloadBoolValueByFlag(pJson, value) RyanJsonSetPayloadFlagField((pJson), 3, RyanJsonGetMask(1), (value))
205199
#define RyanJsonGetPayloadNumberIsDoubleByFlag(pJson) RyanJsonGetPayloadFlagField((pJson), 3, RyanJsonGetMask(1))
@@ -353,7 +347,7 @@ extern RyanJson_t RyanJsonGetObjectByKeys(RyanJson_t pJson, const char *key, ...
353347
* @note 建议调用前先用 `RyanJsonIsObject/RyanJsonIsArray` 做类型校验。
354348
* @note Add/Insert 在 `item` 为游离节点时失败会自动释放 `item`。
355349
* @note `item` 非游离节点时失败不会释放 `item`(保护原树)。
356-
* @note Object key 必须唯一,重复 key 会失败(Parse 也拒绝重复 key)
350+
* @note 严格模式下 Object key 必须唯一;非严格模式允许重复 key,但按 key 的 API 通常只命中首个节点
357351
* @note `AddItem` 仅接受 Array/Object 节点;标量请使用 `AddInt/AddString` 等接口。
358352
*/
359353
#define RyanJsonAddNullToObject(pJson, key) RyanJsonInsert(pJson, RyanJsonAddPosition, RyanJsonCreateNull(key))
@@ -393,9 +387,10 @@ extern RyanJsonBool_e RyanJsonChangeBoolValue(RyanJson_t pJson, RyanJsonBool_e b
393387
* @note 示例:`RyanJsonReplaceByIndex(arr, i, RyanJsonCreateString(NULL, "v"));`
394388
* @note Replace 成功后,`item` 所有权转移到目标树。
395389
* @note Replace 失败后,调用方仍持有 `item`,需自行释放或复用。
390+
* @note `ReplaceByIndex` 可用于 Object,但不推荐。
396391
*/
397392
extern RyanJsonBool_e RyanJsonReplaceByKey(RyanJson_t pJson, const char *key, RyanJson_t item);
398-
extern RyanJsonBool_e RyanJsonReplaceByIndex(RyanJson_t pJson, uint32_t index, RyanJson_t item); // object对象也可以使用,但是不推荐
393+
extern RyanJsonBool_e RyanJsonReplaceByIndex(RyanJson_t pJson, uint32_t index, RyanJson_t item);
399394

400395
#ifdef __cplusplus
401396
}

0 commit comments

Comments
 (0)