压缩算法简介
压缩算法的本质是通过寻找数据中的规律和重复出现的模式,来减少数据的存储空间。在数据中存在重复的字节时,压缩算法可以通过存储这些重复的字节的位置和数量,来减少存储这些数据所需的空间。
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,说明压缩和解压缩是没有问题的。