压缩算法简介

压缩算法的本质是通过寻找数据中的规律和重复出现的模式,来减少数据的存储空间。在数据中存在重复的字节时,压缩算法可以通过存储这些重复的字节的位置和数量,来减少存储这些数据所需的空间。

LZ4压缩算法是一种基于字典的压缩算法,它通过维护一个固定大小的字典,来寻找输入数据中的重复模式。当LZ4算法发现输入数据中存在一个与字典中的某个字符串匹配的子串时,它会用一个指向字典中该字符串的指针来代替该子串,从而实现对数据的压缩。

因此,当输入数据中存在重复的字节时,LZ4算法可以利用这些重复的字节来寻找匹配的子串,并将其替换为指向字典中的字符串的指针,从而实现对数据的压缩。如果输入数据中没有重复的字节,LZ4算法就无法通过寻找重复的模式来实现对数据的压缩,因此压缩效果就会变得很差。

请注意,压缩后的数据并不一定比输入数据更短,因为LZ4压缩算法只有在输入数据中存在重复的字节时才会产生压缩效果。

LZ4压缩算法简介

LZ4是一种快速压缩算法,它基于LZ77算法和哈希表加速。LZ4算法分为两个步骤:压缩和解压。

压缩

LZ4的压缩过程分为两个阶段:扫描和匹配。扫描阶段从输入数据中读取字节,并将其插入哈希表中。匹配阶段在哈希表中查找匹配项,并在输出中写入标记和长度。以下是LZ4的压缩过程的简单流程:

(1)将输入数据分为块。

(2)对于每个块,执行以下操作:

扫描阶段:将字节插入哈希表中。
匹配阶段:在哈希表中查找匹配项,并在输出中写入标记和长度。

解压

LZ4的解压过程非常简单。它只是按照压缩过程中写入的标记解压每个块。以下是LZ4的解压过程的简单流程:

(1)读取块头,该块头包含压缩块的大小。

(2)按照压缩过程中写入的标记解压每个块。

总之,LZ4算法是一种快速的压缩算法,适用于需要高性能的压缩和解压缩应用程序。

LZ4简单demo

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lz4.h"

int main(int argc,char* argv[]) {
const char* input = "Hello, World!";
int inputSize = strlen(input) + 1;

int maxOutputSize = LZ4_compressBound(inputSize);
char* compressedData = (char*)malloc(maxOutputSize);
int compressedSize = LZ4_compress_default(input, compressedData, inputSize, maxOutputSize);

printf("Compressed data length: %d\n", compressedSize);
char* decompressedData = (char*)malloc(inputSize);

int decompressedSize = LZ4_decompress_safe(compressedData, decompressedData, compressedSize, inputSize);
printf("Decompressed data: %s\n", decompressedData);

free(compressedData);
free(decompressedData);
return 0;
}

编译报错

编译运行:g++ -o test test.cpp,会出现以下错误。

