web-dev-qa-db-ja.com

zlibでバッファを圧縮する方法は?

Zlib Webサイトに使用例があります。 http://www.zlib.net/zlib_how.html

ただし、この例ではファイルを圧縮しています。メモリ内のバッファに保存されているバイナリデータを圧縮したいと思います。圧縮バッファをディスクに保存したくありません。

基本的にここに私のバッファがあります:

fIplImageHeader->imageData = (char*)imageIn->getFrame();

Zlibで圧縮するにはどうすればよいですか?

私はそれを行う方法のコード例を感謝します。

34
Richard Knop

これは、バッファをzlibでパックし、圧縮されたコンテンツをベクターに保存する例です。

void compress_memory(void *in_data, size_t in_data_size, std::vector<uint8_t> &out_data)
{
 std::vector<uint8_t> buffer;

 const size_t BUFSIZE = 128 * 1024;
 uint8_t temp_buffer[BUFSIZE];

 z_stream strm;
 strm.zalloc = 0;
 strm.zfree = 0;
 strm.next_in = reinterpret_cast<uint8_t *>(in_data);
 strm.avail_in = in_data_size;
 strm.next_out = temp_buffer;
 strm.avail_out = BUFSIZE;

 deflateInit(&strm, Z_BEST_COMPRESSION);

 while (strm.avail_in != 0)
 {
  int res = deflate(&strm, Z_NO_FLUSH);
  assert(res == Z_OK);
  if (strm.avail_out == 0)
  {
   buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE);
   strm.next_out = temp_buffer;
   strm.avail_out = BUFSIZE;
  }
 }

 int deflate_res = Z_OK;
 while (deflate_res == Z_OK)
 {
  if (strm.avail_out == 0)
  {
   buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE);
   strm.next_out = temp_buffer;
   strm.avail_out = BUFSIZE;
  }
  deflate_res = deflate(&strm, Z_FINISH);
 }

 assert(deflate_res == Z_STREAM_END);
 buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE - strm.avail_out);
 deflateEnd(&strm);

 out_data.swap(buffer);
}
34
Jonas Gulle

zlib.hには必要なすべての機能があります:compress(またはcompress2)およびuncompress。回答については、zlibのソースコードを参照してください。

ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen));
/*
         Compresses the source buffer into the destination buffer.  sourceLen is
     the byte length of the source buffer.  Upon entry, destLen is the total size
     of the destination buffer, which must be at least the value returned by
     compressBound(sourceLen).  Upon exit, destLen is the actual size of the
     compressed buffer.

         compress returns Z_OK if success, Z_MEM_ERROR if there was not
     enough memory, Z_BUF_ERROR if there was not enough room in the output
     buffer.
*/

ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen));
/*
         Decompresses the source buffer into the destination buffer.  sourceLen is
     the byte length of the source buffer.  Upon entry, destLen is the total size
     of the destination buffer, which must be large enough to hold the entire
     uncompressed data.  (The size of the uncompressed data must have been saved
     previously by the compressor and transmitted to the decompressor by some
     mechanism outside the scope of this compression library.) Upon exit, destLen
     is the actual size of the uncompressed buffer.

         uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
     enough memory, Z_BUF_ERROR if there was not enough room in the output
     buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.  In
     the case where there is not enough room, uncompress() will fill the output
     buffer with the uncompressed data up to that point.
*/
44
Huiwei

fread()呼び出しとfwrite()呼び出しをデータへの直接ポインターに置き換えることにより、例を簡単に適合させることができます。 zlib圧縮(「deflate "データのすべてを取り出す"」と呼ばれる)の場合、_z_stream_構造体を割り当て、deflateInit()を呼び出してから、

  1. _next_in_を圧縮するデータの次のチャンクで埋めます
  2. _avail_in_を_next_in_で使用可能なバイト数に設定します
  3. _next_out_を、圧縮データを書き込む場所に設定します。通常、圧縮データは、バッファ内のポインタである必要があります。
  4. _avail_out_を_next_out_で使用可能なバイト数に設定します
  5. deflateを呼び出します
  6. _avail_out_がゼロ以外になるまで手順3から5を繰り返します(つまり、出力バッファーにはzlibが必要とする以上のスペースがあります-書き込むデータがこれ以上ありません)
  7. 圧縮するデータがある間、手順1〜6を繰り返します。

