Files
lammps/lib/colvars/colvars_memstream.h
2024-08-06 01:07:43 +02:00

290 lines
9.4 KiB
C++

// -*- 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