I’m using the new OSC block to pass a Cerealized blob of binary data. My current issue is figuring out how to construct a valid stringstream from a ci::Buffer with minimal copying, and vice versa (as Cereal uses std::streams for data i/o).
For converting into a Buffer (handled by appendBlob), I’m fairly certain the following is the best I can hope for. Data is written into the stream, linearized into a string, then finally copied into the message buffer. Not too bad, with two-ish copies.
std::stringstream ss(std::stringstream::binary | std::stringstream::out);
auto str = ss.str();
msg.appendBlob((void*)str.data(), str.size());
Going from Buffer to stringstream, things look a bit worse. The blob is copied out of the message, copied into a string, then copied into a stream before being deserialized. Three-ish copies before we start deserializing.
auto blob = msg[2].blob();
stringstream ss;
auto str = std::string((char*)blob.getData(), blob.getSize());
ss << str;
cereal::PortableBinaryInputArchive archive(ss);
archive(value, another);
Any advice on avoiding unnecessary copies is appreciated. I tried using pubsetbuf
to use the Buffer directly within the stringstream, but without success.
Thanks.
getArgBlobData
Doesn’t copy, which would reduce the number of copies but you’d have to keep the message within the full scope of use.
Does cereal really only allow a stringstream to interface with the library?
Also, I don’t know if you have access to it but string_view
could then be used to interface with stringstream negating another copy.
Yeah, Cereal really only works with streams (any std::stream). Maybe I’ll open an issue there to ask about reading linear arrays of data as a stream.
I’ll have a look at using string_view
in conjunction with getArgBlobData to at least reduce the number of copies. Perhaps there is also a stream_view
that can wrap a linear array of data; would be handy here.
I found a solution when searching for “stream facade for buffer.” You can inherit from std::streambuf and provide a pointer to your custom buffer type to std::stream constructors. The custom buffer type gains access to a protected method that lets it set the buffer’s pointers in memory.
Mine looks like the following:
// Define a custom buffer type so you can point the memory of your choosing
class StreamView : public std::streambuf {
public:
StreamView(char *data, size_t size) {
this->setg(data, data, data + size);
}
};
…
// pull data from OSC blob
const void* data;
size_t size;
msg[1].blobData(&data, &size);
// Construct a streambuf that points to our message blob data
StreamView view((char*)data, size);
std::istream stream(&view);
Referenced the following:
Beginner’s guide to streambuf
C++ reference
Note that the above is probably only a sane maneuver with input streams. If you tried writing to a stream with that buffer, you should be prepared for wild, undefined behavior and memory corruption.