Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Small & portable byte-aligned LZ77 compression

License

NotificationsYou must be signed in to change notification settings

ariya/FastLZ

Repository files navigation

LicenseCode styleAddress Sanitizer

Overview

FastLZ (MIT license) is an ANSI C/C90 implementation ofLempel-Ziv 77 algorithm (LZ77) of lossless data compression. It is suitable to compress series of text/paragraphs, sequences of raw pixel data, or any other blocks of data with lots of repetition. It is not intended to be used on images, videos, and other formats of data typically already in an optimal compressed form.

The focus for FastLZ is a very fast compression and decompression, doing that at the cost of the compression ratio. As an illustration, the comparison with zlib when compressingenwik8 (also inmore details):

RatioCompressionDecompression
FastLZ54.2%159 MB/s305 MB/s
zlib -142.3%50 MB/s184 MB/s
zlib -936.5%11 MB/s185 MB/s

FastLZ is used by many software products, from a number of games (such asDeath Stranding) to various open-source projects (Godot Engine,Facebook HHVM,Apache Traffic Server,Calligra Office,OSv,Netty, etc). It even serves as the basis for other compression projects likeBLOSC.

For other implementations of byte-aligned LZ77, take a look atLZ4,Snappy,Density,LZO,LZF,LZJB,LZRW, etc.

Usage

FastLZ can be used directly in any C/C++ applications. For other programming languages/environments, use the corresponding binding:

FastLZ consists of only two files:fastlz.h andfastlz.c. Just add these files to your project in order to use FastLZ. For the detailed information on the API to perform compression and decompression, seefastlz.h.

ForVcpkg users, FastLZ isalready available:vcpkg install fastlz.

A simple file compressor called6pack is included as an example on how to use FastLZ. The corresponding decompressor is6unpack.

FastLZ supports any standard-conforming ANSI C/C90 compiler, including the popular ones such asGCC,Clang,Visual Studio, and evenTiny CC. FastLZ works well on a number of architectures (32-bit and 64-bit, big endian and little endian), from Intel/AMD, PowerPC, System z, ARM, MIPS, and RISC-V.

The continuous integration system runs an extensive set of compression-decompression round trips on the following systems:

For more details, check the correspondingGitHub Actions build logs.

amd64LinuxWindowsmacOS
GCCamd64_linux_gccamd64_windows_gccamd64_macos_gcc
Clangamd64_linux_clangamd64_windows_clangamd64_macos_clang
TinyCCamd64_linux_tccamd64_windows_tcc
VS 2019amd64_windows_vs2019
i686LinuxWindowsmacOS
GCCi686_linux_gcc
Clangi686_linux_clang
TinyCCi686_windows_tcc
VS 2019i686_windows_vs2019
i586LinuxDOS
GCCi586_dos_gcc_cross
Linux
powerpc
GCCpowerpc_linux_gcc
ppc64(le)
GCCppc64_linux_gcc
GCCppc64le_linux_gcc
s390x
GCCs390x_linux_gcc
armhf
GCCarmhf_linux_gcc
arm64
GCCarm64_linux_gcc
mips(el)
GCCmipsel_linux_gcc
GCCmips_linux_gcc
mips64(el)
GCCmips64el_linux_gcc
GCCmips64_linux_gcc
riscv
GCCriscv_linux_gcc
riscv64
GCCriscv64_linux_gcc

Block Format

Let us assume that FastLZ compresses an array of bytes, called theuncompressed block, into another array of bytes, called thecompressed block. To understand what will be stored in the compressed block, it is illustrative to demonstrate how FastLZ willdecompress the block to retrieve the original uncompressed block.

The first 3-bit of the block, i.e. the 3 most-significant bits of the first byte, is theblock tag. Currently the block tag determines the compression level used to produce the compressed block.

Block tagCompression level
0Level 1
1Level 2

The content of the block will vary depending on the compression level.

Block Format for Level 1

FastLZ Level 1 implements LZ77 compression algorithm with 8 KB sliding window and up to 264 bytes of match length.

The compressed block consists of one or moreinstructions.Each instruction starts with a 1-byte opcode, 2-byte opcode, or 3-byte opcode.

Instruction typeOpcode[0]Opcode[1]Opcode[2]
Literal run000, L₄-L₀--
Short matchM₂-M₀, R₁₂-R₈R₇-R₀-
Long match111, R₁₂-R₈M₇-M₀R₇-R₀

Note that thevery first instruction in a compressed block is always a literal run.

Literal run instruction

For the literal run instruction, there is one or more bytes following the code. This is called the literal run.

The 5 least-significant bits ofopcode[0],L, determines thenumber of literals following the opcode. The value of 0 indicates a 1-byte literal run, 1 indicates a 2-byte literal run, and so on. The minimum literal run is 1 and the maximum literal run is 32.

The decompressor copies (L + 1) bytes of literal run, starting from the first one right after opcode.