上述这段代码在 ubuntu20.04 用 GCC 编译器编译报错:
/usr/bin/ld: /tmp/cctkG6Tv.o: in function `main': test.cpp:(.text+0x2f): undefined reference to `LZ4_compressBound' /usr/bin/ld: test.cpp:(.text+0x59): undefined reference to `LZ4_compress_default' /usr/bin/ld: test.cpp:(.text+0x99): undefined reference to `LZ4_decompress_safe' collect2: error: ld returned 1 exit status

编译报错解决

这个错误是由于编译器没有找到lz4库的函数定义导致的。你需要在编译时指定lz4库的路径和名称,以便编译器能够找到库文件并链接到可执行文件中。

以下是一个示例编译命令,假设你的lz4库文件名为liblz4.so,并位于/usr/local/lib目录下:

g++ -o test test.cpp -llz4 -L/usr/local/lib

这个命令使用了-g++编译器,指定了可执行文件的名称为test,指定了lz4库的名称为llz4,并使用-L选项指定了lz4库的路径为/usr/local/lib。请根据你的实际情况进行调整和修改。

你可以使用以下命令来查找liblz4.so库文件所在的目录:

sudo find / -name "liblz4.so"

这个命令会在整个文件系统中查找名为“liblz4.so”的文件,并输出文件所在的路径。请注意,由于需要查找整个文件系统,这个命令可能需要一些时间才能完成。

如果你知道该库文件是由apt或其他软件包管理器安装的,你也可以使用以下命令来查找库文件所在的目录:

dpkg -L liblz4-dev

这个命令会列出liblz4-dev软件包中安装的所有文件和目录,包括liblz4.so库文件所在的路径。请根据你的实际情况进行调整和修改。

运行代码结果如下:

wj@wj:~/WORK/Learning/DT/C++$ g++ -o test test.cpp -llz4 -L/usr/lib/x86_64-linux-gnu
wj@wj:~/WORK/Learning/DT/C++$ ./test
Compressed data length: 15
Decompressed data: Hello, World!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lz4.h"

int main(int argc,char* argv[]) {
const char* input = "Hello, World! Hello,World! Hello,World! Hello,World! Hello,World!";
int inputSize = strlen(input) + 1;

printf("inputSize: %d\n",inputSize);

int maxOutputSize = LZ4_compressBound(inputSize);
printf("maxOutputSize: %d\n",maxOutputSize);

char* compressedData = (char*)malloc(maxOutputSize);
int compressedSize = LZ4_compress_default(input, compressedData, inputSize, maxOutputSize);

printf("compressedData: %s\n",compressedData);

printf("Compressed data length: %d\n", compressedSize);
char* decompressedData = (char*)malloc(inputSize);

int decompressedSize = LZ4_decompress_safe(compressedData, decompressedData, compressedSize, inputSize);
printf("Decompressed data: %s\n", decompressedData);

free(compressedData);
free(decompressedData);

return 0;
}

编译运行:

wj@wj:~/WORK/Learning/DT/C++$ g++ -o test test.cpp -llz4 -L/usr/lib/x86_64-linux-gnu
wj@wj:~/WORK/Learning/DT/C++$ ./test
inputSize: 66
maxOutputSize: 82
compressedData: �Hello, World!
Compressed data length: 27
Decompressed data: Hello, World! Hello,World! Hello,World! Hello,World! Hello,World!

验证压缩和解压缩数据的有效性

我们在项目开发中,经常需要验证压缩之前的数据和解压之后的数据 是否是同一块内存的数据,这样就可以加入 MD5来进行 check了。看一下接下来这个demo。

#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <malloc.h>
#include <lz4.h>
#include <iostream>
#include <openssl/md5.h>
#include <string>
#include <sstream>
#include <iomanip>

using namespace std;

std::string hash_string_md5(const std::string &str) {
unsigned char result[MD5_DIGEST_LENGTH];
MD5((unsigned char*)str.c_str(), str.size(), result);

std::stringstream ss;
for(int i = 0; i < MD5_DIGEST_LENGTH; i++) {
ss << std::hex << std::setw(2) << std::setfill('0') << (int)result[i];
}

return ss.str();
}

int main(int argc,char* argv[])
{
const char* input = "Hello,World,Hello,World,Hello";
int inputSize = strlen(input) + 1;
int maxOutputSize = inputSize;

std::cout << "input: " << hash_string_md5(string(input)) << std::endl;
printf("maxOutputSize: %d\n", maxOutputSize);

char* compressedData = (char*)malloc(maxOutputSize);
int compressedSize = LZ4_compress_default(input, compressedData, inputSize, maxOutputSize);
printf("Compressed data length: %d\n", compressedSize);
printf("Compressed data: %s\n", compressedData);

char* decompressedData = (char*)malloc(inputSize);
int decompressedSize = LZ4_decompress_safe(compressedData, decompressedData, compressedSize, inputSize);
printf("Decompressed data: %s\n", decompressedData);

std::cout << "decompressedData: " << hash_string_md5(string(decompressedData)) << std::endl;

free(compressedData);
free(decompressedData);

return 0;
}

编译输出:

wj@wj:~/WORK/Learning/DT/LZ4$ g++ -o test test.cpp -llz4 -L/usr/local/lib -lssl -lcrypto
wj@wj:~/WORK/Learning/DT/LZ4$ ./test
input: eb5745328340d97aae8eb9d80589e8e9
maxOutputSize: 30
Compressed data length: 21
Compressed data: �Hello,World,

Decompressed data: Hello,World,Hello,World,Hello
decompressedData: eb5745328340d97aae8eb9d80589e8e9

可以看到压缩之前和解压缩之后的数据的hash值都是:eb5745328340d97aae8eb9d80589e8e9,说明压缩和解压缩是没有问题的。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。