rustbinary 0.1.7

A bounded nextjson binary codec with adaptive frames, zero-allocation paths, schema evolution, and authenticated pipelines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
# RustBinary


RustBinary 是一个基于 [nextjson](https://crates.io/crates/nextjson) 的有界二进制编解码库。
类型直接用 nextjson 的 derive 描述,这个库负责把它们变成字节、再变回来。它只做一件窄事:
把结构化数据搬上线或落进文件——对面可能是恶意的,内存可能很紧,而且你希望在解码器跑起来
之前就知道它最多会吃掉多少资源。

这里每个特性都回答一个具体问题——"这个帧我拒得起吗?"、"轻客户端能不能不扫全记录就读
一个字段?"、"这种类型最坏能把我的堆怎么样?"——下文先给答案,再说明代价。本库没有装饰
性功能;凡是取舍而非纯赢的特性,README 都会直说。

## 格式身份


流线格式是**带类型标签的自描述字节流**:每个值前有一字节类型标签,数组与对象以 `0xff`
终结。这就是动态流的格式身份。它不是 bincode 式的紧凑布局,也不是 CBOR 式的长度前缀布局,
也不会悄悄变成这两种中的任何一种。

对静态类型,本库还提供**schema 引导的 compact 简档**(`Config::with_compact_format()` +
`CompactBinary` 派生宏)。它是第二种、且是**追加式**的线格式,不是带标签流的某个模式:
不写逐值类型标签、不写字段名、容器用长度前缀,字节串与浮点数组走 memcpy / 批量端序
快速路径。`Value`、无标签枚举与 `FormatEncoder` 驱动的类型留在自描述流上;静态热路径
可以走 compact 简档。两者永远不会改变彼此的字节。

自描述换来三条整个设计都依赖的性质:

- `Option`、无标签枚举、`nextjson::Value` 无需旁路元数据即可无歧义往返。
- 借用 `&str` 字段直接指向输入 frame,零复制。
- 解码器永远知道一个值在哪里结束、一个 frame 是否完整。

代价是每个值付一字节标签,数值数组每个元素各付一字节标签。这笔税是真实的,而且被
测量过而不是被藏起来——本仓库的基准实验室在同样的数据上把它与 bincode 1、bincode 2、
bincode-next、postcard、rkyv、minicbor 对比,输在哪一行都写得清清楚楚。如果你的负载是
一大堆 `f64` 别的什么都没有,无 schema 的紧凑编解码器在体积和速度上会胜过**带标签流**,
那就去用那个;compact 简档在保持同样有界、受资源策略约束的解码器的同时,基本抹平了
这部分差距。如果你的负载是异构记录、必须无歧义往返、且在没有带外 schema 的情况下可读,
标签税买来的就是这些。

`archive` 特性**不是第二种流格式**。它是独立的存储格式——rkyv 扁平相对指针 + RustBinary
信封——用于只读内存映射对象存储,并独立版本化(`RBARC002`)。流编解码器从不做内存强转、
从不产生相对指针、也从不因为某个 Cargo feature 改变自己的行为。

## 依赖策略


流路径依赖 nextjson,以及可选的派生 crate。可选 pipeline 增加 zstd、
chacha20poly1305、getrandom、zeroize。archive 增加 memmap2、rkyv 与 **blake3**。
供应链策略与密码实现策略是刻意分离的两个决策:

- **供应链**:第三方依赖被限制在真正需要的层(pipeline 编解码、归档存储、Merkle
  哈希),且都是可选 Cargo feature;流核心保持轻依赖。
- **密码实现**:凡是承载安全或完整性语义的原语都用经过审计的 crate,而不是库内
  自研。归档 Merkle 树使用官方 **`blake3`** crate(正式审查过的实现);本库不再
  自带任何哈希原语。熵层完全不需要哈希——它靠重放校验帧,残余检测缺口见熵编码一节。

若威胁模型要求更小的依赖面,`blake3` crate 可替换为任何 `fn(&[u8]) -> [u8; 32]`
实现而不改变归档布局(调用点只有 `src/archive.rs` 里的一处包装);域分离与树几何
归归档模块所有,不归哈希原语所有。

## 分层


|| 模块                   | 默认启用 | 职责                                                                 |
| ----------- | ---------------------- | -------- | -------------------------------------------------------------------- |
| **Core**    | `rustbinary::core`     || 紧凑编解码、资源上限、尾随策略、调用方缓冲区、`no_std`              |
| **Protocol**| `rustbinary::protocol` || Schema 演进、指纹、反射、静态上界、位打包                           |
| **Pipeline**| `rustbinary::pipeline` || CBOR、压缩、加密、有序并行批处理                                    |
| **Sync**    | `sync`                 || rANS 熵编码、差分帧、IBLT 集合协调、信任演算                        |
| **Archive** | `rustbinary::archive`  || Merkle 校验的只读内存映射对象存储                                   |
| **Projection**| `rustbinary::projection`|| 可投影自认证记录,具备投影健全性                                     |

[English](README.md)

## 特性


| 能力                    | 状态       | 说明                                                                        |
| ----------------------- | ---------- | --------------------------------------------------------------------------- |
| nextjson 二进制编解码   | 已实现     | 严格 marker-varint 模式与固定宽度 legacy 模式                               |
| 整数/字符串自适应编码   | 已实现     | 按值选宽度、ZigZag 有符号数、ASCII7 打包                                    |
| `i64` 集合自适应编码    | 已实现     | raw / delta / run-length 三种 frame                                         |
| rANS 熵编码             | 已实现     | 自研静态模型编码器;无哈希重放校验                                          |
| SIMD                    | 仅热路径   | 运行时 AVX2/SSE2/NEON,标量回退;AVX-512/SVE/SME 只探测不使用               |
| 零分配编解码路径        | 已实现     | 精确长度输出与调用方缓冲区                                                   |
| 借用式零复制反序列化    | 已实现     | 嵌套 `&str` 字段直接指向输入 frame                                          |
| 位打包                  | 已实现     | `BitPacked` derive、宽度检查、规范零 padding                                |
| Schema 指纹             | 已实现     | 结构哈希,包含编解码配置(FNV-1a,****密码学)                             |
| 编译期内存上界          | 已实现     | `StaticSize::{MAX_SIZE, PACKED_MAX_BITS, PACKED_MAX_SIZE}`                  |
| RFC 8949 CBOR           | 已实现     | 自研流式 CBOR 编解码(无值树);可选 canonical map 排序                     |
| Schema 演进             | 已实现     | 稳定字段 ID、版本、默认值、跳过未知字段                                     |
| 压缩                    | 已实现     | 自适应 Zstandard;压缩后更大则保留原文                                      |
| 加密                    | 已实现     | XChaCha20-Poly1305、随机 192-bit nonce、认证 header                         |
| 并行序列化              | 已实现     | 有序 batch frame,输出与调度无关                                            |
| 运行时反射              | 已实现     | 编译期生成、无分配的静态元数据(`Reflect`),含逐字段符号表                  |
| 差分帧                  | 已实现     | 基准相对整数差分 + 确定性 HPACK 式动态表                                    |
| IBLT 集合协调           | 已实现     | 自研可逆布隆查找表(Goodrich 与 Mitzenmacher)                              |
| 信任演算                | 已实现     | 类型级认证状态机;未认证接收在类型层面不可表达                              |
| Merkle 归档             | 已实现     | 审计 BLAKE3 树,O(log n) 证明,仅信封打开                                |
| 可投影自认证记录        | 已实现     | 投影健全性、O(log n) 证明、schema 版本绑定、跳过未知字段                   |
| 资源有界解码            | 已实现     | schema 派生 B/A/D/W 成本代数,预算强制的 `decode_bounded` 并带用量证据      |
| 可配置深度上限          | 已实现     | `Config::with_depth_limit` 在编码与解码两侧限制嵌套深度                     |
| 形式化验证              | Kani 证明  | varint/ZigZag 核心 + 投影树几何 + 预算极限代数                              |
| `no_std`                | 已实现     | Compact slice 编解码与调用方缓冲区无需默认 feature                          |
| `no_std + alloc`        | 已实现     | owned 值、指纹、演进、自适应、熵、集合协调                                  |

## 安装


```toml
[dependencies]
rustbinary = "0.1"
nextjson = { version = "0.1", features = ["derive"] }
```

按需启用:

```toml
rustbinary = { version = "0.1", features = ["protocol"] }   # 整个 Protocol 层
rustbinary = { version = "0.1", features = ["sync"] }       # 熵 + 集合协调 + 信任
rustbinary = { version = "0.1", features = ["archive"] }    # Merkle mmap 归档
```

Zstandard 需要构建主机上有 C 工具链;其余全部是纯 Rust,熵编码器零依赖,归档用审计过的
`blake3` crate 做哈希(见依赖策略)。

### 特性矩阵


| Feature            | 默认 | 用途                                                                        |
| ------------------ | ---- | --------------------------------------------------------------------------- |
| `std`              || owned Core 与 I/O API;Pipeline、SIMD、trust 需要                           |
| `alloc`            | via std | 兼容标记;owned API 始终可用                                          |
| `protocol`         || 聚合:adaptive, bit-packing, derive, fingerprint, reflection, schema-evolution, static-size |
| `pipeline`         || 聚合:cbor, compression, encryption, parallel                               |
| `sync`             || 聚合:entropy, reconcile, trust                                             |
| `archive`          || Merkle 校验的 mmap 归档;需要 `std`, rkyv, memmap2                          |
| `derive`           || 重导出过程宏及其运行时 feature                                              |
| `fingerprint`      || 结构指纹运行时与 frame                                                      |
| `reflection`       || 无分配反射运行时                                                            |
| `static-size`      || 编译期上界运行时                                                            |
| `simd`             || 运行时探测与热路径分发;绝不改变线格式                                      |
| `bit-packing`      || 位级 trait 与调用方缓冲区 codec                                             |
| `adaptive`         || 调用方缓冲的自适应字符串/集合;隐含 `bit-packing`                          |
| `entropy`          || 静态模型 rANS 熵编码;隐含 `reflection`                                     |
| `reconcile`        || 差分帧(`delta`)与 IBLT(`ibl`|
| `trust`            || 类型级信任演算与会话状态机                                                  |
| `cbor`             || 经 nextjson 中继的 RFC 8949 CBOR                                           |
| `compression`      || 自适应 Zstandard frame                                                      |
| `encryption`       || XChaCha20-Poly1305、OS 随机、zeroize 密钥                                   |
| `parallel`         || scoped 线程有序 batch frame                                                 |
| `schema-evolution` || 稳定字段 ID 版本化 frame                                                    |
| `bounded`          || `DecodeBounded` 成本代数(B/A/D/W)、`Budget``decode_bounded`;需要 `std``derive` |
| `projection`       || 可投影自认证记录与投影证明;需要 `std` 与审计过的 `blake3`                  |

## 快速开始


```rust
use nextjson::{NsonDeserialize, NsonSerialize};

#[derive(Debug, PartialEq, NsonSerialize, NsonDeserialize)]

struct Packet {
    sequence: u64,
    payload: Vec<u8>,
}

let config = rustbinary::options()
    .with_varint_encoding()
    .with_little_endian()
    .with_limit(8 * 1024 * 1024)
    .with_collection_limit(100_000)
    .reject_trailing_bytes();

let packet = Packet { sequence: 42, payload: vec![1, 2, 3] };
let bytes = config.serialize(&packet)?;
assert_eq!(config.deserialize::<Packet>(&bytes)?, packet);
# Ok::<(), rustbinary::Error>(())

```

`options()` 与顶层函数使用严格紧凑模式:小端、规范 marker-varint、ZigZag 有符号数、
64 MiB 字节上限、1,000,000 元素集合上限、拒绝尾随字节。`legacy_options()` 是旧的
无限定固定宽度模式,只适合可信的内存内数据——它被命名成这样就是让你注意到它。

### 配置链


改变格式的方法返回不同的包装类型,因此变换顺序在类型中可见:

```text
Config -> CborConfig -> CompressedConfig -> EncryptedConfig
```

```rust
let secure = rustbinary::options()
    .with_limit(16 * 1024 * 1024)
    .with_cbor_format()
    .with_deterministic_encoding()
    .with_zstd_compression(3)
    .with_compression_threshold(256)
    .with_encryption(rustbinary::EncryptionKey::new([0xA5; 32]));
# let value = vec![1u32, 2, 3];

let frame = secure.serialize(&value)?;
assert_eq!(secure.deserialize::<Vec<u32>>(&frame)?, value);
# Ok::<(), rustbinary::Error>(())

```

密钥必须来自真实的密钥管理系统;硬编码密钥只适合测试。

## 线格式


该格式编码的是值,不是 Rust 对象内存:没有 padding、原生指针、vtable 或 `repr(Rust)`
布局。

| nextjson 值             | 线表示                                                    |
| ----------------------- | --------------------------------------------------------- |
| `null` / unit / `None`  | tag `0x00`                                                |
| `false` / `true`        | tags `0x01` / `0x02`                                      |
| `u64` / `u128`          | tags `0x03` / `0x04` + 无符号负载                         |
| `i64` / `i128`          | tags `0x05` / `0x06` + ZigZag 负载                        |
| `f64` / `f32`           | tags `0x07` / `0x08` + 配置端序下的 IEEE 754 位           |
| string / char           | tag `0x09` + 编码字节长度 + UTF-8                         |
| array                   | tag `0x0a` + 元素 + `0xff`                                |
| object                  | tag `0x0b` + (`字符串键` + 值) 对 + `0xff`                |

默认模式下整数与长度负载使用规范 marker-varint:

| Marker    | 负载     | 可接受的最小值           |
| --------- | -------- | ------------------------ |
| `0..=250` || 0                        |
| `251`     | 2 字节   | 251                      |
| `252`     | 4 字节   | 65,536                   |
| `253`     | 8 字节   | 4,294,967,296            |
| `254`     | 16 字节  | 18,446,744,073,709,551,616 |
| `255`     | 保留     | 永不接受                 |

解码器拒绝非最小形式、收窄溢出、畸形 UTF-8、非法标签、截断、越限与不允许的尾随
字节。varint 与 ZigZag 机制只存在于一处(`canonical`),编解码两侧共用,Kani 证明其
往返、有界与规范唯一性(见验证一节)。

## 零分配与零复制


`serialized_size` 用一次计数写入完成测量,不分配。`serialize_into_slice` 一次写入
调用方内存并返回精确初始化长度;slice 过小时 `Error::BufferTooSmall` 携带精确所需
大小。

Slice 反序列化把嵌套 `&str` 字段直接从输入借用:

```rust
use nextjson::{NsonDeserialize, NsonSerialize};

#[derive(NsonSerialize, NsonDeserialize)]

struct View<'a> {
    name: &'a str,
    #[njson(borrow)]
    payload: &'a str,
}

let source = View { name: "edge", payload: "frame" };
let config = rustbinary::options().with_limit(4096);
let mut storage = vec![0; config.serialized_size(&source)? as usize];
let written = config.serialize_into_slice(&mut storage, &source)?;
let view: View<'_> = config.deserialize(&storage[..written])?;
assert_eq!(view.payload, "frame");
# Ok::<(), rustbinary::Error>(())

```

该路径上 codec 不分配;用户自定义的 nextjson 实现内部仍可能分配。基于 reader 的解码
要求 owned 目标;把引用返回到临时 reader 缓冲区内是不健全的。打包后的 ASCII7 字符串
展开为 owned 文本;原始自适应 UTF-8 可以 `Cow::Borrowed` 返回。

## 自适应编码


`with_adaptive_encoding()` 保持紧凑模式并增加显式的数据感知 API。frame 携带稳定策略
标签,解码器校验规范 varint、padding、长度、delta 溢出与 RLE 游程。

```rust
let adaptive = rustbinary::options()
    .with_limit(1 << 20)
    .with_adaptive_encoding();

let values = [1000, 1001, 1002, 1003];
let required = adaptive.encoded_i64_slice_size(&values)?;
let mut output = vec![0; required];
adaptive.encode_i64_slice_into_slice(&mut output, &values)?;
assert_eq!(adaptive.decode_i64_vec(&output)?, values);

let encoded = adaptive.encode_string("telemetry/primary")?;
assert_eq!(adaptive.decode_string(&encoded)?, "telemetry/primary");
# Ok::<(), rustbinary::Error>(())

```

字符串 frame 含策略字节、规范解码长度 varint 与负载。策略 0 是原始 UTF-8;策略 1 是
低位在前的 ASCII7 打包,仅当每个字节都是 ASCII 且打包形式严格更小时才选。`i64` 集合
比较三种完整编码——独立 ZigZag 值、首值 + 带检查差分、值/游程对——按文档规定的平局
顺序选严格最小者。

## rANS 熵编码


`with_entropy_encoding()` 启用 `entropy` 模块:自研 rANS 编码器(range Asymmetric
Numeral Systems;16 位重归一化;64 位状态),配以**由 `Reflect` schema 推导的静态模型**。
它不是 zstd 或任何东西的包装:无 C、不传输字典、`no_std` + `alloc`。

模型在不传输任何东西的情况下推导:

- `#[derive(Reflect)]` 逐字段记录精确符号表:枚举变体基数、`#[bits = N]` 范围、
  显式 `#[entropy(symbols = N)]`,或已知原语(`bool` 到 2、`u8`/`i8` 到 256)。
- `Model::from_uniform` 在该精确符号表上建立均匀先验;`Model::from_weights` 从应用
  权重建立静态先验。
- `SchemaModel::from_reflect` 遍历 shape,逐字段产出一个模型。两端编译同一类型,
  因此推导出同一张表;解码器除了它已有的 schema 之外不需要任何东西。

### 不使用哈希如何检测损坏


rANS 流不是自认证的。最终状态检查能拒绝截断和大部分替换,但对"字节变了但仍然能解码"
的情况有非零漏检率。这个模块的第一版用帧内 SHA-256 摘要掩盖了这个问题;这一版把哈希
整个去掉,换成精确的东西:

**重放校验。** 解码器用同一组模型把解码结果重新编码,并要求结果与帧中存储的负载和
最终状态逐字节一致。只有当帧是"它解码出的负载的规范编码"时才被接受——即
`frame == encode(decode(frame))`。

接受规则就是全部保证,失败模式如下:

- 截断或状态/计数损坏会在消费与状态检查处失败。
- 仍可解码的字节变更会产生**不同的**负载,而它的规范编码几乎不可能等于被损坏的帧,
  因此重放不一致、帧被拒绝。
- 残余缺口:被损坏的帧原则上可能是**另一个**负载的规范编码(`frame == encode(x)`  `x` 不同于原文),此时重放会带着错误内容接受它。任何无哈希方案都关不掉这个缺口。
  注意:无密钥的帧摘要****捕获这类意外翻转——重放校验是用这点检测缺口换取零哈希。
  无论重放还是摘要都挡不住能改写帧的攻击者;认证完整性属于 AEAD/信任层。
- raw 回退帧存储字面输入、无冗余,因此只做长度校验。`without_raw_fallback()` 关闭该
  回退,让每个帧都保持编码态、都可被重放校验。

重放校验默认开启,代价是解码时多一次编码(基准表可见)。`without_replay_verification()`
可在传输层已认证字节的场景下关闭。

```rust
use rustbinary::{Model, RansEncoder, RansDecoder};

// 精确 5 符号表每个符号约 log2(5) = 2.32 位,而不是 3 位。
let model = Model::from_uniform(5)?;
let mut encoder = RansEncoder::new();
for _ in 0..100 { encoder.put_symbol(&model, 3)?; }
let (final_state, payload) = encoder.finish();
let mut decoder = RansDecoder::new(final_state, &payload);
let mut kinds = Vec::new();
for _ in 0..100 { kinds.push(decoder.get_symbol(&model)?); }
decoder.finish()?;
kinds.reverse();
# assert!(kinds.iter().all(|&k| k == 3));

# Ok::<(), rustbinary::Error>(())

```

见 [entropy.rs](examples/entropy.rs) 的 schema 驱动流程与带偏斜先验的独立字节 codec
(重复遥测数据 2 倍以上压缩,已在基准 crate 中实测)。

## 位打包


`BitPacked` 为有界字段派生位级 codec。`#[bits = N]` 字段用 `BitValue` 范围校验;其他
字段递归使用 `BitPack`。枚举标签用最小位宽并拒绝未知解码标签。

```rust
#[derive(Debug, PartialEq, rustbinary::BitPacked)]

struct Header {
    #[bits = 3]
    mode: u8,
    enabled: bool,
    #[bits = 7]
    delta: i16,
}

let config = rustbinary::options().with_bit_packing();
let header = Header { mode: 2, enabled: true, delta: -1 };
let packed = config.serialize(&header)?;
assert_eq!(config.deserialize::<Header>(&packed)?, header);
# Ok::<(), rustbinary::Error>(())

```

`BitWriter` 清空输出使末端 padding 为规范零;`BitReader` 拒绝非零 padding 与(配置时)
尾随字节。

## SIMD


`simd_backend()` 在运行时选择 AVX2、SSE2、NEON 或标量路径并缓存结果。自适应 ASCII
分类与单字节 varint 扫描使用这些内核。所有非对齐加载都由安全分发器做边界检查;
unsafe 代码局限于目标特定模块,crate 全局拒绝 `unsafe_op_in_unsafe_fn`。

AVX-512、SVE、SME 由 `hardware_capabilities()` 探测并报告,但没有任何内核使用它们;
更宽的向量对小型 codec 记录未必更快,这里也没有对应的硬件 CI 覆盖。

## 指纹、反射与静态上界


```rust
use rustbinary::StaticSize as _;

#[derive(

    NsonSerialize,
    NsonDeserialize,
    rustbinary::Fingerprint,
    rustbinary::Reflect,
    rustbinary::StaticSize,
)]
struct Header {
    enabled: bool,
    count: u16,
    coordinates: [i32; 2],
}

let config = rustbinary::options().with_fingerprint();
let value = Header { enabled: true, count: 7, coordinates: [2, 3] };
let frame = config.serialize(&value)?;
let _: Header = config.deserialize(&frame)?;
assert!(Header::MAX_SIZE >= frame.len() - 16);
# Ok::<(), rustbinary::Error>(())

```

- `Fingerprint` 对字段与变体名、声明类型、声明顺序、整数编码、有效端序、尾随策略、
  资源上限与 CBOR 确定性模式求哈希。它是基于 FNV-1a 的兼容性标识——**不是**密码学
  哈希,不能替代 AEAD、签名或授权。
- `StaticSize` 为静态类型提供最坏情况普通与位打包大小上界;动态集合刻意不实现它。
- `Reflect` 在编译期生成无分配元数据(类型名、字段、变体),无运行时注册表。每个
  `FieldInfo` 还携带字段符号表(`symbols`),供 rANS schema 模型消费。

## Schema 演进


`schema-evolution` 特性以稳定 schema ID、schema 版本、规范字段 ID 排序、长度分隔字段
与未知字段跳过为值加框。字段 ID 与 schema ID 是显式的协议决策,不是重构时会变化的
哈希。

frame 以 magic `RBE1`、格式版本、flags、schema ID、schema 版本、字段数与
`(field_id, payload)` 条目开头。编码器对 ID 排序并拒绝重复;解码器要求严格递增的 ID,
并在切片前校验全部长度运算。

应用规则:每个兼容类型族一个永久 schema ID;永不把字段 ID 复用于不同含义或不相容的
类型;重命名 Rust 字段时保留 ID;为向后兼容添加可选或默认字段;用编码版本表达刻意的
语义迁移;需要转发或保留时检查未知字段。

## 可投影自认证记录(投影健全性)


`projection` 特性是一种**协议格式,不是紧凑 codec**:规范、自认证的记录,其字段可
针对可信根**逐个**验证与解码,而无需扫描或反序列化记录的其余部分。保证是**投影健全性**:

```text
Verify(P, π, q) = v   ⟹   v = Project_q(Decode(P))
```

`P` 是记录,`q` 是投影查询(字段 ID 集合),`π` 是证明,`Decode(P)` 是唯一规范解码
(唯一性来自格式的规范性:严格递增字段 ID、定宽头部、无重复)。`q` 之外的字段永不
被读取,但其真实性仍被保证:每个字段都绑定进 Merkle 根,篡改或替换未读字段会改变根
并使验证失败。

- **构造**:字段为 `(field_id, payload_len, payload)` 三元组;根是
  `BLAKE3(schema_version ‖ merkle_root)`,因此证明无法针对不同 schema 版本重放。
  `RecordBuilder` 强制规范顺序;`prove` 提取批量证明(最小兄弟集);`verify` 永不
  触碰记录,并要求**可信锚点**(记录根,由认证来源承诺——区块头、带密钥的承诺、
  签名的索引)。本模块绑定完整性;带密钥的认证是调用方的信任锚。`verify_untrusted`
  只检查内部一致性,检测损坏而非替换。
- **诚实的复杂度**`prove` 为 O(n);证明大小最坏 O(|q| · log(n/|q|)),单字段或连续
  区间为 O(log n);`verify` 做 O(|q| + log n) 次哈希运算。Merkle 开销意味着该格式面向
  字段数适中的记录;对负载为主的记录,逐字段哈希成本可忽略。
- **已验证**:Kani 证明 `small_tree_proof_agrees_with_root` 证明聚合/重算协议对任意
  哈希与任意查询在代数上一致;`leaf_count_is_complete_and_bounded` 证明树完整且至多
  翻倍。

## 资源有界解码(成本代数)


`bounded` 特性把 `StaticSize` 从“最坏输出字节数”推进到**可证明的资源语义**。
`#[derive(DecodeBounded)]` 为每个类型生成镜像解析器的成本代数:

```text
B(T)  一次解码 T 最多消费的输入字节数
A(T)  一次解码 T 最多分配的堆字节数
D(T)  最大解析器嵌套深度
W(T)  最坏工作量(读字节 + 逐字段开销)
```

`decode_bounded` 在 [`Budget`] 下运行解码并返回携带**证据**的 `Decoded<T>`
(`ResourceUse`:精确读字节数,加上分配、深度、工作的可证明上界)。代数与解析器同构
——derive 镜像编码器/解码器遍历的确切容器/键结构——因此对静态有界类型,常量是精确的:
这样的解码按构造至多读 `B(T)` 字节、分配 `A(T)` 字节(通常为 0)、嵌套 `D(T)` 层、
做 `W(T)` 工作。

动态类型(`Vec`、`String`、`&str`)对内容相关的资源报告 `usize::MAX`,由运行时预算
强制执行调用方的上限。分配上限是保守且对 derive 覆盖的类型精确的:

- **数据**:输入中物化到堆的每一字节(字符串与字节缓冲本体)都被字节上限约束,因此
  `数据 ≤ 读取`- **结构**:集合后备缓冲与 Box 超出其线数据之外的部分。derive 计算
  `MAX_STRUCTURAL_ELEMENT`——类型中所有集合里最坏的单元素结构分配(`Vec<T>`/`Box<T>`
  `size_of::<T>()``String` 为 0)。一次解码至多有 `D(T)` 层嵌套集合,每层受集合
  上限约束,因此
  `分配 ≤ 读取 + MAX_STRUCTURAL_ELEMENT · D(T) · collection_limit ≤ max_input + max_alloc`  报告的 `alloc_bound` 即该上界。未声明 `MAX_STRUCTURAL_ELEMENT` 的手写 `DecodeBounded`
  实现回退到预算的 `element_structure_bytes` 旋钮(默认 `ELEMENT_STRUCTURE_BYTES` = 64,
  覆盖标准集合形态;宽元组或大内联元素布局应调高)。

每次失败都会报告超出的是哪个维度(`BudgetExceeded`)。这是 DoS 敏感消费者的入口:
区块链节点、enclave、网关从策略或 `Budget::from_type::<T>()`(由代数派生紧致默认值)
选择 `Budget`,并拿到本次解码消耗的证据。`Config::with_depth_limit` 把容器嵌套上限
压到库级 128 之下,并做了钳制,恶意上限不会导致越界索引。

## CBOR、压缩与加密


pipeline 显式有序:序列化、可选压缩、再加密。确定性 CBOR 递归排序规范 map 键。压缩
只在超过大小阈值时运行,且仅当 Zstandard 输出严格更小时才存储。加密把完整 frame 头
(算法、nonce、长度)作为 AEAD 关联数据认证,每次使用全新 192-bit nonce,因此密文
刻意不确定。

- CBOR 是 crate 自有的流式 RFC 8949 编解码器(`src/cbor_codec.rs`):值直接在 `T`  字节之间编码/解码,没有中间值树、没有 JSON 文本往返,解码值的内存峰值就是解码值
  本身。支持定长/不定长容器、bignum tag 2/3、半精度浮点与原生字节串;字节与集合上限
  在解码过程中内联强制。确定性 canonical map 排序是唯一的显式例外,需要物化值树来
  排序键(opt-in)。
- 压缩使用 magic `RBZ1`、记录 raw 与 stored 长度的 24 字节头;解码器拒绝未知 flags、
  不一致长度、解压长度不匹配、截断与越限。即使未配置上限,解压始终有界。
- 加密使用 magic `RBX1``EncryptionKey` 拥有 32 字节、`Debug` 脱敏、析构时 zeroize。
  密钥派生、轮换、存储与访问控制仍是应用/KMS 的职责。

## 并行批处理


`with_parallel_serialization()` 在 scoped 工作线程上编码独立 batch 元素,并输出有序
`u64` 长度表后跟负载区,因此输出字节与调度无关。它面向大型独立记录;小值可能因线程
与合并开销而更慢。

## 带 Merkle 证明的内存映射归档


`archive` 特性是存储格式:rkyv 扁平相对指针布局包在 128 字节 RustBinary 信封里。`build`
产出信封、小端负载,以及覆盖固定大小负载块的 BLAKE3 Merkle 树。哈希使用审计过的
`blake3` crate(见依赖策略);域分离(`LEAF`/`NODE`/`PAD` 标签加大端索引)与树几何
是本模块自己的。信封记录格式版本、flags、非零应用 schema
ID、负载/文件
长度、块大小与块数、Merkle 根与哈希区位置。

两种访问模式:

- `MappedArchive::open` 一次性校验信封、schema、对齐、完整相对指针图,**以及** Merkle
  根;之后的 `root()` 是零复制的。
- `MappedArchive::open_header_only` 只校验信封(O(1)),**没有 `root()`**——类型化零
  复制访问需要完整校验或已验证的证明。`proof_for` 为任意负载字节区间以 O(log n) 构建
  自包含的 `MerkleProof`,从存储的哈希区读取兄弟哈希。`verify()` 由携带的块与兄弟
  哈希重算根;`extract()` 返回已验证字节。证明自包含,因此只持有根的轻客户端可以在
  没有文件其余部分的情况下验证区间。

对固定区间宽度,证明构建与验证都是 O(log n),这把归档验证从一次性成本变成按访问成本。
树是补齐到 2 的幂的完全二叉树,使用域分离哈希,因此根是 `(payload, block_size)` 的纯
函数;默认每块一个 4 KiB 叶子。

打开任何归档都是 `unsafe`:每个进程必须在映射存活期内保持映射文件不可变且不被截断。
发布新文件并原子切换应用引用;绝不在原地更新映射文件。schema ID 由应用拥有,根布局
不兼容变更后必须改变;它是身份检查,不是密码学认证。

## 差分帧与 IBLT 集合协调


`reconcile` 特性面向 gossip/共识传输——接收方往往已持有基准状态:

- `DeltaConfig::encode_delta``value - base` 编码为规范 ZigZag varint。基准带外协商
  (例如最后提交状态的哈希),永不重复。
- `DeltaTable` 是确定性 HPACK 式 FIFO 表。`DeltaConfig::encode_updates` 对已见值发表
  引用,否则发字面量;两侧重放完全相同的插入/逐出规则,因此表状态是更新流的纯函数,
  永不传输。
- `Iblt`(可逆布隆查找表)协调**无序集合**:两个对等方编码各自的集合,一方相减,剥离
  后精确恢复 `mine \ theirs``theirs \ mine`。自研实现,三个 splitmix64 哈希,
  `no_std` + `alloc`,无依赖。

过小 IBLT 的解码以 `Error::Iblt` 干净失败,而不是返回错误数据。

## 信任演算


`trust` 特性把配置链提升为认证状态机:

- `TrustedConfig<C, Untrusted>` 可以反序列化,但只能通过显式命名的
  `deserialize_untrusted`。到认证状态不存在 `From`/`Into` 路径——唯一迁移是
  `authenticate`,它要求一个 `Verifier`- `TrustedConfig<C, Authenticated>` 是唯一拥有朴素 `deserialize` 名字的配置。
  `deserialize_verified` 把结果包进 `Verified`,其唯一构造函数是认证路径。
- `Session<C, Handshake, _>` **没有 `recv` 方法**。只有 `authenticate` 把会话移到认证
  状态后接收才出现;`close` 把会话移到终态 `Closed`,它不暴露任何东西。"反序列化未
  认证数据"因此不可表示,而不只是不鼓励。会话对任意 `Codec` 泛型,因此能与链上每个
  配置组合。

`EncryptedConfig`(XChaCha20-Poly1305)是内置的认证 `Codec`;应用验证器(MAC、签名、
握手证明)实现 `Verifier`。

## 

`serialize_into` 直接写 `std::io::Write`;`deserialize_from` 从 `std::io::Read` 读 owned
值。只有 slice 解码能返回借用值。压缩与加密流 reader 传入 `&mut R` 时消费一个声明帧,
留下后续帧未读,并在分配正文前校验头长度关系与配置的上限。

## 安全与审计


安全姿态是:处处有界、该认证的地方认证、对不受保护的部分诚实交代。

- 每个值以一字节类型标签开头;`0xff` 终结容器。
- 浮点保留 IEEE 754 位模式;端序显式。
- 变长整数拒绝 marker 255 与非最小编码。
- 压缩与加密 frame 校验版本、flags、长度与上限;解密先认证后反序列化。
- 熵帧只有在规范时被接受(无哈希重放);截断与替换被捕获,除了"损坏帧恰好是另一个
  负载的合法帧"这一情况——任何非认证方案都无法区分它。
- 归档携带 Merkle 根;证明与完整打开都做校验。
- 指纹是兼容性检查,不是密码学认证。

本轮审计发现并修复了四个问题:

| 发现 | 严重度 | 修复 |
| ---- | ------ | ---- |
| `delta` 变长整数解码器在恶意输入下可能把最后一组移位越过第 127 位(debug 下 panic / release 下回绕) || 移位前拒绝溢出 `u128` 的组 |
| 仅信封打开未按树几何校验哈希区长度;畸形文件可能驱动 `read_section_hash` 越界 || 在信封解析时校验 `hash_len == (leaf_count - 1) * 32` |
| `build``validate_archive` 各自重复计算了一次 Merkle 树 || 只算一次层级,根取自顶层 |
| `Session` 硬编码 `Config`,而 `TrustedConfig``Codec` 泛型 | 低(耦合) | `Session<C: Codec, S, R>` 并带显式帧长上限 |

在每个不可信边界:设置现实的字节与集合上限、除非外层协议拥有它们否则拒绝尾随字节、
对对抗数据做认证、把解压/反序列化错误当作输入失败。

两条边界值得单独说明。即使未配置字节上限,解压始终有界:解压大小对照 frame 头校验,
并在 `with_no_limit` / legacy 模式下封顶于 crate 级默认值。集合上限作用于序列与 map
元素数;字符串由字节上限约束。

## 验证


### 机器证明(Kani)


`src/canonical.rs` 是规范小端 varint 与 ZigZag 的单一实现,编解码器共用。
`src/kani_proofs.rs` 中的 Kani harness 在完整 `u128`/`i128` 域上符号化证明:

- 往返:对所有 `u128``decode(encode(v)) == v`;ZigZag 双向往返。
- 有界:编码形式至多 17 字节且使用规范(最小)宽度。
- 规范唯一:往返加上 `decode` 的确定性蕴含任意两个不同值不会共享同一编码。

```text
cargo kani -p rustbinary --harness canonical::varint_roundtrip
cargo kani -p rustbinary --harness canonical::zigzag_roundtrip
cargo kani -p rustbinary --harness canonical::zigzag_injective
cargo kani -p rustbinary --harness canonical::varint_bounded_and_minimal
```

归档的 BLAKE3 用官方 BLAKE3 测试向量校验,覆盖单块、块组边界与多块组长度。
测试向量只校验正确性,不是实现级审计——见依赖策略。

### 属性测试(proptest)


`tests/entropy_roundtrip.rs` 与 `tests/canonical_proptest.rs` 对公开 API 做随机化:
字节与均匀符号表往返、逐字节损坏性质(报错,或**不同的**负载——绝不可能是原始负载)、
截断拒绝、非规范形式拒绝、整数往返。

### 模糊测试(cargo-fuzz)


`fuzz/` crate(独立,非 workspace 成员)向紧凑与 legacy 解码器喂任意字节(不得 panic、
每个错误都可分类),并对结构化随机记录做往返。

```text
cargo +nightly fuzz run decode_arbitrary_bytes
cargo +nightly fuzz run decode_structured_roundtrip
```

### 基准测试


`rustbinary-bench/` 是独立 crate(非 workspace 成员),提供两套基准:

- `cargo run --release`——9 次中位数表,在共享数据集(小型头部、遥测帧、大批数值、
  大批字符串)上对比 rustbinary 与 bincode 1、bincode 2、postcard、cbor4ii、
  minicbor,外加独立 rANS 字节 codec 与精确符号表枚举编码。
- `cargo bench --bench lab`——**公平基准实验室**(criterion),覆盖五类负载:
  `homogeneous`(1024 条相同记录)、`heterogeneous`(混合枚举变体)、`borrowed`
  (零拷贝 `&str`)、`adversarial`(10 万元素向量)、`schema-evolution`(用 V2
  类型解码 V1 字节)。对手为 bincode 1、bincode 2、**bincode-next**、postcard、
  rkyv、minicbor——所有 codec 由同一次调用、同一份 `[profile.release]` 编译,
  criterion 报告校准后的中位数统计、`black_box`,并逐对打印编码字节数。

实验室是这个库愿意接受评判的对比。每次 push 到 `main` 都会在全新的 GitHub
Actions 运行器上重跑一次,完整报告存于
[`github_action_benchmark.md`](github_action_benchmark.md)——由
`.github/workflows/benchmark.yml` 重新生成,绝不是过期的截图。以下是最近一次本机
实测(Windows 11、Intel i7-11850H、Rust 1.97、release 配置)的要点,完整逐行
数据见报告文件。

**homogeneous**(1024 条相同记录;单操作中位数):

| codec | encode | decode | bytes |
|---|---|---:|---:|---:|
| rustbinary | 120.5 µs | 272.6 µs | 51716 |
| bincode 1 | 3.2 µs | 2.0 µs | 14344 |
| bincode 2 | 10.1 µs | 4.2 µs | 13829 |
| bincode-next | 7.9 µs | 8.6 µs | 13829 |
| postcard | 22.5 µs | 9.1 µs | 13686 |
| rkyv | 5.7 µs | 445.1 ns | 24584 |

**schema-evolution**(用带追加 `#[serde(default)]` 字段的 V2 类型解码 V1 字节):

| codec | encode-v1 | decode-v1-as-v2 | bytes |
|---|---|---:|---:|---:|
| rustbinary | 306.8 ns | 217.3 ns | 68 |
| bincode 1 | 50.7 ns | error | 26 |
| bincode 2 | 182.3 ns | error | 18 |
| bincode-next | 173.7 ns | 71.3 ns | 18 |
| postcard | 185.7 ns | error | 17 |

读这张表要带着格式身份来读。rustbinary 是带类型标签的自描述格式:在巨型同构数组
上每个值都付一个标签,因此字节与速度都会输——表格毫不掩饰这一点。这笔税换来的
东西在 `schema-evolution` 里看得最清楚:bincode 1、bincode 2、postcard 无法用
V2 类型解码追加字段后的 V1 字节(顺序格式不带字段元数据,缺失值直接报错);
bincode-next 会记录字段数,因此成功;rustbinary 靠稳定字段 ID 成功。同样的诚实
口径也适用于其他组——`borrowed` 不含 bincode 2 与 bincode-next,因为它们的
`decode_from_slice` 需要 `T: for<'de> Deserialize<'de>`,借用型 serde 类型无法满足;
在 `adversarial` 的 10 万 `Vec<u64>` 上 rustbinary 解码约 3.3 ms,而 bincode 1
只要 88.6 µs。这些是自描述的真实代价,如实报告而非遮掩;五类负载的完整逐 codec
表格都在 `github_action_benchmark.md` 里。

数字随机器与构建变化;基准 crate 存在就是为了让对比可以被重新运行,而不是被断言。

### 完整验证命令


```text
cargo fmt --all -- --check
cargo test --workspace --all-targets --all-features
cargo test --workspace --all-features --release
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo doc --workspace --all-features --no-deps
```

### 示例


| 示例                                                       | 覆盖内容                               | 命令                                                                                           |
| ---------------------------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------- |
| [complete.rs]examples/complete.rs                        | 端到端、全特性                         | `cargo run --example complete --all-features`                                                  |
| [core_codec.rs]examples/core_codec.rs                    | 有界核心、缓冲区、借用、错误           | `cargo run --example core_codec`                                                               |
| [zero_copy.rs]examples/zero_copy.rs                      | 嵌套借用与指针证明                     | `cargo run --example zero_copy`                                                                |
| [entropy.rs]examples/entropy.rs                          | schema 驱动 rANS 编码                  | `cargo run --example entropy --features entropy,derive`                                        |
| [merkle_archive.rs]examples/merkle_archive.rs            | Merkle 证明、仅信封访问                | `cargo run --example merkle_archive --features archive`                                        |
| [mmap_archive.rs]examples/mmap_archive.rs                | 校验的 mmap 对象图                     | `cargo run --example mmap_archive --features archive`                                          |
| [delta_sync.rs]examples/delta_sync.rs                    | 差分帧 + IBLT 集合协调                 | `cargo run --example delta_sync --features reconcile`                                          |
| [trust_session.rs]examples/trust_session.rs              | 信任演算 + 会话状态机                  | `cargo run --example trust_session --features trust`                                           |
| [adaptive_zero_alloc.rs]examples/adaptive_zero_alloc.rs  | 自适应决策与调用方缓冲区               | `cargo run --example adaptive_zero_alloc --features adaptive`                                  |
| [secure_pipeline.rs]examples/secure_pipeline.rs          | 确定性 CBOR、压缩、AEAD                | `cargo run --example secure_pipeline --features cbor,compression,encryption`                   |
| [schema_evolution.rs]examples/schema_evolution.rs        | 双向 schema V1/V2                      | `cargo run --example schema_evolution --features schema-evolution`                             |
| [parallel_batch.rs]examples/parallel_batch.rs            | 有序多线程批次                         | `cargo run --example parallel_batch --features parallel`                                       |
| [metadata.rs]examples/metadata.rs                        | 指纹、反射、上界、打包                 | `cargo run --example metadata --features bit-packing,derive,fingerprint,reflection,static-size` |

## docs.rs 与兼容性


包元数据以全特性构建 docs.rs。版本化 wrapper 拒绝未知版本与保留 flags,而不是猜测。
1.0 之前,线格式可能在 minor 版本之间变化,并必须在发布说明中点名。长期部署应锁定
版本、记录完整配置、保留 golden vectors、使用显式 schema ID。存在两个独立版本化的
格式族:流格式(`RBAN` 熵 frame、`RBZ1`/`RBX1` pipeline frame)与归档存储格式
(`RBARC002`)。一个格式族的变更绝不静默影响另一个。

## 非目标


- **** codec 中直接从序列化内存强转任意 Rust 结构体(archive 特性是独立的、
  显式校验的存储格式,有自己的信封与 Merkle 根)。
- 可变共享内存对象图或对映射文件的原地更新。
- 用误导性的 async 门面包装阻塞 I/O。
- 在核心模式自动排序随机化 map。
- 在没有测试内核的情况下宣称 AVX-512/SVE 加速。
- 替代应用密钥管理、授权或 schema 治理。
- 用无 schema 紧凑格式替换带标签流格式:格式身份固定,对体积敏感的路径走 entropy、
  delta 或 archive 层。
- 宣称 FNV-1a 指纹或无密钥重放检查具备密码学强度;认证完整性属于 AEAD/信任层。

## 许可证


RustBinary 以 [Apache License, Version 2.0](LICENSE) 授权。你可以按该许可条款使用、
复制、修改与再分发本项目。再分发必须保留版权声明、许可文本与要求的署名声明。源码
变更应被清晰标识,Apache License 专利条款与免责声明同样适用。

完整法律文本见 [`LICENSE`](LICENSE)。本项目不附带任何形式的担保或条件。