Appendix B. 编码和解码示例 (Encoding and Decoding Examples)
本附录提供 QPACK 编码和解码操作示例, 展示 encoder 和 decoder 如何通过各自的流交互, 以及 dynamic table 如何演进.
B.1 带名称引用的 Literal Field Line (Literal Field Line With Name Reference)
此示例展示如何编码一个名称存在于 static table 中的 field line. encoder 使用带 static name reference 的 literal representation.
编码 (Encoding)
待编码的 field line:
custom-key: custom-value
假设 "custom-key" 位于 static table index 15, encoder 生成:
Encoded Field Section:
0x00 0x00 # Required Insert Count = 0, Base = 0
0x50 0x0c 0x63 0x75 0x73 0x74 0x6f 0x6d 0x2d 0x76 0x61 0x6c 0x75 0x65
# Literal with static name reference, index 15, value "custom-value"
解码 (Decoding)
decoder 收到已编码 field section 并进行处理:
- 读取 Required Insert Count = 0 (不需要 dynamic table 条目)
- 读取 Base = 0
- 解码 literal field line representation
- 生成 field line:
custom-key: custom-value
B.2 Dynamic Table 插入 (Dynamic Table Insertions)
此示例展示如何将条目插入 dynamic table, 并在后续 field sections 中引用它们.
Step 1: Encoder 插入条目
encoder 决定将一个新的 field line 插入 dynamic table:
Encoder Stream:
0xc0 0x0c 0x63 0x75 0x73 0x74 0x6f 0x6d 0x2d 0x76 0x61 0x6c 0x75 0x65
# Insert with name reference (static index 15), value "custom-value"
Dynamic Table State (after insertion):
[0] custom-key: custom-value
Insert Count: 1
Step 2: Encoder 引用动态条目
现在 encoder 可以在 field section 中引用此条目:
Encoded Field Section:
0x02 0x00 # Required Insert Count = 1, Base = 0
0x80 # Indexed Field Line, dynamic table index 0
Step 3: Decoder 处理
decoder:
- 接收 encoder stream instruction 并插入该条目
- 接收已编码 field section
- 检查 Required Insert Count = 1 (需要 absolute index 0 处的条目)
- 从 dynamic table index 0 解码 indexed field line
- 生成:
custom-key: custom-value
Step 4: Decoder 确认
Decoder Stream:
0x80 # Section Acknowledgment (stream ID encoded)
这会通知 encoder, decoder 已处理引用 dynamic table 条目 0 的 field section.
B.3 推测性插入 (Speculative Insertion)
此示例展示 encoder 如何预期未来会使用某个条目, 并在该条目实际使用前将其插入 dynamic table.
Step 1: Encoder 推测性插入
Encoder Stream:
0x4a 0x63 0x75 0x73 0x74 0x6f 0x6d 0x2d 0x6b 0x65 0x79
0x0c 0x63 0x75 0x73 0x74 0x6f 0x6d 0x2d 0x76 0x61 0x6c 0x75 0x65
# Insert with literal name "custom-key", value "custom-value"
Dynamic Table State:
[0] custom-key: custom-value
Insert Count: 1
Step 2: Encoder 稍后使用条目
在后续 field section 中:
Encoded Field Section:
0x02 0x00 # Required Insert Count = 1, Base = 0
0x80 # Indexed Field Line, dynamic table index 0
B.4 Duplicate 指令 (Duplicate Instruction)
此示例展示 duplicate instruction, 它会将现有 dynamic table 条目复制到表的前端.
初始 Dynamic Table 状态
[0] custom-key: custom-value
[1] another-key: another-value
Insert Count: 2
Encoder 复制条目
Encoder Stream:
0x01 # Duplicate, relative index 1
Dynamic Table State (after duplication):
[0] another-key: another-value # Duplicated entry
[1] custom-key: custom-value
[2] another-key: another-value # Original entry
Insert Count: 3
被复制的条目成为最新条目 (absolute index 2, relative index 0).
B.5 Post-Base 索引 (Post-Base Indexing)
此示例说明 Post-Base indexing, 其中 encoder 引用在 Base 值之后插入的条目.
Step 1: Encoder 设置 Base
encoder 以 Base = 0 开始编码 field section:
Encoded Field Section Prefix:
0x02 0x80 # Required Insert Count = 1, Base = 0, Sign = 1 (negative delta)
这表示 Required Insert Count = 1, 但 Base = 0, 意味着该 field section 将引用 absolute index 0 处的条目.
Step 2: Encoder 插入并引用
编码过程中, encoder 插入一个新条目:
Encoder Stream:
0xc0 0x0c 0x63 0x75 0x73 0x74 0x6f 0x6d 0x2d 0x76 0x61 0x6c 0x75 0x65
# Insert with name reference (static index 15), value "custom-value"
Dynamic Table State:
[0] custom-key: custom-value
Insert Count: 1
Encoded Field Section (continued):
0x10 # Post-Base Indexed Field Line, Post-Base Index = 0
这会引用 absolute index 0 处的条目 (Base + Post-Base Index = 0 + 0 = 0).
Step 3: Decoder 处理
decoder:
- 接收 encoder stream instruction
- 将条目插入 dynamic table
- 接收带 Post-Base reference 的 field section
- 必要时等待, 直到所需条目可用
- 解码 field line
B.6 Stream Cancellation
此示例展示 decoder 取消某个流, 从而通知 encoder 某个 field section 不会被处理.
场景 (Scenario)
decoder 在流 4 上收到 field section, 但该流在处理完成前被重置.
Decoder Stream:
0x40 0x04 # Stream Cancellation, stream ID = 4
这会通知 encoder:
- 流 4 上的 field section 未被处理
- encoder 不应等待确认
- 只被该 field section 引用的任何 dynamic table 条目可以更早被驱逐
B.7 Insert Count Increment
此示例展示 Insert Count Increment 指令, decoder 使用它来确认多个插入, 而无需在 field sections 中引用这些插入.
场景 (Scenario)
encoder 已插入 5 个条目, 但 decoder 仅通过 Section Acknowledgments 确认了其中 2 个.
Decoder Stream:
0x03 # Insert Count Increment, increment = 3
这会通知 encoder, decoder 已在先前确认的插入之外, 又收到并处理了 3 个 dynamic table 插入.
B.8 完整示例: 多个 Field Sections
此综合示例展示包含多个 field sections 的完整交换.
初始状态 (Initial State)
encoder 和 decoder 均以空 dynamic tables 开始.
Exchange 1: 第一个请求
Encoder Stream:
0xc0 0x0c 0x63 0x75 0x73 0x74 0x6f 0x6d 0x2d 0x76 0x61 0x6c 0x75 0x65
# Insert: custom-key: custom-value
Encoded Field Section (Stream 0):
0x02 0x00 # Required Insert Count = 1, Base = 0
0x80 # Indexed, dynamic index 0
0x51 0x04 0x74 0x65 0x73 0x74 # Literal with static name, value "test"
Dynamic Table State:
[0] custom-key: custom-value
Insert Count: 1
Decoded Field Lines:
custom-key: custom-value
another-key: test
Decoder Stream:
0x80 # Section Acknowledgment for stream 0
Exchange 2: 第二个请求
Encoded Field Section (Stream 4):
0x02 0x00 # Required Insert Count = 1, Base = 0
0x80 # Indexed, dynamic index 0
Decoded Field Lines:
custom-key: custom-value
Decoder Stream:
0x84 # Section Acknowledgment for stream 4
Exchange 3: 驱逐
收到确认后, 如新插入需要, encoder 可以安全地驱逐该条目.
总结 (Summary)
这些示例展示了:
- Literal Representations: 不使用 dynamic table 编码 field lines
- Dynamic Table 管理: 插入, 引用和复制条目
- 同步: Decoder acknowledgments 和 encoder tracking
- Post-Base Indexing: 引用最近插入的条目
- 错误恢复: 对未完成处理使用 stream cancellation
- 高效确认: 使用 Insert Count Increment 进行批量确认
encoder stream 和 decoder stream 之间的交互在允许乱序交付和高效压缩的同时, 确保了正确同步.