use super::{ByteOrder, Number};
use core::hint::select_unpredictable;
use core::ops::{Add, AddAssign};
use core::{mem, ptr};
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
target_pointer_width = "64",
any(
target_vendor = "apple",
target_os = "linux",
target_os = "android",
target_os = "windows",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia"
),
not(miri)
))]
#[path = "add/aarch64.rs"]
mod kernels;
#[cfg(all(target_arch = "x86_64", not(miri)))]
#[path = "add/x86_64.rs"]
mod kernels;
#[cfg(not(any(
all(
target_arch = "aarch64",
target_endian = "little",
target_pointer_width = "64",
any(
target_vendor = "apple",
target_os = "linux",
target_os = "android",
target_os = "windows",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia"
),
not(miri)
),
all(target_arch = "x86_64", not(miri))
)))]
#[path = "add/portable.rs"]
mod kernels;
use kernels::{add_n_be, add_n_le, sub_n_be, sub_n_le};
#[cfg(target_endian = "little")]
#[inline]
pub(super) unsafe fn add_limbs(r: *mut u64, a: *const u64, b: *const u64, n: usize) -> bool {
unsafe { add_n_le(r.cast(), a.cast(), b.cast(), n, false) }
}
#[cfg(target_endian = "little")]
#[inline]
pub(super) unsafe fn sub_limbs(r: *mut u64, a: *const u64, b: *const u64, n: usize) -> bool {
unsafe { sub_n_le(r.cast(), a.cast(), b.cast(), n, false) }
}
impl Add<&Number> for &Number {
type Output = Number;
fn add(self, rhs: &Number) -> Number {
if small(self, rhs) {
return small_number_of(self, rhs, rhs.negative);
}
if medium(self, rhs) {
return medium_new(self, rhs, rhs.negative);
}
ref_plus_ref(self, rhs)
}
}
impl Add<&Number> for Number {
type Output = Number;
fn add(mut self, rhs: &Number) -> Number {
self += rhs;
self
}
}
impl Add<Number> for &Number {
type Output = Number;
fn add(self, mut rhs: Number) -> Number {
if small(self, &rhs) {
let sum = small_sum(self, &rhs, rhs.negative);
let order = self.byte_order;
unsafe {
if rhs.value.capacity() >= sum.len {
write_small_in(&mut rhs.value, order, &sum);
} else {
replace_small(&mut rhs.value, order, sum.lo, sum.hi, sum.len, sum.k);
}
}
return Number {
value: rhs.value,
negative: sum.negative,
byte_order: order,
};
}
if medium(self, &rhs) {
let (b, negative): (*mut Number, bool) = (&mut rhs, rhs.negative);
unsafe { medium_into(self, b, negative, b, Fallback::New) };
return rhs;
}
ref_plus_value(self, &mut rhs)
}
}
impl Add<Number> for Number {
type Output = Number;
fn add(mut self, rhs: Number) -> Number {
self += rhs;
self
}
}
impl AddAssign<&Number> for Number {
#[inline]
fn add_assign(&mut self, rhs: &Number) {
if small(self, rhs) {
small_add_to(self, rhs, rhs.negative);
return;
}
if medium(self, rhs) {
let (a, b) = (ptr::from_mut(self), ptr::from_ref(rhs).cast_mut());
unsafe { medium_into(a, b, rhs.negative, a, Fallback::New) };
return;
}
add_assign_ref(self, rhs);
}
}
impl AddAssign<Number> for Number {
#[inline]
fn add_assign(&mut self, mut rhs: Number) {
if small(self, &rhs) {
let sum = small_sum(self, &rhs, rhs.negative);
self.negative = sum.negative;
let order = self.byte_order;
unsafe {
if self.value.capacity() >= sum.len {
write_small_in(&mut self.value, order, &sum);
} else if rhs.value.capacity() >= sum.len {
write_small_in(&mut rhs.value, order, &sum);
mem::swap(&mut self.value, &mut rhs.value);
} else {
replace_small(&mut self.value, order, sum.lo, sum.hi, sum.len, sum.k);
}
}
return;
}
if medium(self, &rhs) {
let (a, negative) = (ptr::from_mut(self), rhs.negative);
unsafe { medium_into(a, &mut rhs, negative, a, Fallback::Other) };
return;
}
add_assign_value(self, &mut rhs);
}
}
#[inline(never)]
pub(super) fn ref_plus_ref(a: &Number, b: &Number) -> Number {
let plan = plan(a, b);
let value = unsafe { sum_new(a, b, &plan) };
Number {
value,
negative: plan.negative,
byte_order: a.byte_order,
}
}
#[inline(never)]
fn ref_plus_value(a: &Number, b: &mut Number) -> Number {
let plan = plan(a, b);
let order = a.byte_order;
let reverse = b.byte_order != order && plan.len > 0;
if reverse {
b.value.reverse();
b.byte_order = order;
}
let value = if plan.fits(b.value.capacity()) {
unsafe { sum_in(&mut b.value, order, true, &a.value, &plan, reverse) };
mem::take(&mut b.value)
} else {
unsafe { sum_new(a, b, &plan) }
};
Number {
value,
negative: plan.negative,
byte_order: order,
}
}
#[inline(never)]
pub(super) fn add_assign_ref<'a>(a: &'a mut Number, b: &Number) -> &'a mut Number {
let plan = plan(a, b);
if !plan.fits(a.value.capacity()) {
replace_with_sum(a, b, &plan);
return a;
}
let reverse = b.byte_order != a.byte_order && plan.len > 0;
if reverse {
a.value.reverse();
}
unsafe { sum_in(&mut a.value, b.byte_order, false, &b.value, &plan, reverse) };
if reverse {
a.value.reverse();
}
a.negative = plan.negative;
a
}
#[inline(never)]
fn add_assign_value(a: &mut Number, b: &mut Number) {
let plan = plan(a, b);
let order = a.byte_order;
let reverse = b.byte_order != order && plan.len > 0;
if reverse {
b.value.reverse();
b.byte_order = order;
}
let in_a = plan.fits(a.value.capacity());
if !in_a && !plan.fits(b.value.capacity()) {
replace_with_sum(a, b, &plan);
return;
}
let (v, other) = if in_a {
(&mut a.value, &b.value)
} else {
(&mut b.value, &a.value)
};
unsafe { sum_in(v, order, !in_a, other, &plan, reverse) };
if !in_a {
mem::swap(&mut a.value, &mut b.value);
}
a.negative = plan.negative;
}
#[cold]
#[inline(never)]
fn replace_with_sum(a: &mut Number, b: &Number, plan: &Plan) {
a.value = unsafe { sum_new(a, b, plan) };
a.negative = plan.negative;
}
pub(super) const SMALL: usize = 16;
#[inline(always)]
pub(super) fn small(a: &Number, b: &Number) -> bool {
a.value.len() <= SMALL && b.value.len() <= SMALL
}
#[inline(always)]
pub(super) fn small_number_of(a: &Number, b: &Number, negative_b: bool) -> Number {
let sum = small_sum(a, b, negative_b);
unsafe { small_number(a.byte_order, sum.lo, sum.hi, sum.len, sum.k, sum.negative) }
}
#[inline(always)]
pub(super) fn small_add_to(a: &mut Number, b: &Number, negative_b: bool) {
let sum = small_sum(a, b, negative_b);
a.negative = sum.negative;
let order = a.byte_order;
unsafe {
if a.value.capacity() >= sum.len {
write_small_in(&mut a.value, order, &sum);
} else {
replace_small(&mut a.value, order, sum.lo, sum.hi, sum.len, sum.k);
}
}
}
struct Small {
lo: u64,
hi: u64,
len: usize,
k: usize,
negative: bool,
}
#[inline(always)]
fn small_sum(a: &Number, b: &Number, negative_b: bool) -> Small {
let (x, y) = unsafe {
(
read_small(&a.value, a.byte_order),
read_small(&b.value, b.byte_order),
)
};
let subtract = a.negative != negative_b;
let (sum, carry) = x.overflowing_add(y);
let (difference, borrow) = x.overflowing_sub(y);
let difference = select_unpredictable(borrow, y.wrapping_sub(x), difference);
let magnitude = select_unpredictable(subtract, difference, sum);
let carry = carry & !subtract;
let len = select_unpredictable(
carry,
SMALL + 1,
SMALL - (magnitude | 1).leading_zeros() as usize / 8,
);
Small {
lo: magnitude as u64,
hi: (magnitude >> 64) as u64,
len,
k: a.value.len().max(b.value.len()),
negative: a.negative != (subtract && borrow) && (magnitude != 0 || carry),
}
}
#[inline(always)]
pub(super) unsafe fn read_small(v: &[u8], order: ByteOrder) -> u128 {
let (p, n) = (v.as_ptr(), v.len());
let be = order == ByteOrder::BigEndian;
unsafe {
if n >= 4 {
if n >= 8 {
let lo = if be {
u64::from_be_bytes(read(p.add(n - 8)))
} else {
u64::from_le_bytes(read(p))
};
let hi = if n > 8 {
let top = if be {
u64::from_be_bytes(read(p))
} else {
u64::from_le_bytes(read(p.add(n - 8)))
};
top.wrapping_shr((n as u32).wrapping_neg().wrapping_mul(8))
} else {
0
};
u128::from(lo) | (u128::from(hi) << 64)
} else {
let (lo, top) = if be {
(
u32::from_be_bytes(read(p.add(n - 4))),
u32::from_be_bytes(read(p)),
)
} else {
(
u32::from_le_bytes(read(p)),
u32::from_le_bytes(read(p.add(n - 4))),
)
};
u128::from(u64::from(lo) | (u64::from(top) << (8 * (n - 4))))
}
} else if n >= 2 {
let (lo, top) = if be {
(
u16::from_be_bytes(read(p.add(n - 2))),
u16::from_be_bytes(read(p)),
)
} else {
(
u16::from_le_bytes(read(p)),
u16::from_le_bytes(read(p.add(n - 2))),
)
};
u128::from(u32::from(lo) | (u32::from(top) << (8 * (n - 2))))
} else if n == 1 {
u128::from(*p)
} else {
0
}
}
}
#[inline(always)]
pub(super) unsafe fn write_small(p: *mut u8, order: ByteOrder, lo: u64, hi: u64, len: usize) {
let be = order == ByteOrder::BigEndian;
let shift = (len as u32).wrapping_neg().wrapping_mul(8);
unsafe {
if len >= 8 {
if len > SMALL {
if be {
*p = 1;
write(p.add(1), hi.to_be_bytes());
write(p.add(9), lo.to_be_bytes());
} else {
write(p, lo.to_le_bytes());
write(p.add(8), hi.to_le_bytes());
*p.add(16) = 1;
}
} else {
let top = hi.wrapping_shl(shift);
if be {
write(p, top.to_be_bytes());
write(p.add(len - 8), lo.to_be_bytes());
} else {
write(p.add(len - 8), top.to_le_bytes());
write(p, lo.to_le_bytes());
}
}
} else if len >= 4 {
let top = ((lo >> 32) as u32).wrapping_shl(shift);
if be {
write(p, top.to_be_bytes());
write(p.add(len - 4), (lo as u32).to_be_bytes());
} else {
write(p.add(len - 4), top.to_le_bytes());
write(p, (lo as u32).to_le_bytes());
}
} else {
let (mid, last) = (len / 2, len - 1);
let (b0, b1, b2) = (lo as u8, (lo >> (8 * mid)) as u8, (lo >> (8 * last)) as u8);
if be {
*p.add(last) = b0;
*p.add(last - mid) = b1;
*p = b2;
} else {
*p = b0;
*p.add(mid) = b1;
*p.add(last) = b2;
}
}
}
}
#[inline(always)]
unsafe fn write_sum(p: *mut u8, order: ByteOrder, lo: u64, hi: u64, len: usize, k: usize) {
let be = order == ByteOrder::BigEndian;
unsafe {
if k >= 8 {
if len < 8 {
write_small(p, order, lo, hi, len);
} else if k == SMALL && len >= SMALL {
let top = select_unpredictable(len > SMALL, 1, (hi >> 56) as u8);
if be {
write(p.add(len - 8), lo.to_be_bytes());
write(p.add(len - 16), hi.to_be_bytes());
*p = top;
} else {
write(p, lo.to_le_bytes());
write(p.add(8), hi.to_le_bytes());
*p.add(len - 1) = top;
}
} else {
let top = hi.wrapping_shl((len as u32).wrapping_neg().wrapping_mul(8));
if be {
write(p, top.to_be_bytes());
write(p.add(len - 8), lo.to_be_bytes());
} else {
write(p.add(len - 8), top.to_le_bytes());
write(p, lo.to_le_bytes());
}
}
} else if k >= 4 && len >= 4 {
let shift = (len as u32).wrapping_neg().wrapping_mul(8);
let top = ((lo >> 32) as u32).wrapping_shl(shift);
if be {
write(p, top.to_be_bytes());
write(p.add(len - 4), (lo as u32).to_be_bytes());
} else {
write(p.add(len - 4), top.to_le_bytes());
write(p, (lo as u32).to_le_bytes());
}
} else if k == 3 && len >= 2 {
let shift = (len as u32).wrapping_neg().wrapping_mul(8) % 16;
let top = ((lo >> 16) as u16) << shift;
if be {
write(p, top.to_be_bytes());
write(p.add(len - 2), (lo as u16).to_be_bytes());
} else {
write(p.add(len - 2), top.to_le_bytes());
write(p, (lo as u16).to_le_bytes());
}
} else {
let (mid, last) = (len / 2, len - 1);
let (b0, b1, b2) = (lo as u8, (lo >> (8 * mid)) as u8, (lo >> (8 * last)) as u8);
if be {
*p.add(last) = b0;
*p.add(last - mid) = b1;
*p = b2;
} else {
*p = b0;
*p.add(mid) = b1;
*p.add(last) = b2;
}
}
}
}
#[inline(always)]
unsafe fn write_small_in(v: &mut Vec<u8>, order: ByteOrder, sum: &Small) {
unsafe {
write_sum(v.as_mut_ptr(), order, sum.lo, sum.hi, sum.len, sum.k);
v.set_len(sum.len);
}
}
#[inline(always)]
unsafe fn new_small(order: ByteOrder, lo: u64, hi: u64, len: usize, k: usize) -> Vec<u8> {
let mut v = Vec::with_capacity(len);
unsafe {
write_sum(v.as_mut_ptr(), order, lo, hi, len, k);
v.set_len(len);
}
v
}
#[inline(never)]
unsafe fn small_number(
order: ByteOrder,
lo: u64,
hi: u64,
len: usize,
k: usize,
negative: bool,
) -> Number {
unsafe {
core::hint::assert_unchecked(len != 0);
core::hint::assert_unchecked(len <= SMALL + 1);
Number {
value: new_small(order, lo, hi, len, k),
negative,
byte_order: order,
}
}
}
#[cold]
#[inline(never)]
unsafe fn replace_small(v: &mut Vec<u8>, order: ByteOrder, lo: u64, hi: u64, len: usize, k: usize) {
*v = unsafe { new_small(order, lo, hi, len, k) };
}
pub(super) const MEDIUM: usize = 64;
#[inline(always)]
pub(super) fn medium(a: &Number, b: &Number) -> bool {
a.value.len() <= MEDIUM && b.value.len() <= MEDIUM
}
struct Medium {
limbs: [u64; 9],
negative: bool,
}
macro_rules! by_limbs {
($a:expr, $b:expr, $f:ident($($arg:expr),*)) => {
match $a.len().max($b.len()).div_ceil(8) {
..=3 => $f::<3>($($arg),*),
4 => $f::<4>($($arg),*),
5 => $f::<5>($($arg),*),
6 => $f::<6>($($arg),*),
7 => $f::<7>($($arg),*),
_ => $f::<8>($($arg),*),
}
};
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum Fallback {
New,
Other,
}
#[inline(never)]
pub(super) unsafe fn medium_into(
a: *const Number,
b: *mut Number,
negative_b: bool,
out: *mut Number,
fallback: Fallback,
) {
unsafe {
let (x, y) = (&(*a).value, &(*b).value);
by_limbs!(x, y, medium_into_in(a, b, negative_b, out, fallback))
}
}
#[inline(never)]
unsafe fn medium_into_in<const L: usize>(
a: *const Number,
b: *mut Number,
negative_b: bool,
out: *mut Number,
fallback: Fallback,
) {
unsafe {
let order = (*a).byte_order;
let m = medium_sum::<L>(
read_limbs::<L>(&(*a).value, order),
read_limbs::<L>(&(*b).value, (*b).byte_order),
(*a).negative,
negative_b,
);
let r = &m.limbs;
let (len, top) = top_of::<L>(r);
let v = &mut (*out).value;
if !long::<L>(r) || v.capacity() < len {
return medium_slow(a, b, negative_b, out, fallback);
}
write_long::<L>(v.as_mut_ptr(), order, r, len, top);
v.set_len(len);
(*out).negative = m.negative;
(*out).byte_order = order;
}
}
#[inline(never)]
pub(super) fn medium_new(a: &Number, b: &Number, negative_b: bool) -> Number {
unsafe { by_limbs!(a.value, b.value, medium_new_in(a, b, negative_b)) }
}
#[inline(never)]
unsafe fn medium_new_in<const L: usize>(a: &Number, b: &Number, negative_b: bool) -> Number {
let order = a.byte_order;
let m = unsafe {
medium_sum::<L>(
read_limbs::<L>(&a.value, order),
read_limbs::<L>(&b.value, b.byte_order),
a.negative,
negative_b,
)
};
let r = &m.limbs;
if !long::<L>(r) {
return medium_new_slow(a, b, negative_b);
}
let (len, top) = top_of::<L>(r);
let mut v = Vec::with_capacity(len);
unsafe {
write_long::<L>(v.as_mut_ptr(), order, r, len, top);
v.set_len(len);
}
Number {
value: v,
negative: m.negative,
byte_order: order,
}
}
struct Bytes {
bytes: [u8; 72],
len: usize,
negative: bool,
}
impl Bytes {
fn of(a: &Number, b: &Number, negative_b: bool) -> Self {
let limbs = |n: &Number| {
let mut x = [0u64; 9];
let len = n.value.len();
for (i, &byte) in n.value.iter().enumerate() {
let p = if n.byte_order == ByteOrder::BigEndian {
len - 1 - i
} else {
i
};
x[p / 8] |= u64::from(byte) << (8 * (p % 8));
}
x
};
let m = medium_sum::<8>(limbs(a), limbs(b), a.negative, negative_b);
let mut bytes = [0; 72];
for (chunk, limb) in bytes.as_chunks_mut::<8>().0.iter_mut().zip(m.limbs) {
*chunk = limb.to_le_bytes();
}
let top = bytes.iter().rposition(|&b| b != 0);
Bytes {
bytes,
len: top.map_or(1, |p| p + 1),
negative: m.negative && top.is_some(),
}
}
unsafe fn put(&self, v: &mut Vec<u8>, order: ByteOrder) {
let (p, len) = (v.as_mut_ptr(), self.len);
unsafe {
if order == ByteOrder::BigEndian {
for (i, &byte) in self.bytes[..len].iter().enumerate() {
*p.add(len - 1 - i) = byte;
}
} else {
ptr::copy_nonoverlapping(self.bytes.as_ptr(), p, len);
}
v.set_len(len);
}
}
fn into_new(self, order: ByteOrder) -> Number {
let mut value = Vec::with_capacity(self.len);
unsafe { self.put(&mut value, order) };
Number {
value,
negative: self.negative,
byte_order: order,
}
}
}
#[cold]
#[inline(never)]
unsafe fn medium_slow(
a: *const Number,
b: *mut Number,
negative_b: bool,
out: *mut Number,
fallback: Fallback,
) {
unsafe {
let sum = Bytes::of(&*a, &*b, negative_b);
let order = (*a).byte_order;
let v = &mut (*out).value;
if v.capacity() < sum.len {
if fallback == Fallback::Other && (*b).value.capacity() >= sum.len {
mem::swap(v, &mut (*b).value);
} else {
*v = Vec::with_capacity(sum.len);
}
}
sum.put(v, order);
(*out).negative = sum.negative;
(*out).byte_order = order;
}
}
#[cold]
#[inline(never)]
fn medium_new_slow(a: &Number, b: &Number, negative_b: bool) -> Number {
Bytes::of(a, b, negative_b).into_new(a.byte_order)
}
#[inline(always)]
fn adc(x: u64, y: u64, carry: bool) -> (u64, bool) {
let (s, c1) = x.overflowing_add(y);
let (s, c2) = s.overflowing_add(u64::from(carry));
(s, c1 | c2)
}
#[inline(always)]
pub(super) unsafe fn read_limbs<const L: usize>(v: &[u8], order: ByteOrder) -> [u64; 9] {
let n = v.len();
unsafe {
if n > 8 * (L - 1) {
if order == ByteOrder::BigEndian {
exact_limbs::<true, L>(v.as_ptr(), n)
} else {
exact_limbs::<false, L>(v.as_ptr(), n)
}
} else if n <= SMALL {
let s = read_small(v, order);
let mut x = [0; 9];
x[0] = s as u64;
x[1] = (s >> 64) as u64;
x
} else if order == ByteOrder::BigEndian {
clamped_limbs::<true, L>(v.as_ptr(), n)
} else {
clamped_limbs::<false, L>(v.as_ptr(), n)
}
}
}
#[inline(always)]
unsafe fn limb_at<const BE: bool>(v: *const u8, n: usize, p: usize) -> u64 {
unsafe {
if BE {
u64::from_be_bytes(read(v.add(n - 8 - p)))
} else {
u64::from_le_bytes(read(v.add(p)))
}
}
}
#[inline(always)]
unsafe fn exact_limbs<const BE: bool, const L: usize>(v: *const u8, n: usize) -> [u64; 9] {
let mut x = [0; 9];
unsafe {
for (i, limb) in x.iter_mut().enumerate().take(L - 1) {
*limb = limb_at::<BE>(v, n, 8 * i);
}
let shift = (n as u32).wrapping_mul(8).wrapping_neg();
x[L - 1] = limb_at::<BE>(v, n, n - 8).wrapping_shr(shift);
}
x
}
#[inline(always)]
unsafe fn clamped_limbs<const BE: bool, const L: usize>(v: *const u8, n: usize) -> [u64; 9] {
let mut x = [0; 9];
unsafe {
for (i, limb) in x.iter_mut().enumerate().take(L) {
let c = (8 * i).min(n - 8);
let w = limb_at::<BE>(v, n, c);
*limb = if 8 * i < n { w >> (8 * (8 * i - c)) } else { 0 };
}
}
x
}
#[inline(always)]
fn medium_sum<const L: usize>(
x: [u64; 9],
y: [u64; 9],
negative_x: bool,
negative_y: bool,
) -> Medium {
let subtract = negative_x != negative_y;
let m = u64::from(subtract).wrapping_neg();
let (mut s, mut d) = ([0u64; 9], [0u64; 9]);
let (d0, borrow_d) = y[0].overflowing_sub(x[0]);
let (mut carry, mut carry_d) = (subtract, !borrow_d);
d[0] = d0;
(s[0], carry) = adc(x[0], y[0] ^ m, carry);
for i in 1..L {
(s[i], carry) = adc(x[i], y[i] ^ m, carry);
(d[i], carry_d) = adc(y[i], !x[i], carry_d);
}
let borrow = subtract & !carry;
let mut r = [0u64; 9];
for i in 0..L {
r[i] = select_unpredictable(borrow, d[i], s[i]);
}
r[L] = u64::from(carry & !subtract);
Medium {
limbs: r,
negative: negative_x != borrow,
}
}
#[inline(always)]
pub(super) fn long<const L: usize>(r: &[u64; 9]) -> bool {
(r[L] | r[L - 1]) != 0 || r[L - 2] >> 56 != 0
}
#[inline(always)]
pub(super) fn top_of<const L: usize>(r: &[u64; 9]) -> (usize, u64) {
let below = select_unpredictable(
r[L - 1] != 0,
(L - 1, r[L - 1], r[L - 2]),
(L - 2, r[L - 2], r[L - 3]),
);
let (t, high, low) = select_unpredictable(r[L] != 0, (L, r[L], r[L - 1]), below);
let z = high.leading_zeros();
let shift = z & 56;
(
8 * t + 8 - z as usize / 8,
(high << shift) | ((low >> 1) >> (63 - shift)),
)
}
#[inline(always)]
unsafe fn write_long<const L: usize>(
p: *mut u8,
order: ByteOrder,
r: &[u64; 9],
len: usize,
top: u64,
) {
let c = (8 * (L - 1)).min(len - 8);
unsafe {
if order == ByteOrder::BigEndian {
for (i, limb) in r.iter().enumerate().take(L - 1) {
write(p.add(len - 8 - 8 * i), limb.to_be_bytes());
}
write(p.add(len - 8 - c), r[L - 1].to_be_bytes());
write(p, top.to_be_bytes());
} else {
for (i, limb) in r.iter().enumerate().take(L - 1) {
write(p.add(8 * i), limb.to_le_bytes());
}
write(p.add(c), r[L - 1].to_le_bytes());
write(p.add(len - 8), top.to_le_bytes());
}
}
}
struct Plan {
sub: bool,
x_is_b: bool,
la: usize,
lb: usize,
len: usize,
negative: bool,
}
impl Plan {
fn lx(&self) -> usize {
if self.x_is_b { self.lb } else { self.la }
}
fn ly(&self) -> usize {
if self.x_is_b { self.la } else { self.lb }
}
fn fits(&self, capacity: usize) -> bool {
capacity >= self.len.max(1)
}
fn alias_is_x(&self, alias_is_b: bool) -> bool {
alias_is_b == self.x_is_b
}
}
fn plan(a: &Number, b: &Number) -> Plan {
match (a.byte_order, b.byte_order) {
(ByteOrder::BigEndian, ByteOrder::BigEndian) => plan_in::<true, true>(a, b),
(ByteOrder::BigEndian, ByteOrder::LittleEndian) => plan_in::<true, false>(a, b),
(ByteOrder::LittleEndian, ByteOrder::BigEndian) => plan_in::<false, true>(a, b),
(ByteOrder::LittleEndian, ByteOrder::LittleEndian) => plan_in::<false, false>(a, b),
}
}
fn plan_in<const A: bool, const B: bool>(a: &Number, b: &Number) -> Plan {
let la = significant_len::<A>(&a.value);
let lb = significant_len::<B>(&b.value);
let (pa, pb) = (end::<A>(&a.value), end::<B>(&b.value));
let negative_a = a.negative && la > 0;
let negative_b = b.negative && lb > 0;
if negative_a == negative_b || la == 0 || lb == 0 {
let x_is_b = lb > la;
let carry = unsafe {
if x_is_b {
carries::<B, A>(pb, pa, lb, la)
} else {
carries::<A, B>(pa, pb, la, lb)
}
};
return Plan {
sub: false,
x_is_b,
la,
lb,
len: la.max(lb) + usize::from(carry),
negative: negative_a || negative_b,
};
}
unsafe { plan_sub::<A, B>(pa, pb, la, lb, negative_a) }
}
#[inline(never)]
unsafe fn plan_sub<const A: bool, const B: bool>(
pa: *const u8,
pb: *const u8,
la: usize,
lb: usize,
negative_a: bool,
) -> Plan {
let (x_is_b, top) = if la != lb {
(lb > la, la.max(lb) - 1)
} else {
match unsafe { highest_difference::<A, B>(pa, pb, lb, la) } {
Some(k) => (unsafe { get::<B>(pb, k) > get::<A>(pa, k) }, k),
None => {
return Plan {
sub: true,
x_is_b: false,
la,
lb,
len: 0,
negative: false,
};
}
}
};
let len = unsafe {
if A == B {
let (x, y, ly) = select_unpredictable(x_is_b, (pb, pa, la), (pa, pb, lb));
difference_len::<A, B>(x, y, ly, top)
} else if x_is_b {
difference_len::<B, A>(pb, pa, la, top)
} else {
difference_len::<A, B>(pa, pb, lb, top)
}
};
Plan {
sub: true,
x_is_b,
la,
lb,
len,
negative: negative_a != x_is_b,
}
}
#[inline]
pub(super) fn significant_len<const BE: bool>(v: &[u8]) -> usize {
let (p, len) = (v.as_ptr(), v.len());
unsafe {
if BE {
let mut i = 0;
while len - i >= 8 && p.add(i).cast::<[u8; 8]>().read() == [0; 8] {
i += 8;
}
while i < len && *p.add(i) == 0 {
i += 1;
}
len - i
} else {
let mut end = len;
while end >= 8 && p.add(end - 8).cast::<[u8; 8]>().read() == [0; 8] {
end -= 8;
}
while end > 0 && *p.add(end - 1) == 0 {
end -= 1;
}
end
}
}
}
#[inline]
unsafe fn carries<const X: bool, const Y: bool>(
x: *const u8,
y: *const u8,
lx: usize,
ly: usize,
) -> bool {
let mut p = lx;
unsafe {
while p >= ly + 8 {
if load::<X>(x, p - 8) != u64::MAX {
return false;
}
p -= 8;
}
while p > ly {
if get::<X>(x, p - 1) != 0xFF {
return false;
}
p -= 1;
}
while p >= 8 {
let (sum, overflow) = load::<X>(x, p - 8).overflowing_add(load::<Y>(y, p - 8));
if overflow || sum != u64::MAX {
return overflow;
}
p -= 8;
}
while p > 0 {
let sum = u16::from(get::<X>(x, p - 1)) + u16::from(get::<Y>(y, p - 1));
if sum != 0xFF {
return sum > 0xFF;
}
p -= 1;
}
}
false
}
#[inline]
unsafe fn highest_difference<const X: bool, const Y: bool>(
x: *const u8,
y: *const u8,
ly: usize,
mut p: usize,
) -> Option<usize> {
unsafe {
while p >= ly + 8 {
let v = load::<X>(x, p - 8);
if v != 0 {
return Some(p - 1 - (v.leading_zeros() / 8) as usize);
}
p -= 8;
}
while p > ly {
if get::<X>(x, p - 1) != 0 {
return Some(p - 1);
}
p -= 1;
}
while p >= 8 {
let v = load::<X>(x, p - 8) ^ load::<Y>(y, p - 8);
if v != 0 {
return Some(p - 1 - (v.leading_zeros() / 8) as usize);
}
p -= 8;
}
while p > 0 {
if get::<X>(x, p - 1) != get::<Y>(y, p - 1) {
return Some(p - 1);
}
p -= 1;
}
}
None
}
#[inline]
unsafe fn difference_len<const X: bool, const Y: bool>(
x: *const u8,
y: *const u8,
ly: usize,
k: usize,
) -> usize {
unsafe {
if get::<X>(x, k).wrapping_sub(get_or_zero::<Y>(y, ly, k)) != 1 {
return k + 1;
}
let mut p = k;
if p <= ly {
while p >= 8 {
let v = load::<X>(x, p - 8) | !load::<Y>(y, p - 8);
if v != 0 {
p -= (v.leading_zeros() / 8) as usize;
break;
}
p -= 8;
}
while p > 0 && get::<X>(x, p - 1) == 0 && get::<Y>(y, p - 1) == 0xFF {
p -= 1;
}
}
let borrow = match highest_difference::<X, Y>(x, y, ly, p) {
Some(q) => get::<X>(x, q) < get_or_zero::<Y>(y, ly, q),
None => false,
};
p + 1 - usize::from(borrow)
}
}
unsafe fn sum_new(a: &Number, b: &Number, plan: &Plan) -> Vec<u8> {
if plan.len == 0 {
return vec![0];
}
unsafe {
match (a.byte_order, b.byte_order) {
(ByteOrder::BigEndian, ByteOrder::BigEndian) => {
new_from::<true>(end::<true>(&a.value), end::<true>(&b.value), plan)
}
(ByteOrder::LittleEndian, ByteOrder::LittleEndian) => {
new_from::<false>(end::<false>(&a.value), end::<false>(&b.value), plan)
}
(ByteOrder::BigEndian, ByteOrder::LittleEndian) => {
new_with_copy::<true>(end::<true>(&a.value), &b.value, plan)
}
(ByteOrder::LittleEndian, ByteOrder::BigEndian) => {
new_with_copy::<false>(end::<false>(&a.value), &b.value, plan)
}
}
}
}
unsafe fn new_from<const BE: bool>(a: *const u8, b: *const u8, plan: &Plan) -> Vec<u8> {
let len = plan.len;
let mut v = Vec::<u8>::with_capacity(len);
let base = v.as_mut_ptr();
unsafe {
let r = if BE { base.add(len) } else { base };
let (x, y) = select_unpredictable(plan.x_is_b, (b, a), (a, b));
compute_new::<BE>(r, x, y, plan);
v.set_len(len);
}
v
}
unsafe fn new_with_copy<const BE: bool>(a: *const u8, b: &[u8], plan: &Plan) -> Vec<u8> {
let len = plan.len;
let m = plan.lb.min(len);
let mut v = Vec::<u8>::with_capacity(len);
let base = v.as_mut_ptr();
let copy_is_x = plan.alias_is_x(true);
debug_assert!(!copy_is_x || m == if plan.sub { len } else { plan.lx() });
unsafe {
if BE {
copy_reversed(base.add(len - m), b.as_ptr(), m);
} else {
copy_reversed(base, b.as_ptr().add(b.len() - m), m);
}
let r = if BE { base.add(len) } else { base };
let copy = r.cast_const();
let (x, y) = select_unpredictable(copy_is_x, (copy, a), (a, copy));
compute_new::<BE>(r, x, y, plan);
v.set_len(len);
}
v
}
#[inline(always)]
unsafe fn sum_in(
v: &mut Vec<u8>,
order: ByteOrder,
alias_is_b: bool,
other: &[u8],
plan: &Plan,
reversed: bool,
) {
unsafe {
if plan.len == 0 {
*v.as_mut_ptr() = 0;
v.set_len(1);
return;
}
match order {
ByteOrder::BigEndian if !reversed && long_difference(plan, LONG_DIFFERENCE_BE) => {
long_in_vector::<true>(v, alias_is_b, end::<true>(other), plan)
}
ByteOrder::LittleEndian if !reversed && long_difference(plan, LONG_DIFFERENCE_LE) => {
long_in_vector::<false>(v, alias_is_b, end::<false>(other), plan)
}
ByteOrder::BigEndian => {
in_vector::<true, false>(v, alias_is_b, end::<true>(other), plan)
}
ByteOrder::LittleEndian => {
in_vector::<false, false>(v, alias_is_b, end::<false>(other), plan)
}
}
}
}
#[inline(always)]
unsafe fn in_vector<const BE: bool, const LONG: bool>(
v: &mut Vec<u8>,
alias_is_b: bool,
other: *const u8,
plan: &Plan,
) {
let (old_len, len) = (v.len(), plan.len);
let base = v.as_mut_ptr();
unsafe {
let (r, alias) = if !BE {
(base, base)
} else if len >= old_len {
(base.add(len), base.add(old_len))
} else {
let m = if alias_is_b { plan.lb } else { plan.la }.min(len);
move_down(base.add(old_len), base.add(len), m);
(base.add(len), base.add(len))
};
let alias = alias.cast_const();
let alias_is_x = plan.alias_is_x(alias_is_b);
if LONG {
let (l_alias, l_other) = if alias_is_b {
(plan.lb, plan.la)
} else {
(plan.la, plan.lb)
};
if alias_is_x {
difference_in::<BE, false>(r, alias, other, l_other, len);
} else {
difference_in::<BE, true>(r, alias, other, l_alias, len);
}
} else {
let (x, y) = select_unpredictable(alias_is_x, (alias, other), (other, alias));
compute::<BE>(r, x, y, plan);
}
v.set_len(len);
}
}
#[inline(always)]
fn long_difference(plan: &Plan, long: usize) -> bool {
plan.sub && plan.la.min(plan.lb) >= long
}
#[inline(never)]
unsafe fn long_in_vector<const BE: bool>(
v: &mut Vec<u8>,
alias_is_b: bool,
other: *const u8,
plan: &Plan,
) {
unsafe { in_vector::<BE, true>(v, alias_is_b, other, plan) }
}
#[cold]
#[inline(never)]
unsafe fn move_down(from: *mut u8, to: *mut u8, m: usize) {
unsafe { ptr::copy(from.sub(m), to.sub(m), m) }
}
#[inline(always)]
unsafe fn compute<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, plan: &Plan) {
unsafe {
if plan.sub {
sub_into::<BE>(r, x, y, plan.ly(), plan.len);
} else {
add_into::<BE>(r, x, y, plan.lx(), plan.ly(), plan.len);
}
}
}
#[inline(never)]
unsafe fn compute_new<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, plan: &Plan) {
unsafe { compute::<BE>(r, x, y, plan) }
}
#[inline(always)]
unsafe fn add_into<const BE: bool>(
r: *mut u8,
x: *const u8,
y: *const u8,
lx: usize,
ly: usize,
len: usize,
) {
let n = ly / 8;
unsafe {
let carry = add_n::<BE>(r, x, y, n, false);
let mut carry = rest_of_y::<BE, false>(r, x, y, n, ly, carry);
if ly < lx {
let p;
(p, carry) = carry_on::<BE, false>(r, x, ly, lx, carry);
if !carry {
copy_rest::<BE>(r, x, p, lx);
}
}
debug_assert_eq!(len, lx + usize::from(carry), "the planned length is wrong");
let top = get::<BE>(r.cast_const(), lx - 1);
set::<BE>(r, len - 1, select_unpredictable(len > lx, 1, top));
}
}
#[inline(always)]
unsafe fn sub_into<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, ly: usize, len: usize) {
unsafe { sub_below::<BE>(r, x, y, ly.min(len), len) }
}
const LONG_DIFFERENCE_LE: usize = 256;
const LONG_DIFFERENCE_BE: usize = 512;
#[inline(never)]
unsafe fn difference_in<const BE: bool, const SWAPPED: bool>(
r: *mut u8,
alias: *const u8,
other: *const u8,
ly: usize,
len: usize,
) {
let (x, y) = if SWAPPED {
(other, alias)
} else {
(alias, other)
};
unsafe {
if ly <= len {
sub_below::<BE>(r, x, y, ly, len);
} else {
short_difference::<BE>(r, x, y, len);
}
}
}
#[cold]
#[inline(never)]
unsafe fn short_difference<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, len: usize) {
unsafe { sub_below::<BE>(r, x, y, len, len) }
}
#[inline(always)]
unsafe fn sub_below<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, ly: usize, len: usize) {
let n = ly / 8;
unsafe {
let borrow = sub_n::<BE>(r, x, y, n, false);
let borrow = rest_of_y::<BE, true>(r, x, y, n, ly, borrow);
if ly < len {
let (p, _) = carry_on::<BE, true>(r, x, ly, len, borrow);
copy_rest::<BE>(r, x, p, len);
}
debug_assert!(
get::<BE>(r.cast_const(), len - 1) != 0,
"the planned length is wrong"
);
}
}
#[inline(always)]
unsafe fn rest_of_y<const BE: bool, const SUB: bool>(
r: *mut u8,
x: *const u8,
y: *const u8,
n: usize,
ly: usize,
mut carry: bool,
) -> bool {
let mut p = 8 * n;
let rem = ly - p;
if rem == 0 {
return carry;
}
unsafe {
if rem >= 3 && n > 0 {
let shift = 8 * (8 - rem) as u32;
let (xs, ys) = (
load::<BE>(x, ly - 8) >> shift,
load::<BE>(y, ly - 8) >> shift,
);
let c = u64::from(carry);
let s = if SUB {
xs.wrapping_sub(ys).wrapping_sub(c)
} else {
xs + ys + c
};
carry = (s >> (8 * rem)) & 1 != 0;
let below = load::<BE>(r.cast_const(), p - 8) >> (8 * rem);
store::<BE>(r, ly - 8, below | (s << shift));
} else {
while p < ly {
let (xb, yb, c) = (get::<BE>(x, p), get::<BE>(y, p), u16::from(carry));
let s = if SUB {
u16::from(xb).wrapping_sub(u16::from(yb)).wrapping_sub(c)
} else {
u16::from(xb) + u16::from(yb) + c
};
set::<BE>(r, p, s as u8);
carry = s >> 8 != 0;
p += 1;
}
}
}
carry
}
#[inline(always)]
unsafe fn carry_on<const BE: bool, const SUB: bool>(
r: *mut u8,
x: *const u8,
mut p: usize,
top: usize,
mut carry: bool,
) -> (usize, bool) {
let passes = if SUB { 0 } else { u64::MAX };
unsafe {
let c = u64::from(carry);
if top - p >= 8 {
let v = load::<BE>(x, p);
store::<BE>(
r,
p,
if SUB {
v.wrapping_sub(c)
} else {
v.wrapping_add(c)
},
);
carry &= v == passes;
p += 8;
} else if p < top && p >= 8 {
let m = top - p;
let shift = 8 * (8 - m) as u32;
let v = load::<BE>(x, top - 8) >> shift;
let s = if SUB { v.wrapping_sub(c) } else { v + c };
let below = load::<BE>(r.cast_const(), p - 8) >> (8 * m);
store::<BE>(r, top - 8, below | (s << shift));
return (top, (s >> (8 * m)) & 1 != 0);
}
while carry && top - p >= 8 {
let v = load::<BE>(x, p);
store::<BE>(
r,
p,
if SUB {
v.wrapping_sub(1)
} else {
v.wrapping_add(1)
},
);
carry = v == passes;
p += 8;
}
while carry && p < top {
let v = get::<BE>(x, p);
set::<BE>(
r,
p,
if SUB {
v.wrapping_sub(1)
} else {
v.wrapping_add(1)
},
);
carry = v == passes as u8;
p += 1;
}
}
(p, carry)
}
#[inline(always)]
unsafe fn copy_rest<const BE: bool>(r: *mut u8, x: *const u8, p: usize, top: usize) {
if p < top && !ptr::eq(r.cast_const(), x) {
let m = top - p;
unsafe {
let (src, dst) = if BE {
(x.sub(top), r.sub(top))
} else {
(x.add(p), r.add(p))
};
if m >= 16 {
ptr::copy(src, dst, m);
} else if m >= 8 {
let (u, v) = (read::<8>(src), read::<8>(src.add(m - 8)));
write::<8>(dst, u);
write::<8>(dst.add(m - 8), v);
} else if m >= 4 {
let (u, v) = (read::<4>(src), read::<4>(src.add(m - 4)));
write::<4>(dst, u);
write::<4>(dst.add(m - 4), v);
} else if m >= 2 {
let (u, v) = (read::<2>(src), read::<2>(src.add(m - 2)));
write::<2>(dst, u);
write::<2>(dst.add(m - 2), v);
} else {
*dst = *src;
}
}
}
}
#[inline(always)]
unsafe fn add_n<const BE: bool>(
r: *mut u8,
x: *const u8,
y: *const u8,
n: usize,
carry: bool,
) -> bool {
unsafe {
if BE {
add_n_be(r, x, y, n, carry)
} else {
add_n_le(r, x, y, n, carry)
}
}
}
#[inline(always)]
unsafe fn sub_n<const BE: bool>(
r: *mut u8,
x: *const u8,
y: *const u8,
n: usize,
borrow: bool,
) -> bool {
unsafe {
if BE {
sub_n_be(r, x, y, n, borrow)
} else {
sub_n_le(r, x, y, n, borrow)
}
}
}
#[inline(always)]
fn end<const BE: bool>(v: &[u8]) -> *const u8 {
if BE { v.as_ptr_range().end } else { v.as_ptr() }
}
#[inline(always)]
unsafe fn get<const BE: bool>(end: *const u8, p: usize) -> u8 {
unsafe { if BE { *end.sub(p + 1) } else { *end.add(p) } }
}
#[inline(always)]
unsafe fn get_or_zero<const BE: bool>(end: *const u8, len: usize, p: usize) -> u8 {
if p < len {
unsafe { get::<BE>(end, p) }
} else {
0
}
}
#[inline(always)]
unsafe fn set<const BE: bool>(end: *mut u8, p: usize, v: u8) {
unsafe {
if BE {
*end.sub(p + 1) = v;
} else {
*end.add(p) = v;
}
}
}
#[inline(always)]
unsafe fn load<const BE: bool>(end: *const u8, p: usize) -> u64 {
unsafe {
if BE {
u64::from_be_bytes(end.sub(p + 8).cast::<[u8; 8]>().read())
} else {
u64::from_le_bytes(end.add(p).cast::<[u8; 8]>().read())
}
}
}
#[inline(always)]
unsafe fn store<const BE: bool>(end: *mut u8, p: usize, v: u64) {
unsafe {
if BE {
end.sub(p + 8).cast::<[u8; 8]>().write(v.to_be_bytes());
} else {
end.add(p).cast::<[u8; 8]>().write(v.to_le_bytes());
}
}
}
#[inline(always)]
unsafe fn copy_reversed(dst: *mut u8, src: *const u8, m: usize) {
let mut i = 0;
unsafe {
while m - i >= 8 {
let bytes = src.add(m - i - 8).cast::<[u8; 8]>().read();
dst.add(i)
.cast::<[u8; 8]>()
.write(u64::from_be_bytes(bytes).to_le_bytes());
i += 8;
}
while i < m {
*dst.add(i) = *src.add(m - 1 - i);
i += 1;
}
}
}
#[inline(always)]
pub(super) unsafe fn read<const N: usize>(p: *const u8) -> [u8; N] {
unsafe { p.cast::<[u8; N]>().read() }
}
#[inline(always)]
pub(super) unsafe fn write<const N: usize>(p: *mut u8, v: [u8; N]) {
unsafe { p.cast::<[u8; N]>().write(v) }
}