Commit d1eae33f authored by Matthias Vallentin's avatar Matthias Vallentin

Workaround bug in standard

Turns out gpbump/pbump are not 64-bit safe in the C++ standard.
parent 027d2735
...@@ -23,7 +23,9 @@ ...@@ -23,7 +23,9 @@
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <limits>
#include <streambuf> #include <streambuf>
#include <type_traits>
#include <vector> #include <vector>
#include "caf/config.hpp" #include "caf/config.hpp"
...@@ -31,11 +33,53 @@ ...@@ -31,11 +33,53 @@
namespace caf { namespace caf {
/// The base class for all stream buffer implementations.
template <class CharT = char, class Traits = std::char_traits<CharT>>
class stream_buffer : public std::basic_streambuf<CharT, Traits> {
protected:
/// The standard only defines pbump(int), which can overflow on 64-bit
/// architectures. All stream buffer implementations should therefore use
/// these function instead. For a detailed discussion, see:
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47921
template <class T = int>
typename std::enable_if<sizeof(T) == 4>::type
safe_pbump(std::streamsize n) {
while (n > std::numeric_limits<int>::max()) {
this->pbump(std::numeric_limits<int>::max());
n -= std::numeric_limits<int>::max();
}
this->pbump(static_cast<int>(n));
}
template <class T = int>
typename std::enable_if<sizeof(T) == 8>::type
safe_pbump(std::streamsize n) {
this->pbump(static_cast<int>(n));
}
// As above, but for the get area.
template <class T = int>
typename std::enable_if<sizeof(T) == 4>::type
safe_gbump(std::streamsize n) {
while (n > std::numeric_limits<int>::max()) {
this->gbump(std::numeric_limits<int>::max());
n -= std::numeric_limits<int>::max();
}
this->gbump(static_cast<int>(n));
}
template <class T = int>
typename std::enable_if<sizeof(T) == 8>::type
safe_gbump(std::streamsize n) {
this->gbump(static_cast<int>(n));
}
};
/// A streambuffer abstraction over a fixed array of bytes. This streambuffer /// A streambuffer abstraction over a fixed array of bytes. This streambuffer
/// cannot overflow/underflow. Once it has reached its end, attempts to read /// cannot overflow/underflow. Once it has reached its end, attempts to read
/// characters will return `trait_type::eof`. /// characters will return `trait_type::eof`.
template <class CharT = char, class Traits = std::char_traits<CharT>> template <class CharT = char, class Traits = std::char_traits<CharT>>
class arraybuf : public std::basic_streambuf<CharT, Traits> { class arraybuf : public stream_buffer<CharT, Traits> {
public: public:
using base = std::basic_streambuf<CharT, Traits>; using base = std::basic_streambuf<CharT, Traits>;
using char_type = typename base::char_type; using char_type = typename base::char_type;
...@@ -108,9 +152,7 @@ protected: ...@@ -108,9 +152,7 @@ protected:
this->setg(this->eback(), this->eback() + pos, this->egptr()); this->setg(this->eback(), this->eback() + pos, this->egptr());
if (put) { if (put) {
this->setp(this->pbase(), this->epptr()); this->setp(this->pbase(), this->epptr());
CAF_ASSERT(pos >= std::numeric_limits<int>::min()); this->safe_pbump(pos);
CAF_ASSERT(pos <= std::numeric_limits<int>::max());
this->pbump(static_cast<int>(pos));
} }
return pos; return pos;
} }
...@@ -152,9 +194,7 @@ protected: ...@@ -152,9 +194,7 @@ protected:
} }
new_off += off; new_off += off;
this->setp(this->pbase(), this->epptr()); this->setp(this->pbase(), this->epptr());
CAF_ASSERT(new_off >= std::numeric_limits<int>::min()); this->safe_pbump(new_off);
CAF_ASSERT(new_off <= std::numeric_limits<int>::max());
this->pbump(static_cast<int>(new_off));
} }
return new_off; return new_off;
} }
...@@ -166,7 +206,7 @@ protected: ...@@ -166,7 +206,7 @@ protected:
auto actual = std::min(n, static_cast<std::streamsize>(available)); auto actual = std::min(n, static_cast<std::streamsize>(available));
std::memcpy(this->pptr(), s, std::memcpy(this->pptr(), s,
static_cast<size_t>(actual) * sizeof(char_type)); static_cast<size_t>(actual) * sizeof(char_type));
this->pbump(static_cast<int>(actual)); this->safe_pbump(actual);
return actual; return actual;
} }
...@@ -177,7 +217,7 @@ protected: ...@@ -177,7 +217,7 @@ protected:
auto actual = std::min(n, static_cast<std::streamsize>(available)); auto actual = std::min(n, static_cast<std::streamsize>(available));
std::memcpy(s, this->gptr(), std::memcpy(s, this->gptr(),
static_cast<size_t>(actual) * sizeof(char_type)); static_cast<size_t>(actual) * sizeof(char_type));
this->gbump(static_cast<int>(actual)); this->safe_gbump(actual);
return actual; return actual;
} }
}; };
...@@ -186,7 +226,7 @@ protected: ...@@ -186,7 +226,7 @@ protected:
/// reading in the same style as `arraybuf`, but is unbounded for output. /// reading in the same style as `arraybuf`, but is unbounded for output.
template <class Container> template <class Container>
class containerbuf class containerbuf
: public std::basic_streambuf< : public stream_buffer<
typename Container::value_type, typename Container::value_type,
std::char_traits<typename Container::value_type> std::char_traits<typename Container::value_type>
> { > {
...@@ -251,7 +291,7 @@ protected: ...@@ -251,7 +291,7 @@ protected:
auto actual = std::min(n, static_cast<std::streamsize>(available)); auto actual = std::min(n, static_cast<std::streamsize>(available));
std::memcpy(s, this->gptr(), std::memcpy(s, this->gptr(),
static_cast<size_t>(actual) * sizeof(char_type)); static_cast<size_t>(actual) * sizeof(char_type));
this->gbump(static_cast<int>(actual)); this->safe_gbump(actual);
return actual; return actual;
} }
......
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