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