diff --git a/include/boost/multiprecision/cpp_int/import_export.hpp b/include/boost/multiprecision/cpp_int/import_export.hpp index f2f15f64..3a2473b0 100644 --- a/include/boost/multiprecision/cpp_int/import_export.hpp +++ b/include/boost/multiprecision/cpp_int/import_export.hpp @@ -85,46 +85,97 @@ namespace boost { *newval.limbs() = 0; } + template + number, ExpressionTemplates>& + import_bits_generic( + number, ExpressionTemplates>& val, Iterator i, Iterator j, unsigned chunk_size = 0, bool msv_first = true) + { + typename number, ExpressionTemplates>::backend_type newval; + + typedef typename std::iterator_traits::value_type value_type; + typedef typename boost::make_unsigned::type unsigned_value_type; + typedef typename std::iterator_traits::difference_type difference_type; + typedef typename boost::make_unsigned::type size_type; + typedef typename cpp_int_backend::trivial_tag tag_type; + + if(!chunk_size) + chunk_size = std::numeric_limits::digits; + + size_type limbs = std::distance(i, j); + size_type bits = limbs * chunk_size; + + detail::resize_to_bit_size(newval, static_cast(bits), tag_type()); + + difference_type bit_location = msv_first ? bits - chunk_size : 0; + difference_type bit_location_change = msv_first ? -static_cast(chunk_size) : chunk_size; + + while(i != j) + { + detail::assign_bits(newval, static_cast(*i), static_cast(bit_location), chunk_size, tag_type()); + ++i; + bit_location += bit_location_change; + } + + newval.normalize(); + + val.backend().swap(newval); + return val; + } + + template + inline typename boost::disable_if_c >::value, number, ExpressionTemplates>&>::type + import_bits_fast( + number, ExpressionTemplates>& val, T* i, T* j, unsigned chunk_size = 0) + { + std::size_t byte_len = (j - i) * (chunk_size ? chunk_size / CHAR_BIT : sizeof(*i)); + std::size_t limb_len = byte_len / sizeof(limb_type); + if(byte_len % sizeof(limb_type)) + ++limb_len; + cpp_int_backend& result = val.backend(); + result.resize(limb_len, limb_len); // checked types may throw here if they're not large enough to hold the data! + result.limbs()[result.size() - 1] = 0u; + std::memcpy(result.limbs(), i, std::min(byte_len, result.size() * sizeof(limb_type))); + result.normalize(); // In case data has leading zeros. + return val; + } + template + inline typename boost::enable_if_c >::value, number, ExpressionTemplates>&>::type + import_bits_fast( + number, ExpressionTemplates>& val, T* i, T* j, unsigned chunk_size = 0) + { + cpp_int_backend& result = val.backend(); + std::size_t byte_len = (j - i) * (chunk_size ? chunk_size / CHAR_BIT : sizeof(*i)); + std::size_t limb_len = byte_len / sizeof(result.limbs()[0]); + if(byte_len % sizeof(result.limbs()[0])) + ++limb_len; + result.limbs()[0] = 0u; + result.resize(limb_len, limb_len); // checked types may throw here if they're not large enough to hold the data! + std::memcpy(result.limbs(), i, std::min(byte_len, result.size() * sizeof(result.limbs()[0]))); + result.normalize(); // In case data has leading zeros. + return val; + } } template - number, ExpressionTemplates>& + inline number, ExpressionTemplates>& import_bits( number, ExpressionTemplates>& val, Iterator i, Iterator j, unsigned chunk_size = 0, bool msv_first = true) { - typename number, ExpressionTemplates>::backend_type newval; - - typedef typename std::iterator_traits::value_type value_type; - typedef typename boost::make_unsigned::type unsigned_value_type; - typedef typename std::iterator_traits::difference_type difference_type; - typedef typename boost::make_unsigned::type size_type; - typedef typename cpp_int_backend::trivial_tag tag_type; - - if(!chunk_size) - chunk_size = std::numeric_limits::digits; - - size_type limbs = std::distance(i, j); - size_type bits = limbs * chunk_size; - - detail::resize_to_bit_size(newval, static_cast(bits), tag_type()); - - difference_type bit_location = msv_first ? bits - chunk_size : 0; - difference_type bit_location_change = msv_first ? -static_cast(chunk_size) : chunk_size; - - while(i != j) - { - detail::assign_bits(newval, static_cast(*i), static_cast(bit_location), chunk_size, tag_type()); - ++i; - bit_location += bit_location_change; - } - - newval.normalize(); - - val.backend().swap(newval); - return val; + return detail::import_bits_generic(val, i, j, chunk_size, msv_first); } + template + inline number, ExpressionTemplates>& + import_bits( + number, ExpressionTemplates>& val, T* i, T* j, unsigned chunk_size = 0, bool msv_first = true) + { +#ifdef BOOST_LITTLE_ENDIAN + if(((chunk_size % CHAR_BIT) == 0) && !msv_first) + return detail::import_bits_fast(val, i, j, chunk_size); +#endif + return detail::import_bits_generic(val, i, j, chunk_size, msv_first); + } namespace detail { @@ -152,6 +203,7 @@ namespace boost { boost::uintmax_t mask = count == std::numeric_limits::digits ? ~static_cast(0) : (static_cast(1u) << count) - 1; return (result >> location) & mask; } + } template diff --git a/test/test_cpp_int_import_export.cpp b/test/test_cpp_int_import_export.cpp index 83cbed35..50d6cef7 100644 --- a/test/test_cpp_int_import_export.cpp +++ b/test/test_cpp_int_import_export.cpp @@ -63,16 +63,38 @@ void test_round_trip() T newval; import_bits(newval, cv.begin(), cv.end()); BOOST_CHECK_EQUAL(val, newval); + // Should get the same value if we reverse the bytes: + std::reverse(cv.begin(), cv.end()); + newval = 0; + import_bits(newval, cv.begin(), cv.end(), 8, false); + BOOST_CHECK_EQUAL(val, newval); + // Also try importing via pointers as these may memcpy: + newval = 0; + import_bits(newval, &cv[0], &cv[0] + cv.size(), 8, false); + BOOST_CHECK_EQUAL(val, newval); cv.clear(); export_bits(val, std::back_inserter(cv), 8, false); import_bits(newval, cv.begin(), cv.end(), 8, false); BOOST_CHECK_EQUAL(val, newval); + std::reverse(cv.begin(), cv.end()); + newval = 0; + import_bits(newval, cv.begin(), cv.end(), 8, true); + BOOST_CHECK_EQUAL(val, newval); std::vector bv; export_bits(val, std::back_inserter(bv), std::numeric_limits::digits); import_bits(newval, bv.begin(), bv.end()); BOOST_CHECK_EQUAL(val, newval); + // Should get the same value if we reverse the values: + std::reverse(bv.begin(), bv.end()); + newval = 0; + import_bits(newval, bv.begin(), bv.end(), std::numeric_limits::digits, false); + BOOST_CHECK_EQUAL(val, newval); + // Also try importing via pointers as these may memcpy: + newval = 0; + import_bits(newval, &bv[0], &bv[0] + bv.size(), std::numeric_limits::digits, false); + BOOST_CHECK_EQUAL(val, newval); bv.clear(); export_bits(val, std::back_inserter(bv), std::numeric_limits::digits, false);