Search code examples
c++boostzlibcompressionboost-iostreams

Boost Iostreams zlib_error with Custom Source


I am trying to use a zlib_decompressor to decompress data through an istreambuf_iterator. I couldn't find an in built way to use an input iterator as input to a stream (please point out a way if one exists already) so I wrote this source:

template <class cha_type, class iterator_type>
class IteratorSource {
    public:
        typedef cha_type char_type;
        typedef boost::iostreams::source_tag category;
        iterator_type& i;
        iterator_type eof;

        IteratorSource(iterator_type& it, iterator_type end) : i(it), eof(end) {
        }

        std::streamsize read(char* s, std::streamsize n) {
            for(int j = 0; j < n; j++) {
                if(i == eof) {
                    std::cout << "Reached eof after " << j << " bytes\n";
                    return -1;
                }
                char next = *i++;
                std::cout << "Reading " << next << "\n";
                *s++ = next;
            }
            return n;
        }
};

And used it like this:

int main() {       
    std::vector<char> data_back = {'\x78', '\x9c', '\x73', '\x04', '\x00', '\x00', '\x42', '\x00', '\x42'};
    auto start = data_back.begin();
    IteratorSource<char, decltype(data_back)::iterator> data(start, data_back.end());

    boost::iostreams::filtering_istreambuf def;
    def.push(boost::iostreams::zlib_decompressor());
    def.push(data);
    boost::iostreams::copy(def, std::cout);
    return 0;
}

To give this output:

Reading x
Reading �
Reading s
Reading 
Reading 
Reading 
Reading B
Reading 
Reading B
Reached eof after 9 bytes
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::iostreams::zlib_error> >'
  what():  zlib error
Aborted (core dumped)

I am not sure why this is producing an error because loading from a file works fine.


Solution

  • EDIT In response to the clarified question (in the comments below), here's a trivial adaptation I did of your original sample, that JustWorks™ on my box:

    #include <boost/iostreams/copy.hpp>
    #include <boost/iostreams/filter/zlib.hpp>
    #include <boost/iostreams/filtering_streambuf.hpp>
    #include <iostream>
    #include <sstream>
    
    template <class cha_type, class iterator_type>
    struct my_source {
        typedef cha_type char_type;
        typedef boost::iostreams::source_tag category;
    
        iterator_type& it;
        iterator_type end;
    
        my_source(iterator_type& it, iterator_type end = {}) : it(it), end(end) 
        { }
    
        std::streamsize read(char* s, std::streamsize n) {
            std::streamsize result = 0;
            while ((it!=end) && n--) {
                ++result;
                *s++ = *it++;
            }
            return result;
        }
    };
    
    int main() {       
        std::string const rawdata {'x', '\234', '\313', 'H', '\315', '\311', '\311', 'W', '(', '\317', '/', '\312', 'I', '\341', '\002', '\0', '\036', 'r', '\004', 'g' };
        std::istringstream iss(rawdata, std::ios::binary);
    
        auto start = std::istreambuf_iterator<char>(iss);
        my_source<char, decltype(start)> data(start);
    
        boost::iostreams::filtering_istreambuf def;
        def.push(boost::iostreams::zlib_decompressor());
        def.push(data);
    
        boost::iostreams::copy(def, std::cout);
    }
    

    See it Live On Coliru


    Old answer:

    I think you can use just any stream, like stringstream:

    std::istringstream iss("hello world\n");
    
    filtering_streambuf<input> def;
    def.push(zlib_compressor());
    def.push(iss);
    boost::iostreams::copy(def, std::cout);
    

    or to decompress:

    std::string const rawdata {'x', '\234', '\313', 'H', '\315', '\311', '\311', 'W', '(', '\317', '/', '\312', 'I', '\341', '\002', '\0', '\036', 'r', '\004', 'g' };
    std::istringstream iss(rawdata, std::ios::binary);
    
    filtering_streambuf<input> def;
    def.push(zlib_decompressor());
    def.push(iss);
    boost::iostreams::copy(def, std::cout);
    

    This works like a charm here. (Sorry for the octal escapes: it's what bash gave me

    printf "%q\n" "$(echo hello world | zlib-flate -compress)"
    

    and I'm lazy enough to keep it that way).

    See full example Live on Coliru


    Alternatively Boost Iostreams accepts a streambuffer, so you can equivalently

    def.push(*iss.rdbuf());