How to compress a buffer with zlib?

后端 未结 5 513
無奈伤痛
無奈伤痛 2020-12-23 13:15

There is a usage example at the zlib website: http://www.zlib.net/zlib_how.html

However in the example they are compressing a file. I would like to compress a binary

5条回答
  •  情书的邮戳
    2020-12-23 13:42

    The classic way more convenient with C++ features

    Here's a full example which demonstrates compression and decompression using C++ std::vector objects:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    void add_buffer_to_vector(std::vector &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 source, std::vector &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 source, std::vector &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 &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 uncompressed(0);
        auto *my_string = (char *) "Hello, world!";
        add_string_to_vector(uncompressed, my_string);
    
        std::vector compressed(0);
        int compression_result = compress_vector(uncompressed, compressed);
        assert(compression_result == F_OK);
    
        std::vector 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 your main.cpp simply call:

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

    The output produced:

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

    The Boost way

    #include 
    #include 
    #include 
    #include 
    
    std::string compress(const std::string &data) {
        boost::iostreams::filtering_streambuf output_stream;
        output_stream.push(boost::iostreams::zlib_compressor());
        std::stringstream string_stream;
        output_stream.push(string_stream);
        boost::iostreams::copy(boost::iostreams::basic_array_source(data.c_str(),
                                                                          data.size()), output_stream);
        return string_stream.str();
    }
    
    std::string decompress(const std::string &cipher_text) {
        std::stringstream string_stream;
        string_stream << cipher_text;
        boost::iostreams::filtering_streambuf input_stream;
        input_stream.push(boost::iostreams::zlib_decompressor());
    
        input_stream.push(string_stream);
        std::stringstream unpacked_text;
        boost::iostreams::copy(input_stream, unpacked_text);
        return unpacked_text.str();
    }
    
    TEST_CASE("zlib") {
        std::string plain_text = "Hello, world!";
        const auto cipher_text = compress(plain_text);
        const auto decompressed_plain_text = decompress(cipher_text);
        REQUIRE(plain_text == decompressed_plain_text);
    }
    

提交回复
热议问题