使用 XZ Utils 获得更高的压缩率--关于 liblzma API
- UID
- 1066743
|
使用 XZ Utils 获得更高的压缩率--关于 liblzma API
liblzma API关于 liblzma APIliblzma API 提供了 LZMA1/LZMA2 算法的实现,并具有类似于 Zlib 的 API 接口。也和 gzip、bzip2 一样 XZ Utils 是流压缩格式。通过 liblzma API 接口,可以相对简单的编程实现对数据的压缩和解压缩。
使用 liblzma API 压缩数据使用 liblzma 压缩数据的过程,大致过程如下。
- 定义 lzma_stream 结构体变量并使用 LZMA_STREAM_INIT 初始化,这个 lzma_stream 结构体变量会在整个数据压缩过程中被使用。有点类似于 C 中标准库中文件处理使用的结构体 FILE 。
1
| lzma_stream strm = LZMA_STREAM_INIT;
|
- 调用 lzma_easy_encoder 函数做压缩准备工作。函数的第一参数是 lzma_stream. 结构体变量的指针,第二参数则指明了期望压缩率大小。有效值为 [0,9], 数字越高压缩率越高。第三参数是指明数据完整性检查方法,LZMA_CHECK_CRC64 可以满足大多数情况需要。当然,还有 LZMA_CHECK_CRC32, LZMA_CHECK_SH256 可供选择,或者使用 LZMA_CHECK_NONE 不进行 数据完整性检查。 lzma_easy_encoder (&strm, 6, LZMA_CHECK_CRC64);
- 通过设定 lzma_stream 结构体变量中的 next_in 和 avail_in 字段,指明待压缩的数据开始 地址和长度。
1
2
| strm.next_in = in_buf; //input buffer address.
strm.avail_in = in_len; //data length.
|
- 通过设定 lzma_stream 结构体变量中的 next_out 和 avail_out 字段,指明存放压缩结果 buffer 地址和长度。
1
2
| strm.next_out = out_buf; //output buffer address
strm.avail_out = 4096; //output buffer length.
|
- 然后通过调用 lzma_code 函数来压缩数据,或结束压缩数据。lzma_code 函数有两个参数,第一个参数是 lzma_stream 指针。而第二个参数用来指明 lzma_code 的动作: LZMA_RUN: 进行数据处理; LZMA_FINISH: 结束数据处理。
1
2
| lzma_code (&strm, LZMA_RUN); /* compress data */
lzma_code (&strm, LZMA_FINISH); /* Finish operation.*/
|
- 最后要调用 lzma_end 函数释放资源,退出。
下面是一段完整的示例程序。所示程序从标准输入 (stdin) 读入数据,压缩后写到标准输出 (stdout). 为了更便于理解压缩过程,示例代码省略了所有的异常处理和一些其他次要细节。
清单 1. 压缩数据的代码示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| #include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
#include <lzma.h>
void xz_compress (FILE *in_file, FILE *out_file) {
lzma_check check = LZMA_CHECK_CRC64;
/* alloc and init lzma_stream struct */
lzma_stream strm = LZMA_STREAM_INIT;
uint8_t in_buf [4096];
uint8_t out_buf [4096];
bool in_finished = false;
/* initialize xz encoder */
lzma_easy_encoder (&strm, 6, LZMA_CHECK_CRC64);
while (! in_finished) {
/* read incoming data */
size_t in_len = fread (in_buf, 1, 4096, in_file);
if (feof (in_file)) {
in_finished = true;
}
strm.next_in = in_buf;
strm.avail_in = in_len;
/* if no more data from in_buf, flushes the internal xz buffers and
* closes the xz data with LZMA_FINISH */
lzma_action action = in_finished ? LZMA_FINISH : LZMA_RUN;
/* loop until there's no pending compressed output */
do {
strm.next_out = out_buf;
strm.avail_out = 4096;
lzma_code (&strm, action); /* compress data */
size_t out_len = 4096 - strm.avail_out;
fwrite (out_buf, 1, out_len, out_file); /* write compressed data */
}while (strm.avail_out == 0);
}
lzma_end (&strm);
}
int main () {
xz_compress (stdin, stdout);
return 0;
}
|
编译命令 :
1
| $ gcc -g -o xz_pipe_mini xz_pipe_comp_mini.c -llzma
|
使用例子 :
1
| $ cat a.txt | xz_pipe_mini > a.txt.xz
|
使用 liblzma API 解压数据使用 liblzma 进行解压缩的过程和压缩数据的过程基本一致。主要的区别是使用 lzma_stream_decoder 函数代替 lzma_easy_decoder 函数来进行 decoder 的初始化。
1
| lzma_stream_decoder (&strm, memory_limit, flags);
|
lzma_stream_decoder 函数的第一参数是数是 lzma_stream 结构体变量的指针。第二参数则指明了在解压缩过程中使用内存的最大值。若 UINT64_MAX 则为不限制内存使用量。第三参数用来指明一些其他 flags 值:
- LZMA_TELL_NO_CHECK: 如果压缩数据流中未指明数据完整性检查方式,则函数 lzma_code 返回 LZMA_TELL_NO_CHECK 。
- LZMA_TELL_UNSUPPORTED_CHECK: 如果压缩数据流使用了不被支持的数据完整性检查方式,则函数 lzma_code 会返回 LZMA_TELL_UNSUPPORTED_CHECK 。
- LZMA_CONCATENATED: 支持多个压缩流连接到一个 xz 文件内。
下面是一段完整的示例程序。所示程序从标准输入 (stdin) 读入数据,解压缩的数据写到标准输出 (stdout). 示例代码省略了所有的异常处理和一些其他次要细节。
清单 2. 解压缩数据的代码示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
| #include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
#include <lzma.h>
#define IN_BUF_MAX 4096
#define OUT_BUF_MAX 4096
#define RET_OK 0
#define RET_ERROR 1
int xz_decompress (FILE *in_file, FILE *out_file) {
/* alloc and init lzma_stream struct */
lzma_stream strm = LZMA_STREAM_INIT;
uint8_t in_buf [IN_BUF_MAX];
uint8_t out_buf [OUT_BUF_MAX];
bool in_finished = false;
bool out_finished = false;
lzma_action action;
lzma_ret ret_xz;
int ret;
ret = RET_OK;
/* initialize xz decoder */
ret_xz = lzma_stream_decoder (&strm, UINT64_MAX,
LZMA_TELL_UNSUPPORTED_CHECK|LZMA_CONCATENATED );
if (ret_xz != LZMA_OK)
return RET_ERROR;
while ((! in_finished) && (! out_finished)) {
/* read incoming data */
size_t in_len = fread (in_buf, 1, IN_BUF_MAX, in_file);
if (feof (in_file))
in_finished = true;
strm.next_in = in_buf;
strm.avail_in = in_len;
/* if no more data from in_buf, flushes the
internal xz buffers and closes the decompressed data
with LZMA_FINISH */
action = in_finished ? LZMA_FINISH : LZMA_RUN;
/* loop until there's no pending decompressed output */
do {
/* out_buf is clean at this point */
strm.next_out = out_buf;
strm.avail_out = OUT_BUF_MAX;
/* decompress data */
ret_xz = lzma_code (&strm, action);
if ((ret_xz != LZMA_OK) && (ret_xz != LZMA_STREAM_END)) {
out_finished = true;
ret = RET_ERROR;
} else {
/* write decompressed data */
size_t out_len = OUT_BUF_MAX - strm.avail_out;
fwrite (out_buf, 1, out_len, out_file);
if (ferror (out_file)) {
out_finished = true;
ret = RET_ERROR;
}
}
} while (strm.avail_out == 0);
}
lzma_end (&strm);
return ret;
}
int main () {
return xz_decompress (stdin, stdout);
}
|
编译命令:
1
| $ gcc -g -o xz_pipe_decomp_mini xz_pipe_decomp_mini.c -llzma
|
使用例子:
1
| $ cat a.txt.xz | xz_pipe_decomp_mini > a.txt
|
|
|
|
|
|
|