スポンサーリンク

zlibを使って圧縮 (deflate使用)

巨大なデータを細切れに読み込んで少しずつ圧縮していく使い方をする。

#pragma warning(disable:4996)

#include <vector>

// 圧縮に必要
#include <zlib.h>

#include <cassert>
#include <array>

// 圧縮に必要
#if _DEBUG
#pragma comment(lib,"zlibstaticd.lib")
#else
#pragma comment(lib,"zlibstatic.lib")
#endif

constexpr int BUFFER_SIZE = 1000;

//! @brief 入出力バッファを格納するデータ型。 struct buffer_t { size_t size; //!< 有効なデータが入っているサイズ std::array<Byte, BUFFER_SIZE> buffer; //! @brief 確保したバッファ buffer_t( const size_t _size, const std::array<Byte, BUFFER_SIZE>& data ) : size(_size), buffer(data) {} }; //! @brief ファイルからデータを読み込む //! @param [in] filename ファイル名 //! @param [out] ファイルを読み込んだ、固定長のバッファの一覧 //! @return ファイル一個分のデータ void read_buffer(const char* filename, std::vector< buffer_t >& datalist) { // データの準備 std::array<unsigned char, BUFFER_SIZE> buffer; FILE* fp = fopen(filename, "rb"); do { size_t readsize = fread(buffer.data(), 1, buffer.size(), fp); // データと読み込んだサイズを保存 datalist.emplace_back( readsize, buffer ); } while (feof(fp) == 0); fclose(fp); }
bool my_compress(
  std::vector<buffer_t>& zippedBuffer,
  const std::vector< buffer_t >& data_list) {

  // 構造体の初期化
  z_stream strm;
  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;


  //初期化
  int ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);

  if (ret != Z_OK) {
    return false;
  }

  int flush = Z_NO_FLUSH;

  for (size_t i = 0; i < data_list.size(); i++) {

    // これから圧縮するデータ。
    const buffer_t& inbuffer = data_list[i];

    // バッファが小さい場合、読み込みを分割せざるを得ない。
    // zlibではデータを複数回に分けて入力し、
    // すべて出そろった段階で処理を行えるような構造になっている。
    // ここにfreadを書くのもわかりにくいので今回は data_listとしてデータ一覧を用意する


    strm.next_in = (Bytef*)inbuffer.buffer.data(); //圧縮前データ
    strm.avail_in = (uInt)inbuffer.size;        // 圧縮前データのバイト数

    // 全てのデータが処理されたら、一番最後にZ_FINISH設定
    // つまり最後の一回だけ特別な処理をする。
    if (i == data_list.size() - 1) {
      flush = Z_FINISH;
    }
    else {
      flush = Z_NO_FLUSH;
    }

    // 圧縮関数に渡して処理されたデータを受け取るためのバッファ
    std::array<Byte, BUFFER_SIZE> tmpbuffer;

    do {
      strm.avail_out = BUFFER_SIZE;// 出力先バッファのサイズ
      strm.next_out = tmpbuffer.data(); // 出力先バッファ

      ret = deflate(&strm, flush);  /* no bad return value */
      assert(ret != Z_STREAM_ERROR);  /* state not clobbered */

      // バッファが全部使われた場合、avail_outは0となる
      // 従って BUFFER_SIZE - 0 で have==BUFFER_SIZE となる。

      //圧縮したデータサイズを取得
      unsigned have = BUFFER_SIZE - strm.avail_out;

      // 結果の出力先に積んでいく
      zippedBuffer.emplace_back(have,tmpbuffer);

    } while (strm.avail_out == 0);

  }

  // 終了時呼び出す。
  deflateEnd(&strm);

  return true;
}

      
int main()
{
  // 結果の出力先
  // 固定長バッファの配列。
  // 結果のサイズを一括で確保しない方針の実装。
  std::vector<buffer_t> zippedBuffer;

  // 圧縮データ一覧を用意

  // freadで読み込むデータ一覧。
  // バッファが小さく一つの配列に収まらない状況を再現するため、
  // 小さめの領域のリストとして全てのデータを読み込む
  std::vector< buffer_t > data_list;

  // 元データ読込
  read_buffer("C:/data/.cipd_client.exe", data_list);

  // 圧縮実行
  my_compress(zippedBuffer, data_list);

  // ファイル書き出し
  FILE* fp = fopen("C:\\data\\cmp.data", "wb");
  for (size_t i = 0; i < zippedBuffer.size(); i++) {

    fwrite(
      zippedBuffer[i].buffer.data(), // バッファの先頭
      1,               // 1要素のサイズ
      zippedBuffer[i].size,      // 要素数 
      fp);
  }
  fclose(fp);


}

本当は伸長もやりたかったが圧縮に結構体力を使ったので次回にする。

 

zlibで単一のデータを圧縮( compress関数 )

zlibを使って圧縮 (deflate使用)

zlibを使って伸長 (inflate使用)

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: