1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
|
// -*- c++ -*-
// This file is part of the Collective Variables module (Colvars).
// The original version of Colvars and its updates are located at:
// https://github.com/Colvars/colvars
// Please update all Colvars source files before making any changes.
// If you wish to distribute your changes, please submit them to the
// Colvars repository at GitHub.
#ifndef MEMORY_STREAM_H
#define MEMORY_STREAM_H
#include <cstring>
#include <string>
#include <typeinfo>
#include <vector>
#include <iomanip>
// Work around missing std::is_trivially_copyable in old GCC and Clang versions
// TODO remove this after CentOS 7 has been beyond EOL for a while
#if (defined(__GNUC__) && (__GNUC__ < 5) && !defined(__clang__)) || (defined(__clang__) && (__clang_major__ < 7))
// Clang needs an exception, because it defines __GNUC__ as well
#define IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T)
#else
#define IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable<T>::value
#endif
class cvm::memory_stream {
public:
/// Set up an empty stream with an internal buffer, suitable for writing to
/// \param max_length Maximum allowed capacity (default is 64 GiB)
memory_stream(size_t max_length = (static_cast<size_t>(1L) << 36)) : max_length_(max_length) {}
/// Set up a stream based on an external input buffer
memory_stream(size_t n, unsigned char const *buf)
: external_input_buffer_(buf), internal_buffer_(), data_length_(n), max_length_(data_length_)
{
}
/// Set up a stream based on an external output buffer
memory_stream(std::vector<unsigned char> &buf) : memory_stream()
{
external_output_buffer_ = &buf;
}
/// Length of the buffer
inline size_t length() const { return data_length_; }
/// Output buffer
inline unsigned char *output_buffer()
{
return (external_output_buffer_ ? external_output_buffer_->data() : internal_buffer_.data());
}
/// Next location to write to
inline unsigned char *output_location() { return output_buffer() + data_length_; }
/// Input buffer
inline unsigned char const *input_buffer() const
{
return (external_input_buffer_ ? external_input_buffer_ : internal_buffer_.data());
}
/// Next location to read from
inline unsigned char const *input_location() const { return input_buffer() + read_pos_; }
/// Cast operator to be used to test for errors
inline explicit operator bool() const { return state_ == std::ios::goodbit; }
/// Write a simple object to the output buffer
template <typename T> void write_object(T const &t);
/// Wrapper to write_object()
template <typename T> friend memory_stream &operator<<(memory_stream &os, T const &t);
/// Write a vector of simple objects to the output buffer
template <typename T> void write_vector(std::vector<T> const &t);
/// Wrapper to write_vector()
template <typename T>
friend memory_stream &operator<<(memory_stream &os, std::vector<T> const &t);
/// Read a simple object from the buffer
template <typename T> void read_object(T &t);
/// Wrapper to read_object()
template <typename T> friend memory_stream &operator>>(memory_stream &is, T &t);
/// Read a vector of simple objects from the buffer
template <typename T> void read_vector(std::vector<T> &t);
/// Wrapper to read_vector()
template <typename T> friend memory_stream &operator>>(memory_stream &is, std::vector<T> &t);
// Compatibility with STL stream functions
/// Report the current position in the buffer
inline size_t tellg() const { return read_pos_; }
/// Report the current position in the buffer
inline memory_stream & seekg(size_t pos) { read_pos_ = pos; return *this; }
/// Ignore formatting operators
inline void setf(decltype(std::ios::fmtflags(0)), decltype(std::ios::floatfield)) {}
/// Ignore formatting operators
inline void flags(decltype(std::ios::fmtflags(0))) {}
/// Get the current formatting flags (i.e. none because this stream is unformatted)
inline decltype(std::ios::fmtflags(0)) flags() const { return std::ios::fmtflags(0); }
/// Get the error code
inline std::ios::iostate rdstate() const { return state_; }
/// Set the error code
inline void setstate(std::ios::iostate new_state) { state_ |= new_state; }
/// Clear the error code
inline void clear() { state_ = std::ios::goodbit; }
protected:
/// External output buffer
std::vector<unsigned char> *external_output_buffer_ = nullptr;
/// External input buffer
unsigned char const *external_input_buffer_ = nullptr;
/// Internal buffer (may server for both input and output)
std::vector<unsigned char> internal_buffer_;
/// Length of the data buffer (either internal or external)
size_t data_length_ = 0L;
/// Largest allowed capacity of the data buffer
size_t const max_length_;
/// Error status
std::ios::iostate state_ = std::ios::goodbit;
/// Add the requester number of bytes to the array capacity; return false if buffer is external
bool expand_output_buffer(size_t add_bytes);
/// Move the buffer position past the data just written
inline void incr_write_pos(size_t c) { data_length_ += c; }
/// Current position when reading from the buffer
size_t read_pos_ = 0L;
/// Begin an attempt to read an object; assume EOF unless there is space remaining
inline void begin_reading() { setstate(std::ios::eofbit); }
/// Mark the reading attempt succesful
inline void done_reading() { clear(); }
/// Move the buffer position past the data just read
inline void incr_read_pos(size_t c) { read_pos_ += c; }
/// Check that the buffer contains enough bytes to read as the argument says, set error
/// otherwise
inline bool has_remaining(size_t c) { return c <= (data_length_ - read_pos_); }
};
template <typename T> void cvm::memory_stream::write_object(T const &t)
{
static_assert(IS_TRIVIALLY_COPYABLE(T), "Cannot use write_object() on complex type");
size_t const new_data_size = sizeof(T);
if (expand_output_buffer(new_data_size)) {
std::memcpy(output_location(), &t, sizeof(T));
incr_write_pos(new_data_size);
}
}
template <typename T> cvm::memory_stream &operator<<(cvm::memory_stream &os, T const &t)
{
os.write_object<T>(t);
return os;
}
template <typename T> void cvm::memory_stream::write_vector(std::vector<T> const &t)
{
static_assert(IS_TRIVIALLY_COPYABLE(T), "Cannot use write_vector() on complex type");
size_t const vector_length = t.size();
size_t const new_data_size = sizeof(size_t) + sizeof(T) * vector_length;
if (expand_output_buffer(new_data_size)) {
std::memcpy(output_location(), &vector_length, sizeof(size_t));
incr_write_pos(sizeof(T));
std::memcpy(output_location(), t.data(), t.size() * sizeof(T));
incr_write_pos(t.size() * sizeof(T));
}
}
template <typename T>
cvm::memory_stream &operator<<(cvm::memory_stream &os, std::vector<T> const &t)
{
os.write_vector<T>(t);
return os;
}
template <typename T> void cvm::memory_stream::read_object(T &t)
{
static_assert(IS_TRIVIALLY_COPYABLE(T), "Cannot use read_object() on complex type");
begin_reading();
if (has_remaining(sizeof(T))) {
std::memcpy(&t, input_location(), sizeof(T));
incr_read_pos(sizeof(T));
done_reading();
}
}
template <typename T> cvm::memory_stream &operator>>(cvm::memory_stream &is, T &t)
{
is.read_object<T>(t);
return is;
}
template <typename T> void cvm::memory_stream::read_vector(std::vector<T> &t)
{
static_assert(IS_TRIVIALLY_COPYABLE(T), "Cannot use read_vector() on complex type");
begin_reading();
size_t vector_length = 0;
if (has_remaining(sizeof(size_t))) {
std::memcpy(&vector_length, input_location(), sizeof(size_t));
incr_read_pos(sizeof(size_t));
if (has_remaining(vector_length * sizeof(T))) {
t.resize(vector_length);
std::memcpy(t.data(), input_location(), vector_length * sizeof(T));
incr_read_pos(vector_length * sizeof(T));
done_reading();
} else {
setstate(std::ios::failbit);
}
}
}
template <typename T> cvm::memory_stream &operator>>(cvm::memory_stream &is, std::vector<T> &t)
{
is.read_vector<T>(t);
return is;
}
template <typename T> cvm::memory_stream &operator<<(cvm::memory_stream &os,
decltype(std::setprecision(10)) const &)
{
return os;
}
#if !defined(_MSC_VER) && !defined(__SUNPRO_CC)
// Visual Studio and MSVC use the same return type for both modifiers
template <typename T> cvm::memory_stream &operator<<(cvm::memory_stream &os,
decltype(std::setw(10)) const &)
{
return os;
}
#endif
// Declare specializations
template <> void cvm::memory_stream::write_object(std::string const &t);
template <> cvm::memory_stream &operator<<(cvm::memory_stream &os, std::string const &t);
template <> void cvm::memory_stream::write_object(colvarvalue const &t);
template <> cvm::memory_stream &operator<<(cvm::memory_stream &os, colvarvalue const &x);
template <> void cvm::memory_stream::write_object(cvm::vector1d<cvm::real> const &t);
template <>
cvm::memory_stream &operator<<(cvm::memory_stream &os, cvm::vector1d<cvm::real> const &t);
template <> void cvm::memory_stream::read_object(std::string &t);
template <> cvm::memory_stream &operator>>(cvm::memory_stream &is, std::string &t);
template <> void cvm::memory_stream::read_object(colvarvalue &t);
template <> cvm::memory_stream &operator>>(cvm::memory_stream &is, colvarvalue &t);
template <> void cvm::memory_stream::read_object(cvm::vector1d<cvm::real> &t);
template <> cvm::memory_stream &operator>>(cvm::memory_stream &is, cvm::vector1d<cvm::real> &t);
#endif
|