1 #ifndef DIY_SERIALIZATION_HPP
2 #define DIY_SERIALIZATION_HPP
3
4 #include <vector>
5 #include <valarray>
6 #include <map>
7 #include <set>
8 #include <string>
9 #include <fstream>
10
11 #include <tuple>
12 #include <unordered_map>
13 #include <unordered_set>
14 #include <type_traits> // this is used for a safety check for default serialization
15
16 namespace diy
17 {
18 //! A serialization buffer. \ingroup Serialization
19 struct BinaryBuffer
20 {
21 virtual ~BinaryBuffer() =default;
22 virtual void save_binary(const char* x, size_t count) =0; //!< copy `count` bytes from `x` into the buffer
23 virtual inline void append_binary(const char* x, size_t count) =0; //!< append `count` bytes from `x` to end of buffer
24 virtual void load_binary(char* x, size_t count) =0; //!< copy `count` bytes into `x` from the buffer
25 virtual void load_binary_back(char* x, size_t count) =0; //!< copy `count` bytes into `x` from the back of the buffer
26 };
27
28 struct MemoryBuffer: public BinaryBuffer
29 {
MemoryBufferdiy::MemoryBuffer30 MemoryBuffer(size_t position_ = 0):
31 position(position_) {}
32
33 virtual inline void save_binary(const char* x, size_t count) override; //!< copy `count` bytes from `x` into the buffer
34 virtual inline void append_binary(const char* x, size_t count) override; //!< append `count` bytes from `x` to end of buffer
35 virtual inline void load_binary(char* x, size_t count) override; //!< copy `count` bytes into `x` from the buffer
36 virtual inline void load_binary_back(char* x, size_t count) override; //!< copy `count` bytes into `x` from the back of the buffer
37
cleardiy::MemoryBuffer38 void clear() { buffer.clear(); reset(); }
wipediy::MemoryBuffer39 void wipe() { std::vector<char>().swap(buffer); reset(); }
resetdiy::MemoryBuffer40 void reset() { position = 0; }
skipdiy::MemoryBuffer41 void skip(size_t s) { position += s; }
swapdiy::MemoryBuffer42 void swap(MemoryBuffer& o) { std::swap(position, o.position); buffer.swap(o.buffer); }
emptydiy::MemoryBuffer43 bool empty() const { return buffer.empty(); }
sizediy::MemoryBuffer44 size_t size() const { return buffer.size(); }
reservediy::MemoryBuffer45 void reserve(size_t s) { buffer.reserve(s); }
operator booldiy::MemoryBuffer46 operator bool() const { return position < buffer.size(); }
47
48 //! copy a memory buffer from one buffer to another, bypassing making a temporary copy first
49 inline static void copy(MemoryBuffer& from, MemoryBuffer& to);
50
51 //! multiplier used for the geometric growth of the container
growth_multiplierdiy::MemoryBuffer52 static float growth_multiplier() { return 1.5; }
53
54 // simple file IO
writediy::MemoryBuffer55 void write(const std::string& fn) const { std::ofstream out(fn.c_str()); out.write(&buffer[0], size()); }
readdiy::MemoryBuffer56 void read(const std::string& fn)
57 {
58 std::ifstream in(fn.c_str(), std::ios::binary | std::ios::ate);
59 buffer.resize(static_cast<size_t>(in.tellg()));
60 in.seekg(0);
61 in.read(&buffer[0], static_cast<std::streamsize>(size()));
62 position = 0;
63 }
64
65 size_t position;
66 std::vector<char> buffer;
67 };
68
69 namespace detail
70 {
71 struct Default {};
72 }
73
74 //!\addtogroup Serialization
75 //!@{
76
77 /**
78 * \brief Main interface to serialization, meant to be specialized for the
79 * types that require special handling. `diy::save()` and `diy::load()` call
80 * the static member functions of this class.
81 *
82 * The default (unspecialized) version copies
83 * `sizeof(T)` bytes from `&x` to or from `bb` via
84 * its `diy::BinaryBuffer::save_binary()` and `diy::BinaryBuffer::load_binary()`
85 * functions. This works out perfectly for plain old data (e.g., simple structs).
86 * To save a more complicated type, one has to specialize
87 * `diy::Serialization<T>` for that type. Specializations are already provided for
88 * `std::vector<T>`, `std::map<K,V>`, and `std::pair<T,U>`.
89 * As a result one can quickly add a specialization of one's own
90 *
91 */
92 template<class T>
93 struct Serialization: public detail::Default
94 {
95 #if (defined(__clang__) && !defined(__ppc64__)) || (defined(__GNUC__) && __GNUC__ >= 5)
96 //exempt power-pc clang variants due to: https://gitlab.kitware.com/vtk/vtk-m/issues/201
97 static_assert(std::is_trivially_copyable<T>::value, "Default serialization works only for trivially copyable types");
98 #endif
99
savediy::Serialization100 static void save(BinaryBuffer& bb, const T& x) { bb.save_binary((const char*) &x, sizeof(T)); }
loaddiy::Serialization101 static void load(BinaryBuffer& bb, T& x) { bb.load_binary((char*) &x, sizeof(T)); }
sizediy::Serialization102 static size_t size(const T& x) { return sizeof(T); }
103 };
104
105 //! Saves `x` to `bb` by calling `diy::Serialization<T>::save(bb,x)`.
106 template<class T>
save(BinaryBuffer & bb,const T & x)107 void save(BinaryBuffer& bb, const T& x) { Serialization<T>::save(bb, x); }
108
109 //! Loads `x` from `bb` by calling `diy::Serialization<T>::load(bb,x)`.
110 template<class T>
load(BinaryBuffer & bb,T & x)111 void load(BinaryBuffer& bb, T& x) { Serialization<T>::load(bb, x); }
112
113 //! Optimization for arrays. If `diy::Serialization` is not specialized for `T`,
114 //! the array will be copied all at once. Otherwise, it's copied element by element.
115 template<class T>
116 void save(BinaryBuffer& bb, const T* x, size_t n);
117
118 //! Optimization for arrays. If `diy::Serialization` is not specialized for `T`,
119 //! the array will be filled all at once. Otherwise, it's filled element by element.
120 template<class T>
121 void load(BinaryBuffer& bb, T* x, size_t n);
122
123 //! Supports only binary data copying (meant for simple footers).
124 template<class T>
load_back(BinaryBuffer & bb,T & x)125 void load_back(BinaryBuffer& bb, T& x) { bb.load_binary_back((char*) &x, sizeof(T)); }
126
127 //@}
128
129
130 namespace detail
131 {
132 template<typename T>
133 struct is_default
134 {
135 typedef char yes;
136 typedef int no;
137
138 static yes test(Default*);
139 static no test(...);
140
141 enum { value = (sizeof(test((T*) 0)) == sizeof(yes)) };
142 };
143 }
144
145 template<class T>
save(BinaryBuffer & bb,const T * x,size_t n)146 void save(BinaryBuffer& bb, const T* x, size_t n)
147 {
148 if (!detail::is_default< Serialization<T> >::value)
149 for (size_t i = 0; i < n; ++i)
150 diy::save(bb, x[i]);
151 else // if Serialization is not specialized for U, just save the binary data
152 bb.save_binary((const char*) &x[0], sizeof(T)*n);
153 }
154
155 template<class T>
load(BinaryBuffer & bb,T * x,size_t n)156 void load(BinaryBuffer& bb, T* x, size_t n)
157 {
158 if (!detail::is_default< Serialization<T> >::value)
159 for (size_t i = 0; i < n; ++i)
160 diy::load(bb, x[i]);
161 else // if Serialization is not specialized for U, just load the binary data
162 bb.load_binary((char*) &x[0], sizeof(T)*n);
163 }
164
165
166 // save/load for MemoryBuffer
167 template<>
168 struct Serialization< MemoryBuffer >
169 {
savediy::Serialization170 static void save(BinaryBuffer& bb, const MemoryBuffer& x)
171 {
172 diy::save(bb, x.position);
173 if (x.position > 0)
174 diy::save(bb, &x.buffer[0], x.position);
175 }
176
loaddiy::Serialization177 static void load(BinaryBuffer& bb, MemoryBuffer& x)
178 {
179 diy::load(bb, x.position);
180 x.buffer.resize(x.position);
181 if (x.position > 0)
182 diy::load(bb, &x.buffer[0], x.position);
183 }
184
sizediy::Serialization185 static size_t size(const MemoryBuffer& x)
186 {
187 return sizeof(x.position) + x.position;
188 }
189 };
190
191 // save/load for std::vector<U>
192 template<class U>
193 struct Serialization< std::vector<U> >
194 {
195 typedef std::vector<U> Vector;
196
savediy::Serialization197 static void save(BinaryBuffer& bb, const Vector& v)
198 {
199 size_t s = v.size();
200 diy::save(bb, s);
201 if (s > 0)
202 diy::save(bb, &v[0], v.size());
203 }
204
loaddiy::Serialization205 static void load(BinaryBuffer& bb, Vector& v)
206 {
207 size_t s;
208 diy::load(bb, s);
209 v.resize(s);
210 if (s > 0)
211 diy::load(bb, &v[0], s);
212 }
213 };
214
215 template<class U>
216 struct Serialization< std::valarray<U> >
217 {
218 typedef std::valarray<U> ValArray;
219
savediy::Serialization220 static void save(BinaryBuffer& bb, const ValArray& v)
221 {
222 size_t s = v.size();
223 diy::save(bb, s);
224 if (s > 0)
225 diy::save(bb, &v[0], v.size());
226 }
227
loaddiy::Serialization228 static void load(BinaryBuffer& bb, ValArray& v)
229 {
230 size_t s;
231 diy::load(bb, s);
232 v.resize(s);
233 if (s > 0)
234 diy::load(bb, &v[0], s);
235 }
236 };
237
238 // save/load for std::string
239 template<>
240 struct Serialization< std::string >
241 {
242 typedef std::string String;
243
savediy::Serialization244 static void save(BinaryBuffer& bb, const String& s)
245 {
246 size_t sz = s.size();
247 diy::save(bb, sz);
248 diy::save(bb, s.c_str(), sz);
249 }
250
loaddiy::Serialization251 static void load(BinaryBuffer& bb, String& s)
252 {
253 size_t sz;
254 diy::load(bb, sz);
255 s.resize(sz);
256 for (size_t i = 0; i < sz; ++i)
257 {
258 char c;
259 diy::load(bb, c);
260 s[i] = c;
261 }
262 }
263 };
264
265 // save/load for std::pair<X,Y>
266 template<class X, class Y>
267 struct Serialization< std::pair<X,Y> >
268 {
269 typedef std::pair<X,Y> Pair;
270
savediy::Serialization271 static void save(BinaryBuffer& bb, const Pair& p)
272 {
273 diy::save(bb, p.first);
274 diy::save(bb, p.second);
275 }
276
loaddiy::Serialization277 static void load(BinaryBuffer& bb, Pair& p)
278 {
279 diy::load(bb, p.first);
280 diy::load(bb, p.second);
281 }
282 };
283
284 // save/load for std::map<K,V>
285 template<class K, class V>
286 struct Serialization< std::map<K,V> >
287 {
288 typedef std::map<K,V> Map;
289
savediy::Serialization290 static void save(BinaryBuffer& bb, const Map& m)
291 {
292 size_t s = m.size();
293 diy::save(bb, s);
294 for (typename std::map<K,V>::const_iterator it = m.begin(); it != m.end(); ++it)
295 diy::save(bb, *it);
296 }
297
loaddiy::Serialization298 static void load(BinaryBuffer& bb, Map& m)
299 {
300 size_t s;
301 diy::load(bb, s);
302 for (size_t i = 0; i < s; ++i)
303 {
304 K k;
305 diy::load(bb, k);
306 diy::load(bb, m[k]);
307 }
308 }
309 };
310
311 // save/load for std::set<T>
312 template<class T>
313 struct Serialization< std::set<T> >
314 {
315 typedef std::set<T> Set;
316
savediy::Serialization317 static void save(BinaryBuffer& bb, const Set& m)
318 {
319 size_t s = m.size();
320 diy::save(bb, s);
321 for (typename std::set<T>::const_iterator it = m.begin(); it != m.end(); ++it)
322 diy::save(bb, *it);
323 }
324
loaddiy::Serialization325 static void load(BinaryBuffer& bb, Set& m)
326 {
327 size_t s;
328 diy::load(bb, s);
329 for (size_t i = 0; i < s; ++i)
330 {
331 T p;
332 diy::load(bb, p);
333 m.insert(p);
334 }
335 }
336 };
337
338 // save/load for std::unordered_map<K,V,H,E,A>
339 template<class K, class V, class H, class E, class A>
340 struct Serialization< std::unordered_map<K,V,H,E,A> >
341 {
342 typedef std::unordered_map<K,V,H,E,A> Map;
343
savediy::Serialization344 static void save(BinaryBuffer& bb, const Map& m)
345 {
346 size_t s = m.size();
347 diy::save(bb, s);
348 for (auto& x : m)
349 diy::save(bb, x);
350 }
351
loaddiy::Serialization352 static void load(BinaryBuffer& bb, Map& m)
353 {
354 size_t s;
355 diy::load(bb, s);
356 for (size_t i = 0; i < s; ++i)
357 {
358 std::pair<K,V> p;
359 diy::load(bb, p);
360 m.emplace(std::move(p));
361 }
362 }
363 };
364
365 // save/load for std::unordered_set<T,H,E,A>
366 template<class T, class H, class E, class A>
367 struct Serialization< std::unordered_set<T,H,E,A> >
368 {
369 typedef std::unordered_set<T,H,E,A> Set;
370
savediy::Serialization371 static void save(BinaryBuffer& bb, const Set& m)
372 {
373 size_t s = m.size();
374 diy::save(bb, s);
375 for (auto& x : m)
376 diy::save(bb, x);
377 }
378
loaddiy::Serialization379 static void load(BinaryBuffer& bb, Set& m)
380 {
381 size_t s;
382 diy::load(bb, s);
383 for (size_t i = 0; i < s; ++i)
384 {
385 T p;
386 diy::load(bb, p);
387 m.emplace(std::move(p));
388 }
389 }
390 };
391
392 // save/load for std::tuple<...>
393 // TODO: this ought to be default (copying) serialization
394 // if all arguments are default
395 template<class... Args>
396 struct Serialization< std::tuple<Args...> >
397 {
398 typedef std::tuple<Args...> Tuple;
399
savediy::Serialization400 static void save(BinaryBuffer& bb, const Tuple& t) { save<0>(bb, t); }
401
402 template<std::size_t I = 0>
403 static
404 typename std::enable_if<I == sizeof...(Args), void>::type
savediy::Serialization405 save(BinaryBuffer&, const Tuple&) {}
406
407 template<std::size_t I = 0>
408 static
409 typename std::enable_if<I < sizeof...(Args), void>::type
410 save(BinaryBuffer& bb, const Tuple& t) { diy::save(bb, std::get<I>(t)); save<I+1>(bb, t); }
411
loaddiy::Serialization412 static void load(BinaryBuffer& bb, Tuple& t) { load<0>(bb, t); }
413
414 template<std::size_t I = 0>
415 static
416 typename std::enable_if<I == sizeof...(Args), void>::type
loaddiy::Serialization417 load(BinaryBuffer&, Tuple&) {}
418
419 template<std::size_t I = 0>
420 static
421 typename std::enable_if<I < sizeof...(Args), void>::type
422 load(BinaryBuffer& bb, Tuple& t) { diy::load(bb, std::get<I>(t)); load<I+1>(bb, t); }
423
424 };
425 }
426
427 void
428 diy::MemoryBuffer::
save_binary(const char * x,size_t count)429 save_binary(const char* x, size_t count)
430 {
431 if (position + count > buffer.capacity())
432 {
433 double newsize = static_cast<double>(position + count) * growth_multiplier(); // if we have to grow, grow geometrically
434 buffer.reserve(static_cast<size_t>(newsize));
435 }
436
437 if (position + count > buffer.size())
438 buffer.resize(position + count);
439
440 std::copy_n(x, count, &buffer[position]);
441 position += count;
442 }
443
444 void
445 diy::MemoryBuffer::
append_binary(const char * x,size_t count)446 append_binary(const char* x, size_t count)
447 {
448 if (buffer.size() + count > buffer.capacity()) // growth/copying will be triggered
449 {
450 size_t cur_size = buffer.size() - position;
451 size_t new_size = cur_size + count;
452 if (new_size * growth_multiplier() <= buffer.capacity()) // we have enough space in this buffer, copy in place
453 {
454 // copy the data to the beginning of the buffer and reduce its size
455 for (size_t i = 0; i < cur_size; ++i)
456 buffer[i] = buffer[position++];
457
458 buffer.resize(cur_size);
459 position = 0;
460 } else
461 {
462 std::vector<char> tmp;
463 tmp.reserve(new_size * static_cast<size_t>(growth_multiplier()));
464 tmp.resize(cur_size);
465
466 for (size_t i = 0; i < tmp.size(); ++i)
467 tmp[i] = buffer[position++];
468
469 buffer.swap(tmp);
470 position = 0;
471 }
472 }
473
474 size_t temp_pos = position;
475 position = size();
476 save_binary(x, count);
477 position = temp_pos;
478 }
479
480 void
481 diy::MemoryBuffer::
load_binary(char * x,size_t count)482 load_binary(char* x, size_t count)
483 {
484 std::copy_n(&buffer[position], count, x);
485 position += count;
486 }
487
488 void
489 diy::MemoryBuffer::
load_binary_back(char * x,size_t count)490 load_binary_back(char* x, size_t count)
491 {
492 std::copy_n(&buffer[buffer.size() - count], count, x);
493 buffer.resize(buffer.size() - count);
494 }
495
496 void
497 diy::MemoryBuffer::
copy(MemoryBuffer & from,MemoryBuffer & to)498 copy(MemoryBuffer& from, MemoryBuffer& to)
499 {
500 size_t sz;
501 diy::load(from, sz);
502 from.position -= sizeof(size_t);
503
504 size_t total = sizeof(size_t) + sz;
505 to.buffer.resize(to.position + total);
506 std::copy_n(&from.buffer[from.position], total, &to.buffer[to.position]);
507 to.position += total;
508 from.position += total;
509 }
510
511 #endif
512