ring/arithmetic/limbs512/
storage.rs

1// Copyright 2025 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15use crate::{
16    error::LenMismatchError,
17    limb::{Limb, LIMB_BITS},
18    polyfill::slice::{self, AsChunksMut},
19};
20use core::mem::{align_of, size_of};
21
22// Some x86_64 assembly is written under the assumption that some of its
23// input data and/or temporary storage is aligned to `MOD_EXP_CTIME_ALIGN`
24// bytes, which was/is 64 in OpenSSL.
25//
26// We use this in the non-X86-64 implementation of exponentiation as well,
27// with the hope of converging th two implementations into one.
28
29#[repr(C, align(64))]
30pub struct AlignedStorage<const N: usize>([Limb; N]);
31
32const _LIMB_SIZE_DIVIDES_ALIGNMENT: () =
33    assert!(align_of::<AlignedStorage<1>>() % size_of::<Limb>() == 0);
34
35pub const LIMBS_PER_CHUNK: usize = 512 / LIMB_BITS;
36
37impl<const N: usize> AlignedStorage<N> {
38    pub fn zeroed() -> Self {
39        assert_eq!(N % LIMBS_PER_CHUNK, 0); // TODO: const.
40        Self([0; N])
41    }
42
43    // The result will have every chunk aligned on a 64 byte boundary.
44    pub fn aligned_chunks_mut(
45        &mut self,
46        num_entries: usize,
47        chunks_per_entry: usize,
48    ) -> Result<AsChunksMut<Limb, LIMBS_PER_CHUNK>, LenMismatchError> {
49        let total_limbs = num_entries * chunks_per_entry * LIMBS_PER_CHUNK;
50        let len = self.0.len();
51        let flattened = self
52            .0
53            .get_mut(..total_limbs)
54            .ok_or_else(|| LenMismatchError::new(len))?;
55        match slice::as_chunks_mut(flattened) {
56            (chunks, []) => Ok(chunks),
57            (_, r) => Err(LenMismatchError::new(r.len())),
58        }
59    }
60}