1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
use core::ptr;
use awint_internals::*;
use const_fn::const_fn;
use crate::Bits;
/// # Casting between `Bits` of arbitrary sizes
impl Bits {
/// Resize-copy-assigns `rhs` to `self`. If `self.bw() >= rhs.bw()`, the
/// copied value of `rhs` will be extended with bits set to `extension`. If
/// `self.bw() < rhs.bw()`, the copied value of `rhs` will be truncated.
#[const_fn(cfg(feature = "const_support"))]
pub const fn resize_(&mut self, rhs: &Self, extension: bool) {
// Safety: the exact number of digits needed are copied or set
unsafe {
if self.bw() <= rhs.bw() {
// truncation
ptr::copy_nonoverlapping(rhs.as_ptr(), self.as_mut_ptr(), self.total_digits());
self.clear_unused_bits();
} else {
ptr::copy_nonoverlapping(rhs.as_ptr(), self.as_mut_ptr(), rhs.total_digits());
if extension && (rhs.unused() != 0) {
*self.get_unchecked_mut(rhs.total_digits() - 1) |= MAX << rhs.extra();
}
self.digit_set(
extension,
rhs.total_digits()..self.total_digits(),
extension,
)
}
}
}
/// Zero-resize-copy-assigns `rhs` to `self` and returns overflow. This is
/// the same as `lhs.resize_(rhs, false)`, but returns `true` if the
/// unsigned meaning of the integer is changed.
#[const_fn(cfg(feature = "const_support"))]
pub const fn zero_resize_(&mut self, rhs: &Self) -> bool {
self.resize_(rhs, false);
if self.bw() < rhs.bw() {
// Safety: `self.total_digits() <= rhs.total_digits()` because of the above
// check
unsafe {
// check if there are set bits that would be truncated
if (self.extra() != 0)
&& ((rhs.get_unchecked(self.total_digits() - 1) >> self.extra()) != 0)
{
return true
}
const_for!(i in {self.total_digits()..rhs.total_digits()} {
if rhs.get_unchecked(i) != 0 {
return true
}
});
}
}
false
}
/// Sign-resize-copy-assigns `rhs` to `self` and returns overflow. This is
/// the same as `lhs.resize_(rhs, rhs.msb())`, but returns `true` if
/// the signed meaning of the integer is changed.
#[const_fn(cfg(feature = "const_support"))]
pub const fn sign_resize_(&mut self, rhs: &Self) -> bool {
self.resize_(rhs, rhs.msb());
// this function is far harder to implement than it would first seem
if self.bw() < rhs.bw() {
// Safety: `self.total_digits() <= rhs.total_digits()` because of the above
// check
unsafe {
if rhs.msb() {
// check if the new most significant bit is unset (which would mean overflow
// from negative to positive)
if !self.msb() {
return true
}
// check if there are unset bits that would be truncated
if self.total_digits() == rhs.total_digits() {
// first and only digit
if rhs.extra() != 0 {
// rhs extra mask and lhs cutoff mask
let expected = (MAX >> (BITS - rhs.extra())) & (MAX << self.extra());
if (rhs.last() & expected) != expected {
return true
}
} else {
let expected = MAX << self.extra();
if (rhs.last() & expected) != expected {
return true
}
}
// avoid the other tests if this is the only digit
return false
}
// first digit
if self.extra() != 0 {
let expected = MAX << self.extra();
if (rhs.get_unchecked(self.total_digits() - 1) & expected) != expected {
return true
}
}
// middle digits
const_for!(i in {self.total_digits()..(rhs.total_digits() - 1)} {
if rhs.get_unchecked(i) != MAX {
return true
}
});
// last digit
if rhs.extra() != 0 {
let expected = MAX >> (BITS - rhs.extra());
if (rhs.last() & expected) != expected {
return true
}
} else if rhs.last() != MAX {
return true
}
} else {
// check if the new most significant bit is set (which would mean overflow from
// positive to negative)
if self.msb() {
return true
}
// check if there are set bits that would be truncated
if (self.extra() != 0)
&& ((rhs.get_unchecked(self.total_digits() - 1) >> self.extra()) != 0)
{
return true
}
// Safety: `self.total_digits() <= rhs.total_digits()` because of the above
// check
const_for!(i in {self.total_digits()..rhs.total_digits()} {
if rhs.get_unchecked(i) != 0 {
return true
}
});
}
}
}
false
}
}