Search code examples
c++boostzlibboost-iostreams

How to streaming decompress using boost iostreams


I'm using boost iostreams (1.64.0) in order to decompress zlib data. I want to do streaming decompression. That means the compressed data are unpredictable size. I wrote the following code example.

#include <sstream>
#include <string>
#include <iostream>

#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/zlib.hpp>

int main() {
    // Compress
    std::stringstream sender;
    boost::iostreams::filtering_streambuf<boost::iostreams::input> out;
    out.push(boost::iostreams::zlib_compressor());
    out.push(sender);
    sender << "Hello World";
    std::stringstream compressed;
    boost::iostreams::copy(out, compressed);

    // Decompress
    boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
    in.push(boost::iostreams::zlib_decompressor());
    in.push(compressed);
    std::istream is(&in);
    std::size_t const buf_size = 256;
    char buf[buf_size] = { '\0' };
#if 0
    is.getline(buf, buf_size); // works fine
#else
    std::size_t read_size = is.readsome(buf, buf_size);
    std::cout << "read_size:" << read_size << std::endl;
#endif
    // http://www.cplusplus.com/reference/ios/ios/rdstate/
    std::cout << "rdstate:" << is.rdstate() << std::endl;
    std::cout << buf << std::endl;

}

I use readsome() because the size of data is unpredictable. I got the following output:

read_size:0
rdstate:0

It was unexpected for me.

If I use getline() instead of readsome(), I got the following output:

rdstate:2
Hello World

It was expected output.

I think that when I use readsome(), the out put should be the same. I can't use getline() my in actual code because the original data is binary format.

Is there any way to use readsome() with filtering_streambuf or any good way to streaming decompress unpredictable length binary data?


Solution

  • Thanks to sehe's comment, the problem is solved.

    I wrote the response for the comment but it is difficult to read because codes are not well formatted. So I answer by myself. I hope it will help for other people who have similar problem.

    I replaced

    std::size_t read_size = is.readsome(buf, buf_size);
    

    with

    is.read(buf, buf_size);
    std::size_t read_size = is.gcount();
    

    , then problem is solved.

    I had misunderstood that std::istream::read blocks until reading buf_size length data. It is not true. Even if the actual read size is less than buf_size, the function returns. See http://www.cplusplus.com/reference/istream/istream/read/. In order to get read_size, I call std::istream::gcount(). See http://www.cplusplus.com/reference/istream/istream/gcount/.

    NOTE: I had gotten confused with boost::asio::read and boost::asio::ip::tcp::socket::read_some. But their behavior are different from std::istream's one.

    Here is the complete code of the fixed version:

    #include <sstream>
    #include <string>
    #include <iostream>
    
    #include <boost/iostreams/filtering_streambuf.hpp>
    #include <boost/iostreams/copy.hpp>
    #include <boost/iostreams/filter/zlib.hpp>
    
    int main() {
        // Compress
        std::stringstream sender;
        boost::iostreams::filtering_streambuf<boost::iostreams::input> out;
        out.push(boost::iostreams::zlib_compressor());
        out.push(sender);
        sender << "Hello World";
        std::stringstream compressed;
        boost::iostreams::copy(out, compressed);
    
        // Decompress
        boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
        in.push(boost::iostreams::zlib_decompressor());
        in.push(compressed);
        std::istream is(&in);
        std::size_t const buf_size = 256;
        char buf[buf_size] = { '\0' };
        is.read(buf, buf_size);
        std::size_t read_size = is.gcount();
        std::cout << "read_size:" << read_size << std::endl;
        // http://www.cplusplus.com/reference/ios/ios/rdstate/
        std::cout << "rdstate:" << is.rdstate() << std::endl;
        std::cout << buf << std::endl;
    
    }