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/mp_integer.hpp"
30 
31 #define BOOST_TEST_MODULE mp_integer_04_test
32 #include <boost/test/included/unit_test.hpp>
33 
34 #define FUSION_MAX_VECTOR_SIZE 20
35 
36 #include <atomic>
37 #include <boost/archive/xml_iarchive.hpp>
38 #include <boost/archive/xml_oarchive.hpp>
39 #include <boost/filesystem.hpp>
40 #include <boost/fusion/algorithm.hpp>
41 #include <boost/fusion/include/algorithm.hpp>
42 #include <boost/fusion/include/sequence.hpp>
43 #include <boost/fusion/sequence.hpp>
44 #include <initializer_list>
45 #include <limits>
46 #include <random>
47 #include <sstream>
48 #include <stdexcept>
49 #include <string>
50 #include <thread>
51 #include <type_traits>
52 #include <vector>
53 
54 #include "../src/config.hpp"
55 #include "../src/exceptions.hpp"
56 #include "../src/init.hpp"
57 #include "../src/s11n.hpp"
58 
59 using namespace piranha;
60 
61 using size_types = boost::mpl::vector<std::integral_constant<int, 0>, std::integral_constant<int, 8>,
62                                       std::integral_constant<int, 16>, std::integral_constant<int, 32>
63 #if defined(PIRANHA_UINT128_T)
64                                       ,
65                                       std::integral_constant<int, 64>
66 #endif
67                                       >;
68 
69 namespace bfs = boost::filesystem;
70 
71 struct tmp_file {
tmp_filetmp_file72     tmp_file()
73     {
74         m_path = bfs::temp_directory_path();
75         // Concatenate with a unique filename.
76         m_path /= bfs::unique_path();
77     }
~tmp_filetmp_file78     ~tmp_file()
79     {
80         bfs::remove(m_path);
81     }
nametmp_file82     std::string name() const
83     {
84         return m_path.string();
85     }
86     bfs::path m_path;
87 };
88 
89 static const std::vector<data_format> dfs = {data_format::boost_binary, data_format::boost_portable,
90                                              data_format::msgpack_binary, data_format::msgpack_portable};
91 
92 static const std::vector<compression> cfs
93     = {compression::none, compression::bzip2, compression::zlib, compression::gzip};
94 
95 template <typename T>
save_roundtrip(const T & x,data_format f,compression c)96 static inline T save_roundtrip(const T &x, data_format f, compression c)
97 {
98     tmp_file file;
99     save_file(x, file.name(), f, c);
100     T retval;
101     load_file(retval, file.name(), f, c);
102     return retval;
103 }
104 
105 constexpr int ntries = 1000;
106 
107 constexpr int ntries_file = 100;
108 
109 template <typename OArchive, typename IArchive, typename T>
boost_roundtrip(const T & x,bool promote=false)110 static inline T boost_roundtrip(const T &x, bool promote = false)
111 {
112     std::stringstream ss;
113     {
114         OArchive oa(ss);
115         boost_save(oa, x);
116     }
117     T retval;
118     if (promote) {
119         retval.promote();
120     }
121     {
122         IArchive ia(ss);
123         boost_load(ia, retval);
124     }
125     return retval;
126 }
127 
128 struct boost_s11n_tester {
129     template <typename T>
operator ()boost_s11n_tester130     void operator()(const T &)
131     {
132         using int_type = mp_integer<T::value>;
133         BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, int_type>::value));
134         BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, int_type &>::value));
135         BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, const int_type &>::value));
136         BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, const int_type>::value));
137         BOOST_CHECK((has_boost_save<boost::archive::text_oarchive &, const int_type>::value));
138         BOOST_CHECK((has_boost_save<boost::archive::text_oarchive &, int_type>::value));
139         BOOST_CHECK((has_boost_save<boost::archive::text_oarchive &, int_type &>::value));
140         BOOST_CHECK((has_boost_save<boost::archive::xml_oarchive &, int_type &>::value));
141         BOOST_CHECK((!has_boost_save<const boost::archive::binary_oarchive &, const int_type>::value));
142         BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, int_type>::value));
143         BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, int_type &>::value));
144         BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive &, int_type &>::value));
145         BOOST_CHECK((has_boost_load<boost::archive::text_iarchive &, int_type &>::value));
146         BOOST_CHECK((has_boost_load<boost::archive::text_iarchive, int_type>::value));
147         BOOST_CHECK((has_boost_load<boost::archive::xml_iarchive, int_type>::value));
148         BOOST_CHECK((!has_boost_load<const boost::archive::binary_iarchive &, int_type &>::value));
149         BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive &, const int_type &>::value));
150         BOOST_CHECK((!has_boost_load<boost::archive::binary_oarchive &, int_type &>::value));
151         // A few checks with zero.
152         BOOST_CHECK_EQUAL(
153             (boost_roundtrip<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(int_type{})),
154             int_type{});
155         int_type tmp;
156         tmp.promote();
157         BOOST_CHECK_EQUAL((boost_roundtrip<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(tmp)),
158                           int_type{});
159         tmp = int_type{};
160         BOOST_CHECK_EQUAL(
161             (boost_roundtrip<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(tmp, true)), int_type{});
162         tmp.promote();
163         BOOST_CHECK_EQUAL(
164             (boost_roundtrip<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(tmp, true)), int_type{});
165         // Random multi-threaded testing.
166         std::atomic<bool> status(true);
167         auto checker = [&status](unsigned n) {
168             std::mt19937 rng(static_cast<std::mt19937::result_type>(n));
169             std::uniform_int_distribution<long long> dist(std::numeric_limits<long long>::min(),
170                                                           std::numeric_limits<long long>::max());
171             std::uniform_int_distribution<int> pdist(0, 1);
172             for (int i = 0; i < ntries; ++i) {
173                 int_type cmp(dist(rng));
174                 if (pdist(rng) && cmp.is_static()) {
175                     cmp.promote();
176                 }
177                 // Make it occupy a few mpz limbs, sometimes.
178                 if (pdist(rng)) {
179                     cmp *= cmp;
180                     cmp *= cmp;
181                 }
182                 // Randomly flip sign (useful if the code above was run, as that forces the value to be positive
183                 // because it squares the original value twice).
184                 if (pdist(rng)) {
185                     cmp.negate();
186                 }
187                 auto tmp2 = boost_roundtrip<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(
188                     cmp, pdist(rng));
189                 // NOTE: binary saving preserves the staticness.
190                 if (tmp2 != cmp || tmp2.is_static() != cmp.is_static()) {
191                     status.store(false);
192                 }
193                 tmp2 = boost_roundtrip<boost::archive::text_oarchive, boost::archive::text_iarchive>(cmp, pdist(rng));
194                 if (tmp2 != cmp) {
195                     status.store(false);
196                 }
197             }
198             // Try with small values as well.
199             dist = std::uniform_int_distribution<long long>(-10, 10);
200             for (int i = 0; i < ntries; ++i) {
201                 int_type cmp(dist(rng));
202                 if (pdist(rng) && cmp.is_static()) {
203                     cmp.promote();
204                 }
205                 auto tmp2 = boost_roundtrip<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(
206                     cmp, pdist(rng));
207                 if (tmp2 != cmp || tmp2.is_static() != cmp.is_static()) {
208                     status.store(false);
209                 }
210                 tmp2 = boost_roundtrip<boost::archive::text_oarchive, boost::archive::text_iarchive>(cmp, pdist(rng));
211                 if (tmp2 != cmp) {
212                     status.store(false);
213                 }
214             }
215         };
216         std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
217         t0.join();
218         t1.join();
219         t2.join();
220         t3.join();
221         BOOST_CHECK(status.load());
222     }
223 };
224 
BOOST_AUTO_TEST_CASE(mp_integer_boost_s11n_test)225 BOOST_AUTO_TEST_CASE(mp_integer_boost_s11n_test)
226 {
227     init();
228     boost::mpl::for_each<size_types>(boost_s11n_tester());
229 }
230 
231 struct save_load_tester {
232     template <typename T>
operator ()save_load_tester233     void operator()(const T &)
234     {
235         using int_type = mp_integer<T::value>;
236         std::atomic<bool> status(true);
237         auto checker = [&status](unsigned n) {
238             std::mt19937 rng(static_cast<std::mt19937::result_type>(n));
239             std::uniform_int_distribution<long long> dist(std::numeric_limits<long long>::min(),
240                                                           std::numeric_limits<long long>::max());
241             std::uniform_int_distribution<int> pdist(0, 1);
242             for (int i = 0; i < ntries_file; ++i) {
243                 for (auto f : dfs) {
244                     for (auto c : cfs) {
245                         int_type tmp(dist(rng));
246                         if (pdist(rng) && tmp.is_static()) {
247                             tmp.promote();
248                         }
249                         // Make it occupy a few mpz limbs, sometimes.
250                         if (pdist(rng)) {
251                             tmp *= tmp;
252                             tmp *= tmp;
253                         }
254                         // Randomly flip sign (useful if the code above was run, as that forces the value to be positive
255                         // because it squares the original value twice).
256                         if (pdist(rng)) {
257                             tmp.negate();
258                         }
259 #if defined(PIRANHA_WITH_MSGPACK) && defined(PIRANHA_WITH_ZLIB) && defined(PIRANHA_WITH_BZIP2)
260                         // NOTE: we are not expecting any failure if we have all optional deps.
261                         auto cmp = save_roundtrip(tmp, f, c);
262                         if (cmp != tmp) {
263                             status.store(false);
264                         }
265 #else
266                         // If msgpack or zlib are not available, we will have not_implemented_error
267                         // failures.
268                         try {
269                             auto cmp = save_roundtrip(tmp, f, c);
270                             if (cmp != tmp) {
271                                 status.store(false);
272                             }
273                         } catch (const not_implemented_error &) {
274                             continue;
275                         }
276 #endif
277                     }
278                 }
279             }
280         };
281         std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
282         t0.join();
283         t1.join();
284         t2.join();
285         t3.join();
286         BOOST_CHECK(status.load());
287     }
288 };
289 
BOOST_AUTO_TEST_CASE(mp_integer_save_load_test)290 BOOST_AUTO_TEST_CASE(mp_integer_save_load_test)
291 {
292     boost::mpl::for_each<size_types>(save_load_tester());
293 }
294 
295 #if defined(PIRANHA_WITH_MSGPACK)
296 
297 template <typename T>
msgpack_roundtrip(const T & x,msgpack_format f,bool promote=false)298 static inline T msgpack_roundtrip(const T &x, msgpack_format f, bool promote = false)
299 {
300     msgpack::sbuffer sbuf;
301     msgpack::packer<msgpack::sbuffer> p(sbuf);
302     msgpack_pack(p, x, f);
303     auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
304     T retval;
305     if (promote) {
306         retval.promote();
307     }
308     msgpack_convert(retval, oh.get(), f);
309     return retval;
310 }
311 
312 struct msgpack_s11n_tester {
313     template <typename T>
operator ()msgpack_s11n_tester314     void operator()(const T &)
315     {
316         using int_type = mp_integer<T::value>;
317         BOOST_CHECK((has_msgpack_pack<std::stringstream, int_type>::value));
318         BOOST_CHECK((has_msgpack_pack<msgpack::sbuffer, int_type>::value));
319         BOOST_CHECK((has_msgpack_pack<msgpack::sbuffer, int_type &>::value));
320         BOOST_CHECK((has_msgpack_pack<msgpack::sbuffer, const int_type &>::value));
321         BOOST_CHECK((!has_msgpack_pack<std::stringstream &, int_type>::value));
322         BOOST_CHECK((!has_msgpack_pack<const std::stringstream, int_type>::value));
323         BOOST_CHECK((!has_msgpack_pack<const std::stringstream &, int_type>::value));
324         BOOST_CHECK((!has_msgpack_pack<int, int_type>::value));
325         BOOST_CHECK((has_msgpack_convert<int_type>::value));
326         BOOST_CHECK((has_msgpack_convert<int_type &>::value));
327         BOOST_CHECK((!has_msgpack_convert<const int_type &>::value));
328         // A few checks with zero.
329         for (auto f : {msgpack_format::portable, msgpack_format::binary}) {
330             BOOST_CHECK_EQUAL((msgpack_roundtrip(int_type{}, f)), int_type{});
331             int_type tmp;
332             tmp.promote();
333             BOOST_CHECK_EQUAL((msgpack_roundtrip(tmp, f)), int_type{});
334             tmp = int_type{};
335             BOOST_CHECK_EQUAL((msgpack_roundtrip(tmp, f)), int_type{});
336             tmp.promote();
337             BOOST_CHECK_EQUAL((msgpack_roundtrip(tmp, f)), int_type{});
338         }
339         // Random multi-threaded testing.
340         std::atomic<bool> status(true);
341         auto checker = [&status](unsigned n) {
342             std::mt19937 rng(static_cast<std::mt19937::result_type>(n));
343             std::uniform_int_distribution<long long> dist(std::numeric_limits<long long>::min(),
344                                                           std::numeric_limits<long long>::max());
345             std::uniform_int_distribution<int> pdist(0, 1);
346             for (int i = 0; i < ntries; ++i) {
347                 for (auto f : {msgpack_format::portable, msgpack_format::binary}) {
348                     int_type cmp(dist(rng));
349                     if (pdist(rng) && cmp.is_static()) {
350                         cmp.promote();
351                     }
352                     // Make it occupy a few mpz limbs, sometimes.
353                     if (pdist(rng)) {
354                         cmp *= cmp;
355                         cmp *= cmp;
356                     }
357                     // Randomly flip sign (useful if the code above was run, as that forces the value to be positive
358                     // because it squares the original value twice).
359                     if (pdist(rng)) {
360                         cmp.negate();
361                     }
362                     auto tmp = msgpack_roundtrip(cmp, f, pdist(rng));
363                     if (tmp != cmp || (f == msgpack_format::binary && tmp.is_static() != cmp.is_static())) {
364                         status.store(false);
365                     }
366                 }
367             }
368             // Small values.
369             dist = std::uniform_int_distribution<long long>(-10, 10);
370             for (int i = 0; i < ntries; ++i) {
371                 for (auto f : {msgpack_format::portable, msgpack_format::binary}) {
372                     int_type cmp(dist(rng));
373                     if (pdist(rng) && cmp.is_static()) {
374                         cmp.promote();
375                     }
376                     auto tmp = msgpack_roundtrip(cmp, f, pdist(rng));
377                     if (tmp != cmp || (f == msgpack_format::binary && tmp.is_static() != cmp.is_static())) {
378                         status.store(false);
379                     }
380                 }
381             }
382         };
383         std::thread t0(checker, 0), t1(checker, 1), t2(checker, 2), t3(checker, 3);
384         t0.join();
385         t1.join();
386         t2.join();
387         t3.join();
388         BOOST_CHECK(status.load());
389         // Test various failure modes.
390         {
391             // Array of only 1 element.
392             msgpack::sbuffer sbuf;
393             msgpack::packer<msgpack::sbuffer> p(sbuf);
394             p.pack_array(1);
395             p.pack(123);
396             auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
397             int_type n{1};
398             BOOST_CHECK_THROW(msgpack_convert(n, oh.get(), msgpack_format::binary), msgpack::type_error);
399             BOOST_CHECK_EQUAL(n, 1);
400         }
401         {
402             // Wrong number of static int limbs.
403             msgpack::sbuffer sbuf;
404             msgpack::packer<msgpack::sbuffer> p(sbuf);
405             p.pack_array(3);
406             p.pack(true);
407             p.pack(true);
408             p.pack_array(3);
409             p.pack(1);
410             p.pack(2);
411             p.pack(3);
412             auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
413             int_type n{1};
414             BOOST_CHECK_THROW(msgpack_convert(n, oh.get(), msgpack_format::binary), std::invalid_argument);
415             BOOST_CHECK_EQUAL(n, 1);
416         }
417         {
418             // Static int, wrong limb type.
419             msgpack::sbuffer sbuf;
420             msgpack::packer<msgpack::sbuffer> p(sbuf);
421             p.pack_array(3);
422             p.pack(true);
423             p.pack(true);
424             p.pack_array(2);
425             p.pack(1);
426             p.pack("hello");
427             auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
428             int_type n{1};
429             BOOST_CHECK_THROW(msgpack_convert(n, oh.get(), msgpack_format::binary), msgpack::type_error);
430             BOOST_CHECK_EQUAL(n, 0);
431         }
432         {
433             // Dynamic int, wrong limb type.
434             msgpack::sbuffer sbuf;
435             msgpack::packer<msgpack::sbuffer> p(sbuf);
436             p.pack_array(3);
437             p.pack(false);
438             p.pack(true);
439             p.pack_array(2);
440             p.pack(1);
441             p.pack("hello");
442             auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
443             int_type n{1};
444             BOOST_CHECK_THROW(msgpack_convert(n, oh.get(), msgpack_format::binary), msgpack::type_error);
445             BOOST_CHECK_EQUAL(n, 0);
446         }
447         {
448             // Wrong string.
449             msgpack::sbuffer sbuf;
450             msgpack::packer<msgpack::sbuffer> p(sbuf);
451             p.pack("booyah");
452             auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
453             int_type n{1};
454             BOOST_CHECK_THROW(msgpack_convert(n, oh.get(), msgpack_format::portable), std::invalid_argument);
455             BOOST_CHECK_EQUAL(n, 1);
456         }
457     }
458 };
459 
BOOST_AUTO_TEST_CASE(mp_integer_msgpack_s11n_test)460 BOOST_AUTO_TEST_CASE(mp_integer_msgpack_s11n_test)
461 {
462     boost::mpl::for_each<size_types>(msgpack_s11n_tester());
463 }
464 
465 #endif
466