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 #include "../src/s11n.hpp"
30 
31 #define BOOST_TEST_MODULE s11n_test
32 #include <boost/test/included/unit_test.hpp>
33 
34 #include <algorithm>
35 #include <array>
36 #include <atomic>
37 #include <boost/algorithm/string/predicate.hpp>
38 #include <boost/filesystem.hpp>
39 #include <boost/fusion/algorithm.hpp>
40 #include <boost/fusion/include/algorithm.hpp>
41 #include <boost/fusion/include/sequence.hpp>
42 #include <boost/fusion/sequence.hpp>
43 #include <boost/mpl/bool.hpp>
44 #include <boost/version.hpp>
45 #include <cstddef>
46 #include <cstdio>
47 #include <functional>
48 #include <initializer_list>
49 #include <limits>
50 #include <random>
51 #include <sstream>
52 #include <stdexcept>
53 #include <string>
54 #include <thread>
55 #include <type_traits>
56 #include <utility>
57 #include <vector>
58 
59 #include "../src/config.hpp"
60 #include "../src/detail/demangle.hpp"
61 #include "../src/exceptions.hpp"
62 #include "../src/init.hpp"
63 #include "../src/is_key.hpp"
64 #include "../src/symbol_set.hpp"
65 
66 using namespace piranha;
67 
68 namespace bfs = boost::filesystem;
69 
70 // Small raii class for creating a tmp file.
71 // NOTE: this will not actually create the file, it will just create
72 // a tmp file name - so one is supposed to use the m_path member to create a file
73 // in the usual way. The destructor will attempt to delete the file at m_path, nothing
74 // will happen if the file does not exist.
75 struct tmp_file {
76     tmp_file()
Mutex()77     {
78         m_path = bfs::temp_directory_path();
79         // Concatenate with a unique filename.
80         m_path /= bfs::unique_path();
81     }
82     ~tmp_file()
83     {
84         bfs::remove(m_path);
85     }
86     std::string name() const
87     {
88         return m_path.string();
89     }
90     bfs::path m_path;
MutexLock(Mutex & m)91 };
RELEASE()92 
93 static const int ntrials = 1000;
94 
95 using integral_types = boost::mpl::vector<char, signed char, short, int, long, long long, unsigned char, unsigned short,
96                                           unsigned, unsigned long, unsigned long long>;
97 
98 using fp_types = boost::mpl::vector<float, double, long double>;
99 
100 // Helper function to roundtrip the the (de)serialization of type T via boost serialization.
Barrier(int num_threads)101 template <typename T>
102 static inline T boost_roundtrip(const T &x)
103 {
104     std::ostringstream oss;
105     {
106         boost::archive::text_oarchive oa(oss);
107         boost_save(oa, x);
108     }
109     T retval;
110     std::istringstream iss;
111     iss.str(oss.str());
112     {
113         boost::archive::text_iarchive ia(iss);
114         boost_load(ia, retval);
115     }
116     return retval;
117 }
118 
119 struct unserial {
120 };
121 
122 // A good saving archive.
123 struct sa0 {
124     using self_t = sa0;
125     using is_loading = boost::mpl::bool_<false>;
126     using is_saving = boost::mpl::bool_<true>;
127     // Disable serialization for struct unserial.
128     template <typename T, typename std::enable_if<!std::is_same<T, unserial>::value, int>::type = 0>
129     self_t &operator<<(const T &);
130     template <typename T>
131     self_t &operator&(const T &);
createBarrier(MutexLock & ml)132     template <typename T>
133     void save_binary(T *, std::size_t);
134     template <typename T>
135     void register_type();
136     unsigned get_library_version() const;
137     template <typename Helper>
138     void get_helper(void *const = nullptr) const;
139 };
140 
141 // Missing methods.
142 struct sa1 {
143     using self_t = sa1;
144     using is_loading = boost::mpl::bool_<false>;
145     using is_saving = boost::mpl::bool_<true>;
146     template <typename T>
147     self_t &operator<<(const T &);
148     template <typename T>
149     self_t &operator&(const T &);
150     template <typename T>
151     void save_binary(T *, std::size_t);
152     template <typename T>
153     void register_type();
154     unsigned get_library_version() const;
155     // template <typename Helper>
156     // void get_helper(void * const = nullptr) const;
157 };
158 
159 struct sa2 {
160     using self_t = sa2;
161     // using is_loading = boost::mpl::bool_<false>;
162     using is_saving = boost::mpl::bool_<true>;
163     template <typename T>
164     self_t &operator<<(const T &);
165     template <typename T>
166     self_t &operator&(const T &);
167     template <typename T>
168     void save_binary(T *, std::size_t);
169     template <typename T>
170     void register_type();
171     unsigned get_library_version() const;
172     template <typename Helper>
173     void get_helper(void *const = nullptr) const;
174 };
175 
176 struct sa3 {
177     using self_t = sa3;
178     using is_loading = boost::mpl::bool_<true>;
179     using is_saving = boost::mpl::bool_<true>;
180     template <typename T>
181     self_t &operator<<(const T &);
182     template <typename T>
183     self_t &operator&(const T &);
184     template <typename T>
185     void save_binary(T *, std::size_t);
186     template <typename T>
187     void register_type();
188     unsigned get_library_version() const;
189     template <typename Helper>
190     void get_helper(void *const = nullptr) const;
191 };
192 
193 struct sa4 {
194     using self_t = sa4;
195     using is_loading = boost::mpl::bool_<false>;
196     using is_saving = boost::mpl::bool_<true>;
197     template <typename T>
198     self_t &operator<<(const T &);
199     template <typename T>
200     self_t &operator&(const T &);
201     template <typename T>
202     void save_binary(T *, std::size_t);
203     template <typename T>
204     void register_type();
205     unsigned get_library_version();
206     template <typename Helper>
207     void get_helper(void *const = nullptr) const;
208 };
209 
210 // A good loading archive.
211 struct la0 {
212     using self_t = la0;
213     using is_loading = boost::mpl::bool_<true>;
214     using is_saving = boost::mpl::bool_<false>;
215     // Disable serialization for struct unserial.
216     template <typename T, typename std::enable_if<!std::is_same<T, unserial>::value, int>::type = 0>
217     self_t &operator>>(T &);
218     template <typename T>
219     self_t &operator&(T &);
220     template <typename T>
221     void load_binary(T *, std::size_t);
222     template <typename T>
223     void register_type();
224     unsigned get_library_version() const;
225     template <typename Helper>
226     void get_helper(void *const = nullptr) const;
227     template <typename T>
228     void reset_object_address(T *, T *);
229     void delete_created_pointers();
230 };
231 
232 struct la1 {
233     using self_t = la1;
234     using is_loading = boost::mpl::bool_<true>;
235     using is_saving = boost::mpl::bool_<false>;
236     // template <typename T, typename std::enable_if<!std::is_same<T,unserial>::value,int>::type = 0>
237     // self_t &operator>>(T &);
238     template <typename T>
239     self_t &operator&(T &);
240     template <typename T>
241     void load_binary(T *, std::size_t);
242     template <typename T>
243     void register_type();
244     unsigned get_library_version() const;
245     template <typename Helper>
246     void get_helper(void *const = nullptr) const;
247     template <typename T>
248     void reset_object_address(T *, T *);
249     void delete_created_pointers();
250 };
251 
252 struct la2 {
253     using self_t = la2;
254     using is_loading = boost::mpl::bool_<true>;
255     using is_saving = boost::mpl::bool_<false>;
256     template <typename T, typename std::enable_if<!std::is_same<T, unserial>::value, int>::type = 0>
257     self_t &operator>>(T &);
258     template <typename T>
259     void operator&(T &);
260     template <typename T>
261     void load_binary(T *, std::size_t);
262     template <typename T>
263     void register_type();
264     unsigned get_library_version() const;
265     template <typename Helper>
266     void get_helper(void *const = nullptr) const;
267     template <typename T>
268     void reset_object_address(T *, T *);
269     void delete_created_pointers();
270 };
271 
272 struct la3 {
273     using self_t = la3;
274     using is_loading = boost::mpl::bool_<true>;
275     using is_saving = boost::mpl::bool_<false>;
276     template <typename T, typename std::enable_if<!std::is_same<T, unserial>::value, int>::type = 0>
277     self_t &operator>>(T &);
278     template <typename T>
279     self_t &operator&(T &);
280     template <typename T>
281     void load_binary(T *, std::size_t);
282     // template <typename T>
283     // void register_type();
284     unsigned get_library_version() const;
285     template <typename Helper>
286     void get_helper(void *const = nullptr) const;
287     template <typename T>
288     void reset_object_address(T *, T *);
289     void delete_created_pointers();
290 };
291 
292 struct la4 {
293     using self_t = la4;
294     using is_loading = boost::mpl::bool_<true>;
295     using is_saving = boost::mpl::bool_<false>;
296     template <typename T, typename std::enable_if<!std::is_same<T, unserial>::value, int>::type = 0>
297     self_t &operator>>(T &);
298     template <typename T>
299     self_t &operator&(T &);
300     template <typename T>
301     void load_binary(T *, std::size_t);
302     template <typename T>
303     void register_type();
304     unsigned get_library_version() const;
305     template <typename Helper>
306     void get_helper(void *const = nullptr) const;
307     template <typename T>
308     void reset_object_address(T *, T *);
309     // void delete_created_pointers();
310 };
311 
312 struct la5 {
313     using self_t = la5;
314     using is_loading = boost::mpl::bool_<true>;
315     using is_saving = boost::mpl::bool_<false>;
316     template <typename T, typename std::enable_if<!std::is_same<T, unserial>::value, int>::type = 0>
317     self_t &operator>>(T &);
318     template <typename T>
319     self_t &operator&(T &);
320     template <typename T>
321     void load_binary(T *, std::size_t);
322     template <typename T>
323     void register_type();
324     unsigned get_library_version() const;
325     template <typename Helper>
326     void get_helper(void *const = nullptr) const;
327     // template <typename T>
328     // void reset_object_address(T *, T *);
329     void delete_created_pointers();
330 };
331 
332 // A key with Boost s11n support.
333 struct keya {
334     keya() = default;
335     keya(const keya &) = default;
336     keya(keya &&) noexcept;
337     keya &operator=(const keya &) = default;
338     keya &operator=(keya &&) noexcept;
339     keya(const symbol_set &);
340     bool operator==(const keya &) const;
341     bool operator!=(const keya &) const;
342     bool is_compatible(const symbol_set &) const noexcept;
343     bool is_ignorable(const symbol_set &) const noexcept;
344     keya merge_args(const symbol_set &, const symbol_set &) const;
345     bool is_unitary(const symbol_set &) const;
346     void print(std::ostream &, const symbol_set &) const;
347     void print_tex(std::ostream &, const symbol_set &) const;
348     template <typename T, typename U>
349     std::vector<std::pair<std::string, keya>> t_subs(const std::string &, const T &, const U &,
350                                                      const symbol_set &) const;
351     void trim_identify(symbol_set &, const symbol_set &) const;
352     keya trim(const symbol_set &, const symbol_set &) const;
353 };
354 
355 // A key without Boost ser support.
356 struct keyb {
357     keyb() = default;
358     keyb(const keyb &) = default;
359     keyb(keyb &&) noexcept;
360     keyb &operator=(const keyb &) = default;
361     keyb &operator=(keyb &&) noexcept;
362     keyb(const symbol_set &);
363     bool operator==(const keyb &) const;
364     bool operator!=(const keyb &) const;
365     bool is_compatible(const symbol_set &) const noexcept;
366     bool is_ignorable(const symbol_set &) const noexcept;
367     keyb merge_args(const symbol_set &, const symbol_set &) const;
368     bool is_unitary(const symbol_set &) const;
369     void print(std::ostream &, const symbol_set &) const;
370     void print_tex(std::ostream &, const symbol_set &) const;
371     template <typename T, typename U>
372     std::vector<std::pair<std::string, keyb>> t_subs(const std::string &, const T &, const U &,
373                                                      const symbol_set &) const;
374     void trim_identify(symbol_set &, const symbol_set &) const;
375     keyb trim(const symbol_set &, const symbol_set &) const;
376 };
377 
378 namespace piranha
379 {
380 
381 template <typename Archive>
382 struct boost_save_impl<Archive, boost_s11n_key_wrapper<keya>> {
383     void operator()(Archive &, const boost_s11n_key_wrapper<keya> &) const;
384 };
385 
386 template <typename Archive>
387 struct boost_load_impl<Archive, boost_s11n_key_wrapper<keya>> {
388     void operator()(Archive &, boost_s11n_key_wrapper<keya> &) const;
389 };
390 }
391 
392 namespace std
393 {
394 
395 template <>
396 struct hash<keya> {
397     std::size_t operator()(const keya &) const;
398 };
399 
400 template <>
401 struct hash<keyb> {
402     std::size_t operator()(const keyb &) const;
403 };
404 }
405 
406 BOOST_AUTO_TEST_CASE(s11n_test_boost_tt)
407 {
408     init();
409     // Saving archive.
410     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive, int>::value));
411     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive, std::string>::value));
412     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive, int *>::value));
413     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive, int const *>::value));
414     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive, int &&>::value));
415     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive, const int &>::value));
416     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive &, int>::value));
417     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive &, int &>::value));
418     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive &, const int &>::value));
419     BOOST_CHECK((!is_boost_saving_archive<const boost::archive::binary_oarchive &, int>::value));
420     BOOST_CHECK((!is_boost_saving_archive<const boost::archive::binary_oarchive, int>::value));
421     BOOST_CHECK((is_boost_saving_archive<boost::archive::binary_oarchive &&, int>::value));
422     BOOST_CHECK((is_boost_saving_archive<boost::archive::text_oarchive, int>::value));
423     BOOST_CHECK((is_boost_saving_archive<boost::archive::text_oarchive &, int>::value));
424     BOOST_CHECK((!is_boost_saving_archive<const boost::archive::text_oarchive &, int>::value));
425     BOOST_CHECK((is_boost_saving_archive<boost::archive::text_oarchive &&, int>::value));
426     BOOST_CHECK((!is_boost_saving_archive<boost::archive::binary_oarchive, void>::value));
427     BOOST_CHECK((!is_boost_saving_archive<void, void>::value));
428     BOOST_CHECK((!is_boost_saving_archive<void, int>::value));
429     // Loading archive.
430     BOOST_CHECK((is_boost_loading_archive<boost::archive::binary_iarchive, int>::value));
431     BOOST_CHECK((is_boost_loading_archive<boost::archive::binary_iarchive, std::string>::value));
432     BOOST_CHECK((is_boost_loading_archive<boost::archive::binary_iarchive, int *>::value));
433     BOOST_CHECK((is_boost_loading_archive<boost::archive::binary_iarchive, int &&>::value));
434     BOOST_CHECK((is_boost_loading_archive<boost::archive::binary_iarchive &&, int>::value));
435     BOOST_CHECK((is_boost_loading_archive<boost::archive::binary_iarchive &, int &>::value));
436     BOOST_CHECK((is_boost_loading_archive<boost::archive::binary_iarchive &, int>::value));
437     BOOST_CHECK((is_boost_loading_archive<boost::archive::binary_iarchive &, int &>::value));
438     BOOST_CHECK((!is_boost_loading_archive<const boost::archive::binary_iarchive &, int &>::value));
439     BOOST_CHECK((!is_boost_loading_archive<boost::archive::binary_iarchive &, const int &>::value));
440     BOOST_CHECK((!is_boost_loading_archive<boost::archive::binary_iarchive, const int &>::value));
441     BOOST_CHECK((!is_boost_loading_archive<const boost::archive::binary_iarchive &, int>::value));
442     BOOST_CHECK((is_boost_loading_archive<boost::archive::binary_iarchive &&, int>::value));
443     BOOST_CHECK((is_boost_loading_archive<boost::archive::text_iarchive, int>::value));
444     BOOST_CHECK((is_boost_loading_archive<boost::archive::text_iarchive &, int>::value));
445     BOOST_CHECK((!is_boost_loading_archive<const boost::archive::text_iarchive &, int>::value));
446     BOOST_CHECK((is_boost_loading_archive<boost::archive::text_iarchive &&, int>::value));
447     BOOST_CHECK((!is_boost_loading_archive<boost::archive::binary_iarchive, void>::value));
448     BOOST_CHECK((!is_boost_loading_archive<void, void>::value));
449     BOOST_CHECK((!is_boost_loading_archive<void, int>::value));
450     // Test custom archives.
451     BOOST_CHECK((is_boost_saving_archive<sa0, int>::value));
452     BOOST_CHECK((!is_boost_saving_archive<sa0, unserial>::value));
453 #if BOOST_VERSION >= 105700
454     BOOST_CHECK((!is_boost_saving_archive<sa1, int>::value));
455 #endif
456     BOOST_CHECK((!is_boost_saving_archive<sa2, int>::value));
457     BOOST_CHECK((!is_boost_saving_archive<sa3, int>::value));
458     BOOST_CHECK((!is_boost_saving_archive<sa4, int>::value));
459     BOOST_CHECK((is_boost_loading_archive<la0, int>::value));
460     BOOST_CHECK((!is_boost_loading_archive<la0, unserial>::value));
461     BOOST_CHECK((!is_boost_loading_archive<la1, int>::value));
462     BOOST_CHECK((!is_boost_loading_archive<la2, int>::value));
463     BOOST_CHECK((!is_boost_loading_archive<la3, int>::value));
464     BOOST_CHECK((!is_boost_loading_archive<la4, int>::value));
465     BOOST_CHECK((!is_boost_loading_archive<la5, int>::value));
466     // Serialization funcs type traits.
467     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, int>::value));
468     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, long double>::value));
469     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, int &>::value));
470     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, const int &>::value));
471     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, const int &>::value));
472     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &&, const int &>::value));
473     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &&, std::string>::value));
474     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &&, std::string &>::value));
475     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &&, std::string const &>::value));
476     BOOST_CHECK((!has_boost_save<boost::archive::binary_oarchive const &, const int &>::value));
477     BOOST_CHECK((!has_boost_save<boost::archive::binary_oarchive const &, std::string>::value));
478     BOOST_CHECK((!has_boost_save<boost::archive::binary_oarchive const, const int &>::value));
479     BOOST_CHECK((!has_boost_save<boost::archive::binary_oarchive, wchar_t>::value));
480     BOOST_CHECK((!has_boost_save<boost::archive::binary_iarchive, int>::value));
481     BOOST_CHECK((!has_boost_save<boost::archive::binary_iarchive, void>::value));
482     BOOST_CHECK((!has_boost_save<void, void>::value));
483     BOOST_CHECK((!has_boost_save<void, int>::value));
484     BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, int>::value));
485     BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, long double>::value));
486     BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, int &>::value));
487     BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, std::string>::value));
488     BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, std::string &>::value));
489     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive, const std::string>::value));
490     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive, const std::string &>::value));
491     BOOST_CHECK((!has_boost_load<const boost::archive::binary_iarchive, int &>::value));
492     BOOST_CHECK((!has_boost_load<const boost::archive::binary_iarchive &, int &>::value));
493     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive, const int &>::value));
494     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive &, const int &>::value));
495     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive &&, const int &>::value));
496     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive const &, const int &>::value));
497     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive const, const int &>::value));
498     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive, wchar_t>::value));
499     BOOST_CHECK((!has_boost_load<boost::archive::binary_oarchive, int>::value));
500     BOOST_CHECK((!has_boost_load<boost::archive::binary_oarchive, void>::value));
501     BOOST_CHECK((!has_boost_load<void, void>::value));
502     BOOST_CHECK((!has_boost_load<void, int>::value));
503     // Key type traits.
504     BOOST_CHECK(is_key<keya>::value);
505     BOOST_CHECK(is_key<keyb>::value);
506     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, boost_s11n_key_wrapper<keya>>::value));
507     BOOST_CHECK((!has_boost_save<boost::archive::binary_oarchive, boost_s11n_key_wrapper<keyb>>::value));
508     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, boost_s11n_key_wrapper<keya>>::value));
509     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, const boost_s11n_key_wrapper<keya>>::value));
510     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, const boost_s11n_key_wrapper<keya> &>::value));
511     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, boost_s11n_key_wrapper<keya> &>::value));
512     BOOST_CHECK((!has_boost_save<const boost::archive::binary_oarchive &, boost_s11n_key_wrapper<keya> &>::value));
513     BOOST_CHECK((!has_boost_save<boost::archive::binary_iarchive &, boost_s11n_key_wrapper<keya> &>::value));
514     BOOST_CHECK((!has_boost_save<void, boost_s11n_key_wrapper<keya> &>::value));
515     BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, boost_s11n_key_wrapper<keya>>::value));
516     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive, boost_s11n_key_wrapper<keyb>>::value));
517     BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive &, boost_s11n_key_wrapper<keya>>::value));
518     BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive &, boost_s11n_key_wrapper<keya> &>::value));
519     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive &, const boost_s11n_key_wrapper<keya> &>::value));
520     BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive &, const boost_s11n_key_wrapper<keya>>::value));
521     BOOST_CHECK((!has_boost_load<void, boost_s11n_key_wrapper<keya>>::value));
522     BOOST_CHECK((!has_boost_load<boost::archive::binary_oarchive, boost_s11n_key_wrapper<keya>>::value));
523 }
524 
525 struct boost_int_tester {
526     template <typename T>
527     void operator()(const T &) const
528     {
529         std::atomic<bool> status(true);
530         auto checker = [&status](int n) {
531             std::uniform_int_distribution<T> dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
532             std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
533             for (auto i = 0; i < ntrials; ++i) {
534                 const auto tmp = dist(eng);
535                 auto cmp = boost_roundtrip(tmp);
536                 if (cmp != tmp) {
537                     status.store(false);
538                 }
539             }
540         };
541         std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
542         t0.join();
543         t1.join();
544         t2.join();
545         t3.join();
546         BOOST_CHECK(status.load());
547     }
548 };
549 
550 BOOST_AUTO_TEST_CASE(s11n_test_boost_int)
551 {
552     boost::mpl::for_each<integral_types>(boost_int_tester());
553 }
554 
555 struct boost_fp_tester {
556     template <typename T>
557     void operator()(const T &) const
558     {
559 #if BOOST_VERSION < 106000
560         // Serialization of fp types appears to be broken in previous
561         // Boost versions for exact roundtrip.
562         return;
563 #endif
564 #if defined(__MINGW32__)
565         // It appears boost serialization of long double is broken on MinGW
566         // at the present time.
567         if (std::is_same<T, long double>::value) {
568             return;
569         }
570 #endif
571         std::atomic<bool> status(true);
572         auto checker = [&status](int n) {
573             std::uniform_real_distribution<T> dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
574             std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
575             for (auto i = 0; i < ntrials; ++i) {
576                 const auto tmp = dist(eng);
577                 auto cmp = boost_roundtrip(tmp);
578                 // NOTE: use a bit of tolerance here. Recent Boost versions do the roundtrip exactly
579                 // with the text archive, but earlier versions don't.
580                 if (std::abs((cmp - tmp) / cmp) > std::numeric_limits<T>::epsilon() * 10.) {
581                     status.store(false);
582                 }
583             }
584         };
585         std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
586         t0.join();
587         t1.join();
588         t2.join();
589         t3.join();
590         BOOST_CHECK(status.load());
591     }
592 };
593 
594 BOOST_AUTO_TEST_CASE(s11n_test_boost_float)
595 {
596     boost::mpl::for_each<fp_types>(boost_fp_tester());
597 }
598 
599 BOOST_AUTO_TEST_CASE(s11n_test_boost_string)
600 {
601     std::atomic<bool> status(true);
602     auto checker = [&status](int n) {
603         // NOTE: the numerical values of the decimal digits are guaranteed to be
604         // consecutive.
605         std::uniform_int_distribution<char> dist('0', '9');
606         std::uniform_int_distribution<unsigned> sdist(0u, 10u);
607         std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
608         auto gen = [&eng, &dist]() { return dist(eng); };
609         std::array<char, 10u> achar;
610         for (auto i = 0; i < ntrials; ++i) {
611             const auto s = sdist(eng);
612             std::generate_n(achar.begin(), s, gen);
613             std::string str(achar.begin(), achar.begin() + s);
614             const auto cmp = boost_roundtrip(str);
615             if (cmp != str) {
616                 status.store(false);
617             }
618         }
619     };
620     std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
621     t0.join();
622     t1.join();
623     t2.join();
624     t3.join();
625     BOOST_CHECK(status.load());
626 }
627 
628 #if defined(PIRANHA_WITH_MSGPACK)
629 
630 #include <cmath>
631 #include <iostream>
632 #include <iterator>
633 
634 using msgpack::packer;
635 using msgpack::sbuffer;
636 
637 template <typename T>
638 using sw = msgpack_stream_wrapper<T>;
639 
640 // A struct with no msgpack support.
641 struct no_msgpack {
642 };
643 
644 // A key with msgpack support.
645 struct key01 {
646     key01() = default;
647     key01(const key01 &) = default;
648     key01(key01 &&) noexcept;
649     key01 &operator=(const key01 &) = default;
650     key01 &operator=(key01 &&) noexcept;
651     key01(const symbol_set &);
652     bool operator==(const key01 &) const;
653     bool operator!=(const key01 &) const;
654     bool is_compatible(const symbol_set &) const noexcept;
655     bool is_ignorable(const symbol_set &) const noexcept;
656     key01 merge_args(const symbol_set &, const symbol_set &) const;
657     bool is_unitary(const symbol_set &) const;
658     void print(std::ostream &, const symbol_set &) const;
659     void print_tex(std::ostream &, const symbol_set &) const;
660     template <typename T, typename U>
661     std::vector<std::pair<std::string, key01>> t_subs(const std::string &, const T &, const U &,
662                                                       const symbol_set &) const;
663     void trim_identify(symbol_set &, const symbol_set &) const;
664     key01 trim(const symbol_set &, const symbol_set &) const;
665     template <typename Stream>
666     int msgpack_pack(msgpack::packer<Stream> &packer, msgpack_format, const symbol_set &) const;
667     int msgpack_convert(const msgpack::object &, msgpack_format, const symbol_set &);
668 };
669 
670 // A key without msgpack support.
671 struct key02 {
672     key02() = default;
673     key02(const key02 &) = default;
674     key02(key02 &&) noexcept;
675     key02 &operator=(const key02 &) = default;
676     key02 &operator=(key02 &&) noexcept;
677     key02(const symbol_set &);
678     bool operator==(const key02 &) const;
679     bool operator!=(const key02 &) const;
680     bool is_compatible(const symbol_set &) const noexcept;
681     bool is_ignorable(const symbol_set &) const noexcept;
682     key02 merge_args(const symbol_set &, const symbol_set &) const;
683     bool is_unitary(const symbol_set &) const;
684     void print(std::ostream &, const symbol_set &) const;
685     void print_tex(std::ostream &, const symbol_set &) const;
686     template <typename T, typename U>
687     std::vector<std::pair<std::string, key02>> t_subs(const std::string &, const T &, const U &,
688                                                       const symbol_set &) const;
689     void trim_identify(symbol_set &, const symbol_set &) const;
690     key02 trim(const symbol_set &, const symbol_set &) const;
691     template <typename Stream>
692     void msgpack_pack(msgpack::packer<Stream> &packer, msgpack_format, const symbol_set &);
693     template <typename Stream>
694     void msgpack_pack(msgpack::packer<Stream> &packer, msgpack_format) const;
695     int msgpack_convert(msgpack::object &, msgpack_format, const symbol_set &);
696     int msgpack_convert(const msgpack::object &, msgpack_format);
697 };
698 
699 namespace std
700 {
701 
702 template <>
703 struct hash<key01> {
704     std::size_t operator()(const key01 &) const;
705 };
706 
707 template <>
708 struct hash<key02> {
709     std::size_t operator()(const key02 &) const;
710 };
711 }
712 
713 // Helper function to roundtrip the conversion to/from msgpack for type T.
714 template <typename T>
715 static inline T msgpack_roundtrip(const T &x, msgpack_format f)
716 {
717     sbuffer sbuf;
718     packer<sbuffer> p(sbuf);
719     msgpack_pack(p, x, f);
720     std::size_t offset = 0u;
721     auto oh = msgpack::unpack(sbuf.data(), sbuf.size(), offset);
722     piranha_assert(offset == sbuf.size());
723     T retval;
724     msgpack_convert(retval, oh.get(), f);
725     return retval;
726 }
727 
728 // As above, but using stringstream as backend.
729 template <typename T>
730 static inline T msgpack_roundtrip_sstream(const T &x, msgpack_format f)
731 {
732     sw<std::stringstream> oss;
733     packer<sw<std::stringstream>> p(oss);
734     msgpack_pack(p, x, f);
735     std::vector<char> vec;
736     std::copy(std::istreambuf_iterator<char>(oss), std::istreambuf_iterator<char>(), std::back_inserter(vec));
737     std::size_t offset = 0u;
738     auto oh = msgpack::unpack(vec.data(), vec.size(), offset);
739     piranha_assert(offset == vec.size());
740     T retval;
741     msgpack_convert(retval, oh.get(), f);
742     return retval;
743 }
744 
745 BOOST_AUTO_TEST_CASE(s11n_test_msgpack_tt)
746 {
747     BOOST_CHECK(is_msgpack_stream<std::ostringstream>::value);
748     BOOST_CHECK(!is_msgpack_stream<std::ostringstream &>::value);
749     BOOST_CHECK(!is_msgpack_stream<void>::value);
750     BOOST_CHECK(!is_msgpack_stream<const std::ostringstream &>::value);
751     BOOST_CHECK(!is_msgpack_stream<const std::ostringstream>::value);
752     BOOST_CHECK(is_msgpack_stream<sbuffer>::value);
753     BOOST_CHECK(!is_msgpack_stream<float>::value);
754     BOOST_CHECK(!is_msgpack_stream<const double>::value);
755     BOOST_CHECK(is_msgpack_stream<sw<std::ostringstream>>::value);
756     BOOST_CHECK(!is_msgpack_stream<sw<std::ostringstream> &>::value);
757     BOOST_CHECK((has_msgpack_pack<sbuffer, int>::value));
758     BOOST_CHECK((!has_msgpack_pack<sbuffer, void>::value));
759     BOOST_CHECK((!has_msgpack_pack<void, void>::value));
760     BOOST_CHECK((!has_msgpack_pack<void, int>::value));
761     BOOST_CHECK((!has_msgpack_pack<sbuffer, no_msgpack>::value));
762     BOOST_CHECK((has_msgpack_pack<std::ostringstream, int>::value));
763     BOOST_CHECK((has_msgpack_pack<std::ostringstream, bool>::value));
764     BOOST_CHECK((has_msgpack_pack<std::ostringstream, bool &>::value));
765     BOOST_CHECK((has_msgpack_pack<std::ostringstream, const bool &>::value));
766     BOOST_CHECK((has_msgpack_pack<std::ostringstream, const bool>::value));
767     BOOST_CHECK((!has_msgpack_pack<std::ostringstream &, const bool>::value));
768     BOOST_CHECK((has_msgpack_pack<std::ostringstream, std::string>::value));
769     BOOST_CHECK((has_msgpack_pack<std::ostringstream, std::string &>::value));
770     BOOST_CHECK((has_msgpack_pack<std::ostringstream, const std::string &>::value));
771     BOOST_CHECK((has_msgpack_pack<std::ostringstream, const std::string>::value));
772     BOOST_CHECK((has_msgpack_pack<sw<std::ostringstream>, int>::value));
773     BOOST_CHECK((!has_msgpack_pack<sbuffer &, int>::value));
774     BOOST_CHECK((!has_msgpack_pack<const std::ostringstream, int>::value));
775     BOOST_CHECK((!has_msgpack_pack<const std::ostringstream &, int>::value));
776     BOOST_CHECK((!has_msgpack_pack<const std::ostringstream &&, int>::value));
777     BOOST_CHECK((!has_msgpack_pack<std::ostringstream &&, int>::value));
778     BOOST_CHECK((has_msgpack_convert<int>::value));
779     BOOST_CHECK((!has_msgpack_convert<void>::value));
780     BOOST_CHECK((has_msgpack_convert<bool>::value));
781     BOOST_CHECK((has_msgpack_convert<bool &>::value));
782     BOOST_CHECK((!has_msgpack_convert<const bool>::value));
783     BOOST_CHECK((has_msgpack_convert<double>::value));
784     BOOST_CHECK((has_msgpack_convert<int &>::value));
785     BOOST_CHECK((has_msgpack_convert<double &>::value));
786     BOOST_CHECK((!has_msgpack_convert<no_msgpack>::value));
787     BOOST_CHECK((!has_msgpack_convert<const int>::value));
788     BOOST_CHECK((!has_msgpack_convert<const double>::value));
789     BOOST_CHECK((has_msgpack_convert<int &&>::value));
790     BOOST_CHECK((has_msgpack_convert<std::string>::value));
791     BOOST_CHECK((has_msgpack_convert<std::string &>::value));
792     BOOST_CHECK((has_msgpack_convert<std::string &&>::value));
793     BOOST_CHECK((!has_msgpack_convert<const std::string>::value));
794     BOOST_CHECK((!has_msgpack_convert<const std::string &>::value));
795     BOOST_CHECK((has_msgpack_convert<double &&>::value));
796     BOOST_CHECK((!has_msgpack_convert<const int &&>::value));
797     BOOST_CHECK((!has_msgpack_convert<const double &&>::value));
798     BOOST_CHECK(is_key<key01>::value);
799     BOOST_CHECK((key_has_msgpack_pack<sbuffer, key01>::value));
800     BOOST_CHECK((!key_has_msgpack_pack<void, key01>::value));
801     BOOST_CHECK((key_has_msgpack_pack<sbuffer, key01 &>::value));
802     BOOST_CHECK((key_has_msgpack_pack<sbuffer, const key01 &>::value));
803     BOOST_CHECK((key_has_msgpack_pack<sbuffer, const key01>::value));
804     BOOST_CHECK((!key_has_msgpack_pack<sbuffer &, key01>::value));
805     BOOST_CHECK((!key_has_msgpack_pack<const sbuffer, key01>::value));
806     BOOST_CHECK(is_key<key02>::value);
807     BOOST_CHECK((!key_has_msgpack_pack<sbuffer, key02>::value));
808     BOOST_CHECK((!key_has_msgpack_convert<key02>::value));
809     BOOST_CHECK((key_has_msgpack_convert<key01>::value));
810     BOOST_CHECK((key_has_msgpack_convert<key01 &>::value));
811     BOOST_CHECK((!key_has_msgpack_convert<const key01 &>::value));
812     BOOST_CHECK((!key_has_msgpack_convert<const key01>::value));
813 }
814 
815 struct int_tester {
816     template <typename T>
817     void operator()(const T &) const
818     {
819         std::atomic<bool> status(true);
820         auto checker = [&status](int n) {
821             std::uniform_int_distribution<T> dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
822             std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
823             for (auto f : {0, 1}) {
824                 for (auto i = 0; i < ntrials; ++i) {
825                     const auto tmp = dist(eng);
826                     auto cmp = msgpack_roundtrip(tmp, static_cast<msgpack_format>(f));
827                     if (cmp != tmp) {
828                         status.store(false);
829                     }
830                     cmp = msgpack_roundtrip_sstream(tmp, static_cast<msgpack_format>(f));
831                     if (cmp != tmp) {
832                         status.store(false);
833                     }
834                 }
835             }
836         };
837         std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
838         t0.join();
839         t1.join();
840         t2.join();
841         t3.join();
842         BOOST_CHECK(status.load());
843     }
844 };
845 
846 BOOST_AUTO_TEST_CASE(s11n_test_msgpack_int)
847 {
848     boost::mpl::for_each<integral_types>(int_tester());
849     // Test bool as well.
850     for (auto f : {0, 1}) {
851         BOOST_CHECK_EQUAL(true, msgpack_roundtrip(true, static_cast<msgpack_format>(f)));
852         BOOST_CHECK_EQUAL(false, msgpack_roundtrip(false, static_cast<msgpack_format>(f)));
853     }
854 }
855 
856 struct fp_tester {
857     template <typename T>
858     void operator()(const T &) const
859     {
860         std::atomic<bool> status(true);
861         auto checker = [&status](int n) {
862             std::uniform_real_distribution<T> dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
863             std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
864             for (auto f : {0, 1}) {
865                 for (auto i = 0; i < ntrials; ++i) {
866                     const auto tmp = dist(eng);
867                     auto cmp = msgpack_roundtrip(tmp, static_cast<msgpack_format>(f));
868                     if (cmp != tmp) {
869                         status.store(false);
870                     }
871                     cmp = msgpack_roundtrip_sstream(tmp, static_cast<msgpack_format>(f));
872                     if (cmp != tmp) {
873                         status.store(false);
874                     }
875                 }
876             }
877         };
878         std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
879         t0.join();
880         t1.join();
881         t2.join();
882         t3.join();
883         BOOST_CHECK(status.load());
884         // Additional checking for non-finite values.
885         if (std::numeric_limits<T>::has_quiet_NaN && std::numeric_limits<T>::has_infinity) {
886             for (auto f : {0, 1}) {
887                 auto tmp = std::numeric_limits<T>::quiet_NaN();
888                 tmp = std::copysign(tmp, T(1.));
889                 auto cmp = msgpack_roundtrip(tmp, static_cast<msgpack_format>(f));
890                 BOOST_CHECK(std::isnan(cmp));
891                 BOOST_CHECK(!std::signbit(cmp));
892                 tmp = std::copysign(tmp, T(-1.));
893                 cmp = msgpack_roundtrip(tmp, static_cast<msgpack_format>(f));
894                 BOOST_CHECK(std::isnan(cmp));
895                 BOOST_CHECK(std::signbit(cmp));
896                 tmp = std::numeric_limits<T>::infinity();
897                 cmp = msgpack_roundtrip(tmp, static_cast<msgpack_format>(f));
898                 BOOST_CHECK(std::isinf(cmp));
899                 BOOST_CHECK(!std::signbit(cmp));
900                 tmp = std::numeric_limits<T>::infinity();
901                 tmp = std::copysign(tmp, T(-1.));
902                 cmp = msgpack_roundtrip(tmp, static_cast<msgpack_format>(f));
903                 BOOST_CHECK(std::isinf(cmp));
904                 BOOST_CHECK(std::signbit(cmp));
905             }
906         }
907         if (std::is_same<T, long double>::value) {
908             // Check that a malformed string in the portable serialization of long double
909             // raises the appropriate exception.
910             sbuffer sbuf;
911             packer<sbuffer> p(sbuf);
912             p.pack("hello world");
913             std::size_t offset = 0;
914             auto oh = msgpack::unpack(sbuf.data(), sbuf.size(), offset);
915             long double tmp;
916             auto msg_checker = [](const std::invalid_argument &ia) -> bool {
917                 return boost::contains(ia.what(), "failed to parse the string 'hello world' as a long double");
918             };
919             BOOST_CHECK_EXCEPTION(msgpack_convert(tmp, oh.get(), msgpack_format::portable), std::invalid_argument,
920                                   msg_checker);
921         }
922     }
923 };
924 
925 BOOST_AUTO_TEST_CASE(s11n_test_msgpack_float)
926 {
927     boost::mpl::for_each<fp_types>(fp_tester());
928 }
929 
930 BOOST_AUTO_TEST_CASE(s11n_test_msgpack_string)
931 {
932     std::atomic<bool> status(true);
933     auto checker = [&status](int n) {
934         std::uniform_int_distribution<char> dist('0', '9');
935         std::uniform_int_distribution<unsigned> sdist(0u, 10u);
936         std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
937         auto gen = [&eng, &dist]() { return dist(eng); };
938         std::array<char, 10u> achar;
939         for (auto i = 0; i < ntrials; ++i) {
940             for (msgpack_format f : {msgpack_format::portable, msgpack_format::binary}) {
941                 const auto s = sdist(eng);
942                 std::generate_n(achar.begin(), s, gen);
943                 std::string str(achar.begin(), achar.begin() + s);
944                 const auto cmp = msgpack_roundtrip(str, f);
945                 if (cmp != str) {
946                     status.store(false);
947                 }
948             }
949         }
950     };
951     std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
952     t0.join();
953     t1.join();
954     t2.join();
955     t3.join();
956     BOOST_CHECK(status.load());
957 }
958 
959 #endif
960 
961 static const int ntrials_file = 20;
962 
963 static const std::vector<data_format> dfs = {data_format::boost_binary, data_format::boost_portable,
964                                              data_format::msgpack_binary, data_format::msgpack_portable};
965 
966 static const std::vector<compression> cfs
967     = {compression::none, compression::bzip2, compression::zlib, compression::gzip};
968 
969 template <typename T>
970 static inline T save_roundtrip(const T &x, data_format f, compression c)
971 {
972     tmp_file file;
973     save_file(x, file.name(), f, c);
974     T retval;
975     load_file(retval, file.name(), f, c);
976     return retval;
977 }
978 
979 struct int_save_load_tester {
980     template <typename T>
981     void operator()(const T &) const
982     {
983         std::atomic<bool> status(true);
984         auto checker = [&status](int n) {
985             std::uniform_int_distribution<T> dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
986             std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
987             for (auto i = 0; i < ntrials_file; ++i) {
988                 for (auto f : dfs) {
989                     for (auto c : cfs) {
990                         const auto tmp = dist(eng);
991 #if defined(PIRANHA_WITH_MSGPACK) && defined(PIRANHA_WITH_ZLIB) && defined(PIRANHA_WITH_BZIP2)
992                         // NOTE: we are not expecting any failure if we have all optional deps.
993                         auto cmp = save_roundtrip(tmp, f, c);
994                         if (cmp != tmp) {
995                             status.store(false);
996                         }
997 #else
998                         // If msgpack or zlib are not available, we will have not_implemented_error
999                         // failures.
1000                         try {
1001                             auto cmp = save_roundtrip(tmp, f, c);
1002                             if (cmp != tmp) {
1003                                 status.store(false);
1004                             }
1005                         } catch (const not_implemented_error &) {
1006                             continue;
1007                         }
1008 #endif
1009                     }
1010                 }
1011             }
1012         };
1013         std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
1014         t0.join();
1015         t1.join();
1016         t2.join();
1017         t3.join();
1018         BOOST_CHECK(status.load());
1019     }
1020 };
1021 
1022 struct fp_save_load_tester {
1023     template <typename T>
1024     void operator()(const T &) const
1025     {
1026 #if BOOST_VERSION < 106000
1027         // Serialization of fp types appears to be broken in previous
1028         // Boost versions for exact roundtrip.
1029         return;
1030 #endif
1031 #if defined(__MINGW32__)
1032         // It appears boost serialization of long double is broken on MinGW
1033         // at the present time.
1034         if (std::is_same<T, long double>::value) {
1035             return;
1036         }
1037 #endif
1038         std::atomic<bool> status(true);
1039         auto checker = [&status](int n) {
1040             std::uniform_real_distribution<T> dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
1041             std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
1042             for (auto i = 0; i < ntrials_file; ++i) {
1043                 for (auto f : dfs) {
1044                     for (auto c : cfs) {
1045                         const auto tmp = dist(eng);
1046 #if defined(PIRANHA_WITH_MSGPACK) && defined(PIRANHA_WITH_ZLIB) && defined(PIRANHA_WITH_BZIP2)
1047                         auto cmp = save_roundtrip(tmp, f, c);
1048                         if (cmp != tmp) {
1049                             status.store(false);
1050                         }
1051 #else
1052                         try {
1053                             auto cmp = save_roundtrip(tmp, f, c);
1054                             if (cmp != tmp) {
1055                                 status.store(false);
1056                             }
1057                         } catch (const not_implemented_error &) {
1058                             continue;
1059                         }
1060 #endif
1061                     }
1062                 }
1063             }
1064         };
1065         std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
1066         t0.join();
1067         t1.join();
1068         t2.join();
1069         t3.join();
1070         BOOST_CHECK(status.load());
1071     }
1072 };
1073 
1074 struct no_boost_msgpack {
1075 };
1076 
1077 struct only_boost {
1078 };
1079 
1080 namespace piranha
1081 {
1082 
1083 template <typename Archive>
1084 class boost_save_impl<Archive, only_boost>
1085 {
1086 public:
1087     void operator()(Archive &, const only_boost &) const
1088     {
1089     }
1090 };
1091 
1092 template <typename Archive>
1093 class boost_load_impl<Archive, only_boost>
1094 {
1095 public:
1096     void operator()(Archive &, only_boost &) const
1097     {
1098     }
1099 };
1100 }
1101 
1102 // Save/load checker for string.
1103 static inline void string_save_load_tester()
1104 {
1105     std::atomic<bool> status(true);
1106     auto checker = [&status](int n) {
1107         // NOTE: the numerical values of the decimal digits are guaranteed to be
1108         // consecutive.
1109         std::uniform_int_distribution<char> dist('0', '9');
1110         std::uniform_int_distribution<unsigned> sdist(0u, 10u);
1111         std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
1112         auto gen = [&eng, &dist]() { return dist(eng); };
1113         std::array<char, 10u> achar;
1114         for (auto i = 0; i < ntrials_file; ++i) {
1115             for (auto f : dfs) {
1116                 for (auto c : cfs) {
1117                     const auto s = sdist(eng);
1118                     std::generate_n(achar.begin(), s, gen);
1119                     std::string str(achar.begin(), achar.begin() + s);
1120 #if defined(PIRANHA_WITH_MSGPACK) && defined(PIRANHA_WITH_ZLIB) && defined(PIRANHA_WITH_BZIP2)
1121                     // NOTE: we are not expecting any failure if we have all optional deps.
1122                     auto cmp = save_roundtrip(str, f, c);
1123                     if (cmp != str) {
1124                         status.store(false);
1125                     }
1126 #else
1127                     // If msgpack or zlib are not available, we will have not_implemented_error
1128                     // failures.
1129                     try {
1130                         auto cmp = save_roundtrip(str, f, c);
1131                         if (cmp != str) {
1132                             status.store(false);
1133                         }
1134                     } catch (const not_implemented_error &) {
1135                         continue;
1136                     }
1137 #endif
1138                 }
1139             }
1140         }
1141     };
1142     std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
1143     t0.join();
1144     t1.join();
1145     t2.join();
1146     t3.join();
1147     BOOST_CHECK(status.load());
1148 }
1149 
1150 BOOST_AUTO_TEST_CASE(s11n_test_get_cdf_from_filename)
1151 {
1152     BOOST_CHECK(get_cdf_from_filename("foo.boostb") == std::make_pair(compression::none, data_format::boost_binary));
1153     BOOST_CHECK(get_cdf_from_filename("foo.boostp") == std::make_pair(compression::none, data_format::boost_portable));
1154     BOOST_CHECK(get_cdf_from_filename("foo.mpackb") == std::make_pair(compression::none, data_format::msgpack_binary));
1155     BOOST_CHECK(get_cdf_from_filename("foo.mpackp")
1156                 == std::make_pair(compression::none, data_format::msgpack_portable));
1157     BOOST_CHECK(get_cdf_from_filename("foo.boostb.bz2")
1158                 == std::make_pair(compression::bzip2, data_format::boost_binary));
1159     BOOST_CHECK(get_cdf_from_filename("foo.boostp.bz2")
1160                 == std::make_pair(compression::bzip2, data_format::boost_portable));
1161     BOOST_CHECK(get_cdf_from_filename("foo.mpackb.bz2")
1162                 == std::make_pair(compression::bzip2, data_format::msgpack_binary));
1163     BOOST_CHECK(get_cdf_from_filename("foo.mpackp.bz2")
1164                 == std::make_pair(compression::bzip2, data_format::msgpack_portable));
1165     BOOST_CHECK(get_cdf_from_filename("foo.boostb.gz") == std::make_pair(compression::gzip, data_format::boost_binary));
1166     BOOST_CHECK(get_cdf_from_filename("foo.boostp.gz")
1167                 == std::make_pair(compression::gzip, data_format::boost_portable));
1168     BOOST_CHECK(get_cdf_from_filename("foo.mpackb.gz")
1169                 == std::make_pair(compression::gzip, data_format::msgpack_binary));
1170     BOOST_CHECK(get_cdf_from_filename("foo.mpackp.gz")
1171                 == std::make_pair(compression::gzip, data_format::msgpack_portable));
1172     BOOST_CHECK(get_cdf_from_filename("foo.boostb.zip")
1173                 == std::make_pair(compression::zlib, data_format::boost_binary));
1174     BOOST_CHECK(get_cdf_from_filename("foo.boostp.zip")
1175                 == std::make_pair(compression::zlib, data_format::boost_portable));
1176     BOOST_CHECK(get_cdf_from_filename("foo.mpackb.zip")
1177                 == std::make_pair(compression::zlib, data_format::msgpack_binary));
1178     BOOST_CHECK(get_cdf_from_filename("foo.mpackp.zip")
1179                 == std::make_pair(compression::zlib, data_format::msgpack_portable));
1180     BOOST_CHECK(get_cdf_from_filename("foo.bz2.boostb")
1181                 == std::make_pair(compression::none, data_format::boost_binary));
1182     BOOST_CHECK_EXCEPTION(get_cdf_from_filename("foo"), std::invalid_argument, [](const std::invalid_argument &iae) {
1183         return boost::contains(iae.what(), "unable to deduce the data format from the filename 'foo'. The filename "
1184                                            "must end with one of ['.boostb','.boostp','.mpackb','.mpackp'], "
1185                                            "optionally followed by one of ['.bz2','gz','zip'].");
1186     });
1187     BOOST_CHECK_EXCEPTION(
1188         get_cdf_from_filename("foo.bz2"), std::invalid_argument, [](const std::invalid_argument &iae) {
1189             return boost::contains(iae.what(),
1190                                    "unable to deduce the data format from the filename 'foo.bz2'. The filename "
1191                                    "must end with one of ['.boostb','.boostp','.mpackb','.mpackp'], "
1192                                    "optionally followed by one of ['.bz2','gz','zip'].");
1193         });
1194     BOOST_CHECK_EXCEPTION(
1195         get_cdf_from_filename("foo.mpackb.bz2.bz2"), std::invalid_argument, [](const std::invalid_argument &iae) {
1196             return boost::contains(
1197                 iae.what(), "unable to deduce the data format from the filename 'foo.mpackb.bz2.bz2'. The filename "
1198                             "must end with one of ['.boostb','.boostp','.mpackb','.mpackp'], "
1199                             "optionally followed by one of ['.bz2','gz','zip'].");
1200         });
1201 }
1202 
1203 BOOST_AUTO_TEST_CASE(s11n_test_save_load)
1204 {
1205     boost::mpl::for_each<integral_types>(int_save_load_tester());
1206     boost::mpl::for_each<fp_types>(fp_save_load_tester());
1207     string_save_load_tester();
1208 #if defined(PIRANHA_WITH_MSGPACK) && defined(PIRANHA_WITH_ZLIB) && defined(PIRANHA_WITH_BZIP2)
1209     // Test failures.
1210     for (auto f : dfs) {
1211         for (auto c : cfs) {
1212             // Unserializable type.
1213             no_boost_msgpack n;
1214             auto msg_checker = [](const not_implemented_error &nie) -> bool {
1215                 return boost::contains(nie.what(),
1216                                        "type '" + detail::demangle<no_boost_msgpack>() + "' does not support");
1217             };
1218             BOOST_CHECK_EXCEPTION(save_file(n, "foo", f, c), not_implemented_error, msg_checker);
1219             BOOST_CHECK_EXCEPTION(load_file(n, "foo", f, c), not_implemented_error, msg_checker);
1220             // Wrong filename for loading.
1221             auto msg_checker2 = [](const std::runtime_error &re) -> bool {
1222                 return boost::contains(re.what(), "file 'foobar123' could not be opened for loading");
1223             };
1224             int m = 0;
1225             BOOST_CHECK_EXCEPTION(load_file(m, "foobar123", f, c), std::runtime_error, msg_checker2);
1226         }
1227     }
1228     BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, only_boost>::value));
1229     BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, only_boost>::value));
1230     BOOST_CHECK_NO_THROW(save_roundtrip(only_boost{}, data_format::boost_portable, compression::none));
1231     BOOST_CHECK_NO_THROW(save_roundtrip(only_boost{}, data_format::boost_binary, compression::none));
1232     BOOST_CHECK_THROW(save_roundtrip(only_boost{}, data_format::msgpack_portable, compression::none),
1233                       not_implemented_error);
1234     BOOST_CHECK_THROW(save_roundtrip(only_boost{}, data_format::msgpack_binary, compression::none),
1235                       not_implemented_error);
1236     // Test the convenience wrappers.
1237     for (auto sf : {".boostb", ".boostp", ".mpackb", ".mpackp"}) {
1238         for (auto sc : {"", ".bz2", ".gz", ".zip"}) {
1239             tmp_file filename;
1240             auto fn = filename.name() + sf + sc;
1241             save_file(42, fn);
1242             int n;
1243             load_file(n, fn);
1244             BOOST_CHECK_EQUAL(n, 42);
1245             std::remove(fn.c_str());
1246         }
1247     }
1248     BOOST_CHECK_THROW(save_file(42, "foo.txt"), std::invalid_argument);
1249     BOOST_CHECK_THROW(save_file(42, "foo.bz2"), std::invalid_argument);
1250 #endif
1251 }
1252 
1253 BOOST_AUTO_TEST_CASE(s11n_boost_s11n_key_wrapper_test)
1254 {
1255     keya ka;
1256     symbol_set ss;
1257     using w_type = boost_s11n_key_wrapper<keya>;
1258     w_type w1{ka, ss};
1259     BOOST_CHECK_EQUAL(&ka, &w1.key());
1260     BOOST_CHECK_EQUAL(&ka, &static_cast<const w_type &>(w1).key());
1261     BOOST_CHECK_EQUAL(&ss, &w1.ss());
1262     w_type w2{static_cast<const keya &>(ka), ss};
1263     BOOST_CHECK_EQUAL(&ka, &static_cast<const w_type &>(w2).key());
1264     BOOST_CHECK_EQUAL(&ss, &w2.ss());
1265     BOOST_CHECK_EXCEPTION(w2.key(), std::runtime_error, [](const std::runtime_error &re) {
1266         return boost::contains(re.what(), "trying to access the mutable key instance of a boost_s11n_key_wrapper "
1267                                           "that was constructed with a const key");
1268     });
1269 }
1270