压缩算法LZ4

LZ4简介

LZ4 是无损压缩算法,提供每个核 大于 500 MB/s 的压缩速度,可通过多核 CPU 进行扩展。LZ4算法解压速度极快,单核解压速度达到GB/s,通常达到多核系统的 RAM 速度限制。

压缩速度可以动态调整,选择一个“加速”因子,牺牲压缩比以获得更快的速度。另外还提供了一个高压缩率函数LZ4_HC,牺牲压缩时间换取更高的压缩比,但解压缩速度不会受影响。

LZ4实现了两种格式:

Benchmarks

从官方提供的Benchmarks数据,可以看出**LZ4 default (v1.9.0)LZ4 HC -9 (v1.9.0)**压缩率和压缩速度不一致,但是解压缩速度却相同。

CompressorRatioCompressionDecompression
memcpy1.00013700 MB/s13700 MB/s
LZ4 default (v1.9.0)2.101780 MB/s4970 MB/s
LZO 2.092.108670 MB/s860 MB/s
QuickLZ 1.5.02.238575 MB/s780 MB/s
Snappy 1.1.42.091565 MB/s1950 MB/s
[Zstandard] 1.4.0 -12.883515 MB/s1380 MB/s
LZF v3.62.073415 MB/s910 MB/s
[zlib] deflate 1.2.11 -12.730100 MB/s415 MB/s
LZ4 HC -9 (v1.9.0)2.72141 MB/s4900 MB/s
[zlib] deflate 1.2.11 -63.09936 MB/s445 MB/s

LZ4 Block Format

LZ4 是一种 LZ77 型压缩器,具有固定的、面向字节的编码格式,

块格式

LZ4压缩块由序列组成。分为三个区域:

  • Literals:指没有重复、首次出现的字节流,即不可压缩的部分
  • Match:指重复项,可以压缩的部分
  • Token:记录literal长度,match长度。

LZ4格式

简单的示例如下:

输入:abcde_bcdefgh_abcdefghxxxxxxx
输出:tokenabcde_(5,4)fgh_(14,5)fghxxxxxxx
格式:[token]literals(offset,match length)[token]literals(offset,match length)....

Token

令牌。一个字节,划分两个4位的字段:高4位为字面长度(Literal length),低4位为匹配长度(Match length)。

  • 高4位:4个位只能表示0-15,再多的话就需要再增加一些字节(高4位为15表示需要增加接下来字节),每个增加的字节可表示0-255。如果为255,表示还有增加一个字节,直到字节中不满255。因此没有大小限制。举例如下:

    字面长度为48表示:Token高4位为15,下一字节为33(48-15);

    字面长度为280表示:Token高4位为15,下一字节为255,再下一字节为10(280-15-255);

    字面长度为15表示:Token高4位为15,下一字节为0(必须输出零,15-15);

  • 低4位:同理高4位,只是匹配长度放在offset之后

Literal length

字面长度字段,0-n字节

Literals

字面量本身字段,字面量可能为0

Offset

两字节的偏移量,小端格式,表示匹配到向前偏移长度。例如3表示当前位置向前有3个数据是匹配的(重复的)。最大值offset为 65535,65536 及以上无法编码。请注意,0 是无效的偏移值。这种值的存在表示无效(损坏)块。

Match length

匹配长度字段,0-n字节。注意这里的0意味着复制操作将是最小的,即匹配的最小长度minmatch为4(Token低4位+4才为match length)。因此 0 值表示 4 个字节,15 值表示 19+ 个字节。

结束块限制

结束块有一些特定限制:

  1. 最后一个序列只包含字面量。
  2. 输入的最后 5 个字节始终是字面量。输入的最后 5 个字节始终是文字。
    • 特别地:如果输入小于 5 个字节,则只有一个序列,它包含整个输入作为字面量。空输入可以用零字节表示,解释为没有字面量和匹配的最终标记。
  3. 最后一个匹配必须在块结束前至少 12 个字节开始。最后一场比赛是倒数第二场比赛的一部分。紧随其后的是最后一个序列,它只包含字面量。
    • 请注意,因此,不能压缩 < 13 字节的独立块,因为匹配必须复制“某些东西”,因此它至少需要一个前字节。
    • 但是,当一个块可以引用另一个块的数据时,它可以立即以匹配而不是字面量开始,因此可以压缩正好 12 个字节的块。

