web-dev-qa-db-fra.com

Comment compresser un tampon avec zlib?

Il existe un exemple d'utilisation sur le site Web zlib: http://www.zlib.net/zlib_how.html

Cependant, dans l'exemple, ils compressent un fichier. Je voudrais compresser une donnée binaire stockée dans un tampon en mémoire. Je ne veux pas enregistrer le tampon compressé sur le disque non plus.

En gros voici mon tampon:

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

Comment puis-je le compresser avec zlib?

J'apprécierais un exemple de code pour savoir comment faire cela.

34
Richard Knop

Ceci est un exemple pour compresser un tampon avec zlib et sauvegarder le contenu compressé dans un vecteur.

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 a toutes les fonctions nécessaires: compress (ou compress2) et uncompress. Voir le code source de zlib pour une réponse.

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

Vous pouvez facilement adapter l'exemple en remplaçant les appels fread() et fwrite() par des pointeurs directs sur vos données. Pour la compression zlib (appelée deflate lorsque vous "extrayez tout l'air de vos données"), vous allouez la structure z_stream, appelez deflateInit(), puis:

  1. remplissez next_in avec le prochain bloc de données que vous souhaitez compresser
  2. définir avail_in au nombre d'octets disponibles dans next_in
  3. définissez next_out sur l'emplacement où les données compressées doivent être écrites, ce qui doit généralement être un pointeur à l'intérieur de la mémoire tampon qui avance au fur et à mesure
  4. définir avail_out au nombre d'octets disponibles dans next_out
  5. appelez deflate
  6. répétez les étapes 3 à 5 jusqu'à ce que avail_out soit différent de zéro (c'est-à-dire qu'il y a plus d'espace dans le tampon de sortie que ce dont zlib a besoin - plus de données à écrire)
  7. répétez les étapes 1 à 6 pendant que vous avez des données à compresser

Finalement, vous appelez deflateEnd() et vous avez terminé.

En gros, vous lui fournissez des morceaux d’entrée et de sortie jusqu’à épuisement des entrées et sorties.

10
kichik

Ce n'est pas une réponse directe à votre question sur l'API zlib, mais vous pouvez être intéressé par la bibliothèque boost::iostreams associée à zlib.

Cela permet d’utiliser des algorithmes de compression pilotés par zlib en utilisant la notation de base des opérations "stream". Vos données pourraient alors être facilement compressées en ouvrant un flux de mémoire et en effectuant l’opération << data.

Dans le cas de boost::iostreams, le filtre de compression correspondant sera automatiquement appelé pour chaque donnée transitant par le flux.

3
Yippie-Ki-Yay

Voici un exemple complet qui illustre la compression et la décompression à l'aide d'objets 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());
}

Dans votre main.cpp, appelez simplement:

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

La sortie produite:

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