Skip to content

Commit

Permalink
Use memmove for bitshifts when machine is little endian.
Browse files Browse the repository at this point in the history
  • Loading branch information
jzmaddock committed Feb 24, 2016
1 parent 2f635b4 commit cb1a418
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 76 deletions.
316 changes: 246 additions & 70 deletions include/boost/multiprecision/cpp_int/bitwise.hpp
Expand Up @@ -306,30 +306,86 @@ BOOST_MP_FORCEINLINE typename enable_if_c<is_unsigned_number<cpp_int_backend<Min
result.normalize();
}

template <unsigned MinBits1, unsigned MaxBits1, cpp_integer_type SignType1, cpp_int_check_type Checked1, class Allocator1>
inline typename enable_if_c<!is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1> >::value>::type
eval_left_shift(
cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
double_limb_type s) BOOST_MP_NOEXCEPT_IF((is_non_throwing_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1> >::value))
template <class Int>
inline void left_shift_byte(Int& result, double_limb_type s)
{
is_valid_bitwise_op(result, typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::checked_type());
if(!s)
limb_type offset = static_cast<limb_type>(s / Int::limb_bits);
limb_type shift = static_cast<limb_type>(s % Int::limb_bits);
unsigned ors = result.size();
if((ors == 1) && (!*result.limbs()))
return; // shifting zero yields zero.
unsigned rs = ors;
if(shift && (result.limbs()[ors - 1] >> (Int::limb_bits - shift)))
++rs; // Most significant limb will overflow when shifted
rs += offset;
result.resize(rs, rs);
rs = result.size();

typename Int::limb_pointer pr = result.limbs();

if(rs != ors)
pr[rs - 1] = 0u;
std::size_t bytes = s / CHAR_BIT;
std::size_t len = std::min(ors * sizeof(limb_type), rs * sizeof(limb_type) - bytes);
if(bytes >= rs * sizeof(limb_type))
result = static_cast<limb_type>(0u);
else
{
unsigned char* pc = reinterpret_cast<unsigned char*>(pr);
std::memmove(pc + bytes, pc, len);
std::memset(pc, 0, bytes);
}
}

template <class Int>
inline void left_shift_limb(Int& result, double_limb_type s)
{
limb_type offset = static_cast<limb_type>(s / Int::limb_bits);
limb_type shift = static_cast<limb_type>(s % Int::limb_bits);

unsigned ors = result.size();
if((ors == 1) && (!*result.limbs()))
return; // shifting zero yields zero.
unsigned rs = ors;
if(shift && (result.limbs()[ors - 1] >> (Int::limb_bits - shift)))
++rs; // Most significant limb will overflow when shifted
rs += offset;
result.resize(rs, rs);
bool truncated = result.size() != rs;

typename Int::limb_pointer pr = result.limbs();

if(offset > rs)
{
// The result is shifted past the end of the result:
result = static_cast<limb_type>(0);
return;
}

limb_type offset = static_cast<limb_type>(s / cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits);
limb_type shift = static_cast<limb_type>(s % cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits);
unsigned i = rs - result.size();
for(; i < ors; ++i)
pr[rs - 1 - i] = pr[ors - 1 - i];
for(; i < rs; ++i)
pr[rs - 1 - i] = 0;
}

template <class Int>
inline void left_shift_generic(Int& result, double_limb_type s)
{
limb_type offset = static_cast<limb_type>(s / Int::limb_bits);
limb_type shift = static_cast<limb_type>(s % Int::limb_bits);

unsigned ors = result.size();
if((ors == 1) && (!*result.limbs()))
return; // shifting zero yields zero.
unsigned rs = ors;
if(shift && (result.limbs()[ors - 1] >> (cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits - shift)))
if(shift && (result.limbs()[ors - 1] >> (Int::limb_bits - shift)))
++rs; // Most significant limb will overflow when shifted
rs += offset;
result.resize(rs, rs);
bool truncated = result.size() != rs;

typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_pointer pr = result.limbs();
typename Int::limb_pointer pr = result.limbs();

if(offset > rs)
{
Expand All @@ -339,105 +395,225 @@ inline typename enable_if_c<!is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBit
}

unsigned i = rs - result.size();
if(shift)
// This code only works when shift is non-zero, otherwise we invoke undefined behaviour!
BOOST_ASSERT(shift);
if(!truncated)
{
// This code only works when shift is non-zero, otherwise we invoke undefined behaviour!
if(!truncated)
if(rs > ors + offset)
{
if(rs > ors + offset)
{
pr[rs - 1 - i] = pr[ors - 1 - i] >> (cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits - shift);
--rs;
}
else
{
pr[rs - 1 - i] = pr[ors - 1 - i] << shift;
if(ors > 1)
pr[rs - 1 - i] |= pr[ors - 2 - i] >> (cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits - shift);
++i;
}
}
for(; ors > 1 + i; ++i)
{
pr[rs - 1 - i] = pr[ors - 1 - i] << shift;
pr[rs - 1 - i] |= pr[ors - 2 - i] >> (cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits - shift);
pr[rs - 1 - i] = pr[ors - 1 - i] >> (Int::limb_bits - shift);
--rs;
}
if(ors >= 1 + i)
else
{
pr[rs - 1 - i] = pr[ors - 1 - i] << shift;
if(ors > 1)
pr[rs - 1 - i] |= pr[ors - 2 - i] >> (Int::limb_bits - shift);
++i;
}
for(; i < rs; ++i)
pr[rs - 1 - i] = 0;
}
else
for(; ors > 1 + i; ++i)
{
for(; i < ors; ++i)
pr[rs - 1 - i] = pr[ors - 1 - i];
for(; i < rs; ++i)
pr[rs - 1 - i] = 0;
pr[rs - 1 - i] = pr[ors - 1 - i] << shift;
pr[rs - 1 - i] |= pr[ors - 2 - i] >> (Int::limb_bits - shift);
}
//
// We may have shifted off the end and have leading zeros:
//
result.normalize();
if(ors >= 1 + i)
{
pr[rs - 1 - i] = pr[ors - 1 - i] << shift;
++i;
}
for(; i < rs; ++i)
pr[rs - 1 - i] = 0;
}