Example: If the compressed block is a 4-byte array of[0x02, 0x41, 0x42, 0x43], then the opcode is0x02 and that means a literal run of 3 bytes. The decompressor will then copy the subsequent 3 bytes,[0x41, 0x42, 0x43], to the output buffer. The output buffer now represents the (original) uncompressed block,[0x41, 0x42, 0x43].

Short match instruction

The 3 most-significant bits ofopcode[0],M, determines thematch length. The value of 1 indicates a 3-byte match, 2 indicates a 4-byte match and so on. The minimum match length is 3 and the maximum match length is 8.

The 5 least-significant bits ofopcode[0] combined with the 8 bits of theopcode[1],R, determines thereference offset. Since the offset is encoded in 13 bits, the minimum is 0 and the maximum is 8191.

The following C code retrieves the match length and reference offset:

M=opcode[0] >>5;R=256* (opcode[0] <<5)+opcode[1];

The decompressor copies(M+2) bytes, starting from the location offsetted byR in the output buffer. Note thatR is aback reference, i.e. the value of 0 corresponds the last byte in the output buffer, 1 is the second to last byte, and so forth.

Example 1: If the compressed block is a 7-byte array of[0x03, 0x41, 0x42, 0x43, 0x44, 0x20, 0x02], then there are two instructions in the there. The first instruction is the literal run of 4 bytes (due toL = 3). Thus, the decompressor copies 4 bytes to the output buffer, resulting in[0x41, 0x42, 0x43, 0x44]. The second instruction is the short match of 3 bytes (fromM = 1, i.e0x20 >> 5) and the offset of 2. Therefore, the compressor goes back 2 bytes from the last position, copies 3 bytes ([0x42, 0x43, 0x44]), and appends them to the output buffer. The output buffer now represents the complete uncompressed data,[0x41, 0x42, 0x43, 0x44, 0x42, 0x43, 0x44].

Example 2: If the compressed block is a 4-byte array of[0x00, 0x61, 0x40, 0x00], then there are two instructions in there. The first instruction is the literal run of just 1 byte (L = 0). Thus, the decompressor copies the byte (0x61) to the output buffer. The output buffer now becomes[0x61]. The second instruction is the short match of 4 bytes (fromM = 2, i.e.0x40 >> 5) and the offset of 0. Therefore, the decompressor copies 4 bytes starting using the back reference of 0 (i.e. the position of0x61). The output buffer now represents the complete uncompressed data,[0x61, 0x61, 0x61, 0x61, 0x61].

Long match instruction

The value ofopcode[1],M, determines thematch length. The value of 0 indicates a 9-byte match, 1 indicates a 10-byte match and so on. The minimum match length is 9 and the maximum match length is 264.

The 5 least-significant bits ofopcode[0] combined with the 8 bits ofopcode[2],R, determines thereference offset. Since the offset is encoded in 13 bits, the minimum is 0 and the maximum is 8191.

The following C code retrieves the match length and reference offset:

M=opcode[1];R=256* (opcode[0] <<5)+opcode[2];

The decompressor copies(M+9) bytes, starting from the location offsetted byR in the output buffer. Note thatR is aback reference, i.e. the value of 0 corresponds to the last byte in the output buffer, 1 is for the second to last byte, and so forth.

Example: If the compressed block is a 4-byte array of[0x01, 0x44, 0x45, 0xE0, 0x01, 0x01], then there are two instructions in there. The first instruction is the literal run with the length of 2 (due toL = 1). Thus, the decompressor copies the 2-byte literal run ([0x44, 0x45]) to the output buffer. The second instruction is the long match with the match length of 10 (fromM = 1) and the offset of 1. Therefore, the decompressor copies 10 bytes starting using the back reference of 1 (i.e. the position of0x44). The output buffer now represents the complete uncompressed data,[0x44, 0x45, 0x44, 0x45, 0x44, 0x45, 0x44, 0x45, 0x44, 0x45, 0x44, 0x45].

Decompressor Reference Implementation

The following 40-line C function implements a fully-functional decompressor for the above block format. Note that it is intended to be educational, e.g. no bound check is implemented, and therefore it is absolutelyunsafe for production.

voidfastlz_level1_decompress(constuint8_t*input,intlength,uint8_t*output) {intsrc=0;intdest=0;while (src<length) {inttype=input[src] >>5;if (type==0) {/* literal run */intrun=1+input[src];src=src+1;while (run>0) {output[dest]=input[src];src=src+1;dest=dest+1;run=run-1;      }    }elseif (type<7) {/* short match */intofs=256* (input[src]&31)+input[src+1];intlen=2+ (input[src] >>5);src=src+2;intref=dest-ofs-1;while (len>0) {output[dest]=output[ref];ref=ref+1;dest=dest+1;len=len-1;      }    }else {/* long match */intofs=256* (input[src]&31)+input[src+2];intlen=9+input[src+1];src=src+3;intref=dest-ofs-1;while (len>0) {output[dest]=output[ref];ref=ref+1;dest=dest+1;len=len-1;      }    }  }}

Block Format for Level 2

(To be written)


[8]ページ先頭

©2009-2025 Movatter.jp