当一个块不遵守这些结束条件时,允许一致的解码器将该块视为不正确而拒绝。

这些规则是为了确保与各种历史解码器的兼容性,这些解码器在面向速度的设计中依赖这些条件。

LZ4 Frame Format

介绍

定义一种无损压缩数据格式,与CPU类型、操作系统、文件系统和字符集无关,适合文件压缩,管道和流压缩。

对于任意长度顺序呈现的输入数据流,支持部分数据的压缩和解压,因此可以用于数据通信。该格式使用 LZ4 压缩方法和xxHash-32 校验(可选,用于检测数据损坏)。

定义的数据格式不允许随机访问压缩数据。

一般结构

MagicNbF. DescriptorBlock(…)EndMarkC. Checksum
4 bytes3-15 bytes4 bytes0-4 bytes
  • MagicNb:Magic Number,魔法数,4字节,小端,值为0x184D2204

  • F. Descriptor:Frame Descriptor,帧描述符,3-15字节,最为重要,Magic_Number 和_Frame_Descriptor_放在一起常叫做LZ4 Frame Header,长度为7-19字节

  • Block:Data Blocks,数据块,存放的压缩数据

  • EndMark:结束标记,在最后一个数据块之后,值为32位的0x00000000

  • C.Checksum:Content Checksum,内容校验和验证这个数据内容是否已正确解码。其是xxHash-32 算法计算的摘要值,该算法使用原始数据(解码后)作为输入,0作为种子。只有在帧描述符Frame Descriptor中设置了相关标志时,才会出现Content_Checksum。Content_Checksum用于验证:所有块都以正确的顺序完成传输,没有错误,编码/解码过程没有产生失真。推荐使用它EndMarkContent_Checksum放在一起常叫做LZ4 Frame Footer,长度为4-8字节

  • Frame Concatenation:在某些情况下,最好附加多个帧,例如为了将新数据添加到现有压缩文件而不重新构建它。在这种情况下,每个帧都有自己的一组描述符标志。每个帧都被认为是独立的,帧之间的唯一关系是它们的顺序。在单个流或文件中解码多个连接帧的能力不在LZ4规范范围内。

Frame Descriptor

FLGBD(Content Size)(Dictionary ID)HC
1 byte1 byte0 - 8 bytes0 - 4 bytes1 byte

描述符使用最少 3 个字节,最多 15 个字节,具体取决于可选参数。

FLG byte

BitNb7-6543210
FieldNameVersionB.IndepB.ChecksumC.SizeC.ChecksumReservedDictID
  • Version:版本号。占两位,必须设置为01
  • B.Indep:Block Independence flag,块独立标志。如果为1,则块是独立的,否则为0,每个块都依赖于先前的块(最大 LZ4 窗口大小,即 64 KB)。在这种情况下,有必要按顺序解码所有块。块相关性提高了压缩率,尤其是对于小块。但是另一方面,将无法随机访问或多线程解码。
  • B.Checksum:Block checksum flag,块校验标志。如果为1,则每个数据块后面跟一个4字节的checksum,通过对原始(压缩后)数据块使用 xxHash-32 算法计算得出,目的是在解码之前立即检测数据损坏(存储或传输错误)。B.Checksum块校验和的使用是可选的。
  • C.Size:Content Size flag,块大小标志。如果为1,则BD之后放未压缩的数据大小字段,其是 8 字节无符号小端值的形式。C.Size也是可选的。
  • C.Checksum:Content checksum flag,内容校验和标志。如果为1,在EndMark之后添加32位的内容校验和。
  • DictID:Dictionary ID flag,字典ID标志。如果为1,在Content Size之后,放4字节的Dict-ID。

BD byte

BitNb76-5-43-2-1-0
FieldNameReservedBlock MaxSizeReserved
  • Block MaxSize:Block Maximum Size,块最大长度。该信息有助于解码器分配内存。这里的大小是指原始(未压缩)数据大小的最大值。块最大大小是下表中的一个值:

    01234567
    N/AN/AN/AN/A64 KB256 KB1 MB4 MB

    解码器可能拒绝分配比较大的的块空间。

  • Reserverd:保留位,必须为0

Content Size bytes

这是原始(未压缩)大小。此信息是可选的,并且仅在设置了相关标志时才出现。内容大小使用无符号 8 字节提供,最大为 16EB容量。格式为小端。此值通常用于显示或内存分配。它可以被解码器跳过,或用于验证内容的正确性。

