Skip to content

Commit 1275e1d

Browse files
committed
docs: 更新readme中的Json结构体描述
1 parent 8b29a14 commit 1275e1d

104 files changed

Lines changed: 13246 additions & 2895 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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ build
1818
default.profdata
1919
default.profraw
2020
test/fuzzer/corpus
21+
./coverage/**
2122

2223
fuzz-*
2324
RyanJson_Technical_Paper.md

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ RyanJson 的核心在于对内存布局的精细控制,**结构体表示最小
4242
// 其余数据(flag、key、stringValue、numberValue、doubleValue 等)均通过动态内存分配管理。
4343
struct RyanJsonNode
4444
{
45+
// 理论上next的低2位也是可以利用起来的
4546
struct RyanJsonNode *next; // 单链表节点指针
4647

4748
/**
@@ -66,20 +67,19 @@ struct RyanJsonNode
6667
*
6768
* - bit3 : 扩展位
6869
* Bool 类型:0=false, 1=true
69-
* Number 类型:0=int(4字节), 1=double(8字节)
70+
* Number 类型:0=int32_t(4字节), 1=double(8字节)
7071
*
71-
* - bit4 : 是否包含 Key
72-
* 0=无 Key(数组元素)
73-
* 1=有 Key(对象成员)
72+
* - bit4-5 : Key 长度字段字节数
73+
* 00:无key
74+
* 01:keyLen=1字节 (≤UINT8_MAX)
75+
* 10:keyLen=2字节 (≤UINT16_MAX)
76+
* 11:keyLen=4字节 (≤UINT32_MAX)
7477
*
75-
* - bit5-6 : Key 长度字段字节数
76-
* 00=1字节 (≤UINT8_MAX)
77-
* 01=2字节 (≤UINT16_MAX)
78-
* 10=3字节 (≤UINT24_MAX)
79-
* 11=4字节 (≤UINT32_MAX)
78+
* - bit6 : 表示key / strValue 存储模式
79+
* 0:inline 模式, 1:ptr 模式
8080
*
81-
* - bit7 : 表示key / strValue 存储模式
82-
* 0:inline 模式, 1=ptr 模式
81+
* - bit7 : 表示是否为当前链表的最后一位,是的话nexe指针会指向Parent(线索化链表)
82+
* 0:next 指向兄弟节点, 1:next 指向Parent节点
8383
*
8484
* @brief 动态载荷存储区
8585
* 目的:
@@ -90,7 +90,7 @@ struct RyanJsonNode
9090
* 存储策略:
9191
* 利用结构体内存对齐产生的 Padding(如 Flag 后的空隙)以及原本用于存储指针的空间,形成一个缓冲区
9292
* 若节点包含 key / strValue,则可能有两种方案:
93-
* 1. inline 模式 (小数据优化)
93+
* inline 模式 (小数据优化)
9494
* - 当 (KeyLen + Key + Value) 的总长度 ≤ 阈值时,直接存储在结构体内部。
9595
* - 阈值计算公式:
9696
* 阈值 = Padding + sizeof(void*) + (malloc头部空间的一半),再向上对齐到字节边界。
@@ -103,16 +103,16 @@ struct RyanJsonNode
103103
* [ KeyLen | Key | Value ]
104104
* 起始地址即为 flag 之后,数据紧凑排列,无需额外 malloc。
105105
*
106-
* 2. ptr 模式 (大数据)
106+
* ptr 模式 (大数据)
107107
* - 当数据长度 > 阈值时,结构体存储一个指针,指向独立的堆区。
108108
* - 存储布局:
109-
* [ KeyLen | *ptr ] -> (ptr指向) [ Key | Value ]
109+
* [ KeyLen | *ptr | Padding ] -> (ptr指向) [ Key | Value ]
110110
* - KeyLen 的大小由 flag 中的长度字段决定 (最多 4 字节)。
111111
* - 这样保证大数据不会撑爆结构体,同时保持 API 一致性。
112112

113113
* 其他类型的存储:
114114
* - null / bool : 由 flag 位直接表示,无需额外空间。
115-
* - number : 根据 flag 扩展位决定存储 int(4字节) 或 double(8字节)。
115+
* - number : 根据 flag 扩展位决定存储 int32_t(4字节) 或 double(8字节)。
116116
* - object : 动态分配空间存储子节点,采用链表结构。
117117
*
118118
* 设计考量:

RyanJson/RyanJson.c

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,14 @@ static RyanJsonBool_e RyanJsonInternalCompare(RyanJson_t leftJson, RyanJson_t ri
335335
while (1)
336336
{
337337
// 比较当前节点的类型、值与规模
338-
RyanJsonCheckReturnFalse(RyanJsonGetType(leftCurrent) == RyanJsonGetType(rightCurrent));
339-
340-
switch (RyanJsonGetType(leftCurrent))
338+
RyanJsonType_e leftType = RyanJsonGetType(leftCurrent);
339+
RyanJsonCheckReturnFalse(leftType == RyanJsonGetType(rightCurrent));
340+
341+
#if defined(__clang__)
342+
#pragma clang diagnostic push
343+
#pragma clang diagnostic ignored "-Wcovered-switch-default"
344+
#endif
345+
switch (leftType)
341346
{
342347
case RyanJsonTypeNull: break;
343348
case RyanJsonTypeBool:
@@ -371,6 +376,9 @@ static RyanJsonBool_e RyanJsonInternalCompare(RyanJson_t leftJson, RyanJson_t ri
371376
case RyanJsonTypeObject: RyanJsonCheckReturnFalse(RyanJsonGetSize(leftCurrent) == RyanJsonGetSize(rightCurrent)); break;
372377
default: return RyanJsonFalse;
373378
}
379+
#if defined(__clang__)
380+
#pragma clang diagnostic pop
381+
#endif
374382

375383
// 容器节点尝试下沉到子节点继续比较
376384
if (_checkType(leftCurrent, RyanJsonTypeArray) || _checkType(leftCurrent, RyanJsonTypeObject))
@@ -407,35 +415,84 @@ static RyanJsonBool_e RyanJsonInternalCompare(RyanJson_t leftJson, RyanJson_t ri
407415
}
408416
}
409417

410-
// 同层前进,必要时回溯
418+
// 走到这里说明当前节点对已经比较完成,且若它们是容器,则其子树也已经比较完毕。
419+
// 接下来要在“不使用递归栈”的前提下,找到 DFS 意义上的下一个待比较节点对:
420+
// 1) 优先尝试同父层的下一个兄弟;
421+
// 2) 当前层没有兄弟时,沿内部 parent 回链逐层回溯;
422+
// 3) 一旦回溯到入口 root,说明整棵树已经完全比较结束。
411423
while (1)
412424
{
425+
// 进入本循环时,leftCurrent/rightCurrent 表示“刚完成比较的节点对”。
426+
// 如果它已经回到入口 root,且前面也没有可继续下沉的子节点,
427+
// 则表示从 root 出发的整棵树都已匹配完成,可以直接返回 True。
413428
if (leftCurrent == leftJson) { return RyanJsonTrue; }
414429

415-
// 优先定位右侧对应的兄弟节点
430+
// 先看左侧当前节点是否还有同父兄弟。
431+
// 有的话,下一个比较目标就必须切到这个兄弟;右侧则去找“语义上与它对应”的兄弟。
416432
RyanJson_t leftNext = RyanJsonGetNext(leftCurrent);
417433
if (leftNext)
418434
{
419435
RyanJson_t rightNext = NULL;
420-
if (RyanJsonFalse == RyanJsonIsKey(leftNext)) { rightNext = RyanJsonGetNext(rightCurrent); }
436+
if (RyanJsonFalse == RyanJsonIsKey(leftNext))
437+
{
438+
// 无 key 节点只会出现在数组路径上,数组比较是严格按顺序进行的,
439+
// 因而右侧可以直接取当前节点的 next 兄弟,不需要额外查找。
440+
rightNext = RyanJsonGetNext(rightCurrent);
441+
}
421442
else
422443
{
423444
RyanJsonCheckAssert(RyanJsonTrue == RyanJsonIsKey(leftNext));
424445
const char *leftNextKey = RyanJsonGetKey(leftNext);
425446
RyanJson_t rightParent = RyanJsonInternalGetParent(rightCurrent);
426447

427-
// 同序快路径:优先尝试右侧当前节点的直接兄弟,未命中再回退到按 key 查找
428-
RyanJson_t rightCandidate = RyanJsonGetNext(rightCurrent);
429-
if (rightCandidate) { RyanJsonCheckAssert(RyanJsonTrue == RyanJsonIsKey(rightCandidate)); }
430-
if (rightCandidate &&
431-
RyanJsonTrue == RyanJsonInternalStrEq(leftNextKey, RyanJsonGetKey(rightCandidate)))
448+
// 对象比较是“同层无序、同 key 对齐”语义,不能像数组那样直接依赖 rightCurrent->next:
449+
// - strict 模式下 key 唯一,按 key 找即可;
450+
// - non-strict 模式下允许重复 key,必须继续保证“同 key 的第 N 次出现”彼此对齐,
451+
// 否则会把后一个重复 key 错配到右侧第一个同名节点,导致 Compare/CompareOnlyKey 语义漂移。
452+
RyanJsonCheckAssert(NULL != rightParent && RyanJsonTrue == RyanJsonIsObject(rightParent));
453+
#if true == RyanJsonStrictObjectKeyCheck
454+
// strict 模式下 key 唯一,直接按 key 查找即可。
455+
rightNext = RyanJsonGetObjectByKey(rightParent, leftNextKey);
456+
#else
457+
// 非严格模式下允许重复 key,按“同 key + 相同出现序号”精确匹配。
458+
RyanJson_t leftParent = RyanJsonInternalGetParent(leftCurrent);
459+
RyanJsonCheckAssert(NULL != leftParent && RyanJsonTrue == RyanJsonIsObject(leftParent));
460+
461+
// 先在左父节点里统计 leftNext 是该 key 的第几次出现(0-based)。
462+
// 例如 leftParent 子节点是 a,b,a,c,则第二个 a 的序号是 1。
463+
uint32_t leftSameKeyIndex = 0;
464+
RyanJson_t leftScan = RyanJsonGetObjectValue(leftParent);
465+
RyanJsonCheckAssert(NULL != leftScan);
466+
while (leftScan != leftNext)
432467
{
433-
rightNext = rightCandidate;
468+
RyanJsonCheckAssert(RyanJsonTrue == RyanJsonIsKey(leftScan));
469+
if (RyanJsonTrue == RyanJsonInternalStrEq(leftNextKey, RyanJsonGetKey(leftScan)))
470+
{
471+
leftSameKeyIndex++;
472+
}
473+
leftScan = RyanJsonGetNext(leftScan);
474+
RyanJsonCheckAssert(NULL != leftScan);
434475
}
435-
else
476+
477+
// 再在右父节点里找到“同 key 的同序号节点”。
478+
// 这样重复 key 的比较就不是“命中任意一个同名节点”,而是稳定地按出现次序对齐。
479+
uint32_t rightSameKeyIndex = 0;
480+
RyanJson_t rightScan = RyanJsonGetObjectValue(rightParent);
481+
while (rightScan)
436482
{
437-
rightNext = RyanJsonGetObjectByKey(rightParent, leftNextKey);
483+
RyanJsonCheckAssert(RyanJsonTrue == RyanJsonIsKey(rightScan));
484+
if (RyanJsonTrue == RyanJsonInternalStrEq(leftNextKey, RyanJsonGetKey(rightScan)))
485+
{
486+
if (rightSameKeyIndex == leftSameKeyIndex)
487+
{
488+
rightNext = rightScan;
489+
break;
490+
}
491+
rightSameKeyIndex++;
492+
}
493+
rightScan = RyanJsonGetNext(rightScan);
438494
}
495+
#endif
439496
}
440497

441498
RyanJsonCheckReturnFalse(NULL != rightNext);
@@ -445,9 +502,17 @@ static RyanJsonBool_e RyanJsonInternalCompare(RyanJson_t leftJson, RyanJson_t ri
445502
break;
446503
}
447504

448-
// 无兄弟可比时回溯到父层
505+
// 当前层已没有可比较的兄弟,开始回溯。
506+
// 这里故意不用 RyanJsonGetNext(leftCurrent):
507+
// - 公开语义下,没有兄弟时它会返回 NULL;
508+
// - 但内部链表约定中,“最后一个兄弟节点的 next”挂的是父节点,
509+
// 正是这里做迭代式 DFS 回溯所需要的那条内部回链。
449510
leftCurrent = leftCurrent->next;
450511

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

RyanJson/RyanJsonItem.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,19 @@ RyanJsonBool_e RyanJsonChangeKey(RyanJson_t pJson, const char *key)
351351
{
352352
RyanJsonCheckReturnFalse(NULL != pJson && NULL != key);
353353
RyanJsonCheckReturnFalse(RyanJsonIsKey(pJson));
354+
355+
#if true == RyanJsonStrictObjectKeyCheck
356+
// 严格模式下,若节点挂在 Object 下,改 key 不能制造重复 key
357+
if (RyanJsonTrue != RyanJsonInternalStrEq(RyanJsonGetKey(pJson), key))
358+
{
359+
RyanJson_t parent = RyanJsonInternalGetParent(pJson);
360+
if (NULL != parent && RyanJsonTrue == RyanJsonIsObject(parent))
361+
{
362+
RyanJsonCheckReturnFalse(RyanJsonFalse == RyanJsonObjectHasKeyConflict(parent, key, pJson));
363+
}
364+
}
365+
#endif
366+
354367
return RyanJsonInternalChangeString(pJson, RyanJsonFalse, key, RyanJsonIsString(pJson) ? RyanJsonGetStringValue(pJson) : NULL);
355368
}
356369

0 commit comments

Comments
 (0)