最終的にdeflateEnd()を呼び出して完了です。

基本的には、入力と出力がなくなるまで、入力と出力の塊を送ります。

10
kichik

これはzlib APIに関する質問に対する直接的な回答ではありませんが、zlibとペアになったboost::iostreamsライブラリに興味があるかもしれません

これにより、基本的な「ストリーム」操作表記法を使用してzlib駆動のパッキングアルゴリズムを使用でき、メモリストリームを開いて<< data操作を実行することでデータを簡単に圧縮できます。

boost::iostreamsの場合、これは、ストリームを通過するすべてのデータに対して対応するパッキングフィルターを自動的に呼び出します。

3
Yippie-Ki-Yay

C++std::vectorオブジェクトを使用した圧縮と解凍を示す完全な例は次のとおりです。

#include <cstdio>
#include <iosfwd>
#include <iostream>
#include <vector>
#include <zconf.h>
#include <zlib.h>
#include <iomanip>
#include <cassert>

void add_buffer_to_vector(std::vector<char> &vector, const char *buffer, uLongf length) {
    for (int character_index = 0; character_index < length; character_index++) {
        char current_character = buffer[character_index];
        vector.Push_back(current_character);
    }
}

int compress_vector(std::vector<char> source, std::vector<char> &destination) {
    unsigned long source_length = source.size();
    uLongf destination_length = compressBound(source_length);

    char *destination_data = (char *) malloc(destination_length);
    if (destination_data == nullptr) {
        return Z_MEM_ERROR;
    }

    Bytef *source_data = (Bytef *) source.data();
    int return_value = compress2((Bytef *) destination_data, &destination_length, source_data, source_length,
                                 Z_BEST_COMPRESSION);
    add_buffer_to_vector(destination, destination_data, destination_length);
    free(destination_data);
    return return_value;
}

int decompress_vector(std::vector<char> source, std::vector<char> &destination) {
    unsigned long source_length = source.size();
    uLongf destination_length = compressBound(source_length);

    char *destination_data = (char *) malloc(destination_length);
    if (destination_data == nullptr) {
        return Z_MEM_ERROR;
    }

    Bytef *source_data = (Bytef *) source.data();
    int return_value = uncompress((Bytef *) destination_data, &destination_length, source_data, source.size());
    add_buffer_to_vector(destination, destination_data, destination_length);
    free(destination_data);
    return return_value;
}

void add_string_to_vector(std::vector<char> &uncompressed_data,
                          const char *my_string) {
    int character_index = 0;
    while (true) {
        char current_character = my_string[character_index];
        uncompressed_data.Push_back(current_character);

        if (current_character == '\00') {
            break;
        }

        character_index++;
    }
}

// https://stackoverflow.com/a/27173017/3764804
void print_bytes(std::ostream &stream, const unsigned char *data, size_t data_length, bool format = true) {
    stream << std::setfill('0');
    for (size_t data_index = 0; data_index < data_length; ++data_index) {
        stream << std::hex << std::setw(2) << (int) data[data_index];
        if (format) {
            stream << (((data_index + 1) % 16 == 0) ? "\n" : " ");
        }
    }
    stream << std::endl;
}

void test_compression() {
    std::vector<char> uncompressed(0);
    auto *my_string = (char *) "Hello, world!";
    add_string_to_vector(uncompressed, my_string);

    std::vector<char> compressed(0);
    int compression_result = compress_vector(uncompressed, compressed);
    assert(compression_result == F_OK);

    std::vector<char> decompressed(0);
    int decompression_result = decompress_vector(compressed, decompressed);
    assert(decompression_result == F_OK);

    printf("Uncompressed: %s\n", uncompressed.data());
    printf("Compressed: ");
    std::ostream &standard_output = std::cout;
    print_bytes(standard_output, (const unsigned char *) compressed.data(), compressed.size(), false);
    printf("Decompressed: %s\n", decompressed.data());
}

main.cppで次を呼び出します。

int main(int argc, char *argv[]) {
    test_compression();
    return EXIT_SUCCESS;
}

生成される出力:

Uncompressed: Hello, world!
Compressed: 78daf348cdc9c9d75128cf2fca495164000024e8048a
Decompressed: Hello, world!
0
BullyWiiPlaza