以前libpng等で使ったzlibでデータを圧縮してみる。
圧縮時、まずcompressBound に元データのサイズを渡し、最低限その戻り値のバイト数分だけメモリを確保する。
圧縮はcompress関数で行い、結果は第一引数で指定したメモリに書き込まれ、書き込まれたサイズは第二引数destLenに返る。
注意点として、この関数で圧縮されたデータには元データのサイズ情報が入っていない。本来ファイル出力するのであればその情報を何らかの形で入れておくべきだが今回は画面表示して伸長プログラム側に直打ちすることにする。
int compress( Bytef* dest, // 結果の格納先のバッファ uLongf* destLen, // 入力:バッファサイズ ,出力:結果データサイズ const Bytef* source, // 元データ uLong sourceLen // 元データのサイズ );
#pragma warning(disable:4996) #include <vector> #include <filesystem> // 圧縮に必要 #include <zlib.h> // 圧縮に必要 #if _DEBUG #pragma comment(lib,"zlibstaticd.lib") #else #pragma comment(lib,"zlibstatic.lib") #endif
//! @brief 圧縮のテスト //! @param [in] source 圧縮したいデータ //! @return 圧縮後のデータ std::vector< Bytef > compress_test(std::vector<unsigned char>& source) { //圧縮前のデータと //そのデータのサイズを準備 unsigned char* src = source.data(); unsigned int sourceLen = source.size(); // compressBoundの戻り値だけメモリを確保 uLong destLen = compressBound(sourceLen); std::vector< Bytef > dest; dest.resize(destLen); // compressで圧縮。 // destLenは呼び出し時に確保したメモリ量を指定すると // 圧縮後の長さが帰ってくる int ret = compress(dest.data(), &destLen, src, sourceLen); if (ret != Z_OK) { return std::vector< Bytef >(); } // 圧縮後のデータの長さがdestLenなので // 配列をdestLenに切り詰める dest.erase(dest.begin() + destLen, dest.end()); return dest; }
std::vector<unsigned char> read_buffer(const char* filename); int main() { // 圧縮テスト std::vector<unsigned char> buffer = read_buffer("C:/data/.cipd_client.exe"); std::vector< Bytef > cmpressed = compress_test(buffer); // ファイル書き出し FILE* fp = fopen("C:\\data\\cmp.data", "wb"); fwrite(cmpressed.data(), 1, cmpressed.size(), fp); fclose(fp); // 注意 圧縮後データには「元のファイルサイズ」が含まれない // これは、伸長時、結果を格納する領域のサイズを知ることができないことを意味する。 // 従って本来、ファイル出力時には、別途元のサイズを記録しておく必要がある。 // 今回はプログラムを複雑にしないため、画面表示してその値を手動で記録しておく。 printf("for decompress : %d bytes\n", buffer.size()); } //! @brief ファイルからデータを読み込む //! @param [in] filename ファイル名 //! @return ファイル一個分のデータ std::vector<unsigned char> read_buffer(const char* filename) { // データの準備 namespace fs = std::filesystem; std::vector<unsigned char> buffer; uintmax_t size = fs::file_size(filename); FILE* fp = fopen(filename, "rb"); buffer.resize(size); fread(buffer.data(), size, 1, fp); fclose(fp); return buffer; }
int uncompress ( Bytef* dest, // 伸長後のデータ格納先 uLongf* destLen, // 入力時:格納先のメモリサイズ 出力時:結果のメモリサイズ const Bytef* source, // 圧縮されたデータ uLong sourceLen // 圧縮されたデータのバイト数 );
#pragma warning(disable:4996) #include <vector> #include <filesystem> // 圧縮に必要 #include <zlib.h> // 圧縮に必要 #if _DEBUG #pragma comment(lib,"zlibstaticd.lib") #else #pragma comment(lib,"zlibstatic.lib") #endif
//! @brief 圧縮されたデータを伸長する //! @param [in] compressed 圧縮されたデータ //! @param [in] original_size 伸長後のデータサイズ //! @return 伸長後のデータ std::vector< Bytef > uncompress_test(const std::vector<unsigned char>& compressed,const unsigned int original_size) { // 元のデータのサイズだけメモリ確保 std::vector< Bytef > dest; dest.resize(original_size); //伸長 uLongf destLen = original_size; int ret = uncompress( dest.data(), // 伸長したデータの格納先 &destLen, // 格納先のメモリサイズ compressed.data(), // 圧縮データ compressed.size() // 圧縮データのサイズ ); if (ret != Z_OK) return std::vector< Bytef >(); return dest; }
std::vector<unsigned char> read_buffer(const char* filename); int main() { std::vector<unsigned char> buffer = read_buffer("C:/data/cmp.data"); // この数字は圧縮時に取得したものを指定する int original_filesize = 18199552; std::vector< Bytef > uncompressed = uncompress_test(buffer, original_filesize); // ファイル書き出し FILE* fp = fopen("C:\\data\\uncmp.data", "wb"); fwrite(uncompressed.data(), 1, uncompressed.size(), fp); fclose(fp); } //! @brief ファイルからデータを読み込む //! @param [in] filename ファイル名 //! @return ファイル一個分のデータ std::vector<unsigned char> read_buffer(const char* filename) { // データの準備 namespace fs = std::filesystem; std::vector<unsigned char> buffer; uintmax_t size = fs::file_size(filename); FILE* fp = fopen(filename, "rb"); buffer.resize(size); fread(buffer.data(), size, 1, fp); fclose(fp); return buffer; }
.cipd_client.exeをcompressし、cmp.dataを作成した。
cmp.dataをuncompressし、uncmp.dataを作成した。
ちゃんと伸長できているか確認するため、コマンドプロンプトからcompコマンドでバイナリ比較する。