Zstandard压缩和'application/zstd'媒体类型
- 状态: Informational
- 发布日期: February 2021
- Stream: IETF
- 废弃了: RFC8478
- 勘误: 无勘误
摘要
Zstandard,简称"zstd"(发音为"zee standard"),是一种无损数据压缩机制.本文档描述了该机制,并注册了通过MIME传输zstd压缩内容时使用的媒体类型、内容编码和结构化语法后缀.
尽管名称中包含"standard"一词,但读者应注意本文档不是互联网标准跟踪规范;它仅出于信息目的发布.
本文档替换并废弃RFC 8478.
本备忘录的状态
本文档不是互联网标准跟踪规范;它出于信息目的发布.
本文档是互联网工程任务组(IETF)的产品.它代表了IETF社区的共识.它已经过公众审查,并获得互联网工程指导组(IESG)的发布批准.并非所有由IESG批准的文档都是任何级别的互联网标准候选;参见RFC 7841第2节.
有关本文档当前状态、任何勘误以及如何提供反馈的信息,可访问 https://www.rfc-editor.org/info/rfc8878.
版权声明
版权所有 (c) 2021 IETF Trust和被确认为文档作者的人员.保留所有权利.
本文档受BCP 78和IETF Trust关于IETF文档的法律规定(https://trustee.ietf.org/license-info)的约束,这些规定在本文档发布之日有效.请仔细阅读这些文档,因为它们描述了您对本文档的权利和限制.从本文档中提取的代码组件必须包含简化BSD许可证文本,如Trust法律规定第4.e节所述,并且按照简化BSD许可证中所述不提供任何担保.
目录
1. 简介 (Introduction)
Zstandard,简称"zstd"(发音为"zee standard"),是一种数据压缩机制,类似于gzip [RFC1952].
尽管名称中包含"standard"一词,但读者应注意本文档不是互联网标准跟踪规范;它仅出于信息目的发布.
本文档描述了Zstandard格式.此外,为了能够传输使用Zstandard压缩的数据对象,本文档注册了媒体类型、内容编码和结构化语法后缀,这些可用于在有效负载中使用此类内容时识别它.
2. 定义 (Definitions)
本文档中其他地方使用的一些术语在此处定义以求清晰.
uncompressed (未压缩): 描述一组任意字节在被压缩之前的原始形式.
compressed (已压缩): 描述一组字节通过此机制处理后的结果.因此原始输入已被压缩.
decompressed (已解压): 描述一组字节通过此机制的逆过程处理后的结果.当成功时,解压后的有效负载与未压缩的有效负载无法区分.
encode (编码): 将数据从一种形式转换为另一种形式的过程;这可能包括压缩,或者可能指本规范中完成的其他转换.
decode (解码): "encode"的逆过程;描述反转先前编码以恢复原始内容的过程.
frame (帧): Zstandard压缩的内容被转换为Zstandard帧.多个帧可以附加到单个文件或流中.帧是完全独立的,有定义的开始和结束,并有一组参数告诉解码器如何解压它.
block (块): 帧封装一个或多个块.每个块包含任意内容,由其头部描述,并具有保证的最大内容大小,该大小取决于帧参数.与帧不同,每个块依赖于先前的块才能正确解码.但是,每个块可以在不等待其后继块的情况下解压,从而允许流式操作.
natural order (自然顺序): 对象或值的序列或排序,这是该类型对象或值的典型顺序.例如,一组唯一整数,如果从集合或序列中的一个元素前进到下一个时,值从不减少,则处于"自然顺序".
本规范中标识符的命名约定是Mixed_Case_With_Underscores(下划线混合大小写).方括号内的标识符表示该标识符在所呈现的上下文中是可选的.
3. 压缩算法 (Compression Algorithm)
本节描述Zstandard算法.
本文档的目的是定义一种无损压缩数据格式,该格式:a) 独立于CPU类型、操作系统、文件系统和字符集,b) 适用于文件压缩以及管道和流式压缩,使用Zstandard算法.规范文本假设具有位和其他原始数据表示级别的编程基本背景.
数据可以被生成或消费,即使对于任意长的顺序呈现的输入数据流,也仅使用预先限定数量的中间存储;因此,它可以用于数据通信.该格式使用Zstandard压缩方法和可选的xxHash-64校验和方法 [XXHASH],用于检测数据损坏.
本规范定义的数据格式不试图允许对压缩数据的随机访问.
除非下文另有说明,符合规范的压缩器必须生成符合此处所述规范的数据集.但是,它不需要支持所有选项.
符合规范的解压器必须能够解压至少一组符合此处所述规范的工作参数集.它也可以忽略信息性字段,例如校验和.当它不支持压缩流中定义的参数时,必须产生明确的错误代码和相关的错误消息,解释哪个参数不受支持.
本规范旨在供将数据压缩为Zstandard格式和/或从Zstandard格式解压数据的软件实现者使用.Zstandard格式由用可移植C语言编写的开源参考实现支持,可在 [ZSTD] 获得.
3.1. 帧 (Frames)
Zstandard压缩数据由一个或多个帧组成.每个帧都是独立的,可以独立于其他帧解压.多个连接帧的解压内容是每个帧解压内容的连接.
为Zstandard定义了两种帧格式:Zstandard帧和可跳过帧.Zstandard帧包含压缩数据,而可跳过帧包含自定义用户元数据.
3.1.1.1. 帧头 (Frame Header)
帧头的大小可变,最小2字节,最大14字节,具体取决于可选参数.Frame_Header的结构如下:
+-------------------------+-----------+
| Frame_Header_Descriptor | 1 byte |
+-------------------------+-----------+
| [Window_Descriptor] | 0-1 byte |
+-------------------------+-----------+
| [Dictionary_ID] | 0-4 bytes |
+-------------------------+-----------+
| [Frame_Content_Size] | 0-8 bytes |
+-------------------------+-----------+
3.1.1.1.1. Frame_Header_Descriptor (帧头描述符)
头部的第一个字节称为Frame_Header_Descriptor.它描述了哪些其他字段存在.解码此字节足以确定Frame_Header的大小.
该字段包含以下位字段(第7位是最高位,第0位是最低位):
- Bit 7-6: Frame_Content_Size_Flag
- Bit 5: Single_Segment_Flag
- Bit 4: (unused) 未使用
- Bit 3: (reserved) 保留
- Bit 2: Content_Checksum_Flag
- Bit 1-0: Dictionary_ID_Flag
Frame_Content_Size_Flag
这是一个2位标志(相当于Frame_Header_Descriptor右移6位),指定是否在头部中提供Frame_Content_Size(解压数据大小).
Single_Segment_Flag
如果设置了此标志,数据必须在单个连续内存段内重新生成.在这种情况下,跳过Window_Descriptor字节,但Frame_Content_Size必然存在.
Content_Checksum_Flag
如果设置了此标志,帧末尾将存在32位Content_Checksum.
Dictionary_ID_Flag
这是一个2位标志(= Frame_Header_Descriptor & 0x3),指示是否在头部中提供字典ID.它还指定此字段的大小为DID_Field_Size:
| Dictionary_ID_Flag | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| DID_Field_Size | 0 | 1 | 2 | 4 |
3.1.1.1.2. Window Descriptor (窗口描述符)
这提供了关于解压帧所需的最小内存缓冲区的保证.此信息对于解码器分配足够的内存很重要.
最小内存缓冲区大小称为Window_Size.它由以下公式描述:
windowLog = 10 + Exponent;
windowBase = 1 << windowLog;
windowAdd = (windowBase / 8) * Mantissa;
Window_Size = windowBase + windowAdd;
最小Window_Size为1 KB.最大Window_Size为(1<<41) + 7*(1<<38)字节,即3.75 TB.
3.1.1.1.3. Dictionary_ID (字典ID)
这是一个可变大小的字段,包含正确解码帧所需的字典ID.此字段是可选的.当不存在时,由解码器决定使用哪个字典.
Dictionary_ID字段大小由DID_Field_Size提供.格式为小端.
3.1.1.1.4. Frame_Content_Size (帧内容大小)
这是原始(未压缩)大小.此信息是可选的.Frame_Content_Size使用可变数量的字节,由FCS_Field_Size提供.格式为小端.
3.1.1.2. 块 (Blocks)
在Magic_Number和Frame_Header之后,有一些数量的块.每个帧必须至少有1个块,但每帧的块数没有上限.
块的结构如下:
+==============+===============+
| Block_Header | Block_Content |
+==============+===============+
| 3 bytes | n bytes |
+--------------+---------------+
Block_Header使用3个字节,使用小端约定编写.它包含三个字段:
| Last_Block | Block_Type | Block_Size |
|---|---|---|
| bit 0 | bits 1-2 | bits 3-23 |
3.1.1.2.1. Last_Block (最后块)
最低位(Last_Block)表示这是否是最后一个块.帧将在此最后一个块之后结束.它可能后跟可选的Content_Checksum.
3.1.1.2.2. Block_Type (块类型)
接下来的2位表示Block_Type.有四种块类型:
| Value | Block_Type |
|---|---|
| 0 | Raw_Block |
| 1 | RLE_Block |
| 2 | Compressed_Block |
| 3 | Reserved |
Raw_Block: 这是一个未压缩的块.Block_Content包含Block_Size字节.
RLE_Block: 这是一个单字节,重复Block_Size次.Block_Content由一个字节组成.在解压端,此字节必须重复Block_Size次.
Compressed_Block: 这是第3.1.1.3节中描述的压缩块.Block_Size是Block_Content的长度,即压缩数据.解压大小未知,但保证其最大可能值.
Reserved: 这不是一个块.此值不能用于当前规范.如果存在这样的值,则被视为损坏的数据,符合规范的解码器必须拒绝它.
3.1.1.2.3. Block_Size (块大小)
Block_Header的高21位表示Block_Size.
当Block_Type为Compressed_Block或Raw_Block时,Block_Size是Block_Content的大小(因此不包括Block_Header).
当Block_Type为RLE_Block时,由于Block_Content的大小始终为1,Block_Size表示此字节必须重复的次数.
3.1.1.2.4. Block_Content和Block_Maximum_Size
Block_Content的大小受Block_Maximum_Size限制,它是以下两者中的较小者:
- Window_Size
- 128 KB
Block_Maximum_Size对于给定帧是常数.此最大值适用于帧中任何块的解压大小和压缩大小.
3.1.1.3. 压缩块 (Compressed Blocks)
要解压压缩块,必须从Block_Header中的Block_Size字段提供压缩大小.
压缩块由两个部分组成:Literals_Section(字面量部分,第3.1.1.3.1节)和Sequences_Section(序列部分,第3.1.1.3.2节).然后将这两部分的结果组合起来,在序列执行(第3.1.1.4节)中产生解压数据.
要解码压缩块,需要以下元素:
- 先前解码的数据,最多到Window_Size的距离,或到帧开始,以较小者为准
- 来自前一个Compressed_Block的"最近偏移量"列表
- 前一个Huffman树(Treeless_Literals_Block类型需要)
- 前一个有限状态熵(FSE)解码表(Repeat_Mode需要),用于每种符号类型(字面量长度码、匹配长度码、偏移量码)
3.1.1.3.1. Literals_Section_Header (字面量部分头)
所有字面量都重新组合在块的第一部分.它们可以先解码,然后在序列执行期间复制,或者可以在序列执行期间即时解码.
字面量可以未压缩存储或使用Huffman前缀码压缩.压缩时,可以存在可选的树描述,后跟1个或4个流.
此字段描述字面量如何打包.它是一个字节对齐的可变大小位字段,范围从1到5个字节,使用小端约定.
Literals_Block_Type字段使用第一个字节的最低两位,描述四种不同的块类型:
| Literals_Block_Type | Value |
|---|---|
| Raw_Literals_Block | 0 |
| RLE_Literals_Block | 1 |
| Compressed_Literals_Block | 2 |
| Treeless_Literals_Block | 3 |
Raw_Literals_Block: 字面量未压缩存储.
RLE_Literals_Block: 字面量由重复Regenerated_Size次的单字节值组成.
Compressed_Literals_Block: 这是标准Huffman压缩块,以Huffman树描述开始.
Treeless_Literals_Block: 这是Huffman压缩块,使用来自前一个Compressed_Literals_Block的Huffman树,或者如果没有前一个Huffman压缩字面量块则使用字典.
3.1.1.3.2. Sequences_Section (序列部分)
压缩块是一系列序列.序列是字面量复制命令,后跟匹配复制命令.字面量复制命令指定长度.它是要从Literals_Section复制(或提取)的字节数.匹配复制命令指定偏移量和长度.
Sequences_Section重新组合解码命令所需的所有符号.有三种符号类型:字面量长度码、偏移量码和匹配长度码.它们在单个"位流"中交错编码.
Sequences_Section以头部开始,后跟每种符号类型的可选概率表,然后是位流.
3.1.1.3.2. 序列部分 (Sequences Section)
Sequences_Section_Header (序列部分头)
此头部由两个项目组成:
- Number_of_Sequences(序列数)
- Symbol_Compression_Modes(符号压缩模式)
Number_of_Sequences是可变大小字段,使用1到3个字节.如果第一个字节是"byte0":
- 如果 (byte0 == 0):没有序列
- 如果 (byte0 < 128):Number_of_Sequences = byte0,使用1字节
- 如果 (byte0 < 255):Number_of_Sequences = ((byte0 - 128) << 8) + byte1,使用2字节
- 如果 (byte0 == 255):Number_of_Sequences = byte1 + (byte2 << 8) + 0x7F00,使用3字节
Symbol_Compression_Modes是单个字节,定义每种符号类型的压缩模式:
| Bit Number | Field Name |
|---|---|
| 7-6 | Literal_Lengths_Mode |
| 5-4 | Offsets_Mode |
| 3-2 | Match_Lengths_Mode |
| 1-0 | Reserved |
Compression_Mode的枚举:
| Value | Compression_Mode |
|---|---|
| 0 | Predefined_Mode |
| 1 | RLE_Mode |
| 2 | FSE_Compressed_Mode |
| 3 | Repeat_Mode |
Predefined_Mode: 使用预定义的FSE分布表.
RLE_Mode: 表描述由单个字节组成,包含符号的值.此符号将用于所有序列.
FSE_Compressed_Mode: 标准FSE压缩.将存在分布表.
Repeat_Mode: 将再次使用在前一个Number_Of_Sequences > 0的Compressed_Block中使用的表,或者如果这是第一个块,则使用字典中的表.
序列代码 (Sequence Codes)
每个符号是其自己上下文中的代码,指定Baseline和Number_of_Bits.代码经FSE压缩,并与原始附加位在同一位流中交错.
字面量长度码范围从0到35,定义0到131071字节的长度.
匹配长度码范围从0到52,定义3到131074字节的长度.
偏移量码范围从0到N.解码器可以自由限制其支持的最大N值.建议至少支持22.
3.1.1.4. 序列执行 (Sequence Execution)
一旦字面量和序列被解码,它们就被组合起来产生块的解码内容.
每个序列由元组(literals_length, offset_value, match_length)组成,如Sequences_Section(第3.1.1.3.2节)中所述解码.要执行序列,首先从解码的字面量中复制literals_length字节到输出.
然后,从先前解码的数据中复制match_length字节.要复制的偏移量由offset_value确定:
- 如果Offset_Value > 3,则偏移量为Offset_Value - 3
- 如果Offset_Value为1-3,则偏移量是特殊的重复偏移量值.参见第3.1.1.5节了解在这种情况下如何确定偏移量
偏移量是从当前位置(复制字面量之后)定义的,因此偏移量为6且匹配长度为3意味着应该从6字节前复制3字节.
注意,所有导向先前解码数据的偏移量必须小于Frame_Header_Descriptor(第3.1.1.1.1节)中定义的Window_Size.
3.1.1.5. 重复偏移量 (Repeat Offsets)
如上所述,前三个值定义重复偏移量;我们将它们称为Repeated_Offset1、Repeated_Offset2和Repeated_Offset3.它们按最近使用顺序排序,Repeated_Offset1表示"最近使用的".
如果offset_value为1,则使用的偏移量为Repeated_Offset1,依此类推.
有一个例外:当当前序列的literals_length为0时,重复偏移量移位1,因此offset_value为1意味着Repeated_Offset2,offset_value为2意味着Repeated_Offset3,offset_value为3意味着Repeated_Offset1 - 1_byte.
对于第一个块,起始偏移量历史填充为以下值:Repeated_Offset1 (1)、Repeated_Offset2 (4)和Repeated_Offset3 (8),除非使用字典,在这种情况下它们来自字典.
然后每个块从最近的Compressed_Block的结束值获得其起始偏移量历史.注意,不是Compressed_Block的块被跳过;它们不对偏移量历史做出贡献.
在执行Compressed_Block的序列期间,Repeated_Offsets的值保持更新,以便它们始终表示三个最近使用的偏移量.为了实现这一点,它们在执行每个序列后以以下方式更新:
当序列的offset_value不引用Repeated_Offsets之一时(当其值大于3时,或当其值为3且序列的literals_length为零时),Repeated_Offsets的值向后移位一位,Repeated_Offset1采用刚刚使用的偏移量的值.
否则,当序列的offset_value引用Repeated_Offsets之一时(当其值为1或2时,或当其值为3且序列的literals_length非零时),Repeated_Offsets被重新排序,使得Repeated_Offset1采用使用的Repeated_Offset的值,现有值从第一个Repeated_Offset向后推到offset_value选择的Repeated_Offset.这有效地对这些偏移量的值执行单步环绕旋转,使它们的顺序再次反映它们使用的最近性.
3.1.2. 可跳过帧 (Skippable Frames)
+==============+============+===========+
| Magic_Number | Frame_Size | User_Data |
+==============+============+===========+
| 4 bytes | 4 bytes | n bytes |
+--------------+------------+-----------+
可跳过帧允许将用户定义的元数据插入到连接帧流中.
本规范中定义的可跳过帧与[LZ4]中的可跳过帧兼容.
从符合规范的解码器角度来看,可跳过帧只需要被跳过,其内容被忽略,在可跳过帧之后恢复解码.
应该注意的是,可跳过帧可用于为连接帧流添加水印,嵌入任何类型的跟踪信息(甚至只是一个通用唯一标识符UUID).对此类可能性持警惕态度的用户应扫描连接帧流,以尝试检测此类帧进行分析或删除.
字段如下:
Magic_Number: 4字节,小端格式.值:0x184D2A5?,表示从0x184D2A50到0x184D2A5F的任何值.所有16个值都有效以识别可跳过帧.
Frame_Size: 这是以下User_Data的大小(以字节为单位)(不包括魔数和大小字段本身).此字段使用4字节表示,小端格式,无符号32位.这意味着User_Data不能大于(2^32 -1)字节.
User_Data: 此字段可以是任何内容.数据将被解码器跳过.
4. 熵编码 (Entropy Encoding)
Zstandard格式使用两种类型的熵编码:FSE和Huffman编码.Huffman用于压缩字面量,而FSE用于所有其他符号(Literals_Length_Code、Match_Length_Code和偏移量码)以及压缩Huffman头部.
4.1. FSE
FSE是Finite State Entropy(有限状态熵)的缩写,是一种基于[ANS]的熵编解码器.FSE编码/解码涉及在符号之间携带的状态,因此解码必须以与编码相反的方向进行.因此,所有FSE位流都是从末尾到开头读取的.注意,流中位的顺序没有反转;它们只是按照与写入时相反的顺序读取.
有关FSE的更多详细信息,请参见"FiniteStateEntropy" [FSE].
FSE解码涉及具有2的幂大小并包含三个元素的解码表:Symbol(符号)、Num_Bits(位数)和Baseline(基线).表大小的以2为底的对数是其Accuracy_Log(精度对数).FSE状态值表示此表中的索引.
要获得初始状态值,从流中以小端值形式消耗Accuracy_Log位.流中的下一个符号是该状态的表中指示的Symbol.要获得下一个状态值,解码器应从流中以小端值形式消耗Num_Bits位并将其添加到Baseline.
4.1.1. FSE表描述 (FSE Table Description)
要解码FSE流,需要构建解码表.Zstandard格式按照此处所述编码FSE表描述.
FSE分布表描述了从0到最后存在的符号(包括)的所有符号在(1 << Accuracy_Log)的归一化比例上的概率.注意,必须有两个或更多具有非零概率的符号.
位流以小端方式向前读取.它首先报告其操作的比例.如果low4bits表示第一个字节的最低4位,则Accuracy_Log = low4bits + 5.
然后是每个符号值,从0到最后存在的.每个字段使用的位数是可变的,取决于剩余概率和解码值.
4.2. Huffman编码 (Huffman Coding)
Zstandard Huffman编码流与FSE位流类似,是向后读取的.因此,要找到位流的开始,需要知道Huffman编码流的最后一个字节的偏移量.
在写入包含信息的最后一位后,压缩器写入单个1位,然后用0位填充字节的其余部分.因此,压缩位流的最后一个字节不能为0.
解压时,包含填充的最后一个字节是要读取的第一个字节.解压器需要跳过最多7位的0填充以及第一个出现的1位.之后,位流的有用部分开始.
位流以小端顺序包含Huffman编码符号,代码由以下方法定义.
4.2.1. Huffman树描述 (Huffman Tree Description)
前缀编码通过位序列(码字)表示来自先验已知字母表的符号,每个符号一个码字,使得不同的符号可以由不同长度的位序列表示,但解析器总是可以明确地逐符号解析编码字符串.
给定具有已知符号频率的字母表,Huffman算法允许使用任何可能的前缀码中最少位数构建最优前缀码.
前缀码不得超过最大码长.更多位提高准确性但产生更大的头部大小并需要更多内存或更复杂的解码操作.本规范将最大码长限制为11位.
5. 字典格式 (Dictionary Format)
Zstandard与"原始内容"字典兼容,不受任何格式限制,只是它们必须至少为8字节.这些字典的功能就像它们只是格式化字典的内容部分一样.
但是,参考实现中由"zstd --train"创建的字典遵循此处描述的特定格式.
字典不包含在压缩内容中,而是带外提供.也就是说,Dictionary_ID标识应使用哪个字典,但本规范不描述在压缩或解压期间使用之前获取字典的机制.
字典有大小,由缓冲区限制或文件大小定义.一般格式为:
+==============+===============+================+=========+
| Magic_Number | Dictionary_ID | Entropy_Tables | Content |
+==============+===============+================+=========+
Magic_Number: 4字节ID,值0xEC30A437,小端格式.
Dictionary_ID: 4字节,以小端格式存储.Dictionary_ID可以是除0之外的任何值(0表示没有Dictionary_ID).它由解码器用于检查它们是否使用正确的字典.
Entropy_Tables: 遵循与压缩块中的表相同的格式.它们按以下顺序存储:字面量的Huffman表、偏移量的FSE表、匹配长度的FSE表和字面量长度的FSE表.这些表填充重复统计字面量模式和序列解码的重复分布模式.最后是3个偏移量值,填充重复偏移量(而不是使用{1,4,8}),按顺序存储,每个4字节小端,总共12字节.每个重复偏移量必须小于字典大小.
Content: 字典的其余部分是其内容.内容充当要压缩或解压的数据前面的"过去",因此可以在序列命令中引用.只要从此帧解码的数据量小于或等于Window_Size,序列命令就可以指定比到目前为止解码输出的总长度更长的偏移量,以引用回字典,甚至是偏移量大于Window_Size的字典部分.
6. 字典的使用 (Use of Dictionaries)
正在探索使用字典与zstd的配置.例如,参见[DICT-SEC].可能的结果将是针对不同用例优化的经过充分测试的字典的注册表以及每个字典的标识符,可能还有使用未注册字典的私有协商机制.
为确保与zstd有效负载使用字典的未来规范兼容,特别是与MIME兼容,此处注册的媒体类型编码的内容不应使用字典.此要求的例外可能是上面建议的私有字典协商,这不是本规范的一部分.
7. IANA注意事项 (IANA Considerations)
IANA已更新两个先前存在的注册并如下所述进行了一个新的注册.
7.1. 'application/zstd'媒体类型
'application/zstd'媒体类型标识使用zstd压缩压缩的数据块.数据是本文档中描述的字节流.IANA已将以下内容添加到"媒体类型"注册表:
Type name: application
Subtype name: zstd
Required parameters: N/A
Optional parameters: N/A
Encoding considerations: binary
Security considerations: 参见RFC 8878第8节
Interoperability considerations: N/A
Published specification: RFC 8878
Applications which use this media type: 数据大小是问题的任何地方
Fragment identifier considerations: 未为此类型定义片段标识符
Additional information:
- Magic number(s): 4字节,小端格式.值:0xFD2FB528
- File extension(s): zst
- Macintosh file type code(s): N/A
Person & email address to contact for further information: Yann Collet [email protected]
Intended usage: common
Author: Murray S. Kucherawy
Change Controller: IETF
7.2. 内容编码 (Content Encoding)
IANA已将以下条目添加到"超文本传输协议(HTTP)参数"注册表中的"HTTP内容编码注册表":
Name: zstd
Description: 使用Zstandard协议压缩的字节流
Reference: RFC 8878
7.3. 结构化语法后缀 (Structured Syntax Suffix)
IANA已将以下内容注册到"结构化语法后缀"注册表中:
Name: Zstandard
+suffix: +zstd
Encoding Considerations: binary
Security Considerations: 参见RFC 8878第8节
Contact: 请参阅'application/zstd'媒体类型的作者
Author/Change Controller: IETF
7.4. 字典 (Dictionaries)
正在进行的工作包括开发将优化特定类型数据的压缩和解压的字典.为公共使用指定此类字典将需要从第3.1.1.1.3节中描述的保留范围注册代码点,并将其与特定字典关联.
目前,没有为公共使用发布此类字典,因此本文档没有立即请求IANA创建此类注册表.
8. 安全考虑 (Security Considerations)
任何数据压缩方法都涉及数据中冗余的减少.Zstandard也不例外,通常的预防措施适用.
永远不应该将内容必须保密的消息与第三方生成的消息一起压缩.这种压缩可用于通过分析熵减少来猜测秘密消息的内容.这在压缩比率信息泄漏简化(CRIME)攻击[CRIME]中得到了证明.
解码器必须展示能够检测和防止压缩帧中任何类型的数据篡改触发系统故障的能力,例如读取或写入超出允许的内存范围.这可以通过实现语言或仔细的边界检查来保证.特别值得注意的是Number_of_Sequences值的编码,这些值导致解码器读入块头部(及以后),以及指示Frame_Content_Size小于实际解压数据的情况,试图触发缓冲区溢出.强烈建议对解码器实现进行模糊测试(即提供无效、意外或随机输入并验证安全操作),以测试和强化它们检测坏帧的能力并在没有任何不利系统副作用的情况下处理它们.
攻击者可能提供具有不合理内存要求的正确形成的压缩帧.解码器必须始终控制内存要求并强制执行一些(特定于系统的)限制,以保护内存使用免受此类情况的影响.
压缩可以通过在各种相关内容有效负载上训练字典来优化.然后,解压有效负载时必须提供此字典.虽然本文档未指定如何获取给定压缩有效负载的字典,但值得注意的是,第三方字典可能与解码器发生意外交互,导致可能的内存或其他资源耗尽攻击.我们期望在即将发布的关于字典获取和传输的RFC的安全考虑部分中更详细地讨论此类主题,但出于谨慎起见,现在突出显示此问题.
如第3.1.2节所述,可以在可跳过帧中存储任意用户元数据.虽然在数据解压期间忽略此类帧,但它们可以用作水印来跟踪压缩有效负载的路径.
附录A. 预定义代码的解码表 (Decoding Tables for Predefined Codes)
本附录包含预定义字面量长度、匹配长度和偏移量码的FSE解码表.这些表是使用第4.1.1节中给出的算法构建的.此处的表可用作示例,以交叉检查实现是否正确构建了其解码表.
A.1. 字面量长度码表 (Literals Length Code Table)
解码表使用6位精度对数(64个状态).
short literalsLength_defaultDistribution[36] =
{ 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1,
-1,-1,-1,-1
};
A.2. 匹配长度码表 (Match Length Code Table)
解码表使用6位精度对数(64个状态).
short matchLengths_defaultDistribution[53] =
{ 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1,-1,
-1,-1,-1,-1,-1
};
A.3. 偏移量码表 (Offset Code Table)
解码表使用5位精度对数(32个状态)并支持最大N值为28,允许最大偏移量值为536,870,908.
如果压缩块中的任何序列需要大于此的偏移量,则无法使用默认分布来表示它.
short offsetCodes_defaultDistribution[29] =
{ 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,-1,-1,-1,-1,-1
};
完整的状态表包括State、Symbol、Number_Of_Bits和Base列,详细信息请参见RFC 8878完整文档.
附录B. 自RFC 8478以来的变更 (Changes since RFC 8478)
以下是本文档相对于RFC 8478的变更:
- 应用了勘误表[Err5786]和[Err6303]
- 阐明了关于字典的前向兼容性
- 阐明了Block_Maximum_Size的应用
- 添加了结构化媒体类型后缀注册
- 阐明了内容校验和始终为4字节
- 阐明了保留和损坏输入的处理
- 向媒体类型注册添加了片段标识符注意事项
致谢 (Acknowledgments)
zstd由Yann Collet开发.
Felix Handte和Nick Terrell提供的反馈进入了此修订版和RFC 8478.RFC 8478还收到了Bobo Bose-Kolanu、Kyle Nekritz和David Schleimer的贡献.
作者地址 (Authors' Addresses)
Yann Collet
Facebook
1 Hacker Way
Menlo Park, CA 94025
United States of America
Email: [email protected]
Murray S. Kucherawy (editor)
Facebook
1 Hacker Way
Menlo Park, CA 94025
United States of America
Email: [email protected]