LZ4 内存压缩算法分析

压缩算法简介

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

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

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

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

LZ4压缩算法简介

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

  1. 压缩

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

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

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

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

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,说明压缩和解压缩是没有问题的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

repinkply

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值