template <unsigned MinBits1, unsigned MaxBits1, cpp_integer_type SignType1, cpp_int_check_type Checked1, class Allocator1>
inline typename enable_if_c<!is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1> >::value>::type
eval_right_shift(
eval_left_shift(
cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
double_limb_type s) BOOST_MP_NOEXCEPT_IF((is_non_throwing_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1> >::value))
{
is_valid_bitwise_op(result, typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::checked_type());
if(!s)
return;

bool is_neg = result.sign();
if(is_neg)
eval_increment(result);
#if defined(BOOST_LITTLE_ENDIAN) && defined(BOOST_MP_USE_LIMB_SHIFT)
static const limb_type limb_shift_mask = cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits - 1;
static const limb_type byte_shift_mask = CHAR_BIT - 1;
if((s & limb_shift_mask) == 0)
{
left_shift_limb(result, s);
}
else if((s & byte_shift_mask) == 0)
{
left_shift_byte(result, s);
}
#elif defined(BOOST_LITTLE_ENDIAN)
static const limb_type byte_shift_mask = CHAR_BIT - 1;
if((s & byte_shift_mask) == 0)
{
left_shift_byte(result, s);
}
#else
static const limb_type limb_shift_mask = cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits - 1;
if((s & limb_shift_mask) == 0)
{
left_shift_limb(result, s);
}
#endif
else
{
left_shift_generic(result, s);
}
//
// We may have shifted off the end and have leading zeros:
//
result.normalize();
}

limb_type offset = static_cast<limb_type>(s / cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits);
limb_type shift = static_cast<limb_type>(s % cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits);
template <class Int>
inline void right_shift_byte(Int& result, double_limb_type s)
{
limb_type offset = static_cast<limb_type>(s / Int::limb_bits);
limb_type shift;
BOOST_ASSERT((s % CHAR_BIT) == 0);
unsigned ors = result.size();
unsigned rs = ors;
if(offset >= rs)
{
if(is_neg)
result = signed_limb_type(-1);
else
result = limb_type(0);
result = limb_type(0);
return;
}
rs -= offset;
typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_pointer pr = result.limbs();
if((pr[ors - 1] >> shift) == 0)
--rs;
if(rs == 0)
typename Int::limb_pointer pr = result.limbs();
unsigned char* pc = reinterpret_cast<unsigned char*>(pr);
shift = static_cast<limb_type>(s / CHAR_BIT);
std::memmove(pc, pc + shift, ors * sizeof(pr[0]) - shift);
shift = (sizeof(limb_type) - shift % sizeof(limb_type)) * CHAR_BIT;
if(shift < Int::limb_bits)
{
if(is_neg)
result = signed_limb_type(-1);
else
result = limb_type(0);
pr[ors - offset - 1] &= (static_cast<limb_type>(1u) << shift) - 1;
if(!pr[ors - offset - 1] && (rs > 1))
--rs;
}
result.resize(rs, rs);
}

template <class Int>
inline void right_shift_limb(Int& result, double_limb_type s)
{
limb_type offset = static_cast<limb_type>(s / Int::limb_bits);
BOOST_ASSERT((s % Int::limb_bits) == 0);
unsigned ors = result.size();
unsigned rs = ors;
if(offset >= rs)
{
result = limb_type(0);
return;
}
rs -= offset;
typename Int::limb_pointer pr = result.limbs();
unsigned i = 0;
if(shift)
for(; i < rs; ++i)
pr[i] = pr[i + offset];
result.resize(rs, rs);
}

template <class Int>
inline void right_shift_generic(Int& result, double_limb_type s)
{
limb_type offset = static_cast<limb_type>(s / Int::limb_bits);
limb_type shift = static_cast<limb_type>(s % Int::limb_bits);
unsigned ors = result.size();
unsigned rs = ors;
if(offset >= rs)
{
result = limb_type(0);
return;
}
rs -= offset;
typename Int::limb_pointer pr = result.limbs();
if((pr[ors - 1] >> shift) == 0)
{
// This code only works for non-zero shift, otherwise we invoke undefined behaviour!
for(; i + offset + 1 < ors; ++i)
if(--rs == 0)
{
pr[i] = pr[i + offset] >> shift;
pr[i] |= pr[i + offset + 1] << (cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::limb_bits - shift);
result = limb_type(0);
return;
}
pr[i] = pr[i + offset] >> shift;
}
else
unsigned i = 0;

// This code only works for non-zero shift, otherwise we invoke undefined behaviour!
BOOST_ASSERT(shift);
for(; i + offset + 1 < ors; ++i)
{
for(; i < rs; ++i)
pr[i] = pr[i + offset];
pr[i] = pr[i + offset] >> shift;
pr[i] |= pr[i + offset + 1] << (Int::limb_bits - shift);
}
pr[i] = pr[i + offset] >> shift;
result.resize(rs, rs);
}

template <unsigned MinBits1, unsigned MaxBits1, cpp_int_check_type Checked1, class Allocator1>
inline typename enable_if_c<!is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, unsigned_magnitude, Checked1, Allocator1> >::value>::type
eval_right_shift(
cpp_int_backend<MinBits1, MaxBits1, unsigned_magnitude, Checked1, Allocator1>& result,
double_limb_type s) BOOST_MP_NOEXCEPT_IF((is_non_throwing_cpp_int<cpp_int_backend<MinBits1, MaxBits1, unsigned_magnitude, Checked1, Allocator1> >::value))
{
is_valid_bitwise_op(result, typename cpp_int_backend<MinBits1, MaxBits1, unsigned_magnitude, Checked1, Allocator1>::checked_type());
if(!s)
return;

#if defined(BOOST_LITTLE_ENDIAN) && defined(BOOST_MP_USE_LIMB_SHIFT)
static const limb_type limb_shift_mask = cpp_int_backend<MinBits1, MaxBits1, signed_magnitude, Checked1, Allocator1>::limb_bits - 1;
static const limb_type byte_shift_mask = CHAR_BIT - 1;
if((s & limb_shift_mask) == 0)
right_shift_limb(result, s);
else if((s & byte_shift_mask) == 0)
right_shift_byte(result, s);
#elif defined(BOOST_LITTLE_ENDIAN)
static const limb_type byte_shift_mask = CHAR_BIT - 1;
if((s & byte_shift_mask) == 0)
right_shift_byte(result, s);
#else
static const limb_type limb_shift_mask = cpp_int_backend<MinBits1, MaxBits1, signed_magnitude, Checked1, Allocator1>::limb_bits - 1;
if((s & limb_shift_mask) == 0)
right_shift_limb(result, s);
#endif
else
right_shift_generic(result, s);
}
template <unsigned MinBits1, unsigned MaxBits1, cpp_int_check_type Checked1, class Allocator1>
inline typename enable_if_c<!is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, signed_magnitude, Checked1, Allocator1> >::value>::type
eval_right_shift(
cpp_int_backend<MinBits1, MaxBits1, signed_magnitude, Checked1, Allocator1>& result,
double_limb_type s) BOOST_MP_NOEXCEPT_IF((is_non_throwing_cpp_int<cpp_int_backend<MinBits1, MaxBits1, signed_magnitude, Checked1, Allocator1> >::value))
{
is_valid_bitwise_op(result, typename cpp_int_backend<MinBits1, MaxBits1, signed_magnitude, Checked1, Allocator1>::checked_type());
if(!s)
return;

bool is_neg = result.sign();
if(is_neg)
eval_increment(result);

#if defined(BOOST_LITTLE_ENDIAN) && defined(BOOST_MP_USE_LIMB_SHIFT)
static const limb_type limb_shift_mask = cpp_int_backend<MinBits1, MaxBits1, signed_magnitude, Checked1, Allocator1>::limb_bits - 1;
static const limb_type byte_shift_mask = CHAR_BIT - 1;
if((s & limb_shift_mask) == 0)
right_shift_limb(result, s);
else if((s & byte_shift_mask) == 0)
right_shift_byte(result, s);
#elif defined(BOOST_LITTLE_ENDIAN)
static const limb_type byte_shift_mask = CHAR_BIT - 1;
if((s & byte_shift_mask) == 0)
right_shift_byte(result, s);
#else
static const limb_type limb_shift_mask = cpp_int_backend<MinBits1, MaxBits1, signed_magnitude, Checked1, Allocator1>::limb_bits - 1;
if((s & limb_shift_mask) == 0)
right_shift_limb(result, s);
#endif
else
right_shift_generic(result, s);
if(is_neg)
eval_decrement(result);
}
Expand Down

0 comments on commit cb1a418

Please sign in to comment.