1 /* Copyright 2009-2016 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8   * the GNU Lesser General Public License as published by the Free
9     Software Foundation; either version 3 of the License, or (at your
10     option) any later version.
11 
12 or
13 
14   * the GNU General Public License as published by the Free Software
15     Foundation; either version 3 of the License, or (at your option) any
16     later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library.  If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #ifndef PIRANHA_S11N_HPP
30 #define PIRANHA_S11N_HPP
31 
32 // Common headers for serialization.
33 #include <boost/algorithm/string/predicate.hpp>
34 #include <boost/archive/binary_iarchive.hpp>
35 #include <boost/archive/binary_oarchive.hpp>
36 #include <boost/archive/text_iarchive.hpp>
37 #include <boost/archive/text_oarchive.hpp>
38 #include <boost/iostreams/filtering_stream.hpp>
39 #include <boost/mpl/bool.hpp>
40 #include <boost/serialization/split_free.hpp>
41 #include <boost/serialization/split_member.hpp>
42 #include <boost/serialization/string.hpp>
43 #include <boost/version.hpp>
44 #include <cstddef>
45 #include <fstream>
46 #include <ios>
47 #include <memory>
48 #include <stdexcept>
49 #include <string>
50 #include <type_traits>
51 #include <utility>
52 
53 #include "detail/demangle.hpp"
54 #include "exceptions.hpp"
55 #include "is_key.hpp"
56 #include "safe_cast.hpp"
57 #include "symbol_set.hpp"
58 #include "type_traits.hpp"
59 
60 namespace piranha
61 {
62 
63 inline namespace impl
64 {
65 
66 // Scalar types directly supported by the all serialization libraries.
67 template <typename T>
68 using is_serialization_scalar = std::
69     integral_constant<bool,
70                       disjunction<std::is_same<char, T>, std::is_same<signed char, T>, std::is_same<unsigned char, T>,
71                                   std::is_same<short, T>, std::is_same<unsigned short, T>, std::is_same<int, T>,
72                                   std::is_same<unsigned, T>, std::is_same<long, T>, std::is_same<unsigned long, T>,
73                                   std::is_same<long long, T>, std::is_same<unsigned long long, T>,
74                                   std::is_same<float, T>, std::is_same<double, T>, std::is_same<bool, T>>::value>;
75 
76 // Implementation of the detection of boost saving archives.
77 namespace ibsa_impl
78 {
79 
80 template <typename A>
81 using is_saving_t = typename A::is_saving;
82 
83 template <typename A>
84 using is_loading_t = typename A::is_loading;
85 
86 template <typename A, typename T>
87 using lshift_t = decltype(std::declval<A &>() << std::declval<const T &>());
88 
89 template <typename A, typename T>
90 using and_t = decltype(std::declval<A &>() & std::declval<const T &>());
91 
92 // NOTE: here it does not make much sense that the pointer is non-const, but we are going by the literal
93 // description in the boost archive concept.
94 template <typename A, typename T>
95 using save_binary_t = decltype(std::declval<A &>().save_binary(std::declval<T *>(), std::declval<std::size_t>()));
96 
97 template <typename A, typename T>
98 using register_type_t = decltype(std::declval<A &>().template register_type<T>());
99 
100 template <typename A>
101 using get_library_version_t = decltype(std::declval<const A &>().get_library_version());
102 
103 struct helper;
104 
105 template <typename A>
106 using get_helper_t_1 = decltype(std::declval<A &>().template get_helper<helper>());
107 
108 template <typename A>
109 using get_helper_t_2 = decltype(std::declval<A &>().template get_helper<helper>(static_cast<void *>(nullptr)));
110 
111 template <typename Archive, typename T>
112 using impl = std::
113     integral_constant<bool,
114                       conjunction<std::is_same<detected_t<is_saving_t, uncvref_t<Archive>>, boost::mpl::bool_<true>>,
115                                   std::is_same<detected_t<is_loading_t, uncvref_t<Archive>>, boost::mpl::bool_<false>>,
116                                   // NOTE: add lvalue ref instead of using Archive &, so we avoid a hard
117                                   // error if Archive is void.
118                                   std::is_same<detected_t<lshift_t, Archive, T>, addlref_t<Archive>>,
119                                   std::is_same<detected_t<and_t, Archive, T>, addlref_t<Archive>>,
120                                   is_detected<save_binary_t, Archive, unref_t<T>>,
121                                   is_detected<register_type_t, Archive, uncvref_t<T>>,
122                                   // NOTE: the docs here mention that get_library_version() is supposed to
123                                   // return an unsigned integral type, but the boost archives apparently
124                                   // return a type which is implicitly convertible to some unsigned int.
125                                   // This seems to work and it should cover also the cases in which the
126                                   // return type is a real unsigned int.
127                                   std::is_convertible<detected_t<get_library_version_t, Archive>, unsigned long long>
128 #if BOOST_VERSION >= 105700
129                                   //  Helper support is available since 1.57.
130                                   ,
131                                   is_detected<get_helper_t_1, Archive>, is_detected<get_helper_t_2, Archive>
132 #endif
133                                   >::value>;
134 }
135 }
136 
137 /// Detect Boost saving archives.
138 /**
139  * This type trait will be \p true if \p Archive is a valid Boost saving archive for type \p T,
140  * \p false otherwise. The Boost saving archive concept is described at
141  * http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/archives.html.
142  */
143 template <typename Archive, typename T>
144 class is_boost_saving_archive
145 {
146     static const bool implementation_defined = ibsa_impl::impl<Archive, T>::value;
147 
148 public:
149     /// Value of the type trait.
150     static const bool value = implementation_defined;
151 };
152 
153 template <typename Archive, typename T>
154 const bool is_boost_saving_archive<Archive, T>::value;
155 
156 inline namespace impl
157 {
158 
159 // Implementation of boost loading archive concept. We reuse some types from the ibsa_impl namespace.
160 namespace ibla_impl
161 {
162 
163 template <typename A, typename T>
164 using rshift_t = decltype(std::declval<A &>() >> std::declval<T &>());
165 
166 template <typename A, typename T>
167 using and_t = decltype(std::declval<A &>() & std::declval<T &>());
168 
169 template <typename A, typename T>
170 using load_binary_t = decltype(std::declval<A &>().load_binary(std::declval<T *>(), std::declval<std::size_t>()));
171 
172 template <typename A, typename T>
173 using reset_object_address_t
174     = decltype(std::declval<A &>().reset_object_address(std::declval<T *>(), std::declval<T *>()));
175 
176 template <typename A>
177 using delete_created_pointers_t = decltype(std::declval<A &>().delete_created_pointers());
178 
179 template <typename Archive, typename T>
180 using impl
181     = std::integral_constant<bool,
182                              conjunction<std::is_same<detected_t<ibsa_impl::is_saving_t, uncvref_t<Archive>>,
183                                                       boost::mpl::bool_<false>>,
184                                          std::is_same<detected_t<ibsa_impl::is_loading_t, uncvref_t<Archive>>,
185                                                       boost::mpl::bool_<true>>,
186                                          std::is_same<detected_t<rshift_t, Archive, T>, addlref_t<Archive>>,
187                                          std::is_same<detected_t<and_t, Archive, T>, addlref_t<Archive>>,
188                                          is_detected<load_binary_t, Archive, unref_t<T>>,
189                                          is_detected<ibsa_impl::register_type_t, Archive, uncvref_t<T>>,
190                                          std::is_convertible<detected_t<ibsa_impl::get_library_version_t, Archive>,
191                                                              unsigned long long>,
192                                          is_detected<reset_object_address_t, Archive, unref_t<T>>,
193                                          is_detected<delete_created_pointers_t, Archive>
194 #if BOOST_VERSION >= 105700
195                                          ,
196                                          is_detected<ibsa_impl::get_helper_t_1, Archive>,
197                                          is_detected<ibsa_impl::get_helper_t_2, Archive>
198 #endif
199                                          >::value>;
200 }
201 }
202 
203 /// Detect Boost loading archives.
204 /**
205  * This type trait will be \p true if \p Archive is a valid Boost loading archive for type \p T,
206  * \p false otherwise. The Boost loading archive concept is described at
207  * http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/archives.html.
208  */
209 template <typename Archive, typename T>
210 class is_boost_loading_archive
211 {
212     static const bool implementation_defined = ibla_impl::impl<Archive, T>::value;
213 
214 public:
215     /// Value of the type trait.
216     static const bool value = implementation_defined;
217 };
218 
219 template <typename Archive, typename T>
220 const bool is_boost_loading_archive<Archive, T>::value;
221 
222 /// Implementation of piranha::boost_save() via the Boost API.
223 /**
224  * This class can be used to implement piranha::boost_save() via a direct call to the Boost serialization API.
225  */
226 template <typename Archive, typename T>
227 struct boost_save_via_boost_api {
228     /// Call operator.
229     /**
230      * The body of this operator is equivalent to:
231      * @code
232      * ar << x;
233      * @endcode
234      * That is, \p x will be inserted into the archive \p ar using the stream insertion operator. In order for this
235      * method to be callable, the type \p T must provide valid overloads of the methods/functions needed by the
236      * Boost serialization API.
237      *
238      * @param ar the archive into which \p x will be serialized.
239      * @param x the serialization argument.
240      *
241      * @throws unspecified any exception thrown by the insertion of \p x into \p ar.
242      */
operator ()piranha::boost_save_via_boost_api243     void operator()(Archive &ar, const T &x) const
244     {
245         ar << x;
246     }
247 };
248 
249 /// Default implementation of piranha::boost_save().
250 /**
251  * The default implementation does not define any call operator, and will thus result
252  * in a compile-time error if used.
253  */
254 template <typename Archive, typename T, typename = void>
255 struct boost_save_impl {
256 };
257 
258 inline namespace impl
259 {
260 
261 // Enabler for the arithmetic specialisation of boost_save().
262 template <typename Archive, typename T>
263 using boost_save_arithmetic_enabler =
264     // NOTE: the check for non-constness of Archive is implicit in the saving archive concept.
265     // NOTE: here we are relying on the fact that the Archive class correctly advertises the capability
266     // to serialize arithmetic types. This is for instance *not* the case for Boost archives, which
267     // accept every type (at the signature level), but since here we are concerned only with arithmetic
268     // types (for which serialization is always available in Boost archives), it should not matter.
269     enable_if_t<conjunction<is_boost_saving_archive<Archive, T>,
270                             disjunction<is_serialization_scalar<T>, std::is_same<T, long double>>>::value>;
271 }
272 
273 /// Specialisation of piranha::boost_save() for arithmetic types.
274 /**
275  * \note
276  * This specialisation is enabled if \p Archive and \p T satisfy piranha::is_boost_saving_archive and
277  * \p T is either a floating-point type, or one of
278  * - \p char, <tt>signed char</tt>, or <tt>unsigned char</tt>,
279  * - \p int or <tt>unsigned</tt>,
280  * - \p long or <tt>unsigned long</tt>,
281  * - <tt>long long</tt> or <tt>unsigned long long</tt>,
282  * - \p bool.
283  */
284 template <typename Archive, typename T>
285 struct boost_save_impl<Archive, T, boost_save_arithmetic_enabler<Archive, T>> : boost_save_via_boost_api<Archive, T> {
286 };
287 
288 inline namespace impl
289 {
290 
291 // Enabler for boost_save() for strings.
292 template <typename Archive>
293 using boost_save_string_enabler = enable_if_t<is_boost_saving_archive<Archive, std::string>::value>;
294 }
295 
296 /// Specialisation of piranha::boost_save() for \p std::string.
297 /**
298  * \note
299  * This specialisation is enabled if \p Archive and \p T satisfy piranha::is_boost_saving_archive and \p T is
300  * \p std::string.
301  */
302 template <typename Archive>
303 struct boost_save_impl<Archive, std::string, boost_save_string_enabler<Archive>>
304     : boost_save_via_boost_api<Archive, std::string> {
305 };
306 
307 inline namespace impl
308 {
309 
310 template <typename Archive, typename T>
311 using boost_save_impl_t = decltype(boost_save_impl<Archive, T>{}(std::declval<Archive &>(), std::declval<const T &>()));
312 
313 // Enabler for boost_save().
314 template <typename Archive, typename T>
315 using boost_save_enabler
316     = enable_if_t<conjunction<is_boost_saving_archive<Archive, T>, is_detected<boost_save_impl_t, Archive, T>>::value,
317                   int>;
318 }
319 
320 /// Save to Boost archive.
321 /**
322  * \note
323  * This function is enabled only if \p Archive and \p T satisfy piranha::is_boost_saving_archive, and the expression
324  * <tt>boost_save_impl<Archive, T>{}(ar, x)</tt> is well-formed.
325  *
326  * This function will save to the Boost archive \p ar the object \p x. The implementation of this function is in
327  * the call operator of the piranha::boost_save_impl functor. The body of this function is equivalent to:
328  * @code
329  * boost_save_impl<Archive, T>{}(ar, x);
330  * @endcode
331  *
332  * @param ar target Boost saving archive.
333  * @param x object to be saved.
334  *
335  * @throws unspecified any exception thrown by the call operator of piranha::boost_save_impl.
336  */
337 template <typename Archive, typename T, boost_save_enabler<Archive, T> = 0>
boost_save(Archive & ar,const T & x)338 inline void boost_save(Archive &ar, const T &x)
339 {
340     boost_save_impl<Archive, T>{}(ar, x);
341 }
342 
343 inline namespace impl
344 {
345 
346 template <typename A, typename T>
347 using boost_save_t = decltype(piranha::boost_save(std::declval<A &>(), std::declval<const T &>()));
348 }
349 
350 /// Detect the presence of piranha::boost_save().
351 /**
352  * This type trait will be \p true if piranha::boost_save() can be called with template arguments \p Archive and
353  * \p T, \p false otherwise.
354  */
355 template <typename Archive, typename T>
356 class has_boost_save
357 {
358     static const bool implementation_defined = is_detected<boost_save_t, Archive, T>::value;
359 
360 public:
361     /// Value of the type trait.
362     static const bool value = implementation_defined;
363 };
364 
365 template <typename Archive, typename T>
366 const bool has_boost_save<Archive, T>::value;
367 
368 /// Implementation of piranha::boost_load() via the Boost API.
369 /**
370  * This class can be used to implement piranha::boost_load() via a direct call to the Boost serialization API.
371  */
372 template <typename Archive, typename T>
373 struct boost_load_via_boost_api {
374     /// Call operator.
375     /**
376      * The body of this operator is equivalent to:
377      * @code
378      * ar >> x;
379      * @endcode
380      * That is, the content of \p ar will loaded into \p x using the stream extraction operator. In order for this
381      * method to be callable, the type \p T must provide valid overloads of the methods/functions needed by the
382      * Boost serialization API.
383      *
384      * @param ar the source archive.
385      * @param x the object into which the content of \p ar will be deserialized.
386      *
387      * @throws unspecified any exception thrown by the extraction of \p ar into \p x.
388      */
operator ()piranha::boost_load_via_boost_api389     void operator()(Archive &ar, T &x) const
390     {
391         ar >> x;
392     }
393 };
394 
395 /// Default implementation of piranha::boost_load().
396 /**
397  * The default implementation does not define any call operator, and will thus result
398  * in a compile-time error if used.
399  */
400 template <typename Archive, typename T, typename = void>
401 struct boost_load_impl {
402 };
403 
404 inline namespace impl
405 {
406 
407 // Enabler for the arithmetic specialisation of boost_load().
408 template <typename Archive, typename T>
409 using boost_load_arithmetic_enabler
410     = enable_if_t<conjunction<is_boost_loading_archive<Archive, T>,
411                               disjunction<is_serialization_scalar<T>, std::is_same<T, long double>>>::value>;
412 }
413 
414 /// Specialisation of piranha::boost_load() for arithmetic types.
415 /**
416  * \note
417  * This specialisation is enabled if \p Archive and \p T satisfy piranha::is_boost_loading_archive and
418  * \p T is either a floating-point type, or one of
419  * - \p char, <tt>signed char</tt>, or <tt>unsigned char</tt>,
420  * - \p int or <tt>unsigned</tt>,
421  * - \p long or <tt>unsigned long</tt>,
422  * - <tt>long long</tt> or <tt>unsigned long long</tt>,
423  * - \p bool.
424  */
425 template <typename Archive, typename T>
426 struct boost_load_impl<Archive, T, boost_load_arithmetic_enabler<Archive, T>> : boost_load_via_boost_api<Archive, T> {
427 };
428 
429 inline namespace impl
430 {
431 
432 // Enabler for boost_load for strings.
433 template <typename Archive>
434 using boost_load_string_enabler = enable_if_t<is_boost_loading_archive<Archive, std::string>::value>;
435 }
436 
437 /// Specialisation of piranha::boost_load() for \p std::string.
438 /**
439  * \note
440  * This specialisation is enabled if \p Archive and \p T satisfy piranha::is_boost_loading_archive and \p T is
441  * \p std::string.
442  */
443 template <typename Archive>
444 struct boost_load_impl<Archive, std::string, boost_load_string_enabler<Archive>>
445     : boost_load_via_boost_api<Archive, std::string> {
446 };
447 
448 inline namespace impl
449 {
450 
451 template <typename Archive, typename T>
452 using boost_load_impl_t = decltype(boost_load_impl<Archive, T>{}(std::declval<Archive &>(), std::declval<T &>()));
453 
454 // Enabler for boost_load().
455 template <typename Archive, typename T>
456 using boost_load_enabler
457     = enable_if_t<conjunction<is_boost_loading_archive<Archive, T>, is_detected<boost_load_impl_t, Archive, T>>::value,
458                   int>;
459 }
460 
461 /// Load from Boost archive.
462 /**
463  * \note
464  * This function is enabled only if \p Archive and \p T satisfy piranha::is_boost_loading_archive, and the expression
465  * <tt>boost_load_impl<Archive, T>{}(ar, x)</tt> is well-formed.
466  *
467  * This function will load the object \p x from the Boost archive \p ar. The implementation of this function is in
468  * the call operator of the piranha::boost_load_impl functor. The body of this function is equivalent to:
469  * @code
470  * boost_load_impl<Archive, T>{}(ar, x);
471  * @endcode
472  *
473  * @param ar the source Boost loading archive.
474  * @param x the object that will be loaded from \p ar.
475  *
476  * @throws unspecified any exception thrown by the call operator of piranha::boost_load_impl.
477  */
478 template <typename Archive, typename T, boost_load_enabler<Archive, T> = 0>
boost_load(Archive & ar,T & x)479 inline void boost_load(Archive &ar, T &x)
480 {
481     boost_load_impl<Archive, T>{}(ar, x);
482 }
483 
484 inline namespace impl
485 {
486 
487 template <typename A, typename T>
488 using boost_load_t = decltype(piranha::boost_load(std::declval<A &>(), std::declval<T &>()));
489 }
490 
491 /// Detect the presence of piranha::boost_load().
492 /**
493  * This type trait will be \p true if piranha::boost_load() can be called with template arguments \p Archive and
494  * \p T, \p false otherwise.
495  */
496 template <typename Archive, typename T>
497 class has_boost_load
498 {
499     static const bool implementation_defined = is_detected<boost_load_t, Archive, T>::value;
500 
501 public:
502     /// Value of the type trait.
503     static const bool value = implementation_defined;
504 };
505 
506 template <typename Archive, typename T>
507 const bool has_boost_load<Archive, T>::value;
508 
509 /// Wrapper for the serialization of keys via Boost.
510 /**
511  * This is a simple struct that stores a reference to a key (possibly const-qualified) and a const reference
512  * to a piranha::symbol_set. The Boost serialization routines of piranha::series will save/load keys via this
513  * wrapper rather than trying to save/load keys directly. The reason for this is that keys might
514  * need external information (in the form of the series' piranha::symbol_set) in order for the (de)serialization to be
515  * successful.
516  *
517  * Consequently, rather that implementing specialisations of piranha::boost_save_impl and piranha::boost_load_impl
518  * for key types, specialisations of piranha::boost_save_impl and piranha::boost_load_impl for this wrapper structure
519  * should be provided instead.
520  *
521  * This class requires \p Key to satisfy piranha::is_key, otherwise a compile-time error will be produced.
522  */
523 template <typename Key>
524 struct boost_s11n_key_wrapper {
525 private:
526     PIRANHA_TT_CHECK(is_key, Key);
527 
528 public:
529     /// Constructor from key and piranha::symbol_set.
530     /**
531      * @param k the input key.
532      * @param ss the reference piranha::symbol_set.
533      */
boost_s11n_key_wrapperpiranha::boost_s11n_key_wrapper534     explicit boost_s11n_key_wrapper(Key &k, const symbol_set &ss)
535         : m_key_m(std::addressof(k)), m_key_c(m_key_m), m_ss(ss)
536     {
537     }
538     /// Constructor from const key and piranha::symbol_set.
539     /**
540      * @param k the input key.
541      * @param ss the reference piranha::symbol_set.
542      */
boost_s11n_key_wrapperpiranha::boost_s11n_key_wrapper543     explicit boost_s11n_key_wrapper(const Key &k, const symbol_set &ss)
544         : m_key_m(nullptr), m_key_c(std::addressof(k)), m_ss(ss)
545     {
546     }
547     /// Reference to the key.
548     /**
549      * This method will return a mutable reference to the key used as a construction argument. If \p this was
550      * constructed from a const key instance, calling this method will raise an exception.
551      *
552      * @return a mutable reference to the key used as a construction argument.
553      *
554      * @throws std::runtime_error if \p this was constructed from a const key instance.
555      */
keypiranha::boost_s11n_key_wrapper556     Key &key()
557     {
558         if (unlikely(!m_key_m)) {
559             piranha_throw(std::runtime_error, "trying to access the mutable key instance of a boost_s11n_key_wrapper "
560                                               "that was constructed with a const key");
561         }
562         return *m_key_m;
563     }
564     /// Const reference to the key.
565     /**
566      * @return a const reference to the key used as a construction argument.
567      */
keypiranha::boost_s11n_key_wrapper568     const Key &key() const
569     {
570         return *m_key_c;
571     }
572     /// Const reference to the symbol set.
573     /**
574      * @return a const reference to the piranha::symbol_set used as a construction argument.
575      */
sspiranha::boost_s11n_key_wrapper576     const symbol_set &ss() const
577     {
578         return m_ss;
579     }
580 
581 private:
582     Key *m_key_m;
583     const Key *m_key_c;
584     const symbol_set &m_ss;
585 };
586 }
587 
588 #include "config.hpp"
589 
590 #if defined(PIRANHA_WITH_MSGPACK)
591 
592 #include <msgpack.hpp>
593 
594 #if MSGPACK_VERSION_MAJOR < 2
595 
596 #error Minimum msgpack-c version supported is 2.
597 
598 #endif
599 
600 #include <algorithm>
601 #include <array>
602 #include <boost/iostreams/device/mapped_file.hpp>
603 #include <cmath>
604 #include <cstdint>
605 #include <ios>
606 #include <iterator>
607 #include <limits>
608 #include <locale>
609 #include <sstream>
610 #include <vector>
611 
612 namespace piranha
613 {
614 
615 // Fwd decls.
616 template <typename, typename>
617 class has_msgpack_pack;
618 
619 template <typename>
620 class has_msgpack_convert;
621 
622 inline namespace impl
623 {
624 
625 // Wrapper for std stream classes for use in msgpack. The reason for this wrapper is that msgpack expects
626 // streams with a write(const char *, std::size_t) method, but in std streams the second param is std::streamsize
627 // (which is a signed int). Hence we wrap the write method to do the safe conversion from size_t to streamsize.
628 template <typename Stream>
629 class msgpack_stream_wrapper : public Stream
630 {
631 public:
632     // Inherit ctors.
633     using Stream::Stream;
write(const typename Stream::char_type * p,std::size_t count)634     auto write(const typename Stream::char_type *p, std::size_t count)
635         -> decltype(std::declval<Stream &>().write(p, std::streamsize(0)))
636     {
637         return static_cast<Stream *>(this)->write(p, safe_cast<std::streamsize>(count));
638     }
639 };
640 
641 template <typename T>
642 using msgpack_stream_write_t
643     = decltype(std::declval<T &>().write(std::declval<const char *>(), std::declval<std::size_t>()));
644 }
645 
646 /// Detect msgpack stream.
647 /**
648  * This type trait will be \p true if \p T is a type which can be used as template argument to <tt>msgpack::packer</tt>,
649  * \p false otherwise. Specifically, in order for this trait to be true \p T must be a non-const, non-reference type
650  * with a <tt>write()</tt> method with a signature compatible with
651  * @code
652  * T::write(const char *, std::size_t);
653  * @endcode
654  * The return type of the <tt>write()</tt> method is ignored by the type trait.
655  */
656 template <typename T>
657 class is_msgpack_stream
658 {
659     static const bool implementation_defined
660         = conjunction<is_detected<msgpack_stream_write_t, T>, negation<std::is_reference<T>>,
661                       negation<std::is_const<T>>>::value;
662 
663 public:
664     /// Value of the type trait.
665     static const bool value = implementation_defined;
666 };
667 
668 template <typename T>
669 const bool is_msgpack_stream<T>::value;
670 
671 /// Serialization format for msgpack.
672 /**
673  * The serialization of non-primitive objects can often be performed in different ways, with different tradeoffs
674  * between performance, storage requirements and portability. The piranha::mp_integer class, for instance, can be
675  * serialized either via a string representation (slow and with high storage requirements, but portable across
676  * architectures, compilers and operating systems) or as an array of platform-dependent unsigned integrals (fast
677  * and compact, but not portable).
678  *
679  * This enum establishes two strategies for msgpack serialization: a portable format, intended
680  * to be usable across different platforms and suitable for the long-term storage of serialized objects, and a binary
681  * format, intended for use in high-performance scenarios (e.g., as temporary on-disk storage during long
682  * or memory-intensive computations). These formats are used by the Piranha msgpack serialization functions
683  * (piranha::msgpack_pack(), piranha::msgpack_convert(), etc.).
684  */
685 enum class msgpack_format {
686     /// Portable.
687     portable,
688     /// Binary.
689     binary
690 };
691 
692 /// Default functor for the implementation of piranha::msgpack_pack().
693 /**
694  * This functor can be specialised via the \p std::enable_if mechanism. Default implementation will not define
695  * the call operator, and will hence result in a compilation error when used.
696  */
697 template <typename Stream, typename T, typename = void>
698 struct msgpack_pack_impl {
699 };
700 
701 inline namespace impl
702 {
703 
704 template <typename Stream, typename T>
705 using msgpack_scalar_enabler = enable_if_t<conjunction<is_msgpack_stream<Stream>, is_serialization_scalar<T>>::value>;
706 }
707 
708 /// Specialisation of piranha::msgpack_pack() for fundamental C++ types supported by msgpack.
709 /**
710  * \note
711  * This specialisation is enabled if \p Stream satisfies piranha::is_msgpack_stream and \p T is one of the
712  * following types:
713  * - \p char, <tt>signed char</tt>, or <tt>unsigned char</tt>,
714  * - \p int or <tt>unsigned</tt>,
715  * - \p long or <tt>unsigned long</tt>,
716  * - <tt>long long</tt> or <tt>unsigned long long</tt>,
717  * - \p float or \p double,
718  * - \p bool.
719  *
720  * The call operator will use directly the <tt>pack()</tt> method of the input msgpack packer.
721  */
722 template <typename Stream, typename T>
723 struct msgpack_pack_impl<Stream, T, msgpack_scalar_enabler<Stream, T>> {
724     /// Call operator.
725     /**
726      * @param packer the target packer.
727      * @param x the object to be packed.
728      *
729      * @throws unspecified any exception thrown by <tt>msgpack::packer::pack()</tt>.
730      */
operator ()piranha::msgpack_pack_impl731     void operator()(msgpack::packer<Stream> &packer, const T &x, msgpack_format) const
732     {
733         packer.pack(x);
734     }
735 };
736 
737 inline namespace impl
738 {
739 
740 template <typename Stream>
741 using msgpack_ld_enabler
742     = enable_if_t<conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, std::string>>::value>;
743 }
744 
745 /// Specialisation of piranha::msgpack_pack() for <tt>long double</tt>.
746 /**
747  * \note
748  * This specialisation is enabled if:
749  * - \p Stream satisfies piranha::is_msgpack_stream,
750  * - \p std::string satisfies piranha::has_msgpack_pack.
751  */
752 template <typename Stream>
753 struct msgpack_pack_impl<Stream, long double, msgpack_ld_enabler<Stream>> {
754     /// Call operator.
755     /**
756      * If \p f is msgpack_format::binary then the byte representation of \p x is packed into \p packer. Otherwise,
757      * \p x is converted into string format and the resulting string will be packed into \p packer.
758      *
759      * @param packer the target packer.
760      * @param x the object to be packed.
761      * @param f the serialization format.
762      *
763      * @throws unspecified any exception thrown by:
764      * - the public interface of \p msgpack::packer and \p std::ostringstream,
765      * - piranha::msgpack_pack().
766      */
operator ()piranha::msgpack_pack_impl767     void operator()(msgpack::packer<Stream> &packer, const long double &x, msgpack_format f) const
768     {
769         if (f == msgpack_format::binary) {
770             packer.pack_bin(sizeof(long double));
771             packer.pack_bin_body(reinterpret_cast<const char *>(&x), sizeof(long double));
772         } else {
773             if (std::isnan(x)) {
774                 if (std::signbit(x)) {
775                     msgpack_pack(packer, std::string("-nan"), f);
776                 } else {
777                     msgpack_pack(packer, std::string("+nan"), f);
778                 }
779             } else if (std::isinf(x)) {
780                 if (std::signbit(x)) {
781                     msgpack_pack(packer, std::string("-inf"), f);
782                 } else {
783                     msgpack_pack(packer, std::string("+inf"), f);
784                 }
785             } else {
786                 std::ostringstream oss;
787                 // Make sure we are using the C locale.
788                 oss.imbue(std::locale::classic());
789                 // Use scientific format.
790                 oss << std::scientific;
791                 // http://stackoverflow.com/questions/554063/how-do-i-print-a-double-value-with-full-precision-using-cout
792                 // NOTE: this does not mean that the *exact* value of the long double is printed, just that the
793                 // value is recovered exactly if reloaded on the same machine. This is a compromise, as the exact
794                 // printing of the value in string form would take up hundreds of digits. On the other hand, there is a
795                 // similar situation also for float and double, as there is not guarantee that they conform to IEEE. In
796                 // the end it seems the only practical approach is to consider all floating-point types as approximate
797                 // values, subject to various platform/architecture vagaries.
798                 oss.precision(std::numeric_limits<long double>::max_digits10);
799                 oss << x;
800                 msgpack_pack(packer, oss.str(), f);
801             }
802         }
803     }
804 };
805 
806 inline namespace impl
807 {
808 
809 template <typename Stream>
810 using msgpack_string_enabler = enable_if_t<is_msgpack_stream<Stream>::value>;
811 }
812 
813 /// Specialisation of piranha::msgpack_pack() for \p std::string.
814 /**
815  * \note
816  * This specialisation is enabled if \p Stream satisfies piranha::is_msgpack_stream.
817  *
818  * The call operator will use directly the <tt>pack()</tt> method of the input msgpack packer.
819  */
820 template <typename Stream>
821 struct msgpack_pack_impl<Stream, std::string, msgpack_string_enabler<Stream>> {
822     /// Call operator.
823     /**
824      * @param packer the target packer.
825      * @param s the string to be packed.
826      *
827      * @throws unspecified any exception thrown by <tt>msgpack::packer::pack()</tt>.
828      */
operator ()piranha::msgpack_pack_impl829     void operator()(msgpack::packer<Stream> &packer, const std::string &s, msgpack_format) const
830     {
831         packer.pack(s);
832     }
833 };
834 
835 inline namespace impl
836 {
837 
838 template <typename Stream, typename T>
839 using msgpack_pack_impl_t = decltype(msgpack_pack_impl<Stream, T>{}(
840     std::declval<msgpack::packer<Stream> &>(), std::declval<const T &>(), std::declval<msgpack_format>()));
841 
842 // Enabler for msgpack_pack.
843 template <typename Stream, typename T>
844 using msgpack_pack_enabler
845     = enable_if_t<conjunction<is_msgpack_stream<Stream>, is_detected<msgpack_pack_impl_t, Stream, T>>::value, int>;
846 }
847 
848 /// Pack generic object in a msgpack stream.
849 /**
850  * \note
851  * This function is enabled only if \p Stream satisfies piranha::is_msgpack_stream and if
852  * <tt>msgpack_pack_impl<Stream, T>{}(packer, x, f)</tt> is a valid expression.
853  *
854  * This function is intended to pack the input value \p x into a msgpack \p packer using the format
855  * \p f. The actual implementation of this function is in the piranha::msgpack_pack_impl functor.
856  * The body of this function is equivalent to:
857  * @code
858  * msgpack_pack_impl<Stream, T>{}(packer, x, f);
859  * @endcode
860  *
861  * @param packer the msgpack packer object.
862  * @param x the object to be packed into \p packer.
863  * @param f the serialization format.
864  *
865  * @throws unspecified any exception thrown by the call operator piranha::msgpack_pack_impl.
866  */
867 template <typename Stream, typename T, msgpack_pack_enabler<Stream, T> = 0>
msgpack_pack(msgpack::packer<Stream> & packer,const T & x,msgpack_format f)868 inline void msgpack_pack(msgpack::packer<Stream> &packer, const T &x, msgpack_format f)
869 {
870     msgpack_pack_impl<Stream, T>{}(packer, x, f);
871 }
872 
873 /// Default functor for the implementation of piranha::msgpack_convert().
874 /**
875  * This functor can be specialised via the \p std::enable_if mechanism. Default implementation will not define
876  * the call operator, and will hence result in a compilation error when used.
877  */
878 template <typename T, typename = void>
879 struct msgpack_convert_impl {
880 };
881 
882 inline namespace impl
883 {
884 
885 template <typename T>
886 using msgpack_convert_scalar_enabler = enable_if_t<is_serialization_scalar<T>::value>;
887 }
888 
889 /// Specialisation of piranha::msgpack_convert() for fundamental C++ types supported by msgpack.
890 /**
891  * \note
892  * This specialisation is enabled if \p T is one of the following types:
893  * - \p char, <tt>signed char</tt>, or <tt>unsigned char</tt>,
894  * - \p int or <tt>unsigned</tt>,
895  * - \p long or <tt>unsigned long</tt>,
896  * - <tt>long long</tt> or <tt>unsigned long long</tt>,
897  * - \p float or \p double,
898  * - \p bool.
899  *
900  * The call operator will use directly the <tt>convert()</tt> method of the input msgpack object.
901  */
902 template <typename T>
903 struct msgpack_convert_impl<T, msgpack_convert_scalar_enabler<T>> {
904     /// Call operator.
905     /**
906      * @param x the output value.
907      * @param o the object to be converted.
908      *
909      * @throws unspecified any exception thrown by <tt>msgpack::object::convert()</tt>.
910      */
operator ()piranha::msgpack_convert_impl911     void operator()(T &x, const msgpack::object &o, msgpack_format) const
912     {
913         o.convert(x);
914     }
915 };
916 
917 /// Specialisation of piranha::msgpack_convert() for \p std::string.
918 /**
919  * The call operator will use directly the <tt>convert()</tt> method of the input msgpack object.
920  */
921 template <>
922 struct msgpack_convert_impl<std::string> {
923     /// Call operator.
924     /**
925      * @param s the output string.
926      * @param o the object to be converted.
927      *
928      * @throws unspecified any exception thrown by the public interface of <tt>msgpack::object</tt>.
929      */
operator ()piranha::msgpack_convert_impl930     void operator()(std::string &s, const msgpack::object &o, msgpack_format) const
931     {
932         o.convert(s);
933     }
934 };
935 
936 inline namespace impl
937 {
938 
939 template <typename T>
940 using msgpack_convert_impl_t = decltype(msgpack_convert_impl<T>{}(
941     std::declval<T &>(), std::declval<const msgpack::object &>(), std::declval<msgpack_format>()));
942 
943 // Enabler for msgpack_convert.
944 template <typename T>
945 using msgpack_convert_enabler
946     = enable_if_t<conjunction<negation<std::is_const<T>>, is_detected<msgpack_convert_impl_t, T>>::value, int>;
947 }
948 
949 /// Convert msgpack object.
950 /**
951  * \note
952  * This function is enabled only if \p T is not const and if
953  * <tt>msgpack_convert_impl<T>{}(x, o, f)</tt> is a valid expression.
954  *
955  * This function is intended to convert the msgpack object \p o into an instance of type \p T, and to write
956  * the converted value into \p x. The actual implementation of this function is in the piranha::msgpack_convert_impl
957  * functor. The body of this function is equivalent to:
958  * @code
959  * msgpack_convert_impl<T>{}(x, o, f);
960  * @endcode
961  *
962  * @param x the output value.
963  * @param o the msgpack object that will be converted into \p x.
964  * @param f the serialization format.
965  *
966  * @throws unspecified any exception thrown by the call operator piranha::msgpack_convert_impl.
967  */
968 template <typename T, msgpack_convert_enabler<T> = 0>
msgpack_convert(T & x,const msgpack::object & o,msgpack_format f)969 inline void msgpack_convert(T &x, const msgpack::object &o, msgpack_format f)
970 {
971     msgpack_convert_impl<T>{}(x, o, f);
972 }
973 
974 inline namespace impl
975 {
976 
977 template <typename T>
978 using msgpack_convert_ld_enabler
979     = enable_if_t<conjunction<std::is_same<T, long double>, has_msgpack_convert<std::string>>::value>;
980 }
981 
982 /// Specialisation of piranha::msgpack_convert() for <tt>long double</tt>.
983 /**
984  * \note
985  * This specialisation is enabled if \p T is <tt>long double</tt> and \p std::string satisfies
986  * piranha::has_msgpack_convert.
987  */
988 template <typename T>
989 struct msgpack_convert_impl<T, msgpack_convert_ld_enabler<T>> {
990     /// Call operator.
991     /**
992      * @param x the output value.
993      * @param o the object to be converted.
994      * @param f the serialization format.
995      *
996      * @throws unspecified any exception thrown by the public interface of <tt>msgpack::object</tt> and
997      * <tt>std::istringstream</tt>.
998      * @throws std::invalid_argument if the serialized value is a non-finite value not supported by the implementation,
999      * or, when using the msgpack_format::portable format, the deserialized string does not represent a floating-point
1000      * value.
1001      */
operator ()piranha::msgpack_convert_impl1002     void operator()(long double &x, const msgpack::object &o, msgpack_format f) const
1003     {
1004         using lim = std::numeric_limits<long double>;
1005         if (f == msgpack_format::binary) {
1006             std::array<char, sizeof(long double)> tmp;
1007             o.convert(tmp);
1008             std::copy(tmp.begin(), tmp.end(), reinterpret_cast<char *>(&x));
1009         } else {
1010             PIRANHA_MAYBE_TLS std::string tmp;
1011             msgpack_convert(tmp, o, f);
1012             if (tmp == "+nan") {
1013                 if (lim::has_quiet_NaN) {
1014                     x = std::copysign(lim::quiet_NaN(), 1.l);
1015                 } else {
1016                     piranha_throw(std::invalid_argument, "cannot deserialize a NaN if the platform does not support "
1017                                                          "quiet NaNs");
1018                 }
1019             } else if (tmp == "-nan") {
1020                 if (lim::has_quiet_NaN) {
1021                     x = std::copysign(lim::quiet_NaN(), -1.l);
1022                 } else {
1023                     piranha_throw(std::invalid_argument, "cannot deserialize a NaN if the platform does not support "
1024                                                          "quiet NaNs");
1025                 }
1026             } else if (tmp == "+inf") {
1027                 if (lim::has_infinity) {
1028                     x = lim::infinity();
1029                 } else {
1030                     piranha_throw(std::invalid_argument, "infinities are not supported by the platform");
1031                 }
1032             } else if (tmp == "-inf") {
1033                 if (lim::has_infinity) {
1034                     x = std::copysign(lim::infinity(), -1.l);
1035                 } else {
1036                     piranha_throw(std::invalid_argument, "infinities are not supported by the platform");
1037                 }
1038             } else {
1039                 std::istringstream iss;
1040                 iss.imbue(std::locale::classic());
1041                 // NOTE: is seems like the std::scientific format flag has an effect on input
1042                 // streams as well. See the example here:
1043                 // http://en.cppreference.com/w/cpp/io/manip/fixed
1044                 iss >> std::scientific;
1045                 iss.str(tmp);
1046                 iss >> x;
1047                 if (unlikely(iss.fail())) {
1048                     piranha_throw(std::invalid_argument, "failed to parse the string '" + tmp + "' as a long double");
1049                 }
1050             }
1051         }
1052     }
1053 };
1054 
1055 inline namespace impl
1056 {
1057 
1058 template <typename Stream, typename T>
1059 using msgpack_pack_t = decltype(piranha::msgpack_pack(std::declval<msgpack::packer<Stream> &>(),
1060                                                       std::declval<const T &>(), std::declval<msgpack_format>()));
1061 }
1062 
1063 /// Detect the presence of piranha::msgpack_pack().
1064 /**
1065  * This type trait will be \p true if piranha::msgpack_pack() can be called with template arguments
1066  * \p Stream and \p T, \p false otherwise.
1067  */
1068 template <typename Stream, typename T>
1069 class has_msgpack_pack
1070 {
1071     static const bool implementation_defined = is_detected<msgpack_pack_t, Stream, T>::value;
1072 
1073 public:
1074     /// Value of the type trait.
1075     static const bool value = implementation_defined;
1076 };
1077 
1078 template <typename Stream, typename T>
1079 const bool has_msgpack_pack<Stream, T>::value;
1080 
1081 inline namespace impl
1082 {
1083 
1084 template <typename T>
1085 using msgpack_convert_t = decltype(piranha::msgpack_convert(
1086     std::declval<T &>(), std::declval<const msgpack::object &>(), std::declval<msgpack_format>()));
1087 }
1088 
1089 /// Detect the presence of piranha::msgpack_convert().
1090 /**
1091  * This type trait will be \p true if piranha::msgpack_convert() can be called with template argument \p T.
1092  */
1093 template <typename T>
1094 class has_msgpack_convert
1095 {
1096     static const bool implementation_defined = is_detected<msgpack_convert_t, T>::value;
1097 
1098 public:
1099     /// Value of the type trait.
1100     static const bool value = implementation_defined;
1101 };
1102 
1103 template <typename T>
1104 const bool has_msgpack_convert<T>::value;
1105 
1106 inline namespace impl
1107 {
1108 
1109 template <typename Stream, typename Key>
1110 using key_msgpack_pack_t = decltype(std::declval<const Key &>().msgpack_pack(
1111     std::declval<msgpack::packer<Stream> &>(), std::declval<msgpack_format>(), std::declval<const symbol_set &>()));
1112 }
1113 
1114 /// Detect the presence of the <tt>%msgpack_pack()</tt> method in keys.
1115 /**
1116  * This type trait will be \p true if \p Stream satisfies piranha::is_msgpack_stream and the \p Key type has
1117  * a method whose signature is compatible with:
1118  * @code
1119  * Key::msgpack_pack(msgpack::packer<Stream> &, msgpack_format, const symbol_set &) const;
1120  * @endcode
1121  * The return type of the method is ignored by this type trait.
1122  *
1123  * If \p Key, after the removal of cv-ref qualifiers, does not satisfy piranha::is_key,
1124  * a compile-time error will be produced.
1125  */
1126 template <typename Stream, typename Key>
1127 class key_has_msgpack_pack
1128 {
1129     PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
1130     static const bool implementation_defined
1131         = conjunction<is_detected<key_msgpack_pack_t, Stream, Key>, is_msgpack_stream<Stream>>::value;
1132 
1133 public:
1134     /// Value of the type trait.
1135     static const bool value = implementation_defined;
1136 };
1137 
1138 template <typename Stream, typename Key>
1139 const bool key_has_msgpack_pack<Stream, Key>::value;
1140 
1141 inline namespace impl
1142 {
1143 
1144 template <typename Key>
1145 using key_msgpack_convert_t = decltype(std::declval<Key &>().msgpack_convert(
1146     std::declval<const msgpack::object &>(), std::declval<msgpack_format>(), std::declval<const symbol_set &>()));
1147 }
1148 
1149 /// Detect the presence of the <tt>%msgpack_convert()</tt> method in keys.
1150 /**
1151  * This type trait will be \p true if the \p Key type has a method whose signature is compatible with:
1152  * @code
1153  * Key::msgpack_convert(const msgpack::object &, msgpack_format, const symbol_set &);
1154  * @endcode
1155  * The return type of the method is ignored by this type trait.
1156  *
1157  * If \p Key, after the removal of cv-ref qualifiers, does not satisfy piranha::is_key,
1158  * a compile-time error will be produced.
1159  */
1160 template <typename Key>
1161 class key_has_msgpack_convert
1162 {
1163     PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
1164     static const bool implementation_defined = is_detected<key_msgpack_convert_t, Key>::value;
1165 
1166 public:
1167     /// Value of the type trait.
1168     static const bool value = implementation_defined;
1169 };
1170 
1171 template <typename Key>
1172 const bool key_has_msgpack_convert<Key>::value;
1173 }
1174 
1175 #endif
1176 
1177 #if defined(PIRANHA_WITH_ZLIB)
1178 
1179 #include <boost/iostreams/filter/gzip.hpp>
1180 #include <boost/iostreams/filter/zlib.hpp>
1181 
1182 #define PIRANHA_ZLIB_CONDITIONAL(expr) expr
1183 
1184 #else
1185 
1186 #define PIRANHA_ZLIB_CONDITIONAL(expr) piranha_throw(not_implemented_error, "zlib support is not enabled")
1187 
1188 #endif
1189 
1190 #if defined(PIRANHA_WITH_BZIP2)
1191 
1192 #include <boost/iostreams/filter/bzip2.hpp>
1193 
1194 #define PIRANHA_BZIP2_CONDITIONAL(expr) expr
1195 
1196 #else
1197 
1198 #define PIRANHA_BZIP2_CONDITIONAL(expr) piranha_throw(not_implemented_error, "bzip2 support is not enabled")
1199 
1200 #endif
1201 
1202 namespace piranha
1203 {
1204 
1205 /// Data format.
1206 /**
1207  * Data format used by high-level serialization functions such as piranha::save_file() and piranha::load_file().
1208  * The Boost formats are based on the Boost serialization library, while the msgpack formats are based on the msgpack
1209  * serialization format.
1210  *
1211  * The portable variants are intended to be usable across different architectures and
1212  * Piranha versions, whereas the binary variants are non-portable high-performance serialization formats intended
1213  * for temporary storage. That is, saving a binary archive created with Piranha version \p N on architecture \p A
1214  * and then loading it on a different architecture \p B or using a different Piranha version \p M will result in
1215  * undefined behaviour.
1216  */
1217 enum class data_format {
1218     /// Boost binary.
1219     /**
1220      * This format is based on the Boost binary archives.
1221      */
1222     boost_binary,
1223     /// Boost portable.
1224     /**
1225      * This format is based on the Boost text archives.
1226      */
1227     boost_portable,
1228     /// msgpack binary.
1229     /**
1230      * This format will employ internally the msgpack_format::binary format.
1231      */
1232     msgpack_binary,
1233     /// msgpack portable.
1234     /**
1235      * This format will employ internally the msgpack_format::portable format.
1236      */
1237     msgpack_portable
1238 };
1239 
1240 /// Compression format.
1241 /**
1242  * Compression formats used by high-level serialization functions such as piranha::save_file() and piranha::load_file().
1243  */
1244 enum class compression {
1245     /// No compression.
1246     none,
1247     /// bzip2 compression.
1248     bzip2,
1249     /// gzip compression.
1250     gzip,
1251     /// zlib compression.
1252     zlib
1253 };
1254 
1255 inline namespace impl
1256 {
1257 
1258 // NOTE: no need for ifdefs guards here, as the compression-specific stuff is hidden in the CompressionFilter type.
1259 template <typename CompressionFilter, typename T>
save_file_boost_compress_impl(const T & x,std::ofstream & ofile,data_format f)1260 inline void save_file_boost_compress_impl(const T &x, std::ofstream &ofile, data_format f)
1261 {
1262     piranha_assert(f == data_format::boost_binary || f == data_format::boost_portable);
1263     // NOTE: there seem to be 2 choices here: stream or streambuf. The first does some formatting, while the second
1264     // one is "raw" but does not provide the stream interface (which is used, e.g., by msgpack). Since we always
1265     // open files in binary mode (as suggested by the Boost serialization library), this should not matter in the end.
1266     // Some resources:
1267     // http://stackoverflow.com/questions/1753469/how-to-hook-up-boost-serialization-iostreams-to-serialize-gzip-an-object-to
1268     // https://code.google.com/p/cloudobserver/wiki/TutorialsBoostIOstreams
1269     // http://stackoverflow.com/questions/8116541/what-exactly-is-streambuf-how-do-i-use-it
1270     // http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/special.html
1271     boost::iostreams::filtering_ostream out;
1272     out.push(CompressionFilter{});
1273     out.push(ofile);
1274     if (f == data_format::boost_binary) {
1275         boost::archive::binary_oarchive oa(out);
1276         boost_save(oa, x);
1277     } else {
1278         boost::archive::text_oarchive oa(out);
1279         boost_save(oa, x);
1280     }
1281 }
1282 
1283 // NOTE: the implementation is the specular of the above.
1284 template <typename DecompressionFilter, typename T>
load_file_boost_compress_impl(T & x,std::ifstream & ifile,data_format f)1285 inline void load_file_boost_compress_impl(T &x, std::ifstream &ifile, data_format f)
1286 {
1287     piranha_assert(f == data_format::boost_binary || f == data_format::boost_portable);
1288     boost::iostreams::filtering_istream in;
1289     in.push(DecompressionFilter{});
1290     in.push(ifile);
1291     if (f == data_format::boost_binary) {
1292         boost::archive::binary_iarchive ia(in);
1293         boost_load(ia, x);
1294     } else {
1295         boost::archive::text_iarchive ia(in);
1296         boost_load(ia, x);
1297     }
1298 }
1299 
1300 // Main save/load functions for Boost format.
1301 template <typename T, enable_if_t<conjunction<has_boost_save<boost::archive::binary_oarchive, T>,
1302                                               has_boost_save<boost::archive::text_oarchive, T>>::value,
1303                                   int> = 0>
save_file_boost_impl(const T & x,const std::string & filename,data_format f,compression c)1304 inline void save_file_boost_impl(const T &x, const std::string &filename, data_format f, compression c)
1305 {
1306     namespace bi = boost::iostreams;
1307     // NOTE: always open in binary mode in order to avoid problems with special formatting in streams.
1308     std::ofstream ofile(filename, std::ios::out | std::ios::binary | std::ios::trunc);
1309     if (unlikely(!ofile.good())) {
1310         piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for saving");
1311     }
1312     switch (c) {
1313         case compression::bzip2:
1314             PIRANHA_BZIP2_CONDITIONAL(save_file_boost_compress_impl<bi::bzip2_compressor>(x, ofile, f));
1315             break;
1316         case compression::gzip:
1317             PIRANHA_ZLIB_CONDITIONAL(save_file_boost_compress_impl<bi::gzip_compressor>(x, ofile, f));
1318             break;
1319         case compression::zlib:
1320             PIRANHA_ZLIB_CONDITIONAL(save_file_boost_compress_impl<bi::zlib_compressor>(x, ofile, f));
1321             break;
1322         case compression::none:
1323             if (f == data_format::boost_binary) {
1324                 boost::archive::binary_oarchive oa(ofile);
1325                 boost_save(oa, x);
1326             } else {
1327                 boost::archive::text_oarchive oa(ofile);
1328                 boost_save(oa, x);
1329             }
1330     }
1331 }
1332 
1333 template <typename T, enable_if_t<disjunction<negation<has_boost_save<boost::archive::binary_oarchive, T>>,
1334                                               negation<has_boost_save<boost::archive::text_oarchive, T>>>::value,
1335                                   int> = 0>
save_file_boost_impl(const T &,const std::string &,data_format,compression)1336 inline void save_file_boost_impl(const T &, const std::string &, data_format, compression)
1337 {
1338     piranha_throw(not_implemented_error,
1339                   "type '" + detail::demangle<T>() + "' does not support serialization via Boost");
1340 }
1341 
1342 template <typename T, enable_if_t<conjunction<has_boost_load<boost::archive::binary_iarchive, T>,
1343                                               has_boost_load<boost::archive::text_iarchive, T>>::value,
1344                                   int> = 0>
load_file_boost_impl(T & x,const std::string & filename,data_format f,compression c)1345 inline void load_file_boost_impl(T &x, const std::string &filename, data_format f, compression c)
1346 {
1347     namespace bi = boost::iostreams;
1348     std::ifstream ifile(filename, std::ios::in | std::ios::binary);
1349     if (unlikely(!ifile.good())) {
1350         piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for loading");
1351     }
1352     switch (c) {
1353         case compression::bzip2:
1354             PIRANHA_BZIP2_CONDITIONAL(load_file_boost_compress_impl<bi::bzip2_decompressor>(x, ifile, f));
1355             break;
1356         case compression::gzip:
1357             PIRANHA_ZLIB_CONDITIONAL(load_file_boost_compress_impl<bi::gzip_decompressor>(x, ifile, f));
1358             break;
1359         case compression::zlib:
1360             PIRANHA_ZLIB_CONDITIONAL(load_file_boost_compress_impl<bi::zlib_decompressor>(x, ifile, f));
1361             break;
1362         case compression::none:
1363             if (f == data_format::boost_binary) {
1364                 boost::archive::binary_iarchive ia(ifile);
1365                 boost_load(ia, x);
1366             } else {
1367                 boost::archive::text_iarchive ia(ifile);
1368                 boost_load(ia, x);
1369             }
1370     }
1371 }
1372 
1373 template <typename T, enable_if_t<disjunction<negation<has_boost_load<boost::archive::binary_iarchive, T>>,
1374                                               negation<has_boost_load<boost::archive::text_iarchive, T>>>::value,
1375                                   int> = 0>
load_file_boost_impl(T &,const std::string &,data_format,compression)1376 inline void load_file_boost_impl(T &, const std::string &, data_format, compression)
1377 {
1378     piranha_throw(not_implemented_error,
1379                   "type '" + detail::demangle<T>() + "' does not support deserialization via Boost");
1380 }
1381 
1382 #if defined(PIRANHA_WITH_MSGPACK)
1383 
1384 // Compressed load/save for msgpack.
1385 template <typename CompressionFilter, typename T>
save_file_msgpack_compress_impl(const T & x,msgpack_stream_wrapper<std::ofstream> & ofile,msgpack_format mf)1386 inline void save_file_msgpack_compress_impl(const T &x, msgpack_stream_wrapper<std::ofstream> &ofile, msgpack_format mf)
1387 {
1388     msgpack_stream_wrapper<boost::iostreams::filtering_ostream> out;
1389     out.push(CompressionFilter{});
1390     out.push(ofile);
1391     msgpack::packer<decltype(out)> packer(out);
1392     msgpack_pack(packer, x, mf);
1393 }
1394 
1395 template <typename DecompressionFilter, typename T>
load_file_msgpack_compress_impl(T & x,const std::string & filename,msgpack_format mf)1396 inline void load_file_msgpack_compress_impl(T &x, const std::string &filename, msgpack_format mf)
1397 {
1398     std::ifstream ifile(filename, std::ios::in | std::ios::binary);
1399     if (unlikely(!ifile.good())) {
1400         piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for loading");
1401     }
1402     std::vector<char> vchar;
1403     boost::iostreams::filtering_istream in;
1404     in.push(DecompressionFilter{});
1405     in.push(ifile);
1406     std::copy(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>(), std::back_inserter(vchar));
1407     auto oh = msgpack::unpack(vchar.data(), safe_cast<std::size_t>(vchar.size()));
1408     msgpack_convert(x, oh.get(), mf);
1409 }
1410 
1411 // Main msgpack load/save functions.
1412 template <
1413     typename T,
1414     enable_if_t<conjunction<has_msgpack_pack<msgpack_stream_wrapper<std::ofstream>, T>,
1415                             has_msgpack_pack<msgpack_stream_wrapper<boost::iostreams::filtering_ostream>, T>>::value,
1416                 int> = 0>
save_file_msgpack_impl(const T & x,const std::string & filename,data_format f,compression c)1417 inline void save_file_msgpack_impl(const T &x, const std::string &filename, data_format f, compression c)
1418 {
1419     namespace bi = boost::iostreams;
1420     const auto mf = (f == data_format::msgpack_binary) ? msgpack_format::binary : msgpack_format::portable;
1421     msgpack_stream_wrapper<std::ofstream> ofile(filename, std::ios::out | std::ios::binary | std::ios::trunc);
1422     if (unlikely(!ofile.good())) {
1423         piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for saving");
1424     }
1425     switch (c) {
1426         case compression::bzip2:
1427             PIRANHA_BZIP2_CONDITIONAL(save_file_msgpack_compress_impl<bi::bzip2_compressor>(x, ofile, mf));
1428             break;
1429         case compression::gzip:
1430             PIRANHA_ZLIB_CONDITIONAL(save_file_msgpack_compress_impl<bi::gzip_compressor>(x, ofile, mf));
1431             break;
1432         case compression::zlib:
1433             PIRANHA_ZLIB_CONDITIONAL(save_file_msgpack_compress_impl<bi::zlib_compressor>(x, ofile, mf));
1434             break;
1435         case compression::none: {
1436             msgpack::packer<decltype(ofile)> packer(ofile);
1437             msgpack_pack(packer, x, mf);
1438         }
1439     }
1440 }
1441 
1442 template <typename T,
1443           enable_if_t<disjunction<negation<has_msgpack_pack<msgpack_stream_wrapper<std::ofstream>, T>>,
1444                                   negation<has_msgpack_pack<msgpack_stream_wrapper<boost::iostreams::filtering_ostream>,
1445                                                             T>>>::value,
1446                       int> = 0>
save_file_msgpack_impl(const T &,const std::string &,data_format,compression)1447 inline void save_file_msgpack_impl(const T &, const std::string &, data_format, compression)
1448 {
1449     piranha_throw(not_implemented_error,
1450                   "type '" + detail::demangle<T>() + "' does not support serialization via msgpack");
1451 }
1452 
1453 template <typename T, enable_if_t<has_msgpack_convert<T>::value, int> = 0>
load_file_msgpack_impl(T & x,const std::string & filename,data_format f,compression c)1454 inline void load_file_msgpack_impl(T &x, const std::string &filename, data_format f, compression c)
1455 {
1456     namespace bi = boost::iostreams;
1457     const auto mf = (f == data_format::msgpack_binary) ? msgpack_format::binary : msgpack_format::portable;
1458     switch (c) {
1459         case compression::bzip2:
1460             PIRANHA_BZIP2_CONDITIONAL(load_file_msgpack_compress_impl<bi::bzip2_decompressor>(x, filename, mf));
1461             break;
1462         case compression::gzip:
1463             PIRANHA_ZLIB_CONDITIONAL(load_file_msgpack_compress_impl<bi::gzip_decompressor>(x, filename, mf));
1464             break;
1465         case compression::zlib:
1466             PIRANHA_ZLIB_CONDITIONAL(load_file_msgpack_compress_impl<bi::zlib_decompressor>(x, filename, mf));
1467             break;
1468         case compression::none: {
1469             // NOTE: two-stage construction for exception handling.
1470             std::unique_ptr<bi::mapped_file> mmap;
1471             try {
1472                 mmap.reset(new bi::mapped_file(filename, bi::mapped_file::readonly));
1473             } catch (...) {
1474                 // NOTE: this is just to beautify a bit the error message, and we assume any error in the line
1475                 // above results from being unable to open the file.
1476                 piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for loading");
1477             }
1478             // NOTE: this might be superfluous, but better safe than sorry.
1479             if (unlikely(!mmap->is_open())) {
1480                 piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for loading");
1481             }
1482             auto oh = msgpack::unpack(mmap->const_data(), safe_cast<std::size_t>(mmap->size()));
1483             msgpack_convert(x, oh.get(), mf);
1484         }
1485     }
1486 }
1487 
1488 template <typename T, enable_if_t<!has_msgpack_convert<T>::value, int> = 0>
load_file_msgpack_impl(T &,const std::string &,data_format,compression)1489 inline void load_file_msgpack_impl(T &, const std::string &, data_format, compression)
1490 {
1491     piranha_throw(not_implemented_error,
1492                   "type '" + detail::demangle<T>() + "' does not support deserialization via msgpack");
1493 }
1494 
1495 #else
1496 
1497 // If msgpack is not available, just error out.
1498 template <typename T>
save_file_msgpack_impl(const T &,const std::string &,data_format,compression)1499 inline void save_file_msgpack_impl(const T &, const std::string &, data_format, compression)
1500 {
1501     piranha_throw(not_implemented_error, "msgpack support is not enabled");
1502 }
1503 
1504 template <typename T>
load_file_msgpack_impl(T &,const std::string &,data_format,compression)1505 inline void load_file_msgpack_impl(T &, const std::string &, data_format, compression)
1506 {
1507     piranha_throw(not_implemented_error, "msgpack support is not enabled");
1508 }
1509 
1510 #endif
1511 
1512 // General enabler for load_file().
1513 template <typename T>
1514 using load_file_enabler = enable_if_t<!std::is_const<T>::value, int>;
1515 
1516 // Utility to deduce compression and data format from a filename.
get_cdf_from_filename(std::string filename)1517 inline std::pair<compression, data_format> get_cdf_from_filename(std::string filename)
1518 {
1519     const auto orig_fname = filename;
1520     compression c = compression::none;
1521     if (boost::ends_with(filename, ".bz2")) {
1522         c = compression::bzip2;
1523         filename.erase(filename.end() - 4, filename.end());
1524     } else if (boost::ends_with(filename, ".gz")) {
1525         c = compression::gzip;
1526         filename.erase(filename.end() - 3, filename.end());
1527     } else if (boost::ends_with(filename, ".zip")) {
1528         c = compression::zlib;
1529         filename.erase(filename.end() - 4, filename.end());
1530     }
1531     data_format f;
1532     if (boost::ends_with(filename, ".boostb")) {
1533         f = data_format::boost_binary;
1534     } else if (boost::ends_with(filename, ".boostp")) {
1535         f = data_format::boost_portable;
1536     } else if (boost::ends_with(filename, ".mpackb")) {
1537         f = data_format::msgpack_binary;
1538     } else if (boost::ends_with(filename, ".mpackp")) {
1539         f = data_format::msgpack_portable;
1540     } else {
1541         piranha_throw(std::invalid_argument,
1542                       "unable to deduce the data format from the filename '" + orig_fname
1543                           + "'. The filename must end with one of ['.boostb','.boostp','.mpackb','.mpackp'], "
1544                             "optionally followed by one of ['.bz2','gz','zip'].");
1545     }
1546     return std::make_pair(c, f);
1547 }
1548 }
1549 
1550 /// Save to file.
1551 /**
1552  * This function will save the generic object \p x to the file named \p filename, using the data format
1553  * \p f and the compression method \p c.
1554  *
1555  * This function is built on lower-level routines such as piranha::boost_save() and piranha::msgpack_pack(). The data
1556  * format \p f establishes both the lower level serialization method to be used and its variant (e.g., portable
1557  * vs binary). If requested (i.e., if \p c is not piranha::compression::none), the output file will be compressed.
1558  *
1559  * @param x object to be saved to file.
1560  * @param filename name of the output file.
1561  * @param f data format.
1562  * @param c compression format.
1563  *
1564  * @throws piranha::not_implemented_error in the following cases:
1565  * - the type \p T does not implement the required serialization method (e.g., \p f is
1566  *   piranha::data_format::boost_binary but \p T does not provide an implementation of piranha::boost_save()),
1567  * - a necessary optional third-party library (e.g., msgpack or one of the compression libraries)
1568  *   is not available on the host platform.
1569  * @throws std::runtime_error in case the file cannot be opened for writing.
1570  * @throws unspecified any exception thrown by:
1571  * - piranha::safe_cast(),
1572  * - the invoked low-level serialization function,
1573  * - the public interface of the Boost iostreams library.
1574  */
1575 template <typename T>
save_file(const T & x,const std::string & filename,data_format f,compression c)1576 inline void save_file(const T &x, const std::string &filename, data_format f, compression c)
1577 {
1578     if (f == data_format::boost_binary || f == data_format::boost_portable) {
1579         save_file_boost_impl(x, filename, f, c);
1580     } else if (f == data_format::msgpack_binary || f == data_format::msgpack_portable) {
1581         save_file_msgpack_impl(x, filename, f, c);
1582     }
1583 }
1584 
1585 /// Save to file.
1586 /**
1587  * This is a convenience function that will invoke the other overload of piranha::save_file() trying to guess
1588  * the data and compression formats from the filename. The heuristic is as follows:
1589  * - if \p filename ends in one of the suffixes <tt>.bz2</tt>, <tt>.gz</tt> or <tt>.zip</tt> then the suffix is removed
1590  *   for further considerations from \p filename, and the corresponding
1591  *   piranha::compression format is assumed (respectively, piranha::compression::bzip2, piranha::compression::gzip
1592  *   and piranha::compression::zlib). Otherwise, piranha::compression::none is assumed;
1593  * - after the removal of any compression suffix, the extension of \p filename is examined again: if the extension is
1594  *   one of <tt>.boostp</tt>, <tt>.boostb</tt>, <tt>.mpackp</tt> and <tt>.mpackb</tt>, then the corresponding data
1595  *   format is selected (respectively, piranha::data_format::boost_portable, piranha::data_format::boost_binary,
1596  *   piranha::data_format::msgpack_portable, piranha::data_format::msgpack_binary). Othwewise, an error will be
1597  *   produced.
1598  *
1599  * Examples:
1600  * - <tt>foo.boostb.bz2</tt> deduces piranha::data_format::boost_binary and piranha::compression::bzip2;
1601  * - <tt>foo.mpackp</tt> deduces piranha::data_format::msgpack_portable and piranha::compression::none;
1602  * - <tt>foo.txt</tt> produces an error;
1603  * - <tt>foo.bz2</tt> produces an error.
1604  *
1605  * @param x the object that will be saved to file.
1606  * @param filename the desired file name.
1607  *
1608  * @throws std::invalid_argument if the compression and data formats cannot be deduced.
1609  * @throws unspecified any exception throw by the first overload of piranha::save_file().
1610  */
1611 template <typename T>
save_file(const T & x,const std::string & filename)1612 inline void save_file(const T &x, const std::string &filename)
1613 {
1614     const auto p = get_cdf_from_filename(filename);
1615     save_file(x, filename, p.second, p.first);
1616 }
1617 
1618 /// Load from file.
1619 /**
1620  * \note
1621  * This function is enabled only if \p T is not const.
1622  *
1623  * This function will load the content of the file named \p filename into the object \p x, assuming that the data is
1624  * stored in the format \p f using the compression method \p c. If \p c is not piranha::compression::none, it will
1625  * be assumed that the file is compressed.
1626  *
1627  * This function is built on lower-level routines such as piranha::boost_load() and piranha::msgpack_convert(). The data
1628  * format \p f establishes both the lower level serialization method to be used and its variant (e.g., portable
1629  * vs binary).
1630  *
1631  * @param x the object into which the content of the file name \p filename will be deserialized.
1632  * @param filename name of the input file.
1633  * @param f data format.
1634  * @param c compression format.
1635  *
1636  * @throws piranha::not_implemented_error in the following cases:
1637  * - the type \p T does not implement the required serialization method (e.g., \p f is
1638  *   piranha::data_format::boost_binary but \p T does not provide an implementation of piranha::boost_load()),
1639  * - a necessary optional third-party library (e.g., msgpack or one of the compression libraries)
1640  *   is not available on the host platform.
1641  * @throws std::runtime_error in case the file cannot be opened for reading.
1642  * @throws unspecified any exception thrown by:
1643  * - piranha::safe_cast(),
1644  * - the invoked low-level serialization function,
1645  * - the \p new operator,
1646  * - the public interface of the Boost iostreams library.
1647  */
1648 template <typename T, load_file_enabler<T> = 0>
load_file(T & x,const std::string & filename,data_format f,compression c)1649 inline void load_file(T &x, const std::string &filename, data_format f, compression c)
1650 {
1651     if (f == data_format::boost_binary || f == data_format::boost_portable) {
1652         load_file_boost_impl(x, filename, f, c);
1653     } else if (f == data_format::msgpack_binary || f == data_format::msgpack_portable) {
1654         load_file_msgpack_impl(x, filename, f, c);
1655     }
1656 }
1657 
1658 /// Load from file .
1659 /**
1660  * \note
1661  * This function is enabled only if \p T is not const.
1662  *
1663  * This is a convenience function that will invoke the other overload of piranha::load_file() trying to guess
1664  * the data and compression formats from the filename. The heuristic is described in the second overload of
1665  * piranha::save_file().
1666  *
1667  * @param x the object into which the file content will be loaded.
1668  * @param filename source file name.
1669  *
1670  * @throws std::invalid_argument if the compression and data formats cannot be deduced.
1671  * @throws unspecified any exception throw by the first overload of piranha::load_file().
1672  */
1673 template <typename T, load_file_enabler<T> = 0>
load_file(T & x,const std::string & filename)1674 inline void load_file(T &x, const std::string &filename)
1675 {
1676     const auto p = get_cdf_from_filename(filename);
1677     load_file(x, filename, p.second, p.first);
1678 }
1679 
1680 inline namespace impl
1681 {
1682 
1683 #if defined(PIRANHA_WITH_MSGPACK)
1684 
1685 // These typedefs are useful when checking the availability of boost save/load member functions, which
1686 // we use fairly often to implement the _impl functors.
1687 template <typename Stream, typename T>
1688 using msgpack_pack_member_t = decltype(
1689     std::declval<const T &>().msgpack_pack(std::declval<msgpack::packer<Stream> &>(), std::declval<msgpack_format>()));
1690 
1691 template <typename T>
1692 using msgpack_convert_member_t = decltype(
1693     std::declval<T &>().msgpack_convert(std::declval<const msgpack::object &>(), std::declval<msgpack_format>()));
1694 
1695 #endif
1696 
1697 // Utility functions to serialize ranges and vector-like types.
1698 template <typename Archive, typename It>
boost_save_range(Archive & ar,It begin,It end)1699 inline void boost_save_range(Archive &ar, It begin, It end)
1700 {
1701     for (; begin != end; ++begin) {
1702         boost_save(ar, *begin);
1703     }
1704 }
1705 
1706 template <typename Archive, typename V>
boost_save_vector(Archive & ar,const V & v)1707 inline void boost_save_vector(Archive &ar, const V &v)
1708 {
1709     boost_save(ar, v.size());
1710     boost_save_range(ar, v.begin(), v.end());
1711 }
1712 
1713 template <typename Archive, typename It>
boost_load_range(Archive & ar,It begin,It end)1714 inline void boost_load_range(Archive &ar, It begin, It end)
1715 {
1716     for (; begin != end; ++begin) {
1717         boost_load(ar, *begin);
1718     }
1719 }
1720 
1721 template <typename Archive, typename V>
boost_load_vector(Archive & ar,V & v)1722 inline void boost_load_vector(Archive &ar, V &v)
1723 {
1724     typename V::size_type size;
1725     boost_load(ar, size);
1726     v.resize(size);
1727     boost_load_range(ar, v.begin(), v.end());
1728 }
1729 
1730 // Introduce also enablers to detect when we can use the vector save/load functions.
1731 template <typename Archive, typename V, typename T = void>
1732 using boost_save_vector_enabler = enable_if_t<conjunction<has_boost_save<Archive, typename V::value_type>,
1733                                                           has_boost_save<Archive, typename V::size_type>>::value,
1734                                               T>;
1735 
1736 template <typename Archive, typename V, typename T = void>
1737 using boost_load_vector_enabler = enable_if_t<conjunction<has_boost_load<Archive, typename V::value_type>,
1738                                                           has_boost_load<Archive, typename V::size_type>>::value,
1739                                               T>;
1740 
1741 #if defined(PIRANHA_WITH_MSGPACK)
1742 
1743 template <typename Stream, typename It, typename Size>
msgpack_pack_range(msgpack::packer<Stream> & p,It begin,It end,Size s,msgpack_format f)1744 inline void msgpack_pack_range(msgpack::packer<Stream> &p, It begin, It end, Size s, msgpack_format f)
1745 {
1746     p.pack_array(safe_cast<std::uint32_t>(s));
1747     for (; begin != end; ++begin) {
1748         msgpack_pack(p, *begin, f);
1749     }
1750 }
1751 
1752 template <typename Stream, typename V>
msgpack_pack_vector(msgpack::packer<Stream> & p,const V & v,msgpack_format f)1753 inline void msgpack_pack_vector(msgpack::packer<Stream> &p, const V &v, msgpack_format f)
1754 {
1755     msgpack_pack_range(p, v.begin(), v.end(), v.size(), f);
1756 }
1757 
1758 // Convert the msgpack array in o to a vector of type V.
1759 template <typename V>
msgpack_convert_array(const msgpack::object & o,V & v,msgpack_format f)1760 inline void msgpack_convert_array(const msgpack::object &o, V &v, msgpack_format f)
1761 {
1762     // First extract a vector of objects from o.
1763     PIRANHA_MAYBE_TLS std::vector<msgpack::object> tmp_obj;
1764     o.convert(tmp_obj);
1765     v.resize(safe_cast<decltype(v.size())>(tmp_obj.size()));
1766     for (decltype(v.size()) i = 0; i < v.size(); ++i) {
1767         piranha::msgpack_convert(v[i], tmp_obj[static_cast<decltype(v.size())>(i)], f);
1768     }
1769 }
1770 
1771 template <typename Stream, typename V, typename T = void>
1772 using msgpack_pack_vector_enabler
1773     = enable_if_t<conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, typename V::value_type>,
1774                               has_safe_cast<std::uint32_t, typename V::size_type>>::value,
1775                   T>;
1776 
1777 template <typename V, typename T = void>
1778 using msgpack_convert_array_enabler
1779     = enable_if_t<conjunction<has_safe_cast<typename V::size_type, typename std::vector<msgpack::object>::size_type>,
1780                               has_msgpack_convert<typename V::value_type>>::value,
1781                   T>;
1782 
1783 #endif
1784 }
1785 }
1786 
1787 #undef PIRANHA_ZLIB_CONDITIONAL
1788 
1789 #endif
1790