use core::arch::asm;
#[inline]
pub(super) unsafe fn add_n_le(
r: *mut u8,
x: *const u8,
y: *const u8,
n: usize,
carry: bool,
) -> bool {
unsafe {
if n < LE_TWO_CHAINS {
chain_add_le(r, x, y, n, carry)
} else if n < LE_FOUR_CHAINS {
parts2_add_le(r, x, y, n, carry)
} else if n < LE_STREAMING {
parts4_add_le(r, x, y, n, carry)
} else if core::ptr::eq(r, x) || core::ptr::eq(r, y) {
parts2_add_le(r, x, y, n, carry)
} else {
parts2_8_add_le(r, x, y, n, carry)
}
}
}
#[inline]
pub(super) unsafe fn sub_n_le(
r: *mut u8,
x: *const u8,
y: *const u8,
n: usize,
borrow: bool,
) -> bool {
unsafe {
if n < LE_TWO_CHAINS {
chain_sub_le(r, x, y, n, borrow)
} else if n < LE_FOUR_CHAINS {
parts2_sub_le(r, x, y, n, borrow)
} else if n < LE_STREAMING {
parts4_sub_le(r, x, y, n, borrow)
} else if core::ptr::eq(r, x) || core::ptr::eq(r, y) {
parts2_sub_le(r, x, y, n, borrow)
} else {
parts2_8_sub_le(r, x, y, n, borrow)
}
}
}
#[inline]
pub(super) unsafe fn add_n_be(
r_end: *mut u8,
x_end: *const u8,
y_end: *const u8,
n: usize,
carry: bool,
) -> bool {
unsafe {
if n < BE_TWO_CHAINS || shifted(r_end, x_end, n) || shifted(r_end, y_end, n) {
chain_add_be(r_end, x_end, y_end, n, carry)
} else {
parts2_add_be(r_end, x_end, y_end, n, carry)
}
}
}
#[inline]
pub(super) unsafe fn sub_n_be(
r_end: *mut u8,
x_end: *const u8,
y_end: *const u8,
n: usize,
borrow: bool,
) -> bool {
unsafe {
if n < BE_TWO_CHAINS || shifted(r_end, x_end, n) || shifted(r_end, y_end, n) {
chain_sub_be(r_end, x_end, y_end, n, borrow)
} else {
parts2_sub_be(r_end, x_end, y_end, n, borrow)
}
}
}
const LE_TWO_CHAINS: usize = 32;
const LE_FOUR_CHAINS: usize = 64;
const LE_STREAMING: usize = 7168;
const BE_TWO_CHAINS: usize = 64;
#[inline(always)]
fn shifted(r: *mut u8, x: *const u8, n: usize) -> bool {
let d = r.addr().wrapping_sub(x.addr());
d != 0 && (d < 8 * n || d.wrapping_neg() < 8 * n)
}
macro_rules! reg {
($name:ident) => {
concat!("{", stringify!($name), "}")
};
}
macro_rules! op {
(add) => {
"adcs"
};
(sub) => {
"sbcs"
};
}
macro_rules! restore {
(add, $c:ident) => {
concat!("cmp ", reg!($c), ", #1\n")
};
(sub, $c:ident) => {
concat!("cmp xzr, ", reg!($c), "\n")
};
}
macro_rules! save {
(add, $c:ident) => {
concat!("cset ", reg!($c), ", cs\n")
};
(sub, $c:ident) => {
concat!("cset ", reg!($c), ", cc\n")
};
}
macro_rules! step1 {
(le, $op:ident, $x:ident, $y:ident, $r:ident) => {
concat!(
concat!("ldr {a0}, [", reg!($x), "], #8\n"),
concat!("ldr {b0}, [", reg!($y), "], #8\n"),
concat!(op!($op), " {a0}, {a0}, {b0}\n"),
concat!("str {a0}, [", reg!($r), "], #8\n"),
)
};
(be, $op:ident, $x:ident, $y:ident, $r:ident) => {
concat!(
concat!("ldr {a0}, [", reg!($x), ", #-8]!\n"),
concat!("ldr {b0}, [", reg!($y), ", #-8]!\n"),
"rev {a0}, {a0}\n",
"rev {b0}, {b0}\n",
concat!(op!($op), " {a0}, {a0}, {b0}\n"),
"rev {a0}, {a0}\n",
concat!("str {a0}, [", reg!($r), ", #-8]!\n"),
)
};
}
macro_rules! step2 {
(le, $op:ident, $x:ident, $y:ident, $r:ident) => {
concat!(
concat!("ldp {a0}, {a1}, [", reg!($x), "], #16\n"),
concat!("ldp {b0}, {b1}, [", reg!($y), "], #16\n"),
concat!(op!($op), " {a0}, {a0}, {b0}\n"),
concat!(op!($op), " {a1}, {a1}, {b1}\n"),
concat!("stp {a0}, {a1}, [", reg!($r), "], #16\n"),
)
};
(be, $op:ident, $x:ident, $y:ident, $r:ident) => {
concat!(
concat!("ldp {a1}, {a0}, [", reg!($x), ", #-16]!\n"),
concat!("ldp {b1}, {b0}, [", reg!($y), ", #-16]!\n"),
"rev {a0}, {a0}\n",
"rev {b0}, {b0}\n",
"rev {a1}, {a1}\n",
"rev {b1}, {b1}\n",
concat!(op!($op), " {a0}, {a0}, {b0}\n"),
concat!(op!($op), " {a1}, {a1}, {b1}\n"),
"rev {a0}, {a0}\n",
"rev {a1}, {a1}\n",
concat!("stp {a1}, {a0}, [", reg!($r), ", #-16]!\n"),
)
};
}
macro_rules! add4 {
($op:ident) => {
concat!(
concat!(op!($op), " {a0}, {a0}, {b0}\n"),
concat!(op!($op), " {a1}, {a1}, {b1}\n"),
concat!(op!($op), " {a2}, {a2}, {b2}\n"),
concat!(op!($op), " {a3}, {a3}, {b3}\n"),
)
};
}
macro_rules! add4_be {
($op:ident) => {
concat!(
"rev {a0}, {a0}\n",
"rev {b0}, {b0}\n",
"rev {a1}, {a1}\n",
"rev {b1}, {b1}\n",
"rev {a2}, {a2}\n",
"rev {b2}, {b2}\n",
"rev {a3}, {a3}\n",
"rev {b3}, {b3}\n",
add4!($op),
"rev {a0}, {a0}\n",
"rev {a1}, {a1}\n",
"rev {a2}, {a2}\n",
"rev {a3}, {a3}\n",
)
};
}
macro_rules! le4 {
($op:ident, $x:ident, $y:ident, $r:ident, $o:literal) => {
concat!(
concat!("ldp {a2}, {a3}, [", reg!($x), ", #(", $o, " + 16)]\n"),
concat!("ldp {a0}, {a1}, [", reg!($x), ", #(", $o, ")]\n"),
concat!("ldp {b2}, {b3}, [", reg!($y), ", #(", $o, " + 16)]\n"),
concat!("ldp {b0}, {b1}, [", reg!($y), ", #(", $o, ")]\n"),
add4!($op),
concat!("stp {a2}, {a3}, [", reg!($r), ", #(", $o, " + 16)]\n"),
concat!("stp {a0}, {a1}, [", reg!($r), ", #(", $o, ")]\n"),
)
};
}
macro_rules! le4w {
($op:ident, $x:ident, $y:ident, $r:ident, $w:literal) => {
concat!(
concat!("ldp {a2}, {a3}, [", reg!($x), ", #16]\n"),
concat!("ldp {a0}, {a1}, [", reg!($x), "], #", $w, "\n"),
concat!("ldp {b2}, {b3}, [", reg!($y), ", #16]\n"),
concat!("ldp {b0}, {b1}, [", reg!($y), "], #", $w, "\n"),
add4!($op),
concat!("stp {a2}, {a3}, [", reg!($r), ", #16]\n"),
concat!("stp {a0}, {a1}, [", reg!($r), "], #", $w, "\n"),
)
};
}
macro_rules! be4 {
($op:ident, $x:ident, $y:ident, $r:ident, $o:literal) => {
concat!(
concat!("ldp {a1}, {a0}, [", reg!($x), ", #(", $o, " - 16)]\n"),
concat!("ldp {a3}, {a2}, [", reg!($x), ", #(", $o, " - 32)]\n"),
concat!("ldp {b1}, {b0}, [", reg!($y), ", #(", $o, " - 16)]\n"),
concat!("ldp {b3}, {b2}, [", reg!($y), ", #(", $o, " - 32)]\n"),
add4_be!($op),
concat!("stp {a1}, {a0}, [", reg!($r), ", #(", $o, " - 16)]\n"),
concat!("stp {a3}, {a2}, [", reg!($r), ", #(", $o, " - 32)]\n"),
)
};
}
macro_rules! be4w {
($op:ident, $x:ident, $y:ident, $r:ident, $o:literal, $w:literal) => {
concat!(
concat!("ldp {a1}, {a0}, [", reg!($x), ", #(", $o, " - 16)]\n"),
concat!("ldp {a3}, {a2}, [", reg!($x), ", #-", $w, "]!\n"),
concat!("ldp {b1}, {b0}, [", reg!($y), ", #(", $o, " - 16)]\n"),
concat!("ldp {b3}, {b2}, [", reg!($y), ", #-", $w, "]!\n"),
add4_be!($op),
concat!("stp {a1}, {a0}, [", reg!($r), ", #(", $o, " - 16)]\n"),
concat!("stp {a3}, {a2}, [", reg!($r), ", #-", $w, "]!\n"),
)
};
}
macro_rules! block {
(le, 4, $op:ident, $x:ident, $y:ident, $r:ident) => {
le4w!($op, $x, $y, $r, "32")
};
(le, 8, $op:ident, $x:ident, $y:ident, $r:ident) => {
concat!(le4w!($op, $x, $y, $r, "64"), le4!($op, $x, $y, $r, "-32"))
};
(le, 16, $op:ident, $x:ident, $y:ident, $r:ident) => {
concat!(
le4w!($op, $x, $y, $r, "128"),
le4!($op, $x, $y, $r, "-96"),
le4!($op, $x, $y, $r, "-64"),
le4!($op, $x, $y, $r, "-32"),
)
};
(be, 4, $op:ident, $x:ident, $y:ident, $r:ident) => {
be4w!($op, $x, $y, $r, "0", "32")
};
(be, 8, $op:ident, $x:ident, $y:ident, $r:ident) => {
concat!(
be4!($op, $x, $y, $r, "0"),
be4w!($op, $x, $y, $r, "-32", "64")
)
};
(be, 16, $op:ident, $x:ident, $y:ident, $r:ident) => {
concat!(
be4!($op, $x, $y, $r, "0"),
be4!($op, $x, $y, $r, "-32"),
be4!($op, $x, $y, $r, "-64"),
be4w!($op, $x, $y, $r, "-96", "128"),
)
};
}
macro_rules! rest {
($ord:ident, $op:ident, $x:ident, $y:ident, $r:ident, $bits:literal) => {
concat!(
"tbz {t}, #0, 3f\n",
step1!($ord, $op, $x, $y, $r),
"3:\n",
"tbz {t}, #1, 4f\n",
step2!($ord, $op, $x, $y, $r),
"4:\n",
"tbz {t}, #2, 5f\n",
block!($ord, 4, $op, $x, $y, $r),
"5:\n",
concat!("ubfx {t}, {t}, #3, #", $bits, "\n"),
"cbz {t}, 7f\n",
"6:\n",
block!($ord, 8, $op, $x, $y, $r),
"sub {t}, {t}, #1\n",
"cbnz {t}, 6b\n",
"7:\n",
)
};
}
macro_rules! chain {
($(#[$doc:meta])* $name:ident, $op:ident, $ord:ident) => {
$(#[$doc])*
#[inline(always)]
unsafe fn $name(r: *mut u8, x: *const u8, y: *const u8, n: usize, carry: bool) -> bool {
let mut c = u64::from(carry);
unsafe {
asm!(
restore!($op, c),
"tbz {n}, #0, 2f",
step1!($ord, $op, x, y, r),
"2:",
"tbz {n}, #1, 3f",
step2!($ord, $op, x, y, r),
"3:",
"tbz {n}, #2, 4f",
block!($ord, 4, $op, x, y, r),
"4:",
"lsr {n}, {n}, #3",
"cbz {n}, 6f",
"5:",
block!($ord, 8, $op, x, y, r),
"sub {n}, {n}, #1",
"cbnz {n}, 5b",
"6:",
save!($op, c),
c = inout(reg) c,
n = inout(reg) n => _,
r = inout(reg) r => _,
x = inout(reg) x => _,
y = inout(reg) y => _,
a0 = out(reg) _,
a1 = out(reg) _,
a2 = out(reg) _,
a3 = out(reg) _,
b0 = out(reg) _,
b1 = out(reg) _,
b2 = out(reg) _,
b3 = out(reg) _,
options(nostack),
);
core::hint::assert_unchecked(c <= 1);
}
c != 0
}
};
}
chain!(
chain_add_le,
add,
le
);
chain!(
chain_sub_le,
sub,
le
);
chain!(
chain_add_be,
add,
be
);
chain!(
chain_sub_be,
sub,
be
);
macro_rules! next_part {
(le, $d:ident, $p:ident) => {
concat!("add ", reg!($d), ", ", reg!($p), ", {s}\n")
};
(be, $d:ident, $p:ident) => {
concat!("sub ", reg!($d), ", ", reg!($p), ", {s}\n")
};
}
macro_rules! chains2 {
($(#[$doc:meta])* $name:ident, $op:ident, $ord:ident) => {
$(#[$doc])*
#[inline(always)]
unsafe fn $name(r: *mut u8, x: *const u8, y: *const u8, s: usize, m: usize, t: usize, c: u64) -> [u64; 2] {
let (c0, c1): (u64, u64);
unsafe {
asm!(
next_part!($ord, x1, x0),
next_part!($ord, y1, y0),
next_part!($ord, r1, r0),
"mov {c1}, #0",
"cbz {m}, 8f",
"2:",
restore!($op, c0),
block!($ord, 16, $op, x0, y0, r0),
save!($op, c0),
restore!($op, c1),
block!($ord, 16, $op, x1, y1, r1),
save!($op, c1),
"sub {m}, {m}, #1",
"cbnz {m}, 2b",
"8:",
"tbz {t}, #4, 9f",
restore!($op, c0),
block!($ord, 8, $op, x0, y0, r0),
save!($op, c0),
restore!($op, c1),
block!($ord, 8, $op, x1, y1, r1),
save!($op, c1),
"9:",
restore!($op, c1),
rest!($ord, $op, x1, y1, r1, "1"),
save!($op, c1),
x0 = inout(reg) x => _,
y0 = inout(reg) y => _,
r0 = inout(reg) r => _,
x1 = out(reg) _,
y1 = out(reg) _,
r1 = out(reg) _,
c0 = inout(reg) c => c0,
c1 = out(reg) c1,
s = in(reg) s,
m = inout(reg) m => _,
t = inout(reg) t => _,
a0 = out(reg) _,
a1 = out(reg) _,
a2 = out(reg) _,
a3 = out(reg) _,
b0 = out(reg) _,
b1 = out(reg) _,
b2 = out(reg) _,
b3 = out(reg) _,
options(nostack),
);
core::hint::assert_unchecked(c0 <= 1 && c1 <= 1);
}
[c0, c1]
}
};
}
macro_rules! chains4 {
($(#[$doc:meta])* $name:ident, $op:ident, $ord:ident) => {
$(#[$doc])*
#[inline(always)]
unsafe fn $name(r: *mut u8, x: *const u8, y: *const u8, s: usize, m: usize, t: usize, c: u64) -> [u64; 4] {
let (c0, c1, c2, c3): (u64, u64, u64, u64);
unsafe {
asm!(
next_part!($ord, x1, x0),
next_part!($ord, y1, y0),
next_part!($ord, r1, r0),
next_part!($ord, x2, x1),
next_part!($ord, y2, y1),
next_part!($ord, r2, r1),
next_part!($ord, x3, x2),
next_part!($ord, y3, y2),
next_part!($ord, r3, r2),
"mov {c1}, #0",
"mov {c2}, #0",
"mov {c3}, #0",
"cbz {m}, 8f",
"2:",
restore!($op, c0),
block!($ord, 16, $op, x0, y0, r0),
save!($op, c0),
restore!($op, c1),
block!($ord, 16, $op, x1, y1, r1),
save!($op, c1),
restore!($op, c2),
block!($ord, 16, $op, x2, y2, r2),
save!($op, c2),
restore!($op, c3),
block!($ord, 16, $op, x3, y3, r3),
save!($op, c3),
"sub {m}, {m}, #1",
"cbnz {m}, 2b",
"8:",
"tbz {t}, #5, 9f",
restore!($op, c0),
block!($ord, 8, $op, x0, y0, r0),
save!($op, c0),
restore!($op, c1),
block!($ord, 8, $op, x1, y1, r1),
save!($op, c1),
restore!($op, c2),
block!($ord, 8, $op, x2, y2, r2),
save!($op, c2),
restore!($op, c3),
block!($ord, 8, $op, x3, y3, r3),
save!($op, c3),
"9:",
restore!($op, c3),
rest!($ord, $op, x3, y3, r3, "2"),
save!($op, c3),
x0 = inout(reg) x => _,
y0 = inout(reg) y => _,
r0 = inout(reg) r => _,
x1 = out(reg) _,
y1 = out(reg) _,
r1 = out(reg) _,
x2 = out(reg) _,
y2 = out(reg) _,
r2 = out(reg) _,
x3 = out(reg) _,
y3 = out(reg) _,
r3 = out(reg) _,
c0 = inout(reg) c => c0,
c1 = out(reg) c1,
c2 = out(reg) c2,
c3 = out(reg) c3,
s = in(reg) s,
m = inout(reg) m => _,
t = inout(reg) t => _,
a0 = out(reg) _,
a1 = out(reg) _,
a2 = out(reg) _,
a3 = out(reg) _,
b0 = out(reg) _,
b1 = out(reg) _,
b2 = out(reg) _,
b3 = out(reg) _,
options(nostack),
);
core::hint::assert_unchecked(c0 <= 1 && c1 <= 1 && c2 <= 1 && c3 <= 1);
}
[c0, c1, c2, c3]
}
};
}
macro_rules! chains2_8 {
($(#[$doc:meta])* $name:ident, $op:ident, $ord:ident) => {
$(#[$doc])*
#[inline(always)]
unsafe fn $name(r: *mut u8, x: *const u8, y: *const u8, s: usize, m: usize, t: usize, c: u64) -> [u64; 2] {
let (c0, c1): (u64, u64);
unsafe {
asm!(
next_part!($ord, x1, x0),
next_part!($ord, y1, y0),
next_part!($ord, r1, r0),
"mov {c1}, #0",
"cbz {m}, 8f",
"2:",
restore!($op, c0),
block!($ord, 8, $op, x0, y0, r0),
save!($op, c0),
restore!($op, c1),
block!($ord, 8, $op, x1, y1, r1),
save!($op, c1),
"sub {m}, {m}, #1",
"cbnz {m}, 2b",
"8:",
restore!($op, c1),
rest!($ord, $op, x1, y1, r1, "1"),
save!($op, c1),
x0 = inout(reg) x => _,
y0 = inout(reg) y => _,
r0 = inout(reg) r => _,
x1 = out(reg) _,
y1 = out(reg) _,
r1 = out(reg) _,
c0 = inout(reg) c => c0,
c1 = out(reg) c1,
s = in(reg) s,
m = inout(reg) m => _,
t = inout(reg) t => _,
a0 = out(reg) _,
a1 = out(reg) _,
a2 = out(reg) _,
a3 = out(reg) _,
b0 = out(reg) _,
b1 = out(reg) _,
b2 = out(reg) _,
b3 = out(reg) _,
options(nostack),
);
core::hint::assert_unchecked(c0 <= 1 && c1 <= 1);
}
[c0, c1]
}
};
}
chains2!(
chains2_add_le,
add,
le
);
chains2!(
chains2_sub_le,
sub,
le
);
chains2!(
chains2_add_be,
add,
be
);
chains2!(
chains2_sub_be,
sub,
be
);
chains4!(
chains4_add_le,
add,
le
);
chains4!(
chains4_sub_le,
sub,
le
);
chains2_8!(
chains2_8_add_le,
add,
le
);
chains2_8!(
chains2_8_sub_le,
sub,
le
);
macro_rules! parts {
(
$(#[$doc:meta])* $name:ident, $chains:ident, $k:literal, $split:ident, $sub:literal,
$fix:ident, $at:ident
) => {
$(#[$doc])*
#[inline(never)]
unsafe fn $name(r: *mut u8, x: *const u8, y: *const u8, n: usize, carry: bool) -> bool {
let (m, t, len, rest) = $split(n, $k);
let c = unsafe { $chains(r, x, y, 8 * len, m, t, u64::from(carry)) };
let mut carry = c[0] != 0;
for (j, &cj) in c.iter().enumerate().skip(1) {
let limbs = if j == $k - 1 { len + rest } else { len };
carry = (cj != 0) | unsafe { $fix::<$sub>($at(r, j * len), limbs, carry) };
}
carry
}
};
}
#[inline(always)]
const fn split16(n: usize, k: usize) -> (usize, usize, usize, usize) {
let m = n / (16 * k);
let t = n % (16 * k);
let len = 16 * m + if t >= 8 * k { 8 } else { 0 };
(m, t, len, t % (8 * k))
}
#[inline(always)]
const fn split8(n: usize, k: usize) -> (usize, usize, usize, usize) {
let m = n / (8 * k);
let t = n % (8 * k);
(m, t, 8 * m, t)
}
#[inline(always)]
fn limb_le(r: *mut u8, i: usize) -> *mut u8 {
r.wrapping_add(8 * i)
}
#[inline(always)]
fn limb_be(r: *mut u8, i: usize) -> *mut u8 {
r.wrapping_sub(8 * i)
}
parts!(
parts2_add_le,
chains2_add_le,
2,
split16,
false,
fix_le,
limb_le
);
parts!(
parts2_sub_le,
chains2_sub_le,
2,
split16,
true,
fix_le,
limb_le
);
parts!(
parts4_add_le,
chains4_add_le,
4,
split16,
false,
fix_le,
limb_le
);
parts!(
parts4_sub_le,
chains4_sub_le,
4,
split16,
true,
fix_le,
limb_le
);
parts!(
parts2_8_add_le,
chains2_8_add_le,
2,
split8,
false,
fix_le,
limb_le
);
parts!(
parts2_8_sub_le,
chains2_8_sub_le,
2,
split8,
true,
fix_le,
limb_le
);
parts!(
parts2_add_be,
chains2_add_be,
2,
split16,
false,
fix_be,
limb_be
);
parts!(
parts2_sub_be,
chains2_sub_be,
2,
split16,
true,
fix_be,
limb_be
);
const fn wrapped<const SUB: bool>() -> u64 {
if SUB { u64::MAX } else { 0 }
}
#[inline(always)]
unsafe fn load_le(p: *const u8) -> u64 {
u64::from_le_bytes(unsafe { p.cast::<[u8; 8]>().read() })
}
#[inline(always)]
unsafe fn store_le(p: *mut u8, v: u64) {
unsafe { p.cast::<[u8; 8]>().write(v.to_le_bytes()) }
}
#[inline(always)]
unsafe fn load_be(p: *const u8) -> u64 {
u64::from_be_bytes(unsafe { p.cast::<[u8; 8]>().read() })
}
#[inline(always)]
unsafe fn store_be(p: *mut u8, v: u64) {
unsafe { p.cast::<[u8; 8]>().write(v.to_be_bytes()) }
}
#[inline(always)]
unsafe fn fix_le<const SUB: bool>(p: *mut u8, len: usize, carry: bool) -> bool {
let v = unsafe { load_le(p) };
let v = if SUB {
v.wrapping_sub(u64::from(carry))
} else {
v.wrapping_add(u64::from(carry))
};
unsafe { store_le(p, v) };
carry && v == wrapped::<SUB>() && unsafe { ripple_le::<SUB>(p, len) }
}
#[cold]
#[inline(never)]
unsafe fn ripple_le<const SUB: bool>(p: *mut u8, len: usize) -> bool {
let through = !wrapped::<SUB>();
let mut i = 1;
while i + 4 <= len {
unsafe {
let q = p.add(8 * i);
let (a, b, c, d) = (
load_le(q),
load_le(q.add(8)),
load_le(q.add(16)),
load_le(q.add(24)),
);
let all = if SUB {
a | b | c | d == 0
} else {
a & b & c & d == u64::MAX
};
if !all {
break;
}
for k in 0..4 {
store_le(q.add(8 * k), wrapped::<SUB>());
}
}
i += 4;
}
while i < len {
unsafe {
let q = p.add(8 * i);
let v = load_le(q);
store_le(
q,
if SUB {
v.wrapping_sub(1)
} else {
v.wrapping_add(1)
},
);
if v != through {
return false;
}
}
i += 1;
}
true
}
#[inline(always)]
unsafe fn fix_be<const SUB: bool>(p: *mut u8, len: usize, carry: bool) -> bool {
let q = unsafe { p.sub(8) };
let v = unsafe { load_be(q) };
let v = if SUB {
v.wrapping_sub(u64::from(carry))
} else {
v.wrapping_add(u64::from(carry))
};
unsafe { store_be(q, v) };
carry && v == wrapped::<SUB>() && unsafe { ripple_be::<SUB>(p, len) }
}
#[cold]
#[inline(never)]
unsafe fn ripple_be<const SUB: bool>(p: *mut u8, len: usize) -> bool {
let through = !wrapped::<SUB>();
let mut i = 1;
while i + 4 <= len {
unsafe {
let q = p.sub(8 * (i + 4));
let (a, b, c, d) = (
load_be(q),
load_be(q.add(8)),
load_be(q.add(16)),
load_be(q.add(24)),
);
let all = if SUB {
a | b | c | d == 0
} else {
a & b & c & d == u64::MAX
};
if !all {
break;
}
for k in 0..4 {
store_be(q.add(8 * k), wrapped::<SUB>());
}
}
i += 4;
}
while i < len {
unsafe {
let q = p.sub(8 * (i + 1));
let v = load_be(q);
store_be(
q,
if SUB {
v.wrapping_sub(1)
} else {
v.wrapping_add(1)
},
);
if v != through {
return false;
}
}
i += 1;
}
true
}