跳到主要内容

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 并进行处理:

  1. 读取 Required Insert Count = 0 (不需要 dynamic table 条目)
  2. 读取 Base = 0
  3. 解码 literal field line representation
  4. 生成 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:

  1. 接收 encoder stream instruction 并插入该条目
  2. 接收已编码 field section
  3. 检查 Required Insert Count = 1 (需要 absolute index 0 处的条目)
  4. 从 dynamic table index 0 解码 indexed field line
  5. 生成: 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:

  1. 接收 encoder stream instruction
  2. 将条目插入 dynamic table
  3. 接收带 Post-Base reference 的 field section
  4. 必要时等待, 直到所需条目可用
  5. 解码 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)

这些示例展示了:

  1. Literal Representations: 不使用 dynamic table 编码 field lines
  2. Dynamic Table 管理: 插入, 引用和复制条目
  3. 同步: Decoder acknowledgments 和 encoder tracking
  4. Post-Base Indexing: 引用最近插入的条目
  5. 错误恢复: 对未完成处理使用 stream cancellation
  6. 高效确认: 使用 Insert Count Increment 进行批量确认

encoder stream 和 decoder stream 之间的交互在允许乱序交付和高效压缩的同时, 确保了正确同步.