Created
April 12, 2019 20:37
-
-
Save polyvertex/ce86fddfa28edcfb19a77f9024a5461c to your computer and use it in GitHub Desktop.
A memory stream buffer class compliant with std::basic_streambuf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
namespace xx { | |
// A memory stream buffer class compliant with `std::basic_streambuf`. | |
// | |
// Usage example: | |
// | |
// std::vector<char> data; | |
// // ... fill-in *data* here ... | |
// xx::basic_memstreambuf<data.value_type> sbuf(&data[0], data.size()); | |
// std::istream in(&sbuf); | |
// // ... read data from *in* ... | |
// | |
// Useful references: | |
// * Deriving from std::streambuf | |
// https://artofcode.wordpress.com/2010/12/12/deriving-from-stdstreambuf/ | |
// * A beginner's guide to writing a custom stream buffer (std::streambuf) | |
// http://www.voidcn.com/article/p-vjnlygmc-gy.html | |
// * membuf.cpp | |
// https://gist.github.com/mlfarrell/28ea0e7b10756042956b579781ac0dd8 | |
// * Memory streambuf and stream | |
// https://codereview.stackexchange.com/questions/138479/memory-streambuf-and-stream | |
template < | |
class CharT, | |
class Traits = std::char_traits<CharT>> | |
class basic_memstreambuf : | |
public std::basic_streambuf<CharT, Traits> | |
{ | |
public: | |
typedef std::basic_streambuf<CharT, Traits> BaseT; | |
using BaseT::int_type; | |
using BaseT::traits_type; | |
public: | |
basic_memstreambuf() | |
: BaseT() | |
{ } | |
explicit basic_memstreambuf(const basic_memstreambuf& rhs) | |
: BaseT(static_cast<BaseT&>(rhs)) | |
{ } | |
// non-standard | |
explicit basic_memstreambuf(const std::basic_string<char_type>& s) | |
: BaseT() | |
{ | |
assert(!s.empty()); | |
setg( | |
const_cast<char_type*>(s.begin()), | |
const_cast<char_type*>(s.begin()), | |
const_cast<char_type*>(s.end())); | |
} | |
// non-standard | |
basic_memstreambuf(const char_type* s, std::streamsize n) | |
: BaseT() | |
{ | |
assert(s); | |
assert(n > 0); | |
setg( | |
const_cast<char_type*>(s), | |
const_cast<char_type*>(s), | |
const_cast<char_type*>(s + n)); | |
} | |
// non-standard | |
basic_memstreambuf(const char_type* begin, const char_type* end) | |
: BaseT() | |
{ | |
assert(begin); | |
assert(end); | |
assert(begin < end); | |
// check size | |
const std::uintmax_t count = end - begin; | |
if (count > static_cast<decltype(count)>( | |
std::numeric_limits<std::streamsize>::max())) | |
{ | |
throw std::invalid_argument("basic_memstreambuf too big"); | |
} | |
setg( | |
const_cast<char_type*>(begin), | |
const_cast<char_type*>(begin), | |
const_cast<char_type*>(end)); | |
} | |
basic_memstreambuf& operator=(const basic_memstreambuf&) = delete; | |
protected: | |
virtual std::streamsize showmanyc() override | |
{ | |
const auto* ptr = gptr(); | |
const auto* end = egptr(); | |
assert(ptr <= end); | |
return (ptr <= end) ? (end - ptr) : 0; | |
} | |
virtual int_type underflow() override | |
{ | |
const auto* ptr = gptr(); | |
if (ptr >= egptr()) | |
return traits_type::eof(); | |
return traits_type::to_int_type(*ptr); | |
} | |
virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override | |
{ | |
if (count == 0) | |
return 0; | |
const auto* ptr = gptr(); | |
const auto to_read = std::min( | |
count, | |
static_cast<std::streamsize>(egptr() - ptr)); | |
if (to_read == 0) | |
{ | |
return traits_type::eof(); | |
} | |
else | |
{ | |
std::memcpy(s, ptr, to_read); | |
gbump(to_read); | |
return to_read; | |
} | |
} | |
virtual pos_type seekoff( | |
off_type off, | |
std::ios_base::seekdir dir, | |
std::ios_base::openmode which = std::ios_base::in) override | |
{ | |
if (which != std::ios_base::in) | |
{ | |
assert(0); | |
throw std::invalid_argument("basic_memstreambuf::seekoff[which]"); | |
} | |
if (dir == std::ios_base::beg) | |
{ | |
if (off >= 0 && off < egptr() - eback()) | |
{ | |
setg(eback(), eback() + off, egptr()); | |
} | |
else | |
{ | |
assert(0); | |
throw std::out_of_range("basic_memstreambuf::seekoff[beg]"); | |
} | |
} | |
else if (dir == std::ios_base::cur) | |
{ | |
if ((off >= 0 && off < egptr() - gptr()) || | |
(off < 0 && std::abs(off) < gptr() - eback())) | |
{ | |
gbump(off); | |
} | |
else | |
{ | |
assert(0); | |
throw std::out_of_range("basic_memstreambuf::seekoff[cur]"); | |
} | |
} | |
else if (dir == std::ios_base::end) | |
{ | |
if (off <= 0 && std::abs(off) < egptr() - eback()) | |
{ | |
setg(eback(), egptr() + off, egptr()); | |
} | |
else | |
{ | |
assert(0); | |
throw std::out_of_range("basic_memstreambuf::seekoff[end]"); | |
} | |
} | |
else | |
{ | |
assert(0); | |
throw std::invalid_argument("basic_memstreambuf::seekoff[dir]"); | |
} | |
return gptr() - eback(); | |
} | |
virtual pos_type seekpos( | |
pos_type pos, | |
std::ios_base::openmode which = std::ios_base::in) override | |
{ | |
if (which != std::ios_base::in) | |
{ | |
assert(0); | |
throw std::invalid_argument("basic_memstreambuf::seekpos[which]"); | |
} | |
if (pos < egptr() - eback()) | |
{ | |
setg(eback(), eback() + pos, egptr()); | |
} | |
else | |
{ | |
assert(0); | |
throw std::out_of_range("memstreambuf::seekpos"); | |
} | |
return pos; | |
} | |
#if 0 | |
virtual int_type pbackfail(int_type c = traits_type::eof()) override | |
{ | |
const auto* begin = eback(); | |
const auto* ptr = gptr(); | |
const auto gc = *(ptr - 1); | |
if (ptr == begin || (c != traits_type::eof() && c != gc)) | |
return traits_type::eof(); | |
gbump(-1); | |
return traits_type::to_int_type(gc); | |
} | |
#endif | |
}; | |
typedef basic_memstreambuf<char> memstreambuf; | |
typedef basic_memstreambuf<wchar_t> wmemstreambuf; | |
} // namespace xx |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment