forked from Colvars/colvars
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcolvars_memstream.h
289 lines (216 loc) · 9.43 KB
/
colvars_memstream.h
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