I have the following code
std::ostringstream compressedstream;
// Contains the source data to compress
boost::interprocess::obufferstream bufferstream((char*)bin.data(), bin.size());
boost::iostreams::filtering_stream<boost::iostreams::input> in;
in.push(boost::iostreams::gzip_compressor());
in.push(bufferstream); // The bufferstream is the 'device'
boost::iostreams::copy(in, compressedstream);
auto compresseddata = compressedstream.str();
fileContents = &std::vector<uint8_t>(compresseddata.begin(), compresseddata.end());
Which I expect to compress one std::vector<uint8_t>
into another (that's the API, using files etc is not an option).
However, this fails with the following
Error C2668 'boost::iostreams::detail::chain_client<Chain>::push': ambiguous call to overloaded function
could be 'void boost::iostreams::detail::chain_client<Chain>::push<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,std::streamsize,std::streamsize)'
or 'void boost::iostreams::detail::chain_client<Chain>::push<char,std::char_traits<char>>(std::basic_streambuf<char,std::char_traits<char>> &,std::streamsize,std::streamsize)'
I understand the error, that obufferstream
is being seen as a basic_streambuf
, and a basic_ostream
, but don't know what to do about it. It's not like I can remove one of the superclasses!
What's the correct way to feed a bufferstream
into a filtering_stream
?
A possible way would be to disambiguate, e.g. using
in.push(bufferstream.rdbuf());
That said, there seems to be a lot of unnecessary steps/complexity. If you wanted to implement this:
using Data = std::vector<uint8_t>;
Data compress(Data const& bin);
I'd do it without bufferstream
(and the interprocess library) in the first place:
Data compress(Data const& bin) {
std::vector<char> bout;
bio::array_source src(reinterpret_cast<char const*>(bin.data()), bin.size());
bio::filtering_ostreambuf dst{bio::gzip_compressor()};
dst.push(bio::back_inserter(bout));
copy(src, dst);
return Data(bout.begin(), bout.end());
}
This sadly requires a copy because back_insert_device
would default char_type to uint8_t
for Data
. That's a shame. We can "trivially" 🤔 work around it by defining an adhoc converting output "device", like so:
Data compress(Data const& bin) {
Data bout;
bio::array_source src(reinterpret_cast<char const*>(bin.data()), bin.size());
bio::filtering_ostreambuf dst{bio::gzip_compressor()};
struct inserter {
using char_type = char;
using category = bio::sink_tag;
std::streamsize write(char_type const* p, std::streamsize n) {
_c.insert(_c.end(), p, p + n);
return n;
}
Data& _c;
};
dst.push(inserter{bout});
copy(src, dst);
return bout;
}
See it Live On Coliru
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <sstream>
#include <vector>
namespace bio = boost::iostreams;
using Data = std::vector<uint8_t>;
Data compress(Data const& bin) {
Data bout;
bio::array_source src(reinterpret_cast<char const*>(bin.data()), bin.size());
bio::filtering_ostreambuf dst{bio::gzip_compressor()};
struct inserter {
using char_type = char;
using category = bio::sink_tag;
std::streamsize write(char_type const* p, std::streamsize n) {
_c.insert(_c.end(), p, p + n);
return n;
}
Data& _c;
};
dst.push(inserter{bout});
copy(src, dst);
return bout;
}
#include <fmt/ranges.h>
int main() {
auto compressed = compress({'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n'});
fmt::print("Compressed: {::02x}\n", compressed);
}
Printing
Compressed: [1f, 8b, 08, 00, 00, 00, 00, 00, 00, ff, f3, 48, cd, c9, c9, 57, 28, cf, 2f, ca, 49, 51, e4, 02, 00, 41, e4, a9, b2, 0d, 00, 00, 00]
Disregard the GCC version on Coliru complaining that
category
is unused - it's actually not.