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