Dictionary ID

仅当设置了相应标志时才存在字典 ID。它是一个无符号的 32 位值,使用 little-endian 约定存储。字典对于压缩短输入序列很有用。压缩器可以利用字典上下文以更紧凑的方式对输入进行编码。它作为一种“已知前缀”工作,压缩器和解压缩器都使用它来“预热”参考表。

解压缩器可以使用 Dict-ID 标识符来确定必须使用哪个字典才能正确解码数据。压缩器和解压缩器必须使用完全相同的字典。假定 32 位 dictID 唯一标识一个字典。

在单个框架内,可以定义单个字典。当帧描述符定义独立的块时,每个块都将使用相同的字典进行初始化。如果帧描述符定义了链接块,则字典只会在帧开始时使用一次。

HC byte

  • hc:Header Checksum,头校验和。其是Frame Descriptor的校验和,值为xxh32(使用0为种子)的第二字节:(xxh32()>>8) & 0xFF。头检验和检验描述符是否正确,头校验和是信息性的,可以跳过

Data Blocks

Block Sizedata(Block Checksum)
4 bytes0 - 4 bytes

Block Size

占用4字节,小端格式。如果最高位为1,该块未压缩;最高位为0,则使用LZ4 块格式规范对块进行 LZ4 压缩。其他位是数据段的长度大小(以字节为单位),该大小不包括校验和(如果存在)。

Block_Size不得大于Block_Maximum_Size。如果源数据不可压缩,可能存在Block_Size等于Block_Maximum_Size。在这种情况下,必须使用未压缩格式传递此类数据块。

0x00000000无效,而是表示EndMark。请注意,这与 (最高位设置为1) 的值不同0x80000000,后者是大小为 0(空)的未压缩块,它是有效的,因此不会结束帧。请注意,如果启用了Block_checksum,则即使是空块也必须后跟 32 位块校验和。

Data

要解码的实际数据所在的位置。它可能会被压缩,也可能不会被压缩,这取决于之前的字段指示。压缩时,数据必须遵守LZ4 块格式规范。请注意,块不一定是满的。未压缩的数据大小可以是最大Block_Maximum_Size,因此它包含的数据可能少于最大块大小。

Block Checksum

仅在设置了相关标志时才存在。这是一个 4 字节的校验和值,采用小端格式,使用xxHash-32 算法原始(未解码)数据块进行计算,种子为零,目的是在解码之前检测数据损坏(存储或传输错误)。

Skippable Frames

Magic NumberFrame SizeUser Data
4 bytes4 bytes

可跳过的帧,其允许将用户定义的数据集成到串联的帧流中。它的设计非常简单,唯一的目标是让解码器快速跳过用户定义的数据并继续解码。

为了便于识别,不鼓励使用可跳过的帧开始连接帧流。如果需要在可跳过帧中使用一些用户数据,建议从零字节 LZ4 帧开始,然后是可跳过帧,将使文件类型标识符更容易。

Magic Number

魔法数,4字节,小端格式。值为0x184D2A5X,表示从 0x184D2A50 到 0x184D2A5F 的任何值。所有 16 个值都可用于识别可跳过的帧。

Frame Size

这是接下来用户数据的大小(以字节为单位)(不包括魔法数或大小字段本身)。4 字节,小端格式,无符号 32 位。这意味着用户数据不能大于 (232-1) 字节。

User Data

用户数据,解码器会跳过这些数据。

压缩示例

数据原文

hello david, hello lily, hello tom, hello lucy, hello bob

保存到文件data

$ echo "hello david, hello lily, hello tom, hello lucy, hello bob" > data

压缩前数据

数据原文的hex形式如下。

$ hexdump -C data
00000000  68 65 6c 6c 6f 20 64 61  76 69 64 2c 20 68 65 6c  |hello david, hel|
00000010  6c 6f 20 6c 69 6c 79 2c  20 68 65 6c 6c 6f 20 74  |lo lily, hello t|
00000020  6f 6d 2c 20 68 65 6c 6c  6f 20 6c 75 63 79 2c 20  |om, hello lucy, |
00000030  68 65 6c 6c 6f 20 62 6f  62 0a                    |hello bob.|
0000003a

压缩后数据

