Commit 8933b78f authored by Matthias Vallentin's avatar Matthias Vallentin

Make container streambuffer more flexible

parent 7d7b6d23
...@@ -34,6 +34,11 @@ namespace caf { ...@@ -34,6 +34,11 @@ namespace caf {
/// The base class for all stream buffer implementations. /// The base class for all stream buffer implementations.
template <class CharT = char, class Traits = std::char_traits<CharT>> template <class CharT = char, class Traits = std::char_traits<CharT>>
class stream_buffer : public std::basic_streambuf<CharT, Traits> { class stream_buffer : public std::basic_streambuf<CharT, Traits> {
public:
using base = std::basic_streambuf<CharT, Traits>;
using pos_type = typename base::pos_type;
using off_type = typename base::off_type;
protected: protected:
/// The standard only defines pbump(int), which can overflow on 64-bit /// The standard only defines pbump(int), which can overflow on 64-bit
/// architectures. All stream buffer implementations should therefore use /// architectures. All stream buffer implementations should therefore use
...@@ -71,6 +76,65 @@ protected: ...@@ -71,6 +76,65 @@ protected:
safe_gbump(std::streamsize n) { safe_gbump(std::streamsize n) {
this->gbump(static_cast<int>(n)); this->gbump(static_cast<int>(n));
} }
pos_type default_seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which) {
auto new_off = pos_type(off_type(-1));
auto get = (which & std::ios_base::in) == std::ios_base::in;
auto put = (which & std::ios_base::out) == std::ios_base::out;
if (!(get || put))
return new_off; // nothing to do
if (get) {
switch (dir) {
default:
return pos_type(off_type(-1));
case std::ios_base::beg:
new_off = 0;
break;
case std::ios_base::cur:
new_off = this->gptr() - this->eback();
break;
case std::ios_base::end:
new_off = this->egptr() - this->eback();
break;
}
new_off += off;
this->setg(this->eback(), this->eback() + new_off, this->egptr());
}
if (put) {
switch (dir) {
default:
return pos_type(off_type(-1));
case std::ios_base::beg:
new_off = 0;
break;
case std::ios_base::cur:
new_off = this->pptr() - this->pbase();
break;
case std::ios_base::end:
new_off = this->egptr() - this->pbase();
break;
}
new_off += off;
this->setp(this->pbase(), this->epptr());
safe_pbump(new_off);
}
return new_off;
}
pos_type default_seekpos(pos_type pos, std::ios_base::openmode which) {
auto get = (which & std::ios_base::in) == std::ios_base::in;
auto put = (which & std::ios_base::out) == std::ios_base::out;
if (!(get || put))
return pos_type(off_type(-1)); // nothing to do
if (get)
this->setg(this->eback(), this->eback() + pos, this->egptr());
if (put) {
this->setp(this->pbase(), this->epptr());
safe_pbump(pos);
}
return pos;
}
}; };
/// A streambuffer abstraction over a fixed array of bytes. This streambuffer /// A streambuffer abstraction over a fixed array of bytes. This streambuffer
...@@ -142,63 +206,12 @@ protected: ...@@ -142,63 +206,12 @@ protected:
pos_type seekpos(pos_type pos, pos_type seekpos(pos_type pos,
std::ios_base::openmode which std::ios_base::openmode which
= std::ios_base::in | std::ios_base::out) override { = std::ios_base::in | std::ios_base::out) override {
auto get = (which & std::ios_base::in) == std::ios_base::in; return this->default_seekpos(pos, which);
auto put = (which & std::ios_base::out) == std::ios_base::out;
if (!(get || put))
return pos_type(off_type(-1)); // nothing to do
if (get)
this->setg(this->eback(), this->eback() + pos, this->egptr());
if (put) {
this->setp(this->pbase(), this->epptr());
this->safe_pbump(pos);
}
return pos;
} }
pos_type seekoff(off_type off, pos_type seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::seekdir dir,
std::ios_base::openmode which) override { std::ios_base::openmode which) override {
auto new_off = pos_type(off_type(-1)); return this->default_seekoff(off, dir, which);
auto get = (which & std::ios_base::in) == std::ios_base::in;
auto put = (which & std::ios_base::out) == std::ios_base::out;
if (!(get || put))
return new_off; // nothing to do
if (get) {
switch (dir) {
default:
return pos_type(off_type(-1));
case std::ios_base::beg:
new_off = 0;
break;
case std::ios_base::cur:
new_off = this->gptr() - this->eback();
break;
case std::ios_base::end:
new_off = this->egptr() - this->eback();
break;
}
new_off += off;
this->setg(this->eback(), this->eback() + new_off, this->egptr());
}
if (put) {
switch (dir) {
default:
return pos_type(off_type(-1));
case std::ios_base::beg:
new_off = 0;
break;
case std::ios_base::cur:
new_off = this->pptr() - this->pbase();
break;
case std::ios_base::end:
new_off = this->egptr() - this->pbase();
break;
}
new_off += off;
this->setp(this->pbase(), this->epptr());
this->safe_pbump(new_off);
}
return new_off;
} }
// -- put area ------------------------------------------------------------- // -- put area -------------------------------------------------------------
...@@ -252,9 +265,10 @@ public: ...@@ -252,9 +265,10 @@ public:
>::type >::type
> >
containerbuf(Container& c) : container_(c) { containerbuf(Container& c) : container_(c) {
this->setg(const_cast<char_type*>(c.data()), // We use a const_cast because C++11 doesn't provide a non-const data()
const_cast<char_type*>(c.data()), // overload. Using std::data(c) would be the right way to write this.
const_cast<char_type*>(c.data()) + c.size()); auto data = const_cast<char_type*>(c.data());
setbuf(data, static_cast<std::streamsize>(c.size()));
} }
// See note in arraybuf(arraybuf&&). // See note in arraybuf(arraybuf&&).
...@@ -279,13 +293,46 @@ public: ...@@ -279,13 +293,46 @@ public:
return traits_type::to_int_type(*this->gptr()); return traits_type::to_int_type(*this->gptr());
} }
// Hides base-class implementation to simplify single-character insert. // Hides base-class implementation to simplify single-character insert
// without a put area.
int_type sputc(char_type c) { int_type sputc(char_type c) {
container_.push_back(c); container_.push_back(c);
return c; return c;
} }
// Hides base-class implementation to simplify multi-character insert without
// a put area.
std::streamsize sputn(const char_type* s, std::streamsize n) {
return xsputn(s, n);
}
protected: protected:
// -- positioning ----------------------------------------------------------
base* setbuf(char_type* s, std::streamsize n) override {
this->setg(s, s, s + n);
return this;
}
pos_type seekpos(pos_type pos,
std::ios_base::openmode which
= std::ios_base::in | std::ios_base::out) override {
// We only have a get area, so no put area (= out) operations.
if ((which & std::ios_base::out) == std::ios_base::out)
return pos_type(off_type(-1));
return this->default_seekpos(pos, which);
}
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which) override {
// We only have a get area, so no put area (= out) operations.
if ((which & std::ios_base::out) == std::ios_base::out)
return pos_type(off_type(-1));
return this->default_seekoff(off, dir, which);
}
// -- get area -------------------------------------------------------------
// We can't get obtain more characters on underflow, so we only optimize // We can't get obtain more characters on underflow, so we only optimize
// multi-character sequential reads. // multi-character sequential reads.
std::streamsize xsgetn(char_type* s, std::streamsize n) override { std::streamsize xsgetn(char_type* s, std::streamsize n) override {
...@@ -297,6 +344,8 @@ protected: ...@@ -297,6 +344,8 @@ protected:
return actual; return actual;
} }
// -- put area -------------------------------------------------------------
// Should never get called, because there is always space in the buffer. // Should never get called, because there is always space in the buffer.
// (But just in case, it does the same thing as sputc.) // (But just in case, it does the same thing as sputc.)
int_type overflow(int_type c = traits_type::eof()) final { int_type overflow(int_type c = traits_type::eof()) final {
......
...@@ -125,3 +125,20 @@ CAF_TEST(containerbuf) { ...@@ -125,3 +125,20 @@ CAF_TEST(containerbuf) {
CAF_CHECK_EQUAL(buf.size(), data.size()); CAF_CHECK_EQUAL(buf.size(), data.size());
CAF_CHECK(std::equal(buf.begin(), buf.end(), data.begin() /*, data.end() */)); CAF_CHECK(std::equal(buf.begin(), buf.end(), data.begin() /*, data.end() */));
} }
CAF_TEST(containerbuf_reset_get_area) {
std::string str{"foobar"};
std::vector<char> buf;
vectorbuf vb{buf};
// We can always write to the underlying buffer; no put area needed.
auto n = vb.sputn(str.data(), str.size());
CAF_REQUIRE_EQUAL(n, 6);
// Readjust the get area.
CAF_REQUIRE_EQUAL(buf.size(), 6u);
vb.pubsetbuf(buf.data() + 3, buf.size() - 3);
// Now read from a new get area into a buffer.
char bar[3];
n = vb.sgetn(bar, 3);
CAF_CHECK_EQUAL(n, 3);
CAF_CHECK_EQUAL(std::string(bar, 3), "bar");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment