Sascha
Sascha

Reputation: 1136

Seek doesn't work on std::istream initialized with boost filtering_istreambuf

I an trying to read a file. It should be memory mapped for performance. I'd like to use the boost filter chain for iostreams to easily include zip, bzip2, and gzip decompression.

I tried to adopt the solution proposed in Using boost::iostreams mapped_file_source and filtering_streambuf to decompress file.

My Problem: when I try to seek() on the stream, I get an exception: "no random access". That's strange, because from the documentation I understood that I could use std::istream as an interface.

I came up with the following code (also on coliru):

#include <iostream>
#include <stdio.h>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>

// and later also include ...
//#include <boost/iostreams/filter/gzip.hpp>
//#include <boost/iostreams/filter/bzip2.hpp>

int main()
{
    namespace io = boost::iostreams;
    io::mapped_file_source inputDevice;                           // the device to read from (file)
    io::stream<io::mapped_file_source> mappedFileStream; // the memory mapped file stream    
    io::filtering_istreambuf filteredInputStream;                 // the source file (stream), together with a chain of filters and/or decompressors


    inputDevice = io::mapped_file_source("main.cpp");
    mappedFileStream.open(inputDevice);      // open device as a readable stream
    // add optional filter/decoder/decompressor
    //filteredInputStream.push(io::bzip2_decompressor());
    //filteredInputStream.push(io::zlib_decompressor());
    //filteredInputStream.push(io::gzip_decompressor());
    //filteredInputStream.push(io::base64_decoder());
    filteredInputStream.push(mappedFileStream);  //finally add the readable stream 
    // now we have a device that is streamed and the contents are filtered the resulting stream can be used like a std::iostream. (in theory)
    std::istream inputStream(&filteredInputStream);

    std::cout << inputStream.get() << std::endl;
    std::cout << inputStream.get() << std::endl;
    //fatal error: class boost::exception_detail::clone_impl<struct boost::exception_detail::error_info_injector<class std::ios_base::failure> >: no random access
    filteredInputStream.pubseekoff(0, std::ios_base::beg, std::ios_base::in | std::ios_base::out);
    inputStream.seekg(0);  // fatal error: class boost::exception_detail::clone_impl<struct boost::exception_detail::error_info_injector<class std::ios_base::failure> >: no random access
    std::cout << inputStream.get() << std::endl;
    std::cout << inputStream.get() << std::endl;
    return 0;
}

Upvotes: 4

Views: 1574

Answers (1)

Sascha
Sascha

Reputation: 1136

While composing the question, I found the solution here.

boost::iostreams, gzip files and tellg

The question is related to gzip, but boost iostream buffer seem to default to non-seekable. I misunderstood the boost documentation.

So here is the solution:

Replace:

io::filtering_istreambuf filteredInputStream;         

By

io::filtering_streambuf<io::input_seekable> filteredInputStream;  

Live on coliru: http://coliru.stacked-crooked.com/a/aff637be181a27da

#include <iostream>
#include <stdio.h>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>


// and later also include ...
//#include <boost/iostreams/filter/gzip.hpp>
//#include <boost/iostreams/filter/bzip2.hpp>

int main()
{
    namespace io = boost::iostreams;
    io::mapped_file_source inputDevice;                               // the device to read from (file)
    io::stream<io::mapped_file_source> mappedFileStream;              // the memory mapped file stream    
    io::filtering_streambuf<io::input_seekable> filteredInputStream;  // the source file (stream), together with a chain of filters and/or decompressors
    //io::filtering_istreambuf filteredInputStream;                   // wrong. Defaults to non-seekable. Throws exception on seek or tell


    inputDevice = io::mapped_file_source("main.cpp");
    mappedFileStream.open(inputDevice);      // open device as a readable stream
    // add optional filter/decoder/decompressor
    //filteredInputStream.push(io::bzip2_decompressor());
    //filteredInputStream.push(io::zlib_decompressor());
    //filteredInputStream.push(io::gzip_decompressor());
    //filteredInputStream.push(io::base64_decoder());
    filteredInputStream.push(mappedFileStream);  //finally add the readable stream 
    // now we have a device that is streamed and the contents are filtered the resulting stream can be used like a std::iostream. (in theory)
    std::istream inputStream(&filteredInputStream);

    std::cout << inputStream.get() << std::endl;
    std::cout << inputStream.get() << std::endl;
    filteredInputStream.pubseekoff(0, std::ios_base::beg, std::ios_base::in | std::ios_base::out);
    inputStream.seekg(0);  
    std::cout << inputStream.get() << std::endl;
    std::cout << inputStream.get() << std::endl;
    return 0;
}

Upvotes: 5

Related Questions