a/math/numbers/add.rs
1//! Addition of [`Number`]s: `+` and `+=`, with either operand owned or borrowed.
2//!
3//! A number is a sign and a magnitude, so a sum is one of two operations on the magnitudes.
4//! When the signs agree, or an operand is zero, the magnitudes add up and the sum has the sign
5//! of the nonzero operands. When they differ, the smaller magnitude is subtracted from the
6//! larger and the sum has the sign of the larger. A zero magnitude is zero whatever `negative`
7//! says, and zero bytes above the most significant one count for nothing. Subtraction (in
8//! `sub`) is the sum with the sign of its right operand turned over, and takes the same paths.
9//!
10//! Operands whose vectors both hold at most 16 bytes take a short path in registers. Their
11//! magnitudes are read into `u128`s, with loads that stay inside the bytes of each vector, and
12//! both their sum and their difference are computed: the one the signs ask for, the sign, and
13//! the length in the shortest form (17 bytes when the sum carries out of the sixteenth) are
14//! then chosen without a branch. The sum is stored in the byte order of the left operand, in
15//! the vector chosen as below, with no reversal and no other pass over its bytes. The stores are
16//! chosen by the length `k` of the longer operand's vector rather than by the length of the sum:
17//! a sum of magnitudes of `k` bytes is `k` or `k + 1` bytes long, a coin toss for random ones,
18//! and the two lengths take the same stores (see [`write_sum`]). Longer operands, and a new
19//! vector for a short sum, are handled out of line, so that the short path saves no registers.
20//!
21//! Operands whose vectors both hold at most 64 bytes, not both at most 16, take the medium path,
22//! out of line, also in registers, with code for each number `L` of limbs of eight bytes of the
23//! longer vector, from 3 to 8. The magnitudes are read as `L` limbs each, loaded where they are
24//! (the top one of a vector from the eight bytes that end it, shifted), and one chain of
25//! additions gives their sum, or `x - y` when the signs differ, while a second one gives `y - x`
26//! alongside, which is taken when the first borrowed: neither the signs nor which magnitude is the
27//! larger, a coin toss for random operands, are branched on. The length of the sum is found from
28//! its top three limbs, and the sum stored from the limbs below its top eight bytes, at their
29//! places, and then those eight bytes; a vector of `+=` is written only once both operands have
30//! been read. A sum shorter than `L - 1` limbs, of operands that nearly cancel or have zero bytes
31//! above their most significant ones, and a sum the vector has no room for, go to slower code
32//! (see [`medium_slow`]).
33//!
34//! For operands of more than 64 bytes, everything about the sum but its bytes is planned first,
35//! from the operands as they are: the operation, which magnitude is `x`, the one the other (`y`)
36//! is added to or subtracted from, the sign, and the exact length of the sum in its shortest
37//! form. A sum of magnitudes of `lx >= ly` significant bytes is `lx` bytes long, or one more
38//! when it carries out of the top, and whether it does is found from the top down: the first
39//! byte position at which the bytes add up to anything but `0xFF` decides. A difference reaches
40//! up to the highest position at which the magnitudes differ, unless that position differs by
41//! exactly 1 and a borrow from below cancels it. It then ends at the bottom of the run of
42//! positions under it at which `x` has `0x00` and `y` has `0xFF`, which pass the borrow on, or
43//! a byte lower when a borrow comes into that run too, as in `0x01_00_00 - 0xFF_FF = 1`. The
44//! planning of a difference is out of line, so that additions keep their registers. The scans
45//! take eight positions at a time, and for random operands they stop in the first eight; only
46//! long runs of carries or borrows, or of zero bytes above the most significant one, make them
47//! read further, each byte once.
48//!
49//! Only then is a vector chosen for the sum, as the operators promise: the vector of an
50//! operand taken by value, or of the target of `+=`, when it has room for the sum (the left
51//! operand's first), and otherwise one new vector of the sum's length. In it the sum is
52//! computed in one pass from the least significant byte up, and only its bytes: there are no
53//! zero bytes to trim afterwards and no temporary vectors, and reusing a vector allocates
54//! nothing. The sum is in the byte order of the left operand. A right operand in the other
55//! order is brought into it without allocating: an owned one is reversed in place; a borrowed
56//! one is copied into the new vector of the sum, or, when the sum is built in the left
57//! operand's vector, the left operand is reversed into the right operand's order and the sum
58//! reversed back.
59//!
60//! The magnitudes are added and subtracted in limbs of eight bytes by kernels chosen at
61//! compile time: inline assembly on `x86_64` (one carry chain through the carry flag) and on
62//! 64-bit little-endian `aarch64` (one chain below 32 limbs little-endian and 64 big-endian,
63//! several interleaved chains from there), and portable Rust elsewhere and under Miri, which
64//! cannot run assembly. A big-endian magnitude is handed to them by its least significant end,
65//! the address one past its last byte, and its limbs go towards lower addresses, so the limbs
66//! of either byte order are read where they are. A big-endian sum that grows in the vector of
67//! an operand ends further on in it than the operand did, as the vector grows at the end where
68//! the least significant byte belongs: the kernels allow an output that lies above an input
69//! like this, as they read each limb before they write over it. The bytes of the shorter
70//! operand above its last whole limb follow as one more limb, read from below their start and
71//! shifted, when there are at least three of them and a whole limb under them, and one at a
72//! time otherwise. Above the shorter operand, a carry or a borrow goes on through the longer
73//! one eight bytes at a time while it lasts, and the rest of it is copied (inline below 16
74//! bytes), or left where it is when the sum is written over it at the same place. The carries
75//! that are coin tosses for random operands, out of the shorter operand and out of the top of
76//! the sum, are taken without a branch, which would mispredict half of them.
77//!
78//! Which magnitude of a difference is the larger is a coin toss for random operands of the same
79//! length too, and `x` and `y` are chosen without a branch as well. But then the kernel waits for
80//! the plan to read and compare the top limbs of both operands before it can load anything,
81//! which costs a long difference 12 to 23 cycles once the operands are out of the first-level
82//! cache. So a difference built in an operand's vector from 256 bytes little-endian and 512
83//! big-endian, the lengths from which the kernels run several chains, takes them by branches
84//! instead: a predicted one lets the kernel start at once, and a mispredicted one costs about
85//! what the wait did.
86
87use super::{ByteOrder, Number};
88use core::hint::select_unpredictable;
89use core::ops::{Add, AddAssign};
90use core::{mem, ptr};
91
92// The kernels behind the computation, one set per target under the name `kernels`.
93//
94// The `aarch64` assembly is for the 64-bit little-endian targets only: the ILP32 ones
95// (`arm64_32-apple-watchos`, `aarch64-unknown-linux-gnu_ilp32`) would pass it 32-bit pointers
96// and sizes in registers whose upper halves are undefined. It is also for the operating
97// systems that let user code load and store at any address only: the assembly loads and
98// stores limbs wherever the bytes of a magnitude lie, and unlike the accesses of Rust code,
99// which LLVM splits on a target that requires alignment, those of assembly reach the memory
100// as they are written. The `aarch64` targets that require it (`+strict-align` in their target
101// specifications, for which rustc has no `cfg`) are bare-metal and embedded ones:
102// `aarch64-unknown-none` and `-none-softfloat`, `aarch64v8r-unknown-none`, `-hermit`, `-nuttx`,
103// `-hyperlight`, `-qtee` and `-teeos`. So the gate names the systems known to allow unaligned
104// accesses rather than those that do not: a new target gets the portable kernels until it is
105// added here.
106#[cfg(all(
107 target_arch = "aarch64",
108 target_endian = "little",
109 target_pointer_width = "64",
110 any(
111 target_vendor = "apple",
112 target_os = "linux",
113 target_os = "android",
114 target_os = "windows",
115 target_os = "freebsd",
116 target_os = "netbsd",
117 target_os = "openbsd",
118 target_os = "fuchsia"
119 ),
120 not(miri)
121))]
122#[path = "add/aarch64.rs"]
123mod kernels;
124#[cfg(all(target_arch = "x86_64", not(miri)))]
125#[path = "add/x86_64.rs"]
126mod kernels;
127#[cfg(not(any(
128 all(
129 target_arch = "aarch64",
130 target_endian = "little",
131 target_pointer_width = "64",
132 any(
133 target_vendor = "apple",
134 target_os = "linux",
135 target_os = "android",
136 target_os = "windows",
137 target_os = "freebsd",
138 target_os = "netbsd",
139 target_os = "openbsd",
140 target_os = "fuchsia"
141 ),
142 not(miri)
143 ),
144 all(target_arch = "x86_64", not(miri))
145)))]
146#[path = "add/portable.rs"]
147mod kernels;
148
149use kernels::{add_n_be, add_n_le, sub_n_be, sub_n_le};
150
151/// `r = a + b` over the `n` native limbs at the pointers, for the multiplication; returns the
152/// carry out. On a little-endian target native limbs are little-endian ones, and the kernels of
153/// the addition take them as they are; `r` may be `a` or `b`.
154///
155/// # Safety
156///
157/// As for the kernels: the `n` limbs at each pointer must be valid, and `r` equal to an input or
158/// apart from both.
159#[cfg(target_endian = "little")]
160#[inline]
161pub(super) unsafe fn add_limbs(r: *mut u64, a: *const u64, b: *const u64, n: usize) -> bool {
162 // SAFETY: as the caller guarantees; a native limb is a little-endian one here.
163 unsafe { add_n_le(r.cast(), a.cast(), b.cast(), n, false) }
164}
165
166/// `r = a - b` over the `n` native limbs at the pointers, as [`add_limbs`] adds them; returns the
167/// borrow out.
168///
169/// # Safety
170///
171/// As for [`add_limbs`].
172#[cfg(target_endian = "little")]
173#[inline]
174pub(super) unsafe fn sub_limbs(r: *mut u64, a: *const u64, b: *const u64, n: usize) -> bool {
175 // SAFETY: as for `add_limbs`.
176 unsafe { sub_n_le(r.cast(), a.cast(), b.cast(), n, false) }
177}
178
179impl Add<&Number> for &Number {
180 type Output = Number;
181
182 /// Returns `self + rhs`, in the byte order of `self`, in the shortest form.
183 ///
184 /// The sum is built in one new vector of exactly its length (one byte for zero), the only
185 /// allocation; both operands are left as they were. The operands may have any signs, byte
186 /// orders and lengths, and need not be in the shortest form.
187 ///
188 /// # Examples
189 ///
190 /// ```
191 /// use a::math::numbers::{ByteOrder, Number};
192 ///
193 /// // 0x01_00 + -0x01_01 = -1, in the byte order of the left operand.
194 /// let a = Number::new_from_i32(0x0100, ByteOrder::BigEndian);
195 /// let b = Number::new_from_i32(-0x0101, ByteOrder::LittleEndian);
196 /// let sum = &a + &b;
197 /// assert_eq!(sum.value, [1]);
198 /// assert!(sum.is_negative());
199 /// assert!(sum.byte_order() == ByteOrder::BigEndian);
200 ///
201 /// // A number plus its negation is zero, one zero byte and not negative.
202 /// let minus_a = Number::new_from_i32(-0x0100, ByteOrder::LittleEndian);
203 /// let zero = &a + &minus_a;
204 /// assert_eq!(zero.value, [0]);
205 /// assert!(!zero.is_negative());
206 /// ```
207 fn add(self, rhs: &Number) -> Number {
208 if small(self, rhs) {
209 return small_number_of(self, rhs, rhs.negative);
210 }
211 if medium(self, rhs) {
212 return medium_new(self, rhs, rhs.negative);
213 }
214 ref_plus_ref(self, rhs)
215 }
216}
217
218impl Add<&Number> for Number {
219 type Output = Number;
220
221 /// Returns `self + rhs`, in the byte order of `self`, in the shortest form, as `self += rhs`
222 /// does.
223 ///
224 /// The sum is built in the vector of `self` when its capacity has room for the sum, which
225 /// allocates nothing, and otherwise in one new vector of exactly its length. `rhs` is left
226 /// as it was.
227 ///
228 /// # Examples
229 ///
230 /// ```
231 /// use a::math::numbers::{ByteOrder, Number};
232 ///
233 /// let mut value = Vec::with_capacity(8);
234 /// value.extend([0xFF, 0xFF]);
235 /// let a = Number::new(value, ByteOrder::BigEndian);
236 /// let pointer = a.value.as_ptr();
237 /// let one = Number::new_from_u8(1, ByteOrder::BigEndian);
238 ///
239 /// // 0xFF_FF + 1 = 0x01_00_00, in the vector of `a`.
240 /// let sum = a + &one;
241 /// assert_eq!(sum.value, [1, 0, 0]);
242 /// assert_eq!(sum.value.as_ptr(), pointer);
243 /// ```
244 fn add(mut self, rhs: &Number) -> Number {
245 self += rhs;
246 self
247 }
248}
249
250impl Add<Number> for &Number {
251 type Output = Number;
252
253 /// Returns `self + rhs`, in the byte order of `self`, in the shortest form.
254 ///
255 /// The sum is built in the vector of `rhs` when its capacity has room for the sum, which
256 /// allocates nothing, also when `rhs` is in the other byte order; otherwise it gets one new
257 /// vector of exactly its length. `self` is left as it was.
258 ///
259 /// # Examples
260 ///
261 /// ```
262 /// use a::math::numbers::{ByteOrder, Number};
263 ///
264 /// let a = Number::new_from_u32(0x01_02_03, ByteOrder::BigEndian);
265 /// let mut value = Vec::with_capacity(3);
266 /// value.extend([0x10, 0x20]);
267 /// let b = Number::new(value, ByteOrder::LittleEndian);
268 /// let pointer = b.value.as_ptr();
269 ///
270 /// // 0x01_02_03 + 0x20_10 = 0x01_22_13, big-endian, in the vector of `b`.
271 /// let sum = &a + b;
272 /// assert_eq!(sum.value, [0x01, 0x22, 0x13]);
273 /// assert_eq!(sum.value.as_ptr(), pointer);
274 /// ```
275 fn add(self, mut rhs: Number) -> Number {
276 if small(self, &rhs) {
277 let sum = small_sum(self, &rhs, rhs.negative);
278 let order = self.byte_order;
279 // SAFETY: a small sum is from 1 to 17 bytes long, and the vector it is written in
280 // has room for it.
281 unsafe {
282 if rhs.value.capacity() >= sum.len {
283 write_small_in(&mut rhs.value, order, &sum);
284 } else {
285 replace_small(&mut rhs.value, order, sum.lo, sum.hi, sum.len, sum.k);
286 }
287 }
288 return Number {
289 value: rhs.value,
290 negative: sum.negative,
291 byte_order: order,
292 };
293 }
294 if medium(self, &rhs) {
295 let (b, negative): (*mut Number, bool) = (&mut rhs, rhs.negative);
296 // SAFETY: both operands are valid and take the medium path, and the sum goes to `b`.
297 unsafe { medium_into(self, b, negative, b, Fallback::New) };
298 return rhs;
299 }
300 // `rhs` is dropped on return, with the vector it holds then, if any.
301 ref_plus_value(self, &mut rhs)
302 }
303}
304
305impl Add<Number> for Number {
306 type Output = Number;
307
308 /// Returns `self + rhs`, in the byte order of `self`, in the shortest form, as `self += rhs`
309 /// does.
310 ///
311 /// The sum is built in the vector of `self` when its capacity has room for the sum, or else
312 /// in the vector of `rhs` when that has room, also when `rhs` is in the other byte order,
313 /// either of which allocates nothing; otherwise it gets one new vector of exactly its
314 /// length.
315 ///
316 /// # Examples
317 ///
318 /// ```
319 /// use a::math::numbers::{ByteOrder, Number};
320 ///
321 /// // 0x01_00 + -0x02_FF = -0x01_FF.
322 /// let a = Number::new_from_i32(0x0100, ByteOrder::LittleEndian);
323 /// let b = Number::new_from_i32(-0x02FF, ByteOrder::BigEndian);
324 /// let sum = a + b;
325 /// assert_eq!(sum.value, [0xFF, 0x01]);
326 /// assert!(sum.is_negative());
327 /// ```
328 fn add(mut self, rhs: Number) -> Number {
329 self += rhs;
330 self
331 }
332}
333
334impl AddAssign<&Number> for Number {
335 /// Adds `rhs` to `self`, keeping the byte order of `self`, in the shortest form.
336 ///
337 /// The sum is built in the vector of `self` when its capacity has room for the sum, which
338 /// allocates nothing, and otherwise in one new vector of exactly its length, which
339 /// replaces it. `rhs` is left as it was.
340 ///
341 /// # Examples
342 ///
343 /// ```
344 /// use a::math::numbers::{ByteOrder, Number};
345 ///
346 /// let mut n = Number::new_from_u8(0xFF, ByteOrder::LittleEndian);
347 /// n += &Number::new_from_u16(0x0101, ByteOrder::BigEndian);
348 /// assert_eq!(n.value, [0x00, 0x02]);
349 /// n += &Number::new_from_i16(-0x0201, ByteOrder::BigEndian);
350 /// assert_eq!(n.value, [1]);
351 /// assert!(n.is_negative());
352 /// ```
353 #[inline]
354 fn add_assign(&mut self, rhs: &Number) {
355 if small(self, rhs) {
356 small_add_to(self, rhs, rhs.negative);
357 return;
358 }
359 if medium(self, rhs) {
360 let (a, b) = (ptr::from_mut(self), ptr::from_ref(rhs).cast_mut());
361 // SAFETY: both operands are valid and take the medium path, the sum goes to `a`, and
362 // `b` is only read.
363 unsafe { medium_into(a, b, rhs.negative, a, Fallback::New) };
364 return;
365 }
366 add_assign_ref(self, rhs);
367 }
368}
369
370impl AddAssign<Number> for Number {
371 /// Adds `rhs` to `self`, keeping the byte order of `self`, in the shortest form.
372 ///
373 /// The sum is built in the vector of `self` when its capacity has room for the sum, or else
374 /// in the vector of `rhs` when that has room, also when `rhs` is in the other byte order,
375 /// either of which allocates nothing; otherwise it gets one new vector of exactly its
376 /// length.
377 ///
378 /// # Examples
379 ///
380 /// ```
381 /// use a::math::numbers::{ByteOrder, Number};
382 ///
383 /// // `n` has no room for 0x01_00, but the vector of the right operand has.
384 /// let mut n = Number::new_from_u8(0xFF, ByteOrder::BigEndian);
385 /// let mut value = Vec::with_capacity(2);
386 /// value.push(1);
387 /// let one = Number::new(value, ByteOrder::BigEndian);
388 /// let pointer = one.value.as_ptr();
389 /// n += one;
390 /// assert_eq!(n.value, [1, 0]);
391 /// assert_eq!(n.value.as_ptr(), pointer);
392 /// ```
393 #[inline]
394 fn add_assign(&mut self, mut rhs: Number) {
395 if small(self, &rhs) {
396 let sum = small_sum(self, &rhs, rhs.negative);
397 self.negative = sum.negative;
398 let order = self.byte_order;
399 // SAFETY: a small sum is from 1 to 17 bytes long, and the vector it is written in
400 // has room for it.
401 unsafe {
402 if self.value.capacity() >= sum.len {
403 write_small_in(&mut self.value, order, &sum);
404 } else if rhs.value.capacity() >= sum.len {
405 write_small_in(&mut rhs.value, order, &sum);
406 // The vector of `self` goes to `rhs`, and is dropped with it.
407 mem::swap(&mut self.value, &mut rhs.value);
408 } else {
409 replace_small(&mut self.value, order, sum.lo, sum.hi, sum.len, sum.k);
410 }
411 }
412 return;
413 }
414 if medium(self, &rhs) {
415 let (a, negative) = (ptr::from_mut(self), rhs.negative);
416 // SAFETY: both operands are valid and take the medium path, the sum goes to `a`, and
417 // `rhs` is taken by value.
418 unsafe { medium_into(a, &mut rhs, negative, a, Fallback::Other) };
419 return;
420 }
421 // `rhs` is dropped on return, with the vector it holds then.
422 add_assign_value(self, &mut rhs);
423 }
424}
425
426// The general path: operands of more than 64 bytes, out of line, so that the paths in registers
427// save no registers for it.
428
429/// `a + b` for borrowed operands, of which at least one has more than 64 bytes.
430#[inline(never)]
431pub(super) fn ref_plus_ref(a: &Number, b: &Number) -> Number {
432 let plan = plan(a, b);
433 // SAFETY: the plan is that of `a` and `b`, in their own byte orders.
434 let value = unsafe { sum_new(a, b, &plan) };
435 Number {
436 value,
437 negative: plan.negative,
438 byte_order: a.byte_order,
439 }
440}
441
442/// `a + b` for a borrowed `a` and an owned `b`, of which at least one has more than 64 bytes.
443/// The sum takes the vector of `b` when it is built there, leaving `b` an empty one; the
444/// caller drops `b` either way.
445#[inline(never)]
446fn ref_plus_value(a: &Number, b: &mut Number) -> Number {
447 let plan = plan(a, b);
448 let order = a.byte_order;
449 let reverse = b.byte_order != order && plan.len > 0;
450 if reverse {
451 b.value.reverse();
452 b.byte_order = order;
453 }
454 let value = if plan.fits(b.value.capacity()) {
455 // SAFETY: the plan is that of `a` and `b`, which are both in `order` now unless the
456 // sum is zero, and the vector of `b` has room for the sum.
457 unsafe { sum_in(&mut b.value, order, true, &a.value, &plan, reverse) };
458 mem::take(&mut b.value)
459 } else {
460 // SAFETY: the plan is that of `a` and `b`, in their own byte orders.
461 unsafe { sum_new(a, b, &plan) }
462 };
463 Number {
464 value,
465 negative: plan.negative,
466 byte_order: order,
467 }
468}
469
470/// `a += b` for a borrowed `b`, where at least one of them has more than 64 bytes; returns `a`,
471/// for the subtraction.
472#[inline(never)]
473pub(super) fn add_assign_ref<'a>(a: &'a mut Number, b: &Number) -> &'a mut Number {
474 let plan = plan(a, b);
475 if !plan.fits(a.value.capacity()) {
476 replace_with_sum(a, b, &plan);
477 return a;
478 }
479 // A borrowed `b` cannot be reversed: when the byte orders differ, `a` is reversed into the
480 // byte order of `b` instead, and the sum reversed back. One call, so that the computation
481 // inlined into it is there once.
482 let reverse = b.byte_order != a.byte_order && plan.len > 0;
483 if reverse {
484 a.value.reverse();
485 }
486 // SAFETY: the plan is that of `a` and `b`, which are in the byte order of `b` now unless the
487 // sum is zero, and the vector of `a` has room for the sum.
488 unsafe { sum_in(&mut a.value, b.byte_order, false, &b.value, &plan, reverse) };
489 if reverse {
490 a.value.reverse();
491 }
492 a.negative = plan.negative;
493 a
494}
495
496/// `a += b` for an owned `b`, where at least one of them has more than 64 bytes. When the sum
497/// is built in the vector of `b`, the vector of `a` goes to `b`; the caller drops `b` either
498/// way.
499#[inline(never)]
500fn add_assign_value(a: &mut Number, b: &mut Number) {
501 let plan = plan(a, b);
502 let order = a.byte_order;
503 let reverse = b.byte_order != order && plan.len > 0;
504 if reverse {
505 b.value.reverse();
506 b.byte_order = order;
507 }
508 let in_a = plan.fits(a.value.capacity());
509 if !in_a && !plan.fits(b.value.capacity()) {
510 replace_with_sum(a, b, &plan);
511 return;
512 }
513 // One call, so that the computation inlined into it is there once.
514 let (v, other) = if in_a {
515 (&mut a.value, &b.value)
516 } else {
517 (&mut b.value, &a.value)
518 };
519 // SAFETY: the plan is that of `a` and `b`, which are both in `order` now unless the sum is
520 // zero, and the vector of `a`, or else that of `b`, has room for the sum.
521 unsafe { sum_in(v, order, !in_a, other, &plan, reverse) };
522 if !in_a {
523 mem::swap(&mut a.value, &mut b.value);
524 }
525 a.negative = plan.negative;
526}
527
528/// `a += b` for operands of which neither vector has room for the sum: its new vector replaces
529/// that of `a`. Out of line and cold, so that the sums built in place keep no registers for
530/// its allocation.
531#[cold]
532#[inline(never)]
533fn replace_with_sum(a: &mut Number, b: &Number, plan: &Plan) {
534 // SAFETY: the plan is that of `a` and `b`, in their own byte orders.
535 a.value = unsafe { sum_new(a, b, plan) };
536 a.negative = plan.negative;
537}
538
539// Small numbers
540
541/// The longest vector, in bytes, of the operands that take the small path: with both this
542/// short, their magnitudes are added or subtracted as `u128`s.
543pub(super) const SMALL: usize = 16;
544
545/// Whether `a + b` takes the small path: both vectors hold at most [`SMALL`] bytes.
546#[inline(always)]
547pub(super) fn small(a: &Number, b: &Number) -> bool {
548 a.value.len() <= SMALL && b.value.len() <= SMALL
549}
550
551// The small paths of the forms with a borrowed right operand, which subtraction shares: `b` is
552// taken with the sign `negative_b`, its own for a sum and the opposite one for a difference.
553// They are inlined, so that the sign is a value in a register either way.
554
555/// `a + b` for small borrowed operands, `b` taken with the sign `negative_b`, in one new
556/// vector.
557#[inline(always)]
558pub(super) fn small_number_of(a: &Number, b: &Number, negative_b: bool) -> Number {
559 let sum = small_sum(a, b, negative_b);
560 // SAFETY: a small sum is from 1 to 17 bytes long.
561 unsafe { small_number(a.byte_order, sum.lo, sum.hi, sum.len, sum.k, sum.negative) }
562}
563
564/// `a += b` for small operands and a borrowed `b`, taken with the sign `negative_b`.
565#[inline(always)]
566pub(super) fn small_add_to(a: &mut Number, b: &Number, negative_b: bool) {
567 let sum = small_sum(a, b, negative_b);
568 a.negative = sum.negative;
569 let order = a.byte_order;
570 // SAFETY: a small sum is from 1 to 17 bytes long, and the vector it is written in has room
571 // for it.
572 unsafe {
573 if a.value.capacity() >= sum.len {
574 write_small_in(&mut a.value, order, &sum);
575 } else {
576 replace_small(&mut a.value, order, sum.lo, sum.hi, sum.len, sum.k);
577 }
578 }
579}
580
581/// The sum of two small operands, as the small path computes it in registers.
582struct Small {
583 /// The low eight bytes of the magnitude.
584 lo: u64,
585 /// The next eight bytes of the magnitude.
586 hi: u64,
587 /// The length of the sum in the shortest form, from 1 to 17: one byte for zero, and 17
588 /// when it carries out of the sixteenth byte, whose seventeenth byte is then 1.
589 len: usize,
590 /// The length of the longer of the operands' vectors, up to 16, which the stores of the sum
591 /// are chosen by (see [`write_sum`]).
592 k: usize,
593 /// Whether the sum is negative; never for zero.
594 negative: bool,
595}
596
597/// The sum of the small operands `a` and `b`, `b` taken with the sign `negative_b`: the sum of
598/// their magnitudes and both differences are computed, and the one the signs ask for chosen
599/// without a branch, as is the difference of the larger magnitude and the smaller.
600#[inline(always)]
601fn small_sum(a: &Number, b: &Number, negative_b: bool) -> Small {
602 // SAFETY: both vectors hold at most 16 bytes, as `small` checked.
603 let (x, y) = unsafe {
604 (
605 read_small(&a.value, a.byte_order),
606 read_small(&b.value, b.byte_order),
607 )
608 };
609 // A zero magnitude with a sign is subtracted from the other one, or the other one from it,
610 // which gives the other one with its own sign, as adding it would; a zero sum is never
611 // negative.
612 let subtract = a.negative != negative_b;
613 let (sum, carry) = x.overflowing_add(y);
614 let (difference, borrow) = x.overflowing_sub(y);
615 let difference = select_unpredictable(borrow, y.wrapping_sub(x), difference);
616 let magnitude = select_unpredictable(subtract, difference, sum);
617 let carry = carry & !subtract;
618 // One byte for zero, and 17 with a carry, chosen without a branch: the carry of two
619 // magnitudes of 16 bytes is often a coin toss, also when the signs differ and it is not
620 // used.
621 let len = select_unpredictable(
622 carry,
623 SMALL + 1,
624 SMALL - (magnitude | 1).leading_zeros() as usize / 8,
625 );
626 Small {
627 lo: magnitude as u64,
628 hi: (magnitude >> 64) as u64,
629 len,
630 k: a.value.len().max(b.value.len()),
631 // The sign of the larger magnitude, which is that of `y` when it is subtracted from a
632 // smaller `x`; never for zero.
633 negative: a.negative != (subtract && borrow) && (magnitude != 0 || carry),
634 }
635}
636
637/// The magnitude in the at most 16 bytes of `v`, in the layout of `order`.
638///
639/// Only the bytes of `v` are read, never its spare capacity: from 8 bytes up, the eight
640/// least significant ones and the eight most significant ones, which overlap below 16 and
641/// agree where they do; from 4 up, the same with four, and from 2 with two.
642///
643/// # Safety
644///
645/// `v` must hold at most 16 bytes.
646#[inline(always)]
647pub(super) unsafe fn read_small(v: &[u8], order: ByteOrder) -> u128 {
648 let (p, n) = (v.as_ptr(), v.len());
649 let be = order == ByteOrder::BigEndian;
650 // SAFETY: every read lies inside the `n` bytes of `v`: `[0, k)` and `[n - k, n)` with
651 // `n >= k`, for `k` of 8, 4 and 2, and the byte `0` with `n = 1`.
652 unsafe {
653 if n >= 4 {
654 if n >= 8 {
655 let lo = if be {
656 u64::from_be_bytes(read(p.add(n - 8)))
657 } else {
658 u64::from_le_bytes(read(p))
659 };
660 let hi = if n > 8 {
661 let top = if be {
662 u64::from_be_bytes(read(p))
663 } else {
664 u64::from_le_bytes(read(p.add(n - 8)))
665 };
666 // `top` holds positions `n - 8..n`; those from 8 up are the high half, a
667 // shift by `8 * (16 - n)` down, which is below 64 and so `-8n` modulo 64.
668 top.wrapping_shr((n as u32).wrapping_neg().wrapping_mul(8))
669 } else {
670 0
671 };
672 u128::from(lo) | (u128::from(hi) << 64)
673 } else {
674 let (lo, top) = if be {
675 (
676 u32::from_be_bytes(read(p.add(n - 4))),
677 u32::from_be_bytes(read(p)),
678 )
679 } else {
680 (
681 u32::from_le_bytes(read(p)),
682 u32::from_le_bytes(read(p.add(n - 4))),
683 )
684 };
685 // `top` holds positions `n - 4..n`, which agree with `lo` where they overlap.
686 u128::from(u64::from(lo) | (u64::from(top) << (8 * (n - 4))))
687 }
688 } else if n >= 2 {
689 let (lo, top) = if be {
690 (
691 u16::from_be_bytes(read(p.add(n - 2))),
692 u16::from_be_bytes(read(p)),
693 )
694 } else {
695 (
696 u16::from_le_bytes(read(p)),
697 u16::from_le_bytes(read(p.add(n - 2))),
698 )
699 };
700 u128::from(u32::from(lo) | (u32::from(top) << (8 * (n - 2))))
701 } else if n == 1 {
702 u128::from(*p)
703 } else {
704 0
705 }
706 }
707}
708
709/// Writes the small sum of magnitude `hi:lo` and length `len` over the first `len` bytes at
710/// `p`, in the layout of `order`.
711///
712/// From 8 bytes up, two stores of eight, the most significant ones first, then the eight least
713/// significant ones over the bytes they overlap; 17 bytes are 16 and the top one. From 4 up, the
714/// same with four. Below 4, the least significant, the middle and the most significant byte. A
715/// sum of two operands of `k` bytes is `k` or `k + 1` bytes long, and for `k` of 1, 2, 4 and 8
716/// both lengths take the same stores.
717///
718/// The bytes are the sum's when `len` is its length as [`Small::len`] gives it.
719///
720/// # Safety
721///
722/// `len` must be from 1 to 17, and `p` valid for writing `len` bytes.
723#[inline(always)]
724pub(super) unsafe fn write_small(p: *mut u8, order: ByteOrder, lo: u64, hi: u64, len: usize) {
725 let be = order == ByteOrder::BigEndian;
726 // The shift that puts the high bytes of the class from 4 or 8 bytes at the top of their
727 // store, `8 * (2k - len)` for stores of `k` bytes, modulo `8k`.
728 let shift = (len as u32).wrapping_neg().wrapping_mul(8);
729 // SAFETY: every write lies inside the `len` bytes at `p`: `[0, 17)` for 17, `[0, k)` and
730 // `[len - k, len)` for `k` of 8 and 4 with `len >= k`, and the bytes `0`, `len / 2` and
731 // `len - 1` below 4. Of two writes that overlap, the later one puts the sum's bytes there.
732 unsafe {
733 if len >= 8 {
734 if len > SMALL {
735 if be {
736 *p = 1;
737 write(p.add(1), hi.to_be_bytes());
738 write(p.add(9), lo.to_be_bytes());
739 } else {
740 write(p, lo.to_le_bytes());
741 write(p.add(8), hi.to_le_bytes());
742 *p.add(16) = 1;
743 }
744 } else {
745 // The high bytes at the top of their eight; the bytes below them do not
746 // matter, as the low eight are written over them. For 8 bytes, `hi` and the
747 // shift are 0.
748 let top = hi.wrapping_shl(shift);
749 if be {
750 write(p, top.to_be_bytes());
751 write(p.add(len - 8), lo.to_be_bytes());
752 } else {
753 write(p.add(len - 8), top.to_le_bytes());
754 write(p, lo.to_le_bytes());
755 }
756 }
757 } else if len >= 4 {
758 let top = ((lo >> 32) as u32).wrapping_shl(shift);
759 if be {
760 write(p, top.to_be_bytes());
761 write(p.add(len - 4), (lo as u32).to_be_bytes());
762 } else {
763 write(p.add(len - 4), top.to_le_bytes());
764 write(p, (lo as u32).to_le_bytes());
765 }
766 } else {
767 let (mid, last) = (len / 2, len - 1);
768 let (b0, b1, b2) = (lo as u8, (lo >> (8 * mid)) as u8, (lo >> (8 * last)) as u8);
769 if be {
770 *p.add(last) = b0;
771 *p.add(last - mid) = b1;
772 *p = b2;
773 } else {
774 *p = b0;
775 *p.add(mid) = b1;
776 *p.add(last) = b2;
777 }
778 }
779 }
780}
781
782/// Writes the small sum of magnitude `hi:lo` and length `len` over the first `len` bytes at
783/// `p`, in the layout of `order`, for operands whose longer vector holds `k` bytes.
784///
785/// The stores are chosen by `k`, not by `len`: a sum of two magnitudes of `k` bytes is `k` or
786/// `k + 1` bytes long, a coin toss for random ones, and a branch on the length would mispredict
787/// half of them where the two lengths take different stores in [`write_small`] (`k` of 3, 7
788/// and 16). So for `k` of 16, two stores of eight bytes and one of the seventeenth or sixteenth
789/// byte; from 8, two of eight that overlap; from 4, two of four, which also store 8 bytes; for 3,
790/// two of two, which also store 4; below, three of a byte. A shorter sum, of operands that nearly
791/// cancel or have zero bytes above their most significant ones, takes the stores of its length.
792///
793/// # Safety
794///
795/// `len` must be from 1 to 17 and at most `k + 1`, and `p` valid for writing `len` bytes.
796#[inline(always)]
797unsafe fn write_sum(p: *mut u8, order: ByteOrder, lo: u64, hi: u64, len: usize, k: usize) {
798 let be = order == ByteOrder::BigEndian;
799 // SAFETY: every write lies inside the `len` bytes at `p`: from 16, `[0, 16)` and the byte
800 // `min(16, len - 1)`; from 8, `[0, 8)` and `[len - 8, len)`; from 4, `[0, 4)` and
801 // `[len - 4, len)`; from 2, `[0, 2)` and `[len - 2, len)`; below, the bytes `0`, `len / 2` and
802 // `len - 1`. Of two writes that overlap, the later one puts the sum's bytes there.
803 unsafe {
804 if k >= 8 {
805 if len < 8 {
806 // A sum much shorter than the operands' vectors.
807 write_small(p, order, lo, hi, len);
808 } else if k == SMALL && len >= SMALL {
809 // The seventeenth byte, 1, with a carry, and otherwise the sixteenth again.
810 let top = select_unpredictable(len > SMALL, 1, (hi >> 56) as u8);
811 if be {
812 write(p.add(len - 8), lo.to_be_bytes());
813 write(p.add(len - 16), hi.to_be_bytes());
814 *p = top;
815 } else {
816 write(p, lo.to_le_bytes());
817 write(p.add(8), hi.to_le_bytes());
818 *p.add(len - 1) = top;
819 }
820 } else {
821 // As `write_small` writes 8 to 16 bytes.
822 let top = hi.wrapping_shl((len as u32).wrapping_neg().wrapping_mul(8));
823 if be {
824 write(p, top.to_be_bytes());
825 write(p.add(len - 8), lo.to_be_bytes());
826 } else {
827 write(p.add(len - 8), top.to_le_bytes());
828 write(p, lo.to_le_bytes());
829 }
830 }
831 } else if k >= 4 && len >= 4 {
832 // As `write_small` writes 4 to 7 bytes, and also 8: the high four bytes of the eight,
833 // shifted by `8 (8 - len)` modulo 32, at the top.
834 let shift = (len as u32).wrapping_neg().wrapping_mul(8);
835 let top = ((lo >> 32) as u32).wrapping_shl(shift);
836 if be {
837 write(p, top.to_be_bytes());
838 write(p.add(len - 4), (lo as u32).to_be_bytes());
839 } else {
840 write(p.add(len - 4), top.to_le_bytes());
841 write(p, (lo as u32).to_le_bytes());
842 }
843 } else if k == 3 && len >= 2 {
844 // Two to four bytes: the high two of the four, shifted by `8 (4 - len)` modulo 16,
845 // at the top.
846 let shift = (len as u32).wrapping_neg().wrapping_mul(8) % 16;
847 let top = ((lo >> 16) as u16) << shift;
848 if be {
849 write(p, top.to_be_bytes());
850 write(p.add(len - 2), (lo as u16).to_be_bytes());
851 } else {
852 write(p.add(len - 2), top.to_le_bytes());
853 write(p, (lo as u16).to_le_bytes());
854 }
855 } else {
856 // One to three bytes, as `write_small` writes them.
857 let (mid, last) = (len / 2, len - 1);
858 let (b0, b1, b2) = (lo as u8, (lo >> (8 * mid)) as u8, (lo >> (8 * last)) as u8);
859 if be {
860 *p.add(last) = b0;
861 *p.add(last - mid) = b1;
862 *p = b2;
863 } else {
864 *p = b0;
865 *p.add(mid) = b1;
866 *p.add(last) = b2;
867 }
868 }
869 }
870}
871
872/// Writes the small sum `sum` over `v`, in the layout of `order`, as its whole value.
873///
874/// # Safety
875///
876/// The capacity of `v` must be at least the length of the sum.
877#[inline(always)]
878unsafe fn write_small_in(v: &mut Vec<u8>, order: ByteOrder, sum: &Small) {
879 // SAFETY: as the caller guarantees; `write_sum` writes all of the first `len` bytes, so
880 // they are initialised when the length is set.
881 unsafe {
882 write_sum(v.as_mut_ptr(), order, sum.lo, sum.hi, sum.len, sum.k);
883 v.set_len(sum.len);
884 }
885}
886
887/// The small sum of magnitude `hi:lo` and length `len`, of operands whose longer vector holds
888/// `k` bytes, in one new vector of exactly its length, in the layout of `order`.
889///
890/// # Safety
891///
892/// `len` must be from 1 to 17 and at most `k + 1`.
893#[inline(always)]
894unsafe fn new_small(order: ByteOrder, lo: u64, hi: u64, len: usize, k: usize) -> Vec<u8> {
895 let mut v = Vec::with_capacity(len);
896 // SAFETY: as the caller guarantees, and the new vector has room for the `len` bytes, all of
897 // which `write_sum` writes.
898 unsafe {
899 write_sum(v.as_mut_ptr(), order, lo, hi, len, k);
900 v.set_len(len);
901 }
902 v
903}
904
905/// The small sum of magnitude `hi:lo`, length `len` and sign `negative`, of operands whose
906/// longer vector holds `k` bytes, as a number in one new vector of exactly its length, in the
907/// byte order `order`. Out of line, and given the sum in registers, so that the small path of
908/// `&a + &b` saves no registers for the allocation.
909///
910/// # Safety
911///
912/// `len` must be from 1 to 17 and at most `k + 1`.
913#[inline(never)]
914unsafe fn small_number(
915 order: ByteOrder,
916 lo: u64,
917 hi: u64,
918 len: usize,
919 k: usize,
920 negative: bool,
921) -> Number {
922 // SAFETY: as the caller guarantees: told so, the compiler checks neither the size of the
923 // allocation nor a zero length.
924 unsafe {
925 core::hint::assert_unchecked(len != 0);
926 core::hint::assert_unchecked(len <= SMALL + 1);
927 Number {
928 value: new_small(order, lo, hi, len, k),
929 negative,
930 byte_order: order,
931 }
932 }
933}
934
935/// Replaces `v` with one new vector of exactly the small sum's length, holding the sum of
936/// magnitude `hi:lo` and length `len`, of operands whose longer vector holds `k` bytes, in the
937/// layout of `order`. Out of line and cold, and given the sum in registers, so that the small
938/// path saves no registers for it.
939///
940/// # Safety
941///
942/// `len` must be from 1 to 17 and at most `k + 1`.
943#[cold]
944#[inline(never)]
945unsafe fn replace_small(v: &mut Vec<u8>, order: ByteOrder, lo: u64, hi: u64, len: usize, k: usize) {
946 // SAFETY: as the caller guarantees.
947 *v = unsafe { new_small(order, lo, hi, len, k) };
948}
949
950// Operands of up to 64 bytes
951
952/// The longest vector, in bytes, of the operands that the medium path adds in registers.
953pub(super) const MEDIUM: usize = 64;
954
955/// Whether `a + b` takes the medium path, when it does not take the small one: both vectors
956/// hold at most [`MEDIUM`] bytes.
957#[inline(always)]
958pub(super) fn medium(a: &Number, b: &Number) -> bool {
959 a.value.len() <= MEDIUM && b.value.len() <= MEDIUM
960}
961
962/// A sum computed in registers by the medium path.
963struct Medium {
964 /// The limbs of the magnitude, the least significant first: those of the operands, then
965 /// the carry out of the top one, then zeros.
966 limbs: [u64; 9],
967 /// Whether the sum is negative, unless it is zero.
968 negative: bool,
969}
970
971/// Calls `$f::<L>($args)` for the number of limbs `L` of the longer of the vectors `$a` and `$b`,
972/// from 3 (for at most 24 bytes) to 8.
973macro_rules! by_limbs {
974 ($a:expr, $b:expr, $f:ident($($arg:expr),*)) => {
975 match $a.len().max($b.len()).div_ceil(8) {
976 ..=3 => $f::<3>($($arg),*),
977 4 => $f::<4>($($arg),*),
978 5 => $f::<5>($($arg),*),
979 6 => $f::<6>($($arg),*),
980 7 => $f::<7>($($arg),*),
981 _ => $f::<8>($($arg),*),
982 }
983 };
984}
985
986/// Where the medium path builds a sum when the vector of the operand it goes to has no room for
987/// it, as the operators promise.
988#[derive(Clone, Copy, PartialEq, Eq)]
989pub(super) enum Fallback {
990 /// In one new vector of exactly its length.
991 New,
992 /// In the vector of `b`, an operand taken by value, when that has room, and otherwise in one
993 /// new vector of exactly its length.
994 Other,
995}
996
997/// Writes `a + b`, `b` taken with the sign `negative_b`, over `out`, one of the operands: its
998/// vector, in the byte order of `a`, its sign and its byte order. The sum is built in the vector
999/// of `out` when that has room for it, and otherwise as `fallback` says; the vector of `out`
1000/// then goes to `b`, or is dropped. For operands of at most 64 bytes, not both of at most 16.
1001///
1002/// The code for the number `L` of limbs of the longer vector computes the sum, and hands a sum
1003/// shorter than `8 (L - 1)` bytes, or one the vector of `out` has no room for, to
1004/// [`medium_slow`]. Each call is the last step of its caller, so that none saves registers.
1005///
1006/// # Safety
1007///
1008/// `a` and `b` must be valid for reading, both their vectors hold at most 64 bytes and one more
1009/// than 16, and `out` must be `a` or `b`, valid for writing. With [`Fallback::Other`], `out` must
1010/// be `a`, and `b` an operand taken by value, valid for writing.
1011#[inline(never)]
1012pub(super) unsafe fn medium_into(
1013 a: *const Number,
1014 b: *mut Number,
1015 negative_b: bool,
1016 out: *mut Number,
1017 fallback: Fallback,
1018) {
1019 // SAFETY: as the caller guarantees; the vectors hold at most `8 L` bytes for the `L` chosen.
1020 unsafe {
1021 let (x, y) = (&(*a).value, &(*b).value);
1022 by_limbs!(x, y, medium_into_in(a, b, negative_b, out, fallback))
1023 }
1024}
1025
1026/// [`medium_into`] for vectors of at most `L` limbs, out of line, so that each length saves only
1027/// the registers it needs, and the call is the dispatch's last step.
1028///
1029/// # Safety
1030///
1031/// As for [`medium_into`], with vectors of at most `8 L` bytes.
1032#[inline(never)]
1033unsafe fn medium_into_in<const L: usize>(
1034 a: *const Number,
1035 b: *mut Number,
1036 negative_b: bool,
1037 out: *mut Number,
1038 fallback: Fallback,
1039) {
1040 // SAFETY: as the caller guarantees. Both operands are read before `out` is written, and
1041 // the references to them end before it is.
1042 unsafe {
1043 let order = (*a).byte_order;
1044 let m = medium_sum::<L>(
1045 read_limbs::<L>(&(*a).value, order),
1046 read_limbs::<L>(&(*b).value, (*b).byte_order),
1047 (*a).negative,
1048 negative_b,
1049 );
1050 let r = &m.limbs;
1051 let (len, top) = top_of::<L>(r);
1052 let v = &mut (*out).value;
1053 if !long::<L>(r) || v.capacity() < len {
1054 return medium_slow(a, b, negative_b, out, fallback);
1055 }
1056 write_long::<L>(v.as_mut_ptr(), order, r, len, top);
1057 v.set_len(len);
1058 (*out).negative = m.negative;
1059 (*out).byte_order = order;
1060 }
1061}
1062
1063/// `a + b`, `b` taken with the sign `negative_b`, in one new vector of exactly its length, for
1064/// operands of at most 64 bytes, not both of at most 16.
1065#[inline(never)]
1066pub(super) fn medium_new(a: &Number, b: &Number, negative_b: bool) -> Number {
1067 // SAFETY: the vectors hold at most `8 L` bytes for the `L` chosen.
1068 unsafe { by_limbs!(a.value, b.value, medium_new_in(a, b, negative_b)) }
1069}
1070
1071/// [`medium_new`] for vectors of at most `L` limbs.
1072///
1073/// # Safety
1074///
1075/// Both vectors must hold at most `8 L` bytes, and one more than 16.
1076#[inline(never)]
1077unsafe fn medium_new_in<const L: usize>(a: &Number, b: &Number, negative_b: bool) -> Number {
1078 let order = a.byte_order;
1079 // SAFETY: as the caller guarantees.
1080 let m = unsafe {
1081 medium_sum::<L>(
1082 read_limbs::<L>(&a.value, order),
1083 read_limbs::<L>(&b.value, b.byte_order),
1084 a.negative,
1085 negative_b,
1086 )
1087 };
1088 let r = &m.limbs;
1089 if !long::<L>(r) {
1090 return medium_new_slow(a, b, negative_b);
1091 }
1092 let (len, top) = top_of::<L>(r);
1093 let mut v = Vec::with_capacity(len);
1094 // SAFETY: the new vector has room for the sum, all of whose bytes are written before the
1095 // length is set.
1096 unsafe {
1097 write_long::<L>(v.as_mut_ptr(), order, r, len, top);
1098 v.set_len(len);
1099 }
1100 Number {
1101 value: v,
1102 negative: m.negative,
1103 byte_order: order,
1104 }
1105}
1106
1107// The cases the medium path leaves to slower code: a sum much shorter than the longer operand's
1108// vector, and no room in the vector it would be built in. The sum is computed as above and
1109// written to a buffer, whose bytes then go where the operator promises.
1110
1111/// A sum of the medium path in a buffer.
1112struct Bytes {
1113 /// The bytes of the magnitude, least significant first.
1114 bytes: [u8; 72],
1115 /// The length of the sum in the shortest form: one byte for zero.
1116 len: usize,
1117 /// Whether the sum is negative; never for zero.
1118 negative: bool,
1119}
1120
1121impl Bytes {
1122 /// `a + b`, `b` taken with the sign `negative_b`, for operands of at most 64 bytes.
1123 fn of(a: &Number, b: &Number, negative_b: bool) -> Self {
1124 // The limbs of a magnitude of at most 64 bytes, a byte at a time: this code is rare, and
1125 // kept short.
1126 let limbs = |n: &Number| {
1127 let mut x = [0u64; 9];
1128 let len = n.value.len();
1129 for (i, &byte) in n.value.iter().enumerate() {
1130 let p = if n.byte_order == ByteOrder::BigEndian {
1131 len - 1 - i
1132 } else {
1133 i
1134 };
1135 x[p / 8] |= u64::from(byte) << (8 * (p % 8));
1136 }
1137 x
1138 };
1139 let m = medium_sum::<8>(limbs(a), limbs(b), a.negative, negative_b);
1140 let mut bytes = [0; 72];
1141 for (chunk, limb) in bytes.as_chunks_mut::<8>().0.iter_mut().zip(m.limbs) {
1142 *chunk = limb.to_le_bytes();
1143 }
1144 let top = bytes.iter().rposition(|&b| b != 0);
1145 Bytes {
1146 bytes,
1147 len: top.map_or(1, |p| p + 1),
1148 negative: m.negative && top.is_some(),
1149 }
1150 }
1151
1152 /// Writes the sum over `v`, in the layout of `order`, as its whole value.
1153 ///
1154 /// # Safety
1155 ///
1156 /// The capacity of `v` must be at least the length of the sum.
1157 unsafe fn put(&self, v: &mut Vec<u8>, order: ByteOrder) {
1158 let (p, len) = (v.as_mut_ptr(), self.len);
1159 // SAFETY: as the caller guarantees; every one of the `len` bytes is written before the
1160 // length is set.
1161 unsafe {
1162 if order == ByteOrder::BigEndian {
1163 for (i, &byte) in self.bytes[..len].iter().enumerate() {
1164 *p.add(len - 1 - i) = byte;
1165 }
1166 } else {
1167 ptr::copy_nonoverlapping(self.bytes.as_ptr(), p, len);
1168 }
1169 v.set_len(len);
1170 }
1171 }
1172
1173 /// The sum as a number in one new vector of exactly its length, in the byte order `order`.
1174 fn into_new(self, order: ByteOrder) -> Number {
1175 let mut value = Vec::with_capacity(self.len);
1176 // SAFETY: the new vector has room for the sum.
1177 unsafe { self.put(&mut value, order) };
1178 Number {
1179 value,
1180 negative: self.negative,
1181 byte_order: order,
1182 }
1183 }
1184}
1185
1186/// [`medium_into`] for a sum much shorter than the longer operand's vector, or that the vector of
1187/// `out` has no room for: out of line and cold.
1188///
1189/// # Safety
1190///
1191/// As for [`medium_into`].
1192#[cold]
1193#[inline(never)]
1194unsafe fn medium_slow(
1195 a: *const Number,
1196 b: *mut Number,
1197 negative_b: bool,
1198 out: *mut Number,
1199 fallback: Fallback,
1200) {
1201 // SAFETY: as the caller guarantees; the sum is computed before anything is written, and the
1202 // references to the operands end before it is.
1203 unsafe {
1204 let sum = Bytes::of(&*a, &*b, negative_b);
1205 let order = (*a).byte_order;
1206 let v = &mut (*out).value;
1207 if v.capacity() < sum.len {
1208 if fallback == Fallback::Other && (*b).value.capacity() >= sum.len {
1209 mem::swap(v, &mut (*b).value);
1210 } else {
1211 *v = Vec::with_capacity(sum.len);
1212 }
1213 }
1214 sum.put(v, order);
1215 (*out).negative = sum.negative;
1216 (*out).byte_order = order;
1217 }
1218}
1219
1220/// [`medium_new`] for a sum much shorter than the longer operand's vector.
1221#[cold]
1222#[inline(never)]
1223fn medium_new_slow(a: &Number, b: &Number, negative_b: bool) -> Number {
1224 Bytes::of(a, b, negative_b).into_new(a.byte_order)
1225}
1226
1227/// `x + y + carry`, and the carry out.
1228#[inline(always)]
1229fn adc(x: u64, y: u64, carry: bool) -> (u64, bool) {
1230 let (s, c1) = x.overflowing_add(y);
1231 let (s, c2) = s.overflowing_add(u64::from(carry));
1232 (s, c1 | c2)
1233}
1234
1235/// The magnitude in `v`, of at most `8 L` bytes, as limbs, those above it zero.
1236///
1237/// # Safety
1238///
1239/// `v` must hold at most `8 L` bytes.
1240#[inline(always)]
1241pub(super) unsafe fn read_limbs<const L: usize>(v: &[u8], order: ByteOrder) -> [u64; 9] {
1242 let n = v.len();
1243 // SAFETY: as the caller guarantees.
1244 unsafe {
1245 if n > 8 * (L - 1) {
1246 if order == ByteOrder::BigEndian {
1247 exact_limbs::<true, L>(v.as_ptr(), n)
1248 } else {
1249 exact_limbs::<false, L>(v.as_ptr(), n)
1250 }
1251 } else if n <= SMALL {
1252 let s = read_small(v, order);
1253 let mut x = [0; 9];
1254 x[0] = s as u64;
1255 x[1] = (s >> 64) as u64;
1256 x
1257 } else if order == ByteOrder::BigEndian {
1258 clamped_limbs::<true, L>(v.as_ptr(), n)
1259 } else {
1260 clamped_limbs::<false, L>(v.as_ptr(), n)
1261 }
1262 }
1263}
1264
1265/// The eight positions from `p` of the `n` bytes at `v` in the layout `BE`, as a `u64`.
1266///
1267/// # Safety
1268///
1269/// `p + 8 <= n`, and the `n` bytes must be valid for reading.
1270#[inline(always)]
1271unsafe fn limb_at<const BE: bool>(v: *const u8, n: usize, p: usize) -> u64 {
1272 // SAFETY: as the caller guarantees.
1273 unsafe {
1274 if BE {
1275 u64::from_be_bytes(read(v.add(n - 8 - p)))
1276 } else {
1277 u64::from_le_bytes(read(v.add(p)))
1278 }
1279 }
1280}
1281
1282/// The `n` bytes at `v` in the layout `BE`, `8 (L - 1) < n <= 8 L`, as `L` limbs.
1283///
1284/// # Safety
1285///
1286/// The `n` bytes must be valid for reading.
1287#[inline(always)]
1288unsafe fn exact_limbs<const BE: bool, const L: usize>(v: *const u8, n: usize) -> [u64; 9] {
1289 let mut x = [0; 9];
1290 // SAFETY: limbs `0..L - 1` lie below `8 (L - 1) < n`, and the top one is read from the eight
1291 // positions that end at `n`, `n >= 8`, and shifted down by `8 L - n` of them.
1292 unsafe {
1293 for (i, limb) in x.iter_mut().enumerate().take(L - 1) {
1294 *limb = limb_at::<BE>(v, n, 8 * i);
1295 }
1296 // `8 (8 L - n)` bits, which is `-8 n` modulo 64.
1297 let shift = (n as u32).wrapping_mul(8).wrapping_neg();
1298 x[L - 1] = limb_at::<BE>(v, n, n - 8).wrapping_shr(shift);
1299 }
1300 x
1301}
1302
1303/// The `n` bytes at `v` in the layout `BE`, `16 < n <= 8 (L - 1)`, as `L` limbs, the top ones
1304/// zero.
1305///
1306/// # Safety
1307///
1308/// The `n` bytes must be valid for reading.
1309#[inline(always)]
1310unsafe fn clamped_limbs<const BE: bool, const L: usize>(v: *const u8, n: usize) -> [u64; 9] {
1311 let mut x = [0; 9];
1312 // SAFETY: the limb at position `8 i` is read at the position `c = min(8 i, n - 8)`, inside
1313 // the `n` bytes, and shifted down by the positions from `c` up to `8 i`, fewer than eight
1314 // unless the limb is above the magnitude.
1315 unsafe {
1316 for (i, limb) in x.iter_mut().enumerate().take(L) {
1317 let c = (8 * i).min(n - 8);
1318 let w = limb_at::<BE>(v, n, c);
1319 *limb = if 8 * i < n { w >> (8 * (8 * i - c)) } else { 0 };
1320 }
1321 }
1322 x
1323}
1324
1325/// `x + y` over `L` limbs, `x` with the sign `negative_x` and `y` with `negative_y`.
1326///
1327/// One chain of additions gives the sum, or, when the signs differ, `x - y` as `x + !y + 1`.
1328/// Alongside it a second one gives `y - x`, which is taken instead when the first one borrowed,
1329/// without a branch: which magnitude is the larger is a coin toss for random operands of the same
1330/// length, and so, for random signs, is whether they are subtracted at all. The second chain
1331/// subtracts whatever the signs are, its low limbs as such and the others as the addition of the
1332/// complement with the carry, `y + !x + c`: for these LLVM makes one chain of `subs` and `adcs`,
1333/// while the carry of a first `y + !x + 1` would be worked out apart.
1334#[inline(always)]
1335fn medium_sum<const L: usize>(
1336 x: [u64; 9],
1337 y: [u64; 9],
1338 negative_x: bool,
1339 negative_y: bool,
1340) -> Medium {
1341 let subtract = negative_x != negative_y;
1342 let m = u64::from(subtract).wrapping_neg();
1343 let (mut s, mut d) = ([0u64; 9], [0u64; 9]);
1344 let (d0, borrow_d) = y[0].overflowing_sub(x[0]);
1345 let (mut carry, mut carry_d) = (subtract, !borrow_d);
1346 d[0] = d0;
1347 (s[0], carry) = adc(x[0], y[0] ^ m, carry);
1348 for i in 1..L {
1349 (s[i], carry) = adc(x[i], y[i] ^ m, carry);
1350 (d[i], carry_d) = adc(y[i], !x[i], carry_d);
1351 }
1352 let borrow = subtract & !carry;
1353 let mut r = [0u64; 9];
1354 for i in 0..L {
1355 r[i] = select_unpredictable(borrow, d[i], s[i]);
1356 }
1357 r[L] = u64::from(carry & !subtract);
1358 Medium {
1359 limbs: r,
1360 negative: negative_x != borrow,
1361 }
1362}
1363
1364/// Whether the sum `r` of operands of at most `L` limbs is at least `8 (L - 1)` bytes long.
1365#[inline(always)]
1366pub(super) fn long<const L: usize>(r: &[u64; 9]) -> bool {
1367 (r[L] | r[L - 1]) != 0 || r[L - 2] >> 56 != 0
1368}
1369
1370/// The length of the sum `r` of operands of at most `L` limbs, at least `8 (L - 1)` bytes long,
1371/// and its eight most significant positions.
1372#[inline(always)]
1373pub(super) fn top_of<const L: usize>(r: &[u64; 9]) -> (usize, u64) {
1374 let below = select_unpredictable(
1375 r[L - 1] != 0,
1376 (L - 1, r[L - 1], r[L - 2]),
1377 (L - 2, r[L - 2], r[L - 3]),
1378 );
1379 let (t, high, low) = select_unpredictable(r[L] != 0, (L, r[L], r[L - 1]), below);
1380 let z = high.leading_zeros();
1381 let shift = z & 56;
1382 (
1383 8 * t + 8 - z as usize / 8,
1384 (high << shift) | ((low >> 1) >> (63 - shift)),
1385 )
1386}
1387
1388/// Writes the sum `r` of operands of at most `L` limbs, of `len >= 8 (L - 1)` bytes whose top
1389/// eight are `top`, over the `len` bytes at `p` in the layout of `order`.
1390///
1391/// Limbs `0..L - 1` lie below the top eight positions of the sum or reach up to them; limb
1392/// `L - 1` is written below the top eight positions when the sum carried out of it, and at them
1393/// otherwise; then `top` over the top eight positions.
1394///
1395/// # Safety
1396///
1397/// `p` must be valid for writing `len` bytes.
1398#[inline(always)]
1399unsafe fn write_long<const L: usize>(
1400 p: *mut u8,
1401 order: ByteOrder,
1402 r: &[u64; 9],
1403 len: usize,
1404 top: u64,
1405) {
1406 let c = (8 * (L - 1)).min(len - 8);
1407 // SAFETY: every write lies inside the `len >= 8 (L - 1)` bytes.
1408 unsafe {
1409 if order == ByteOrder::BigEndian {
1410 for (i, limb) in r.iter().enumerate().take(L - 1) {
1411 write(p.add(len - 8 - 8 * i), limb.to_be_bytes());
1412 }
1413 write(p.add(len - 8 - c), r[L - 1].to_be_bytes());
1414 write(p, top.to_be_bytes());
1415 } else {
1416 for (i, limb) in r.iter().enumerate().take(L - 1) {
1417 write(p.add(8 * i), limb.to_le_bytes());
1418 }
1419 write(p.add(c), r[L - 1].to_le_bytes());
1420 write(p.add(len - 8), top.to_le_bytes());
1421 }
1422 }
1423}
1424
1425// Planning
1426
1427/// The sum `a + b` as planned from the untouched operands, before a vector is chosen for it.
1428///
1429/// The magnitude the other one is added to or subtracted from is `x`, the other one `y`: for
1430/// an addition `x` has at least as many significant bytes as `y`, for a subtraction it is the
1431/// larger magnitude. The significant length of a magnitude counts its bytes up to the most
1432/// significant nonzero one, and is 0 for zero.
1433struct Plan {
1434 /// Whether `y` is subtracted from `x` (the signs differ) rather than added to it.
1435 sub: bool,
1436 /// Whether `x` is `b` rather than `a`.
1437 x_is_b: bool,
1438 /// The significant length of `a`.
1439 la: usize,
1440 /// The significant length of `b`.
1441 lb: usize,
1442 /// The significant length of the sum: 0 for zero.
1443 len: usize,
1444 /// Whether the sum is negative; never for zero.
1445 negative: bool,
1446}
1447
1448impl Plan {
1449 /// The significant length of `x`.
1450 fn lx(&self) -> usize {
1451 if self.x_is_b { self.lb } else { self.la }
1452 }
1453
1454 /// The significant length of `y`.
1455 fn ly(&self) -> usize {
1456 if self.x_is_b { self.la } else { self.lb }
1457 }
1458
1459 /// Whether a vector of capacity `capacity` has room for the sum in the shortest form, one
1460 /// byte for zero.
1461 fn fits(&self, capacity: usize) -> bool {
1462 capacity >= self.len.max(1)
1463 }
1464
1465 /// Whether the operand whose vector holds the sum, `b` if `alias_is_b` and `a` otherwise,
1466 /// is `x`.
1467 fn alias_is_x(&self, alias_is_b: bool) -> bool {
1468 alias_is_b == self.x_is_b
1469 }
1470}
1471
1472/// Plans `a + b`.
1473fn plan(a: &Number, b: &Number) -> Plan {
1474 match (a.byte_order, b.byte_order) {
1475 (ByteOrder::BigEndian, ByteOrder::BigEndian) => plan_in::<true, true>(a, b),
1476 (ByteOrder::BigEndian, ByteOrder::LittleEndian) => plan_in::<true, false>(a, b),
1477 (ByteOrder::LittleEndian, ByteOrder::BigEndian) => plan_in::<false, true>(a, b),
1478 (ByteOrder::LittleEndian, ByteOrder::LittleEndian) => plan_in::<false, false>(a, b),
1479 }
1480}
1481
1482/// Plans `a + b` for `a` in the layout `A` and `b` in the layout `B` (`true` for big-endian).
1483///
1484/// An addition is planned here, a subtraction by [`plan_sub`], out of line: it needs more
1485/// registers, which an addition then does not have to save.
1486fn plan_in<const A: bool, const B: bool>(a: &Number, b: &Number) -> Plan {
1487 let la = significant_len::<A>(&a.value);
1488 let lb = significant_len::<B>(&b.value);
1489 let (pa, pb) = (end::<A>(&a.value), end::<B>(&b.value));
1490 let negative_a = a.negative && la > 0;
1491 let negative_b = b.negative && lb > 0;
1492 if negative_a == negative_b || la == 0 || lb == 0 {
1493 let x_is_b = lb > la;
1494 // SAFETY: the significant bytes of `a` and `b` are inside their vectors, and `x` is
1495 // the one with more of them.
1496 let carry = unsafe {
1497 if x_is_b {
1498 carries::<B, A>(pb, pa, lb, la)
1499 } else {
1500 carries::<A, B>(pa, pb, la, lb)
1501 }
1502 };
1503 return Plan {
1504 sub: false,
1505 x_is_b,
1506 la,
1507 lb,
1508 len: la.max(lb) + usize::from(carry),
1509 // The sign of the operands that are not zero.
1510 negative: negative_a || negative_b,
1511 };
1512 }
1513 // SAFETY: `pa` and `pb` are the least significant ends of the magnitudes of `a` and `b`,
1514 // of `la` and `lb` significant bytes, neither zero.
1515 unsafe { plan_sub::<A, B>(pa, pb, la, lb, negative_a) }
1516}
1517
1518/// Plans the difference of the magnitudes at the least significant ends `pa` and `pb` in the
1519/// layouts `A` and `B`, of `la` and `lb` significant bytes, for `a + b` with the signs of `a`
1520/// and `b` different: the sum has the sign of the larger magnitude, which `negative_a` gives
1521/// for `a`.
1522///
1523/// # Safety
1524///
1525/// The `la` significant bytes at `pa` and the `lb` at `pb` must be valid for reading, and
1526/// neither `la` nor `lb` may be 0.
1527#[inline(never)]
1528unsafe fn plan_sub<const A: bool, const B: bool>(
1529 pa: *const u8,
1530 pb: *const u8,
1531 la: usize,
1532 lb: usize,
1533 negative_a: bool,
1534) -> Plan {
1535 // The longer magnitude is the larger, and of two as long, the one with the larger byte
1536 // where they first differ from the top.
1537 let (x_is_b, top) = if la != lb {
1538 (lb > la, la.max(lb) - 1)
1539 } else {
1540 // SAFETY: both have `la` significant bytes.
1541 match unsafe { highest_difference::<A, B>(pa, pb, lb, la) } {
1542 // SAFETY: position `k` is below the significant length of both.
1543 Some(k) => (unsafe { get::<B>(pb, k) > get::<A>(pa, k) }, k),
1544 None => {
1545 return Plan {
1546 sub: true,
1547 x_is_b: false,
1548 la,
1549 lb,
1550 len: 0,
1551 negative: false,
1552 };
1553 }
1554 }
1555 };
1556 // SAFETY: `x` is the larger magnitude, and `top` the highest position at which `x` and
1557 // `y` differ, below the significant length of `x`.
1558 let len = unsafe {
1559 if A == B {
1560 // Which magnitude is the larger depends on their bytes: one copy of the scan, on
1561 // operands chosen without a branch.
1562 let (x, y, ly) = select_unpredictable(x_is_b, (pb, pa, la), (pa, pb, lb));
1563 difference_len::<A, B>(x, y, ly, top)
1564 } else if x_is_b {
1565 difference_len::<B, A>(pb, pa, la, top)
1566 } else {
1567 difference_len::<A, B>(pa, pb, lb, top)
1568 }
1569 };
1570 Plan {
1571 sub: true,
1572 x_is_b,
1573 la,
1574 lb,
1575 len,
1576 // The signs differ: `b` is negative exactly when `a` is not.
1577 negative: negative_a != x_is_b,
1578 }
1579}
1580
1581/// The significant length of the magnitude `v` in the layout `BE`: its length without the
1582/// zero bytes above its most significant nonzero byte, the leading ones big-endian and the
1583/// trailing ones little-endian. The bytes are read eight at a time while they are zero, so
1584/// a number in the shortest form costs one read, and a long run of zeros little more.
1585#[inline]
1586pub(super) fn significant_len<const BE: bool>(v: &[u8]) -> usize {
1587 let (p, len) = (v.as_ptr(), v.len());
1588 // SAFETY: every read lies inside `v`: big-endian `[i, i + 8)` with `i + 8 <= len` and `i`
1589 // with `i < len`; little-endian `[end - 8, end)` with `end >= 8` and `end - 1` with
1590 // `end > 0`, where `end <= len`.
1591 unsafe {
1592 if BE {
1593 let mut i = 0;
1594 while len - i >= 8 && p.add(i).cast::<[u8; 8]>().read() == [0; 8] {
1595 i += 8;
1596 }
1597 while i < len && *p.add(i) == 0 {
1598 i += 1;
1599 }
1600 len - i
1601 } else {
1602 let mut end = len;
1603 while end >= 8 && p.add(end - 8).cast::<[u8; 8]>().read() == [0; 8] {
1604 end -= 8;
1605 }
1606 while end > 0 && *p.add(end - 1) == 0 {
1607 end -= 1;
1608 }
1609 end
1610 }
1611 }
1612}
1613
1614/// Whether `x + y` carries out of the top position of `x`, for magnitudes of `lx >= ly`
1615/// significant bytes at the least significant ends `x` and `y`, in the layouts `X` and `Y`.
1616///
1617/// It is found from the top down, without computing the sum: the highest position whose
1618/// bytes do not add up to `0xFF` decides, with a carry if they add up to more. Above `ly`
1619/// only `x` has bytes, which can pass a carry on but not start one. The positions go eight at
1620/// a time, as `u64`s, while that many are left: such a group starts a carry if its two
1621/// values overflow, passes one on if they add up to `u64::MAX`, and stops one otherwise.
1622///
1623/// # Safety
1624///
1625/// The `lx` significant bytes of `x` and the `ly` of `y` must be valid for reading.
1626#[inline]
1627unsafe fn carries<const X: bool, const Y: bool>(
1628 x: *const u8,
1629 y: *const u8,
1630 lx: usize,
1631 ly: usize,
1632) -> bool {
1633 let mut p = lx;
1634 // SAFETY: every position read is below `p`, which is at most `lx`, and those of `y` are
1635 // below `ly` as well.
1636 unsafe {
1637 while p >= ly + 8 {
1638 if load::<X>(x, p - 8) != u64::MAX {
1639 return false;
1640 }
1641 p -= 8;
1642 }
1643 while p > ly {
1644 if get::<X>(x, p - 1) != 0xFF {
1645 return false;
1646 }
1647 p -= 1;
1648 }
1649 while p >= 8 {
1650 let (sum, overflow) = load::<X>(x, p - 8).overflowing_add(load::<Y>(y, p - 8));
1651 if overflow || sum != u64::MAX {
1652 return overflow;
1653 }
1654 p -= 8;
1655 }
1656 while p > 0 {
1657 let sum = u16::from(get::<X>(x, p - 1)) + u16::from(get::<Y>(y, p - 1));
1658 if sum != 0xFF {
1659 return sum > 0xFF;
1660 }
1661 p -= 1;
1662 }
1663 }
1664 false
1665}
1666
1667/// The highest position below `p` at which the magnitudes at the least significant ends `x`
1668/// and `y`, in the layouts `X` and `Y`, differ, the positions of `y` from `ly` up counting as
1669/// zeros; `None` if they agree on every position below `p`.
1670///
1671/// The positions go eight at a time while that many are left, as `u64`s whose most
1672/// significant byte is the highest position: the leading zeros of their difference (their
1673/// XOR) tell how far down from it the first difference lies.
1674///
1675/// # Safety
1676///
1677/// The positions of `x` below `p`, and those of `y` below `p` and `ly`, must be valid for
1678/// reading.
1679#[inline]
1680unsafe fn highest_difference<const X: bool, const Y: bool>(
1681 x: *const u8,
1682 y: *const u8,
1683 ly: usize,
1684 mut p: usize,
1685) -> Option<usize> {
1686 // SAFETY: every position read is below `p`, and those of `y` below `ly` as well.
1687 unsafe {
1688 // Above `ly`, the nonzero bytes of `x`.
1689 while p >= ly + 8 {
1690 let v = load::<X>(x, p - 8);
1691 if v != 0 {
1692 return Some(p - 1 - (v.leading_zeros() / 8) as usize);
1693 }
1694 p -= 8;
1695 }
1696 while p > ly {
1697 if get::<X>(x, p - 1) != 0 {
1698 return Some(p - 1);
1699 }
1700 p -= 1;
1701 }
1702 while p >= 8 {
1703 let v = load::<X>(x, p - 8) ^ load::<Y>(y, p - 8);
1704 if v != 0 {
1705 return Some(p - 1 - (v.leading_zeros() / 8) as usize);
1706 }
1707 p -= 8;
1708 }
1709 while p > 0 {
1710 if get::<X>(x, p - 1) != get::<Y>(y, p - 1) {
1711 return Some(p - 1);
1712 }
1713 p -= 1;
1714 }
1715 }
1716 None
1717}
1718
1719/// The significant length of `x - y` for magnitudes at the least significant ends `x` and
1720/// `y`, in the layouts `X` and `Y`, `x` the larger, `y` of `ly` significant bytes, and `k`
1721/// the highest position at which they differ.
1722///
1723/// Position `p` of the difference holds `x_p - y_p - b_p` modulo 256, where the borrow `b_p`
1724/// into it is 1 exactly when `x` has the smaller byte at the highest position below `p` at
1725/// which the two differ. Above `k` the difference is zero, and at `k`, where `x` has the
1726/// larger byte, it is not unless `x_k - y_k = 1`: the length is then `k + 1`.
1727///
1728/// Otherwise take the run of positions below `k` at which `x` has `0x00` and `y` has `0xFF`,
1729/// down to `p`, its lowest (`p = k` for an empty run). Such a position borrows whatever the
1730/// borrow into it is, and holds `1 - b` for a borrow `b` into it. So the positions of the run
1731/// above `p`, and `k` itself, hold 0 when the run is not empty, and position `p` holds
1732/// `1 - b_p`, as `k` does when it is empty. With `b_p = 0` the length is `p + 1`. With
1733/// `b_p = 1` it is `p`: `x` has the smaller byte at the highest position `q` below `p` at
1734/// which the two differ, and position `p - 1` is not zero, either because `q = p - 1`, where
1735/// `x_q - y_q` is between -254 and -1 (the pair there is not `(0x00, 0xFF)`), so that the
1736/// byte is between 1 and 255 whatever the borrow into it, or because the bytes agree from
1737/// `q + 1` to `p - 1` and the borrow out of `q` makes them `0xFF`. In both cases the length is
1738/// `p + 1 - b_p`.
1739///
1740/// The run is found eight positions at a time, and `b_p` by [`highest_difference`] from `p`
1741/// down: a run is read once however long it is (`256^n - (256^n - 1)` has one of `n`
1742/// positions), and only the eight positions at which it ends may be read twice.
1743///
1744/// # Safety
1745///
1746/// The significant bytes of `x` and the `ly` of `y` must be valid for reading, and `k` must be
1747/// below the significant length of `x`.
1748#[inline]
1749unsafe fn difference_len<const X: bool, const Y: bool>(
1750 x: *const u8,
1751 y: *const u8,
1752 ly: usize,
1753 k: usize,
1754) -> usize {
1755 // SAFETY: every position read is at most `k`, below the significant length of `x`; those
1756 // of `y` are read below `ly` only, as the run is looked for only when `k <= ly`.
1757 unsafe {
1758 if get::<X>(x, k).wrapping_sub(get_or_zero::<Y>(y, ly, k)) != 1 {
1759 return k + 1;
1760 }
1761 // Above `ly` the bytes of `y` are zeros, and no run starts.
1762 let mut p = k;
1763 if p <= ly {
1764 while p >= 8 {
1765 // Zero exactly where `x` has 0x00 and `y` has 0xFF.
1766 let v = load::<X>(x, p - 8) | !load::<Y>(y, p - 8);
1767 if v != 0 {
1768 p -= (v.leading_zeros() / 8) as usize;
1769 break;
1770 }
1771 p -= 8;
1772 }
1773 while p > 0 && get::<X>(x, p - 1) == 0 && get::<Y>(y, p - 1) == 0xFF {
1774 p -= 1;
1775 }
1776 }
1777 let borrow = match highest_difference::<X, Y>(x, y, ly, p) {
1778 Some(q) => get::<X>(x, q) < get_or_zero::<Y>(y, ly, q),
1779 None => false,
1780 };
1781 p + 1 - usize::from(borrow)
1782 }
1783}
1784
1785// Building the sum
1786
1787/// Builds the planned sum of `a` and `b` in one new vector, of exactly its length, in the
1788/// byte order of `a`.
1789///
1790/// When `b` is in the other byte order, its low bytes, those the sum needs, are first copied
1791/// into the new vector, in the byte order of the sum and where they belong in it, and the sum
1792/// is then computed over that copy in place.
1793///
1794/// # Safety
1795///
1796/// `plan` must be the plan of `a + b`.
1797unsafe fn sum_new(a: &Number, b: &Number, plan: &Plan) -> Vec<u8> {
1798 if plan.len == 0 {
1799 return vec![0];
1800 }
1801 // SAFETY: as the caller guarantees; the operands are each read in their own layout.
1802 unsafe {
1803 match (a.byte_order, b.byte_order) {
1804 (ByteOrder::BigEndian, ByteOrder::BigEndian) => {
1805 new_from::<true>(end::<true>(&a.value), end::<true>(&b.value), plan)
1806 }
1807 (ByteOrder::LittleEndian, ByteOrder::LittleEndian) => {
1808 new_from::<false>(end::<false>(&a.value), end::<false>(&b.value), plan)
1809 }
1810 (ByteOrder::BigEndian, ByteOrder::LittleEndian) => {
1811 new_with_copy::<true>(end::<true>(&a.value), &b.value, plan)
1812 }
1813 (ByteOrder::LittleEndian, ByteOrder::BigEndian) => {
1814 new_with_copy::<false>(end::<false>(&a.value), &b.value, plan)
1815 }
1816 }
1817 }
1818}
1819
1820/// Builds the planned sum in a new vector in the layout `BE`, from the operands at the least
1821/// significant ends `a` and `b` in that layout.
1822///
1823/// # Safety
1824///
1825/// `plan` must be the plan of the sum of `a` and `b`, and not zero.
1826unsafe fn new_from<const BE: bool>(a: *const u8, b: *const u8, plan: &Plan) -> Vec<u8> {
1827 let len = plan.len;
1828 let mut v = Vec::<u8>::with_capacity(len);
1829 let base = v.as_mut_ptr();
1830 // SAFETY: the vector has room for the `len` bytes of the sum, and as it is apart from the
1831 // operands, `compute` writes every one of them. The end of the sum, `base + len`
1832 // big-endian, is inside or one past the allocation.
1833 unsafe {
1834 let r = if BE { base.add(len) } else { base };
1835 let (x, y) = select_unpredictable(plan.x_is_b, (b, a), (a, b));
1836 compute_new::<BE>(r, x, y, plan);
1837 v.set_len(len);
1838 }
1839 v
1840}
1841
1842/// Builds the planned sum in a new vector in the layout `BE`, from `a` at its least
1843/// significant end in that layout and `b` in the other byte order: the low positions of `b`
1844/// that the sum needs are copied into the vector, where they belong in the sum, and the sum is
1845/// computed over the copy in place.
1846///
1847/// # Safety
1848///
1849/// `plan` must be the plan of the sum of `a` and `b`, and not zero.
1850unsafe fn new_with_copy<const BE: bool>(a: *const u8, b: &[u8], plan: &Plan) -> Vec<u8> {
1851 let len = plan.len;
1852 // No position of `b` at or above the length of the sum changes the sum.
1853 let m = plan.lb.min(len);
1854 let mut v = Vec::<u8>::with_capacity(len);
1855 let base = v.as_mut_ptr();
1856 let copy_is_x = plan.alias_is_x(true);
1857 // The copy is `x` only when `b` has at least as many significant bytes as `a`, so that it
1858 // holds all of `x` that is kept: `lx` bytes for a sum, `len` for a difference.
1859 debug_assert!(!copy_is_x || m == if plan.sub { len } else { plan.lx() });
1860 // SAFETY: the vector has room for the `len` bytes of the sum. `b` holds its `m <= lb`
1861 // lowest positions at its start if it is little-endian and at its end otherwise, and they
1862 // go to positions `0..m` of the sum: `[len - m, len)` big-endian, `[0, m)` little-endian.
1863 // `compute` then reads the copy as an operand that is the output exactly, and writes the
1864 // positions of the sum that differ from it: all of them when the copy is `y`; when it is
1865 // `x`, those below the position at which the carry (borrow) out of `y` stops, the copy
1866 // keeping the rest, which it holds as `m` is all of `x` that the sum needs. So every byte
1867 // below `len` is written before the length is set.
1868 unsafe {
1869 if BE {
1870 copy_reversed(base.add(len - m), b.as_ptr(), m);
1871 } else {
1872 copy_reversed(base, b.as_ptr().add(b.len() - m), m);
1873 }
1874 let r = if BE { base.add(len) } else { base };
1875 let copy = r.cast_const();
1876 let (x, y) = select_unpredictable(copy_is_x, (copy, a), (a, copy));
1877 compute_new::<BE>(r, x, y, plan);
1878 v.set_len(len);
1879 }
1880 v
1881}
1882
1883/// Builds the planned sum in `v`, the vector of one of the operands (`b` if `alias_is_b`, `a`
1884/// otherwise), in the layout of `order`; `other` is the other operand. `reversed` says whether
1885/// one of them was reversed into `order` after the plan was made.
1886///
1887/// It is inlined into the operators' general paths, with everything under it but the kernels
1888/// for many limbs: below a few hundred bytes the calls cost such a sum a sixth of its time. A
1889/// long difference goes out of line instead, to [`long_in_vector`], unless an operand was
1890/// reversed: the reversal takes longer than the plan, which is then done before the kernel
1891/// could start, and the branches of a long difference would only add their mispredictions (3 to
1892/// 4 % from 512 bytes, measured).
1893///
1894/// # Safety
1895///
1896/// `plan` must be the plan of the sum, both operands must be in `order` (unless the sum is
1897/// zero), and the capacity of `v` must have room for the sum.
1898#[inline(always)]
1899unsafe fn sum_in(
1900 v: &mut Vec<u8>,
1901 order: ByteOrder,
1902 alias_is_b: bool,
1903 other: &[u8],
1904 plan: &Plan,
1905 reversed: bool,
1906) {
1907 // SAFETY: as the caller guarantees: for a zero sum, the capacity of `v` holds one byte.
1908 unsafe {
1909 if plan.len == 0 {
1910 *v.as_mut_ptr() = 0;
1911 v.set_len(1);
1912 return;
1913 }
1914 match order {
1915 ByteOrder::BigEndian if !reversed && long_difference(plan, LONG_DIFFERENCE_BE) => {
1916 long_in_vector::<true>(v, alias_is_b, end::<true>(other), plan)
1917 }
1918 ByteOrder::LittleEndian if !reversed && long_difference(plan, LONG_DIFFERENCE_LE) => {
1919 long_in_vector::<false>(v, alias_is_b, end::<false>(other), plan)
1920 }
1921 ByteOrder::BigEndian => {
1922 in_vector::<true, false>(v, alias_is_b, end::<true>(other), plan)
1923 }
1924 ByteOrder::LittleEndian => {
1925 in_vector::<false, false>(v, alias_is_b, end::<false>(other), plan)
1926 }
1927 }
1928 }
1929}
1930
1931/// [`sum_in`] in the layout `BE`, with `other` the least significant end of the other operand,
1932/// for a sum that is not zero; with `LONG`, for a long difference, whose operands are taken by
1933/// [`difference_in`].
1934///
1935/// Little-endian the positions of the sum are those of the operand in `v`, from its start;
1936/// the bytes from its length up are its spare capacity, written and never read. Big-endian
1937/// the sum ends at its own length: a longer sum lies above the operand, which the kernels
1938/// allow, and a shorter one (a difference, or an operand with zero bytes above its most
1939/// significant one) is first given the low bytes it needs from the operand, moved down to end
1940/// where the sum ends. A byte of a sum or a difference depends only on the bytes at or below
1941/// its position, so the bytes of the operand from the length of the sum up are never needed.
1942///
1943/// # Safety
1944///
1945/// As for [`sum_in`], with `other` valid for reading the significant bytes of the other
1946/// operand, and the sum not zero.
1947#[inline(always)]
1948unsafe fn in_vector<const BE: bool, const LONG: bool>(
1949 v: &mut Vec<u8>,
1950 alias_is_b: bool,
1951 other: *const u8,
1952 plan: &Plan,
1953) {
1954 let (old_len, len) = (v.len(), plan.len);
1955 // Every pointer into the vector comes from this one call.
1956 let base = v.as_mut_ptr();
1957 // SAFETY: the capacity of `v` is at least `len`, and its first `old_len` bytes hold the
1958 // operand, so `base + len` and `base + old_len` are inside the allocation or one past it.
1959 // A shorter big-endian sum moves the `m` low bytes of the operand from the end of its
1960 // bytes to the end of the sum, within them. `compute` reads the operand where it now is,
1961 // as the output exactly, or below it big-endian, and writes the positions of the sum that
1962 // differ from it, keeping the operand's own bytes above them when it is `x` and the output
1963 // exactly: those below `old_len`, or among the `m` moved, which are all of `x` that the sum
1964 // needs (`lx` bytes for a sum, `len` for a difference). So every byte below `len` holds the
1965 // sum before the length is set.
1966 unsafe {
1967 let (r, alias) = if !BE {
1968 (base, base)
1969 } else if len >= old_len {
1970 (base.add(len), base.add(old_len))
1971 } else {
1972 let m = if alias_is_b { plan.lb } else { plan.la }.min(len);
1973 move_down(base.add(old_len), base.add(len), m);
1974 (base.add(len), base.add(len))
1975 };
1976 let alias = alias.cast_const();
1977 let alias_is_x = plan.alias_is_x(alias_is_b);
1978 if LONG {
1979 // The significant lengths of the operand in `v` and of the other one.
1980 let (l_alias, l_other) = if alias_is_b {
1981 (plan.lb, plan.la)
1982 } else {
1983 (plan.la, plan.lb)
1984 };
1985 if alias_is_x {
1986 difference_in::<BE, false>(r, alias, other, l_other, len);
1987 } else {
1988 difference_in::<BE, true>(r, alias, other, l_alias, len);
1989 }
1990 } else {
1991 let (x, y) = select_unpredictable(alias_is_x, (alias, other), (other, alias));
1992 compute::<BE>(r, x, y, plan);
1993 }
1994 v.set_len(len);
1995 }
1996}
1997
1998/// Whether the planned sum is a long difference: a subtraction of magnitudes of at least `long`
1999/// significant bytes each.
2000#[inline(always)]
2001fn long_difference(plan: &Plan, long: usize) -> bool {
2002 plan.sub && plan.la.min(plan.lb) >= long
2003}
2004
2005/// [`in_vector`] for a long difference, out of line, so that the code of the other sums is as it
2006/// would be without it.
2007///
2008/// # Safety
2009///
2010/// As for [`in_vector`], with the plan that of a long difference.
2011#[inline(never)]
2012unsafe fn long_in_vector<const BE: bool>(
2013 v: &mut Vec<u8>,
2014 alias_is_b: bool,
2015 other: *const u8,
2016 plan: &Plan,
2017) {
2018 // SAFETY: as the caller guarantees.
2019 unsafe { in_vector::<BE, true>(v, alias_is_b, other, plan) }
2020}
2021
2022/// Moves the `m` bytes that end at `from` down to end at `to`, for the big-endian sum that is
2023/// shorter than its operand's vector. Out of line and cold, so that the call does not cost
2024/// the other sums the registers it needs.
2025///
2026/// # Safety
2027///
2028/// Both ranges of `m` bytes must lie in one allocation, `to <= from`.
2029#[cold]
2030#[inline(never)]
2031unsafe fn move_down(from: *mut u8, to: *mut u8, m: usize) {
2032 // SAFETY: as the caller guarantees; `ptr::copy` allows the ranges to overlap.
2033 unsafe { ptr::copy(from.sub(m), to.sub(m), m) }
2034}
2035
2036// Computing the sum
2037
2038/// Writes the planned sum of `x` and `y`, magnitudes at their least significant ends in the
2039/// layout `BE`, to the output whose least significant end is `r`: positions `0..plan.len`, of
2040/// which, when the output is `x` itself (`r == x`), it keeps those above the position at which
2041/// the carry (borrow) out of `y` stops as they are, the bytes of `x`.
2042///
2043/// # Safety
2044///
2045/// `x` and `y` must be valid for reading their significant bytes, and `r` for writing the
2046/// `plan.len` bytes of the sum. The output must be apart from both, equal to one or both
2047/// (`r == x` or `r == y`), or, big-endian, end `s >= 1` bytes above one of them (`r = x + s` or
2048/// `r = y + s`), as a sum that grows in the vector of its operand does. When `r == x`, the
2049/// output must hold `x` initialised up to its significant length for a sum, and up to
2050/// `plan.len` for a difference.
2051#[inline(always)]
2052unsafe fn compute<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, plan: &Plan) {
2053 // SAFETY: as the caller guarantees.
2054 unsafe {
2055 if plan.sub {
2056 sub_into::<BE>(r, x, y, plan.ly(), plan.len);
2057 } else {
2058 add_into::<BE>(r, x, y, plan.lx(), plan.ly(), plan.len);
2059 }
2060 }
2061}
2062
2063/// [`compute`] for a sum in a new vector, out of line: there the allocation costs more than the
2064/// call, and one copy of the computation serves both ways to build a new vector.
2065///
2066/// # Safety
2067///
2068/// As for [`compute`].
2069#[inline(never)]
2070unsafe fn compute_new<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, plan: &Plan) {
2071 // SAFETY: as the caller guarantees.
2072 unsafe { compute::<BE>(r, x, y, plan) }
2073}
2074
2075/// Writes the `len` bytes of `x + y` to `r`, all least significant ends in the layout `BE`,
2076/// for magnitudes of `lx >= ly` significant bytes whose sum is `len` bytes long, `lx` or
2077/// `lx + 1`.
2078///
2079/// The whole limbs of `y` go to the kernel, and the bytes of `y` above them follow, as one
2080/// partial limb when there are at least three and a whole limb below them, and one at a time
2081/// otherwise. Above `y`, the carry goes on through `x` a limb at a time while it lasts, and
2082/// the rest of `x` is copied, unless the output is `x` itself. Each position is read before
2083/// it is written, from the least significant one up. The top byte, 1 when the sum carries out
2084/// of `x`, is written without a branch.
2085///
2086/// # Safety
2087///
2088/// As for [`compute`].
2089#[inline(always)]
2090unsafe fn add_into<const BE: bool>(
2091 r: *mut u8,
2092 x: *const u8,
2093 y: *const u8,
2094 lx: usize,
2095 ly: usize,
2096 len: usize,
2097) {
2098 let n = ly / 8;
2099 // SAFETY: the positions of `x` read are below `lx`, those of `y` below `ly`, and those of
2100 // `r` written below `len`; each position of the inputs is read before the same position of
2101 // the output is written, so an output equal to an input, or above it, overwrites only
2102 // what has been read.
2103 unsafe {
2104 let carry = add_n::<BE>(r, x, y, n, false);
2105 let mut carry = rest_of_y::<BE, false>(r, x, y, n, ly, carry);
2106 // With as many bytes as `y`, `x` has no more positions, and only the carry out of the
2107 // top is looked at.
2108 if ly < lx {
2109 let p;
2110 (p, carry) = carry_on::<BE, false>(r, x, ly, lx, carry);
2111 if !carry {
2112 copy_rest::<BE>(r, x, p, lx);
2113 }
2114 }
2115 debug_assert_eq!(len, lx + usize::from(carry), "the planned length is wrong");
2116 // A carry out of the top is a coin toss for random operands of the same length. The
2117 // top byte is chosen by the planned length rather than by `carry`: LLVM turns a select
2118 // on `carry` back into a branch, merged with the branches of `carry_on` on it. Without
2119 // a carry the byte under it is written again.
2120 let top = get::<BE>(r.cast_const(), lx - 1);
2121 set::<BE>(r, len - 1, select_unpredictable(len > lx, 1, top));
2122 }
2123}
2124
2125/// Writes the `len` bytes of `x - y` to `r`, all least significant ends in the layout `BE`,
2126/// for a larger magnitude `x`, `y` of `ly` significant bytes, and a difference of `len`
2127/// significant bytes.
2128///
2129/// As [`add_into`] does, over the positions below `len` only: those of `y` above it do not
2130/// change the difference, and the borrow out of the top position, if any, is cancelled by the
2131/// positions of `x` above it.
2132///
2133/// # Safety
2134///
2135/// As for [`compute`].
2136#[inline(always)]
2137unsafe fn sub_into<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, ly: usize, len: usize) {
2138 // SAFETY: as the caller guarantees.
2139 unsafe { sub_below::<BE>(r, x, y, ly.min(len), len) }
2140}
2141
2142/// The significant length from which a little-endian difference built in the vector of one of
2143/// its operands, both at least this long, is long (see [`difference_in`]): the length from which
2144/// the kernels run several carry chains.
2145const LONG_DIFFERENCE_LE: usize = 256;
2146
2147/// [`LONG_DIFFERENCE_LE`] for big-endian operands.
2148const LONG_DIFFERENCE_BE: usize = 512;
2149
2150/// [`sub_into`] for a long difference built in the vector of an operand, at the least
2151/// significant end `alias` in the layout `BE`, from the other operand at `other`: `x` is `alias`
2152/// and `y` is `other`, or the other way round with `SWAPPED`. `ly` is the significant length of
2153/// `y`, and `len` that of the difference.
2154///
2155/// The other sums take `x` and `y` without a branch, as which magnitude is the larger is a coin
2156/// toss for random operands of the same length. The kernel then cannot load a limb before the
2157/// plan has read and compared the top limbs of both operands: its addresses come from that
2158/// choice, and the number of limbs it takes from the length of the difference (`ly` is cut to
2159/// `len`), which the plan finds from those limbs too. Here both come from branches instead: `x`
2160/// from which of the two copies of this function is called, which LLVM cannot merge into one
2161/// call with chosen arguments as it can calls of one function, and the number of limbs from the
2162/// branch on `ly <= len`, a difference shorter than `y` taking the cold path. A predicted branch
2163/// lets the kernel start while the plan is still reading; a mispredicted one costs about what
2164/// the wait would have.
2165///
2166/// Measured in place on an Apple M4 Max as `a += &b` with opposite signs, over 512 pairs of
2167/// operands in the second-level cache: with a predictable larger operand, 12 cycles fewer at 256
2168/// bytes little-endian and 19 to 23 fewer from 320 up, and 7 to 23 fewer big-endian from 512;
2169/// with a random one, which the branch mispredicts half the time, 10 more at 256 bytes and 3 more
2170/// at 320 little-endian, but 11 to 13 fewer from 512, and 3 to 19 fewer big-endian from 512.
2171/// With the operands in the first-level cache, 1 to 24 cycles fewer from 256 bytes. Shorter, a
2172/// random larger operand cost 17 to 23 cycles more (64 to 192 bytes), and big-endian, whose
2173/// loop is bound by how many instructions the core issues, saved nothing at 256 and 320 bytes.
2174///
2175/// # Safety
2176///
2177/// As for [`compute`], with the operands of `x` and `y` as said.
2178#[inline(never)]
2179unsafe fn difference_in<const BE: bool, const SWAPPED: bool>(
2180 r: *mut u8,
2181 alias: *const u8,
2182 other: *const u8,
2183 ly: usize,
2184 len: usize,
2185) {
2186 let (x, y) = if SWAPPED {
2187 (other, alias)
2188 } else {
2189 (alias, other)
2190 };
2191 // SAFETY: as the caller guarantees; `sub_below` takes the positions of `y` below `len`.
2192 unsafe {
2193 if ly <= len {
2194 sub_below::<BE>(r, x, y, ly, len);
2195 } else {
2196 short_difference::<BE>(r, x, y, len);
2197 }
2198 }
2199}
2200
2201/// [`sub_below`] for a long difference shorter than `y`, out of line and cold, so that the
2202/// common case takes the length of `y`, not one that depends on the bytes of the operands.
2203///
2204/// # Safety
2205///
2206/// As for [`sub_into`].
2207#[cold]
2208#[inline(never)]
2209unsafe fn short_difference<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, len: usize) {
2210 // SAFETY: as the caller guarantees.
2211 unsafe { sub_below::<BE>(r, x, y, len, len) }
2212}
2213
2214/// [`sub_into`] for `ly <= len`: every position of `y` it takes is below the length of the
2215/// difference.
2216///
2217/// # Safety
2218///
2219/// As for [`sub_into`], with `ly <= len`.
2220#[inline(always)]
2221unsafe fn sub_below<const BE: bool>(r: *mut u8, x: *const u8, y: *const u8, ly: usize, len: usize) {
2222 let n = ly / 8;
2223 // SAFETY: as in `add_into`, with every position below `len`, which is at most the
2224 // significant length of `x`.
2225 unsafe {
2226 let borrow = sub_n::<BE>(r, x, y, n, false);
2227 let borrow = rest_of_y::<BE, true>(r, x, y, n, ly, borrow);
2228 if ly < len {
2229 let (p, _) = carry_on::<BE, true>(r, x, ly, len, borrow);
2230 copy_rest::<BE>(r, x, p, len);
2231 }
2232 debug_assert!(
2233 get::<BE>(r.cast_const(), len - 1) != 0,
2234 "the planned length is wrong"
2235 );
2236 }
2237}
2238
2239/// Adds (subtracts, with `SUB`) the positions of `y` from `8 * n` up to `ly`, above the `n`
2240/// whole limbs the kernel took, with the carry (borrow) `carry` out of them; returns the
2241/// carry (borrow) out of position `ly - 1`.
2242///
2243/// From three of them, and with a whole limb below, they go as one partial limb: the eight
2244/// positions that end at `ly` are read from `x` and `y` and shifted down to the new ones,
2245/// and written back over the output's last whole limb, whose top bytes the kernel wrote and
2246/// which are read back for that. Below three, one at a time.
2247///
2248/// # Safety
2249///
2250/// As for [`add_into`] (for [`sub_into`] with `SUB`), with `n = ly / 8` and the kernel's
2251/// limbs written.
2252#[inline(always)]
2253unsafe fn rest_of_y<const BE: bool, const SUB: bool>(
2254 r: *mut u8,
2255 x: *const u8,
2256 y: *const u8,
2257 n: usize,
2258 ly: usize,
2259 mut carry: bool,
2260) -> bool {
2261 let mut p = 8 * n;
2262 let rem = ly - p;
2263 if rem == 0 {
2264 return carry;
2265 }
2266 // SAFETY: positions `ly - 8..ly` are below `ly` and, with `n > 0`, at or above 0; those of
2267 // the output below `p` are the kernel's, written and so initialised. Every other position
2268 // is below `ly`.
2269 unsafe {
2270 if rem >= 3 && n > 0 {
2271 let shift = 8 * (8 - rem) as u32;
2272 let (xs, ys) = (
2273 load::<BE>(x, ly - 8) >> shift,
2274 load::<BE>(y, ly - 8) >> shift,
2275 );
2276 let c = u64::from(carry);
2277 let s = if SUB {
2278 xs.wrapping_sub(ys).wrapping_sub(c)
2279 } else {
2280 xs + ys + c
2281 };
2282 carry = (s >> (8 * rem)) & 1 != 0;
2283 let below = load::<BE>(r.cast_const(), p - 8) >> (8 * rem);
2284 store::<BE>(r, ly - 8, below | (s << shift));
2285 } else {
2286 while p < ly {
2287 let (xb, yb, c) = (get::<BE>(x, p), get::<BE>(y, p), u16::from(carry));
2288 let s = if SUB {
2289 u16::from(xb).wrapping_sub(u16::from(yb)).wrapping_sub(c)
2290 } else {
2291 u16::from(xb) + u16::from(yb) + c
2292 };
2293 set::<BE>(r, p, s as u8);
2294 carry = s >> 8 != 0;
2295 p += 1;
2296 }
2297 }
2298 }
2299 carry
2300}
2301
2302/// Takes the carry (borrow, with `SUB`) `carry` on from position `p` through the positions of
2303/// `x` below `top` that pass it on, `0xFF` (`0x00`), writing them to `r`: a limb at a time,
2304/// then a byte at a time. Returns the position at which it stopped, `top` if it did not, and
2305/// whether a carry is left.
2306///
2307/// The carry out of `y` is a coin toss for random operands, so the first step takes it without
2308/// a branch: the limb at `p`, plus or minus the carry, or, with fewer than eight positions left
2309/// and a whole limb under `p`, those positions as one partial limb, read from below their start
2310/// and shifted, and written back over the output's limb under `p`, which is read back for that,
2311/// as in [`rest_of_y`]. Only a carry that gets through that step, whose limb passed it on,
2312/// goes further.
2313///
2314/// # Safety
2315///
2316/// As for [`add_into`] (for [`sub_into`] with `SUB`), with `p <= top` and the positions of the
2317/// output below `p` written.
2318#[inline(always)]
2319unsafe fn carry_on<const BE: bool, const SUB: bool>(
2320 r: *mut u8,
2321 x: *const u8,
2322 mut p: usize,
2323 top: usize,
2324 mut carry: bool,
2325) -> (usize, bool) {
2326 let passes = if SUB { 0 } else { u64::MAX };
2327 // SAFETY: every position read and written is below `top`; the partial limb reads positions
2328 // `top - 8..top` of `x`, at or above 0 as `top > p >= 8`, and `p - 8..p` of the output,
2329 // which are written.
2330 unsafe {
2331 let c = u64::from(carry);
2332 if top - p >= 8 {
2333 let v = load::<BE>(x, p);
2334 store::<BE>(
2335 r,
2336 p,
2337 if SUB {
2338 v.wrapping_sub(c)
2339 } else {
2340 v.wrapping_add(c)
2341 },
2342 );
2343 carry &= v == passes;
2344 p += 8;
2345 } else if p < top && p >= 8 {
2346 let m = top - p;
2347 let shift = 8 * (8 - m) as u32;
2348 let v = load::<BE>(x, top - 8) >> shift;
2349 let s = if SUB { v.wrapping_sub(c) } else { v + c };
2350 let below = load::<BE>(r.cast_const(), p - 8) >> (8 * m);
2351 store::<BE>(r, top - 8, below | (s << shift));
2352 return (top, (s >> (8 * m)) & 1 != 0);
2353 }
2354 while carry && top - p >= 8 {
2355 let v = load::<BE>(x, p);
2356 store::<BE>(
2357 r,
2358 p,
2359 if SUB {
2360 v.wrapping_sub(1)
2361 } else {
2362 v.wrapping_add(1)
2363 },
2364 );
2365 carry = v == passes;
2366 p += 8;
2367 }
2368 while carry && p < top {
2369 let v = get::<BE>(x, p);
2370 set::<BE>(
2371 r,
2372 p,
2373 if SUB {
2374 v.wrapping_sub(1)
2375 } else {
2376 v.wrapping_add(1)
2377 },
2378 );
2379 carry = v == passes as u8;
2380 p += 1;
2381 }
2382 }
2383 (p, carry)
2384}
2385
2386/// Copies positions `p..top` of `x` to the output `r`, unless the output is `x` itself: from
2387/// 16 bytes with `ptr::copy`, and below that inline, with two loads and two stores of 8, 4 or
2388/// 2 bytes that overlap, or one of a byte, the loads first.
2389///
2390/// # Safety
2391///
2392/// The positions of `x` below `top` must be valid for reading, and those of `r` for writing;
2393/// the two may overlap.
2394#[inline(always)]
2395unsafe fn copy_rest<const BE: bool>(r: *mut u8, x: *const u8, p: usize, top: usize) {
2396 if p < top && !ptr::eq(r.cast_const(), x) {
2397 let m = top - p;
2398 // SAFETY: the `m` bytes from `src` and `dst` are positions `p..top` of `x` and `r`;
2399 // each load and store below lies within them, and the loads come first.
2400 unsafe {
2401 let (src, dst) = if BE {
2402 (x.sub(top), r.sub(top))
2403 } else {
2404 (x.add(p), r.add(p))
2405 };
2406 if m >= 16 {
2407 ptr::copy(src, dst, m);
2408 } else if m >= 8 {
2409 let (u, v) = (read::<8>(src), read::<8>(src.add(m - 8)));
2410 write::<8>(dst, u);
2411 write::<8>(dst.add(m - 8), v);
2412 } else if m >= 4 {
2413 let (u, v) = (read::<4>(src), read::<4>(src.add(m - 4)));
2414 write::<4>(dst, u);
2415 write::<4>(dst.add(m - 4), v);
2416 } else if m >= 2 {
2417 let (u, v) = (read::<2>(src), read::<2>(src.add(m - 2)));
2418 write::<2>(dst, u);
2419 write::<2>(dst.add(m - 2), v);
2420 } else {
2421 *dst = *src;
2422 }
2423 }
2424 }
2425}
2426
2427/// The kernel `r = x + y + carry` over `n` limbs in the layout `BE`; returns the carry out.
2428///
2429/// # Safety
2430///
2431/// As for the kernels: `x` and `y` valid for reading and `r` for writing `n` limbs from their
2432/// least significant ends, the output apart from the inputs, equal to them, or, big-endian,
2433/// above them.
2434#[inline(always)]
2435unsafe fn add_n<const BE: bool>(
2436 r: *mut u8,
2437 x: *const u8,
2438 y: *const u8,
2439 n: usize,
2440 carry: bool,
2441) -> bool {
2442 // SAFETY: as the caller guarantees.
2443 unsafe {
2444 if BE {
2445 add_n_be(r, x, y, n, carry)
2446 } else {
2447 add_n_le(r, x, y, n, carry)
2448 }
2449 }
2450}
2451
2452/// The kernel `r = x - y - borrow` over `n` limbs in the layout `BE`; returns the borrow out.
2453///
2454/// # Safety
2455///
2456/// As for [`add_n`].
2457#[inline(always)]
2458unsafe fn sub_n<const BE: bool>(
2459 r: *mut u8,
2460 x: *const u8,
2461 y: *const u8,
2462 n: usize,
2463 borrow: bool,
2464) -> bool {
2465 // SAFETY: as the caller guarantees.
2466 unsafe {
2467 if BE {
2468 sub_n_be(r, x, y, n, borrow)
2469 } else {
2470 sub_n_le(r, x, y, n, borrow)
2471 }
2472 }
2473}
2474
2475// Positions
2476
2477/// The least significant end of the magnitude in `v` in the layout `BE`: the address of its
2478/// first byte little-endian, and the address one past its last byte big-endian.
2479#[inline(always)]
2480fn end<const BE: bool>(v: &[u8]) -> *const u8 {
2481 if BE { v.as_ptr_range().end } else { v.as_ptr() }
2482}
2483
2484/// Reads position `p` of the magnitude at the least significant end `end` in the layout `BE`:
2485/// the byte at `end + p` little-endian, at `end - 1 - p` big-endian.
2486///
2487/// # Safety
2488///
2489/// The byte must be valid for reading.
2490#[inline(always)]
2491unsafe fn get<const BE: bool>(end: *const u8, p: usize) -> u8 {
2492 // SAFETY: as the caller guarantees.
2493 unsafe { if BE { *end.sub(p + 1) } else { *end.add(p) } }
2494}
2495
2496/// Reads position `p` of the magnitude of `len` significant bytes at the least significant end
2497/// `end` in the layout `BE`, as [`get`] does, or 0 from `len` up.
2498///
2499/// # Safety
2500///
2501/// The significant bytes must be valid for reading.
2502#[inline(always)]
2503unsafe fn get_or_zero<const BE: bool>(end: *const u8, len: usize, p: usize) -> u8 {
2504 if p < len {
2505 // SAFETY: as the caller guarantees, with `p < len`.
2506 unsafe { get::<BE>(end, p) }
2507 } else {
2508 0
2509 }
2510}
2511
2512/// Writes position `p` of the magnitude at the least significant end `end` in the layout
2513/// `BE`, as [`get`] reads it.
2514///
2515/// # Safety
2516///
2517/// The byte must be valid for writing.
2518#[inline(always)]
2519unsafe fn set<const BE: bool>(end: *mut u8, p: usize, v: u8) {
2520 // SAFETY: as the caller guarantees.
2521 unsafe {
2522 if BE {
2523 *end.sub(p + 1) = v;
2524 } else {
2525 *end.add(p) = v;
2526 }
2527 }
2528}
2529
2530/// Reads positions `p..p + 8` of the magnitude at the least significant end `end` in the
2531/// layout `BE` as a `u64` whose least significant byte is position `p`: the eight bytes from
2532/// `end + p` as a little-endian `u64`, or the eight bytes up to `end - p` as a big-endian one.
2533///
2534/// # Safety
2535///
2536/// The eight bytes must be valid for reading; they need no alignment.
2537#[inline(always)]
2538unsafe fn load<const BE: bool>(end: *const u8, p: usize) -> u64 {
2539 // SAFETY: as the caller guarantees; `[u8; 8]` is aligned at any address.
2540 unsafe {
2541 if BE {
2542 u64::from_be_bytes(end.sub(p + 8).cast::<[u8; 8]>().read())
2543 } else {
2544 u64::from_le_bytes(end.add(p).cast::<[u8; 8]>().read())
2545 }
2546 }
2547}
2548
2549/// Writes positions `p..p + 8` of the magnitude at the least significant end `end` in the
2550/// layout `BE`, as [`load`] reads them.
2551///
2552/// # Safety
2553///
2554/// The eight bytes must be valid for writing; they need no alignment.
2555#[inline(always)]
2556unsafe fn store<const BE: bool>(end: *mut u8, p: usize, v: u64) {
2557 // SAFETY: as the caller guarantees; `[u8; 8]` is aligned at any address.
2558 unsafe {
2559 if BE {
2560 end.sub(p + 8).cast::<[u8; 8]>().write(v.to_be_bytes());
2561 } else {
2562 end.add(p).cast::<[u8; 8]>().write(v.to_le_bytes());
2563 }
2564 }
2565}
2566
2567/// Copies the `m` bytes at `src` to `dst` in the reverse order, `dst[i] = src[m - 1 - i]`,
2568/// eight at a time while that many are left.
2569///
2570/// # Safety
2571///
2572/// `src` must be valid for reading and `dst` for writing `m` bytes, and they must not overlap.
2573#[inline(always)]
2574unsafe fn copy_reversed(dst: *mut u8, src: *const u8, m: usize) {
2575 let mut i = 0;
2576 // SAFETY: every byte read is below `src + m` and every byte written below `dst + m`.
2577 unsafe {
2578 while m - i >= 8 {
2579 let bytes = src.add(m - i - 8).cast::<[u8; 8]>().read();
2580 dst.add(i)
2581 .cast::<[u8; 8]>()
2582 .write(u64::from_be_bytes(bytes).to_le_bytes());
2583 i += 8;
2584 }
2585 while i < m {
2586 *dst.add(i) = *src.add(m - 1 - i);
2587 i += 1;
2588 }
2589 }
2590}
2591
2592/// Reads the `N` bytes at `p`.
2593///
2594/// # Safety
2595///
2596/// The bytes must be valid for reading; they need no alignment.
2597#[inline(always)]
2598pub(super) unsafe fn read<const N: usize>(p: *const u8) -> [u8; N] {
2599 // SAFETY: as the caller guarantees; `[u8; N]` is aligned at any address.
2600 unsafe { p.cast::<[u8; N]>().read() }
2601}
2602
2603/// Writes the `N` bytes `v` at `p`.
2604///
2605/// # Safety
2606///
2607/// The bytes must be valid for writing; they need no alignment.
2608#[inline(always)]
2609pub(super) unsafe fn write<const N: usize>(p: *mut u8, v: [u8; N]) {
2610 // SAFETY: as the caller guarantees; `[u8; N]` is aligned at any address.
2611 unsafe { p.cast::<[u8; N]>().write(v) }
2612}