压缩算法的调研
复制本地路径 | 在线编辑
项目中遇到了一个需求是对一些内容压缩放入 Flash 中,其中需要用到压缩算法,对其进行了调研。具体的各个常见算法对比,直接问 AI 就行了,能给你一个漂亮的表格。这里我直接说一下我的感受。
直接说结论就是 zstd 就挺好的,在时间和压缩率上达到平衡,更重要的是有很好的官方库支持,用起来很简单。其他的算法,速度快的压缩率很感人,压缩率声称比较高的,似乎也没高多少,唯一相对高的是 zpaq,但是时间真的慢到离谱,在很多项目上是不适配的,而且文档也没有多少,真正调用起来也要费点力气。
所以珍惜生命,使用 zstd 就好了,下载官方的库文件,然后调用就完事了。
// decompress.cpp
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <string.h>
#include "./zstd/include/zstd.h"
using namespace std;
// 读取文件内容,并且放在内存中
char* loadFile(const string filename) {
// 读文件大小
ifstream nowFile(filename, ios::in|ios::binary);
nowFile.seekg(0, ios::end);
size_t bufferSize = nowFile.tellg();
nowFile.seekg(0, ios::beg);
// 写入 Buffer
char* nowBuffer = (char*) malloc(bufferSize * sizeof(char));
nowFile.read(nowBuffer, bufferSize);
nowFile.close();
return nowBuffer;
}
// 获取解压后的第几帧的数据
char* decompressData(char* baseAddr, size_t offset, size_t cSize, size_t& rSize) {
// 获取目前要解压的数据(根据开始地址、偏移量和大小读取)
char* cBuffer = (char*) malloc(cSize * sizeof(char));
memcpy(cBuffer, baseAddr+offset, cSize);
// 计算解压后的大小
rSize = ZSTD_getFrameContentSize(cBuffer, cSize);
char* rBuffer = (char*) malloc(rSize * sizeof(char));
// 解压
ZSTD_decompress(rBuffer, rSize, cBuffer, cSize);
free(cBuffer);
return rBuffer;
}
// 写入文件
void writeFile(char* rBuffer, size_t oneSize, string dstname) {
ofstream nowFile(dstname, ios::out|ios::binary);
nowFile.write(rBuffer, oneSize);
nowFile.close();
}
int main() {
// 读取文件,该参数模拟在 Flash 中的基地址
const string cName = "./test_decompress/compressed_data.zst";
char* baseAddr = loadFile(cName);
// 每个文件的开始相对基地址的偏移量(一个六个文件,但故意给了最后一个结束大小便于后面编码清楚一些)
size_t begAddrs[7] = {
0,
10975912,
27495915,
36407837,
53350923,
78523184,
89035705,
};
// 故意打乱顺序验证通过索引解压的准确性
size_t idxs[6] = {4, 2, 3, 0, 5, 1};
// 循环进行,每次想要解压第 idxs[i] 帧,解压后写入文件
string dstname = "./test_decompress/gendata/x.raw";
for (int i = 0; i < 6; ++i) {
// 现在要解压第几个索引
size_t idx = idxs[i];
// 现在这个索引:相对基地址的偏移量、压缩文件的大小
size_t offset = begAddrs[idx];
size_t nowCSize = begAddrs[idx+1]-begAddrs[idx];
// 还要给一个参数,函数会赋给该参数为解压后的大小
// 该参数用于表示函数体内使用 malloc 的内存大小
size_t resultSize;
char* resultBuffer = decompressData(baseAddr, offset, nowCSize, resultSize);
// 写入文件
dstname[dstname.length()-5] = ('0' + idx);
writeFile(resultBuffer, resultSize, dstname);
// 不需要用到解压后的数据了,一定要 Free 掉!!
free(resultBuffer);
}
free(baseAddr);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <string.h>
#include "./zstd/include/zstd.h"
using namespace std;
// 读取文件内容,并且放在内存中
char* loadFile(const string filename) {
// 读文件大小
ifstream nowFile(filename, ios::in|ios::binary);
nowFile.seekg(0, ios::end);
size_t bufferSize = nowFile.tellg();
nowFile.seekg(0, ios::beg);
// 写入 Buffer
char* nowBuffer = (char*) malloc(bufferSize * sizeof(char));
nowFile.read(nowBuffer, bufferSize);
nowFile.close();
return nowBuffer;
}
// 获取解压后的第几帧的数据
char* decompressData(char* baseAddr, size_t offset, size_t cSize, size_t& rSize) {
// 获取目前要解压的数据(根据开始地址、偏移量和大小读取)
char* cBuffer = (char*) malloc(cSize * sizeof(char));
memcpy(cBuffer, baseAddr+offset, cSize);
// 计算解压后的大小
rSize = ZSTD_getFrameContentSize(cBuffer, cSize);
char* rBuffer = (char*) malloc(rSize * sizeof(char));
// 解压
ZSTD_decompress(rBuffer, rSize, cBuffer, cSize);
free(cBuffer);
return rBuffer;
}
// 写入文件
void writeFile(char* rBuffer, size_t oneSize, string dstname) {
ofstream nowFile(dstname, ios::out|ios::binary);
nowFile.write(rBuffer, oneSize);
nowFile.close();
}
int main() {
// 读取文件,该参数模拟在 Flash 中的基地址
const string cName = "./test_decompress/compressed_data.zst";
char* baseAddr = loadFile(cName);
// 每个文件的开始相对基地址的偏移量(一个六个文件,但故意给了最后一个结束大小便于后面编码清楚一些)
size_t begAddrs[7] = {
0,
10975912,
27495915,
36407837,
53350923,
78523184,
89035705,
};
// 故意打乱顺序验证通过索引解压的准确性
size_t idxs[6] = {4, 2, 3, 0, 5, 1};
// 循环进行,每次想要解压第 idxs[i] 帧,解压后写入文件
string dstname = "./test_decompress/gendata/x.raw";
for (int i = 0; i < 6; ++i) {
// 现在要解压第几个索引
size_t idx = idxs[i];
// 现在这个索引:相对基地址的偏移量、压缩文件的大小
size_t offset = begAddrs[idx];
size_t nowCSize = begAddrs[idx+1]-begAddrs[idx];
// 还要给一个参数,函数会赋给该参数为解压后的大小
// 该参数用于表示函数体内使用 malloc 的内存大小
size_t resultSize;
char* resultBuffer = decompressData(baseAddr, offset, nowCSize, resultSize);
// 写入文件
dstname[dstname.length()-5] = ('0' + idx);
writeFile(resultBuffer, resultSize, dstname);
// 不需要用到解压后的数据了,一定要 Free 掉!!
free(resultBuffer);
}
free(baseAddr);
return 0;
}