使用帧格式,执行lz4命令进行压缩如下。

$ lz4 data data.lz4

数据显示如下。

$ hexdump -C data.lz4
00000000  04 22 4d 18 64 40 a7 29  00 00 00 d2 68 65 6c 6c  |."M.d@.)....hell|
00000010  6f 20 64 61 76 69 64 2c  20 0d 00 44 6c 69 6c 79  |o david, ..Dlily|
00000020  0c 00 34 74 6f 6d 0b 00  34 6c 75 63 17 00 50 20  |..4tom..4luc..P |
00000030  62 6f 62 0a 00 00 00 00  90 ba d9 c9              |bob.........|
0000003c

数据分析

MagicNb

04 22 4D 18

魔法数为0x184D2204

F. Descriptor

64 40 a7
FLGBD(Content Size)(Dictionary ID)HC
64401 byte
  • FLG:0x64=0110 0100

    7-6543210
    VersionB.IndepB.ChecksumC.SizeC.ChecksumReservedDictID
    01100100
    版本01块独立无Block校验无块大小有内容校验保留无字典ID
  • BD:0x40,块最大大小为1MB

  • Content Size:无

  • Dictionary ID:无

  • HC:0xa7

Block

00000007  29 00 00 00 d2 68 65 6c  6c 6f 20 64 61 76 69 64  |)....hello david|
00000017  2c 20 0d 00 44 6c 69 6c  79 0c 00 34 74 6f 6d 0b  |, ..Dlily..4tom.|
00000027  00 34 6c 75 63 17 00 50  20 62 6f 62 0a           |.4luc..P bob.|
00000034
Block Sizedata(Block Checksum)
4 bytes0 - 4 bytes
0x00000029d2 68 65 6c … 62 6f 62 0a
  • Block Size:0x00000029,最高为0,表示压缩,data长度为0x29=41

  • data:41字节的压缩数据

    0000000b  d2 68 65 6c 6c 6f 20 64  61 76 69 64 2c 20 0d 00  |.hello david, ..|
    0000001b  44 6c 69 6c 79 0c 00 34  74 6f 6d 0b 00 34 6c 75  |Dlily..4tom..4lu|
    0000002b  63 17 00 50 20 62 6f 62  0a                       |c..P bob.|
    00000034
    

    LZ4格式

    1. 序列1

      d2 68 65 6c 6c 6f 20 64  61 76 69 64 2c 20 0d 00
      
      • Token:0xd2,字面长度0x0d,匹配长度0x02(实际为0x06)

      • Literals:字面量13字节

        68 65 6c 6c 6f 20 64 61 76 69 64 2c 20
        
      • Offset:0x000d,向前偏移13字节

    2. 序列2

      44 6c 69 6c 79 0c 00
      
      • Token:0x44,字面长度0x04,匹配长度0x04(实际为0x08)

      • Literals:字面量4字节

        6c 69 6c 79
        
      • Offset:0x000c,向前偏移12字节

    3. 序列3

      37 74 6f 6d 0b 00
      
      • Token:0x34,字面长度0x03,匹配长度0x04(实际为0x08)

      • Literals:字面量3字节

        74 6f 6d
        

        Offset:0x000b,向前偏移11字节

    4. 序列4

      34 6c 75 63 17 00
      
      • Token:0x34,字面长度0x03,匹配长度0x04(实际为0x08)

      • Literals:字面量3字节

        6c 75 63
        

        Offset:0x0017,向前偏移23字节

    5. 序列5(最后一个序列)

      50 20 62 6f 62 0a
      
      • Token:0x50,字面长度0x05,匹配长度0x00

      • Literals:字面量5字节

        20 62 6f 62 0a
        
  • Block Checksum:无

EndMark

00 00 00 00

结束标识0x00000000

C. Checksum

90 ba d9 c9
 - Literals:字面量3字节

   ```bash
   6c 75 63
   ```

   Offset:0x0017,向前偏移23字节
  1. 序列5(最后一个序列)

    50 20 62 6f 62 0a
    
    • Token:0x50,字面长度0x05,匹配长度0x00

    • Literals:字面量5字节

      20 62 6f 62 0a
      
  • Block Checksum:无

EndMark

00 00 00 00

结束标识0x00000000

C. Checksum

90 ba d9 c9

欢迎关注“安全有理”微信公众号。

安全有理

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值