ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
memcpy_archive.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2019-2022 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <boost/mpl/bool.hpp>
23#include <boost/serialization/is_bitwise_serializable.hpp>
24#include <boost/serialization/nvp.hpp>
25#include <boost/serialization/serialization.hpp>
26
27#include <cassert>
28#include <cstddef>
29#include <cstring>
30#include <memory>
31#include <span>
32#include <type_traits>
33
34namespace Utils {
35/** @brief Type trait to indicate that a type is
36 * serializable with a static size, e.g. is
37 * suitable for memcpy serialization. Only
38 * specialize this to @c std::true_type if it is
39 * guaranteed that serializing this type always
40 * returns the same number of bytes, independent
41 * of object state.
42 *
43 * @tparam T type under consideration.
44 */
45template <class T>
47 : std::bool_constant<
48 std::is_trivially_copyable_v<T> or
49 boost::serialization::is_bitwise_serializable<T>::value> {};
50
51namespace detail {
52/* Use memcpy for packing */
53template <class T>
54using use_memcpy =
55 std::bool_constant<std::is_trivially_copyable_v<T> or
56 boost::serialization::is_bitwise_serializable<T>::value>;
57/* Use serialize function only if the type is opt-in but not
58 * trivially copyable, in which case memcpy is more efficient. */
59template <class T>
60using use_serialize = std::bool_constant<not use_memcpy<T>::value and
62
63template <class Derived> class BasicMemcpyArchive {
64 /** Buffer to write to */
65 std::span<char> buf;
66 /** Current position in the buffer */
67 char *insert;
68
69protected:
70 // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility)
71 explicit BasicMemcpyArchive(std::span<char> buf)
72 : buf(buf), insert(buf.data()) {}
73
74public:
75 auto get_library_version() const { return std::size_t{4}; }
76
77 auto bytes_processed() const {
78 return static_cast<std::size_t>(insert - buf.data());
79 }
80
81 void skip(std::size_t bytes) {
82 assert((insert + bytes) <= &*buf.end());
83 insert += bytes;
84 }
85
86private:
87 void read(void *data, std::size_t bytes) {
88 /* check that there is enough space left in the buffer */
89 assert((insert + bytes) <= &*buf.end());
90 std::memcpy(data, insert, bytes);
91 insert += bytes;
92 }
93
94 void write(const void *data, std::size_t bytes) {
95 /* check that there is enough space left in the buffer */
96 assert((insert + bytes) <= &*buf.end());
97 std::memcpy(insert, data, bytes);
98 insert += bytes;
99 }
100
101public:
102 template <typename T>
103 auto operator>>(T &value) -> std::enable_if_t<use_memcpy<T>::value> {
104 read(&value, sizeof(T));
105 }
106
107 template <typename T>
108 auto operator<<(T const &value) -> std::enable_if_t<use_memcpy<T>::value> {
109 write(&value, sizeof(T));
110 }
111
112private:
113 template <class T> void process(T &value) {
114 auto const old_pos = insert;
115 boost::serialization::serialize_adl(*static_cast<Derived *>(this), value,
116 4);
117 auto const new_pos = insert;
118 assert(static_cast<std::size_t>(new_pos - old_pos) <= sizeof(T));
119
120 auto const padding_size = sizeof(T) - (new_pos - old_pos);
121 skip(padding_size);
122 }
123
124public:
125 template <class T>
126 auto
127 operator>>(T &value) -> std::enable_if_t<detail::use_serialize<T>::value> {
128 process(value);
129 }
130
131 template <class T>
132 auto
133 operator<<(T &value) -> std::enable_if_t<detail::use_serialize<T>::value> {
134 process(value);
135 }
136
137 template <class T> void operator<<(const boost::serialization::nvp<T> &nvp) {
138 operator<<(nvp.const_value());
139 }
140
141 template <class T> void operator>>(const boost::serialization::nvp<T> &nvp) {
142 operator>>(nvp.value());
143 }
144
145 /**
146 * @brief Determine the static packing size of a type.
147 * @tparam T Type to consider.
148 * @return Packed size in bytes.
149 */
150 template <class T> static constexpr std::size_t packing_size() {
151 return sizeof(T);
152 }
153};
154} // namespace detail
155
156/**
157 * @brief Archive that deserializes from a buffer via memcpy.
158 *
159 * Can only process types that have a static serialization size,
160 * e.g. that serialize to the same number of bytes independent of
161 * the state of the object. This can either be automatically detected
162 * for types that are trivially copyable, or by explicitly assured
163 * by specializing @ref is_statically_serializable to @c std::true_type.
164 */
165class MemcpyIArchive : public detail::BasicMemcpyArchive<MemcpyIArchive> {
166private:
167 using base_type = detail::BasicMemcpyArchive<MemcpyIArchive>;
168
169public:
170 using is_loading = boost::mpl::true_;
171 using is_saving = boost::mpl::false_;
172
173 /**
174 * @param buf Buffer to read from.
175 */
176 explicit MemcpyIArchive(std::span<char> buf) : base_type(buf) {}
177
178 /**
179 * @brief Number of bytes read from the buffer.
180 * @return Number of bytes read.
181 */
182 std::size_t bytes_read() const { return bytes_processed(); }
183
184 /** @copydoc base_type::packing_size */
185 using base_type::packing_size;
186 using base_type::operator>>;
187
188 template <class T> MemcpyIArchive &operator&(T &value) {
189 operator>>(value);
190
191 return *this;
192 }
193};
194
195/**
196 * @brief Archive that serializes to a buffer via memcpy.
197 *
198 * @copydetails MemcpyIArchive
199 */
200class MemcpyOArchive : public detail::BasicMemcpyArchive<MemcpyOArchive> {
201 using base_type = detail::BasicMemcpyArchive<MemcpyOArchive>;
202
203public:
204 using is_loading = boost::mpl::false_;
205 using is_saving = boost::mpl::true_;
206
207 /**
208 * @param buf Buffer to write to.
209 */
210 explicit MemcpyOArchive(std::span<char> buf) : base_type(buf) {}
211
212 /**
213 * @brief Number of bytes written to the buffer.
214 * @return Number of bytes written.
215 */
216 std::size_t bytes_written() const { return bytes_processed(); }
217
218 /** @copydoc base_type::packing_size */
219 using base_type::packing_size;
220 using base_type::operator<<;
221
222 template <class T> MemcpyOArchive &operator&(T &value) {
223 operator<<(value);
224
225 return *this;
226 }
227};
228} // namespace Utils
Archive that deserializes from a buffer via memcpy.
boost::mpl::true_ is_loading
MemcpyIArchive & operator&(T &value)
std::size_t bytes_read() const
Number of bytes read from the buffer.
MemcpyIArchive(std::span< char > buf)
boost::mpl::false_ is_saving
Archive that serializes to a buffer via memcpy.
boost::mpl::false_ is_loading
MemcpyOArchive(std::span< char > buf)
std::size_t bytes_written() const
Number of bytes written to the buffer.
MemcpyOArchive & operator&(T &value)
boost::mpl::true_ is_saving
static std::basic_ostream< char > & operator<<(std::basic_ostream< char > &os, const dim3 &dim)
Type trait to indicate that a type is serializable with a static size, e.g.