webentwicklung-frage-antwort-db.com.de

Wie komprimiere ich einen Puffer mit zlib?

Es gibt ein Anwendungsbeispiel auf der zlib-Website: http://www.zlib.net/zlib_how.html

Im Beispiel wird jedoch eine Datei komprimiert. Ich möchte in einem Speicher gespeicherte binäre Daten komprimieren. Ich möchte den komprimierten Puffer auch nicht auf der Festplatte speichern.

Im Grunde ist hier mein Puffer:

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

Wie kann ich es mit zlib komprimieren?

Ich würde mich über einige Codebeispiele freuen.

34
Richard Knop

Dies ist ein Beispiel, um einen Puffer mit zlib zu packen und den komprimierten Inhalt in einem Vektor zu speichern.

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);
}
33
Jonas Gulle

zlib.h verfügt über alle Funktionen, die Sie benötigen: compress (oder compress2) und uncompress. Eine Antwort finden Sie im Quellcode von 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.
*/
42
Huiwei

Sie können das Beispiel leicht anpassen, indem Sie fread()- und fwrite()-Aufrufe durch direkte Verweise auf Ihre Daten ersetzen. Für die zlib-Komprimierung (als deflate bezeichnet, da Sie alle Daten aus Ihren Daten entfernen), weisen Sie z_stream-Struktur zu. Rufen Sie deflateInit() auf und dann:

  1. füllen Sie next_in mit dem nächsten Datenblock, den Sie komprimieren möchten
  2. setze avail_in auf die Anzahl der in next_in verfügbaren Bytes
  3. setzen Sie next_out auf den Ort, an den die komprimierten Daten geschrieben werden sollen. Dies sollte normalerweise ein Zeiger in Ihrem Puffer sein, der mit dem Fortschreiten fortschreitet
  4. setze avail_out auf die Anzahl der in next_out verfügbaren Bytes
  5. aufruf deflate
  6. wiederholen Sie die Schritte 3 bis 5, bis avail_out ungleich Null ist (d. h. der Ausgabepuffer enthält mehr Platz, als für zlib erforderlich ist - es müssen keine weiteren Daten geschrieben werden.)
  7. wiederholen Sie die Schritte 1-6, während Sie Daten zum Komprimieren haben

Schließlich rufst du deflateEnd() an und bist fertig.

Sie füttern im Wesentlichen Teile der Eingabe und Ausgabe, bis Sie keine Eingabe mehr haben und keine Ausgabe mehr erfolgt.

10
kichik

Dies ist keine direkte Antwort auf Ihre Frage zur zlib-API. Möglicherweise interessieren Sie sich jedoch für die Bibliothek boost::iostreams, die mit zlib gekoppelt ist.

Dies ermöglicht die Verwendung von zlib-gesteuerten Packungsalgorithmen unter Verwendung der grundlegenden "Stream" -Operationen. Dann könnten Ihre Daten leicht komprimiert werden, indem Sie einen Speicherstrom öffnen und die << data-Operation ausführen.

Im Falle von boost::iostreams würde dies automatisch den entsprechenden Verpackungsfilter für alle Daten aufrufen, die den Stream durchlaufen.

3
Yippie-Ki-Yay

Hier ist ein vollständiges Beispiel, das die Komprimierung und Dekomprimierung mit C++std::vector-Objekten veranschaulicht:

#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());
}

In Ihrem main.cpp rufen Sie einfach an:

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

Die erzeugte Ausgabe:

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