// -*- 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 #include #include #include #include // 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::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(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 &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 void write_object(T const &t); /// Wrapper to write_object() template friend memory_stream &operator<<(memory_stream &os, T const &t); /// Write a vector of simple objects to the output buffer template void write_vector(std::vector const &t); /// Wrapper to write_vector() template friend memory_stream &operator<<(memory_stream &os, std::vector const &t); /// Read a simple object from the buffer template void read_object(T &t); /// Wrapper to read_object() template friend memory_stream &operator>>(memory_stream &is, T &t); /// Read a vector of simple objects from the buffer template void read_vector(std::vector &t); /// Wrapper to read_vector() template friend memory_stream &operator>>(memory_stream &is, std::vector &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 *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 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 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 cvm::memory_stream &operator<<(cvm::memory_stream &os, T const &t) { os.write_object(t); return os; } template void cvm::memory_stream::write_vector(std::vector 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 cvm::memory_stream &operator<<(cvm::memory_stream &os, std::vector const &t) { os.write_vector(t); return os; } template 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 cvm::memory_stream &operator>>(cvm::memory_stream &is, T &t) { is.read_object(t); return is; } template void cvm::memory_stream::read_vector(std::vector &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 cvm::memory_stream &operator>>(cvm::memory_stream &is, std::vector &t) { is.read_vector(t); return is; } template 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 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 const &t); template <> cvm::memory_stream &operator<<(cvm::memory_stream &os, cvm::vector1d 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 &t); template <> cvm::memory_stream &operator>>(cvm::memory_stream &is, cvm::vector1d &t); #endif