Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simpler way to create a C++ memorystream from (char*, size_t), without copying the data?

I couldn't find anything ready-made, so I came up with:

class membuf : public basic_streambuf<char> { public:   membuf(char* p, size_t n) {     setg(p, p, p + n);     setp(p, p + n);   } } 

Usage:

char *mybuffer; size_t length; // ... allocate "mybuffer", put data into it, set "length"  membuf mb(mybuffer, length); istream reader(&mb); // use "reader" 

I know of stringstream, but it doesn't seem to be able to work with binary data of given length.

Am I inventing my own wheel here?

EDIT

  • It must not copy the input data, just create something that will iterate over the data.
  • It must be portable - at least it should work both under gcc and MSVC.
like image 803
Marcin Seredynski Avatar asked Jan 17 '10 04:01

Marcin Seredynski


2 Answers

I'm assuming that your input data is binary (not text), and that you want to extract chunks of binary data from it. All without making a copy of your input data.

You can combine boost::iostreams::basic_array_source and boost::iostreams::stream_buffer (from Boost.Iostreams) with boost::archive::binary_iarchive (from Boost.Serialization) to be able to use convenient extraction >> operators to read chunks of binary data.

#include <stdint.h> #include <iostream> #include <boost/iostreams/device/array.hpp> #include <boost/iostreams/stream.hpp> #include <boost/archive/binary_iarchive.hpp>  int main() {     uint16_t data[] = {1234, 5678};     char* dataPtr = (char*)&data;      typedef boost::iostreams::basic_array_source<char> Device;     boost::iostreams::stream_buffer<Device> buffer(dataPtr, sizeof(data));     boost::archive::binary_iarchive archive(buffer, boost::archive::no_header);      uint16_t word1, word2;     archive >> word1 >> word2;     std::cout << word1 << "," << word2 << std::endl;     return 0; } 

With GCC 4.4.1 on AMD64, it outputs:

1234,5678

Boost.Serialization is very powerful and knows how to serialize all basic types, strings, and even STL containers. You can easily make your types serializable. See the documentation. Hidden somewhere in the Boost.Serialization sources is an example of a portable binary archive that knows how to perform the proper swapping for your machine's endianness. This might be useful to you as well.

If you don't need the fanciness of Boost.Serialization and are happy to read the binary data in an fread()-type fashion, you can use basic_array_source in a simpler way:

#include <stdint.h> #include <iostream> #include <boost/iostreams/device/array.hpp> #include <boost/iostreams/stream.hpp>  int main() {     uint16_t data[] = {1234, 5678};     char* dataPtr = (char*)&data;      typedef boost::iostreams::basic_array_source<char> Device;     boost::iostreams::stream<Device> stream(dataPtr, sizeof(data));      uint16_t word1, word2;     stream.read((char*)&word1, sizeof(word1));     stream.read((char*)&word2, sizeof(word2));     std::cout << word1 << "," << word2 << std::endl;      return 0; } 

I get the same output with this program.

like image 73
Emile Cormier Avatar answered Sep 30 '22 01:09

Emile Cormier


I'm not sure what you need, but does this do what you want?

char *mybuffer; size_t length; // allocate, fill, set length, as before  std::string data(mybuffer, length); std::istringstream mb(data); //use mb 
like image 40
crmoore Avatar answered Sep 30 '22 02:09

crmoore