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