use core::any::Any;
use core::fmt::Formatter;
use core::hash::Hash;
use core::marker::PhantomData;
use super::arithmetic;
use super::descriptor::Storage;
use super::private::Sealed;
use crate::field::binary::BinaryField;
use crate::field::private::RowAccess;
use crate::field::{Elem, Field, FieldBuffer, FieldElem};
pub trait PackedWord: Sealed {}
impl Sealed for u8 {}
impl Sealed for u16 {}
impl Sealed for u32 {}
impl Sealed for u64 {}
impl PackedWord for u8 {}
impl PackedWord for u16 {}
impl PackedWord for u32 {}
impl PackedWord for u64 {}
pub struct Components;
pub struct Packed<W: PackedWord>(PhantomData<W>);
pub struct PackedPadded<W: PackedWord>(PhantomData<W>);
#[allow(clippy::expl_impl_clone_on_copy)]
impl Clone for Components {
fn clone(&self) -> Self {
*self
}
}
impl Copy for Components {}
impl Default for Components {
fn default() -> Self {
Self
}
}
impl core::fmt::Debug for Components {
fn fmt(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result {
formatter.write_str("Components")
}
}
macro_rules! storage_marker {
($storage:ident) => {
impl<W: PackedWord> Clone for $storage<W> {
fn clone(&self) -> Self {
*self
}
}
impl<W: PackedWord> Copy for $storage<W> {}
impl<W: PackedWord> Default for $storage<W> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<W: PackedWord> core::fmt::Debug for $storage<W> {
fn fmt(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result {
formatter.write_str(core::stringify!($storage))
}
}
};
}
storage_marker!(Packed);
storage_marker!(PackedPadded);
pub trait ExtensionStorage<B: Field, const D: usize>: Sealed + 'static {
type Raw: Copy + Eq + Ord + Hash + core::fmt::Debug + 'static;
}
impl Sealed for Components {}
impl<B: Field, const D: usize> ExtensionStorage<B, D> for Components {
type Raw = [<B as Field>::Raw; D];
}
pub(crate) trait StorageRepr<B: Field, const D: usize>: ExtensionStorage<B, D> {
const TAG: Storage;
const ZERO: Self::Raw;
const ONE: Self::Raw;
fn canonical(raw: Self::Raw) -> Self::Raw;
fn split(raw: Self::Raw) -> [<B as Field>::Raw; D];
fn join(components: [<B as Field>::Raw; D]) -> Self::Raw;
fn to_pinned_word(raw: Self::Raw) -> u64;
fn from_pinned_word(word: u64) -> Self::Raw;
}
pub(crate) trait StorageBuffer<B: Field, const D: usize>: StorageRepr<B, D> {
const BYTES: usize;
const STORAGE_BITS: u32;
fn decode(bytes: &[u8]) -> Self::Raw;
fn encode(bytes: &mut [u8], raw: &Self::Raw);
}
pub(crate) trait StorageDisplay<B: Field, const D: usize>: StorageRepr<B, D> {
fn fmt(formatter: &mut Formatter<'_>, raw: &Self::Raw, digits: usize) -> core::fmt::Result;
}
impl<B: Field, const D: usize> StorageRepr<B, D> for Components {
const TAG: Storage = Storage::Components;
const ZERO: Self::Raw = [<B as Field>::ZERO_RAW; D];
const ONE: Self::Raw = {
let mut components = [<B as Field>::ZERO_RAW; D];
components[0] = <B as Field>::ONE_RAW;
components
};
fn canonical(raw: Self::Raw) -> Self::Raw {
raw.map(<B as Field>::canonical_raw)
}
fn split(raw: Self::Raw) -> [<B as Field>::Raw; D] {
raw
}
fn join(components: [<B as Field>::Raw; D]) -> Self::Raw {
components.map(<B as Field>::canonical_raw)
}
fn to_pinned_word(raw: Self::Raw) -> u64 {
let limbs = (&raw as &dyn Any)
.downcast_ref::<[u32; 2]>()
.expect("pinned component storage holds two u32 limbs");
u64::from(limbs[0]) | (u64::from(limbs[1]) << 32)
}
#[allow(clippy::cast_possible_truncation)]
fn from_pinned_word(word: u64) -> Self::Raw {
let limbs: [u32; 2] = [word as u32, (word >> 32) as u32];
*(&limbs as &dyn Any)
.downcast_ref::<Self::Raw>()
.expect("pinned component storage holds two u32 limbs")
}
}
impl<B: FieldBuffer, const D: usize> StorageBuffer<B, D> for Components {
const BYTES: usize = D * <B as FieldBuffer>::BYTES;
#[allow(clippy::cast_possible_truncation)]
const STORAGE_BITS: u32 = (8 * D * <B as FieldBuffer>::BYTES) as u32;
fn decode(bytes: &[u8]) -> Self::Raw {
let width = <B as FieldBuffer>::BYTES;
core::array::from_fn(|i| {
let component = &bytes[i * width..(i + 1) * width];
<B as FieldBuffer>::decode(component).to_raw()
})
}
fn encode(bytes: &mut [u8], raw: &Self::Raw) {
let width = <B as FieldBuffer>::BYTES;
for (i, component) in raw.iter().enumerate() {
let element = Elem::<B> { raw: *component };
<B as FieldBuffer>::encode(&mut bytes[i * width..(i + 1) * width], element);
}
}
}
impl<B: Field, const D: usize> StorageDisplay<B, D> for Components
where
Elem<B>: core::fmt::Display,
{
fn fmt(formatter: &mut Formatter<'_>, raw: &Self::Raw, _digits: usize) -> core::fmt::Result {
formatter.write_str("[")?;
for (i, component) in raw.iter().enumerate() {
if i > 0 {
formatter.write_str(", ")?;
}
let element = Elem::<B> { raw: *component };
core::fmt::Display::fmt(&element, formatter)?;
}
formatter.write_str("]")
}
}
macro_rules! packed_storage {
($storage:ident, $word:ty, $bits:literal) => {
impl Sealed for $storage<$word> {}
impl<B: BinaryField, const D: usize> ExtensionStorage<B, D> for $storage<$word> {
type Raw = $word;
}
impl<B: BinaryField, const D: usize> StorageRepr<B, D> for $storage<$word> {
const TAG: Storage = Storage::$storage { width: $bits };
const ZERO: Self::Raw = 0;
#[allow(clippy::cast_possible_truncation)]
const ONE: Self::Raw = {
let base = <B as RowAccess>::DESCRIPTOR;
arithmetic::word_one(base, base.root_index()) as $word
};
fn canonical(raw: Self::Raw) -> Self::Raw {
<Self as StorageRepr<B, D>>::join(<Self as StorageRepr<B, D>>::split(raw))
}
#[allow(clippy::cast_lossless)]
fn split(raw: Self::Raw) -> [<B as Field>::Raw; D] {
let word = raw as u64;
let degree = <B as Field>::DEGREE as usize;
core::array::from_fn(|i| {
let component = extract_bits(word, i * degree, degree);
Elem::<B>::from_index(component as u128)
.expect("packed component word holds excess bits")
.to_raw()
})
}
#[allow(clippy::cast_possible_truncation)]
fn join(components: [<B as Field>::Raw; D]) -> Self::Raw {
let degree = <B as Field>::DEGREE as usize;
let mut word = 0u64;
for (i, component) in components.iter().enumerate() {
let element = Elem::<B> {
raw: <B as Field>::canonical_raw(*component),
};
let component =
u64::try_from(element.to_index()).expect("base index fits the word");
word |= component << (i * degree);
}
word as $word
}
#[allow(clippy::cast_lossless)]
fn to_pinned_word(raw: Self::Raw) -> u64 {
raw as u64
}
#[allow(clippy::cast_possible_truncation)]
fn from_pinned_word(word: u64) -> Self::Raw {
word as $word
}
}
impl<B: BinaryField, const D: usize> StorageDisplay<B, D> for $storage<$word> {
fn fmt(
formatter: &mut Formatter<'_>,
raw: &Self::Raw,
digits: usize,
) -> core::fmt::Result {
write!(formatter, "{:0width$x}", *raw, width = digits)
}
}
};
}
packed_storage!(Packed, u8, 8);
packed_storage!(Packed, u16, 16);
packed_storage!(Packed, u32, 32);
packed_storage!(Packed, u64, 64);
packed_storage!(PackedPadded, u8, 8);
packed_storage!(PackedPadded, u16, 16);
packed_storage!(PackedPadded, u32, 32);
packed_storage!(PackedPadded, u64, 64);
macro_rules! packed_buffer {
($word:ty, $bytes:literal, $bits:literal) => {
impl<B: BinaryField, const D: usize> StorageBuffer<B, D> for Packed<$word> {
const BYTES: usize = $bytes;
const STORAGE_BITS: u32 = $bits;
fn decode(bytes: &[u8]) -> Self::Raw {
let bytes: [u8; $bytes] = bytes
.try_into()
.expect("packed element has the wrong byte width");
<$word>::from_le_bytes(bytes)
}
fn encode(bytes: &mut [u8], raw: &Self::Raw) {
bytes.copy_from_slice(&raw.to_le_bytes());
}
}
};
}
packed_buffer!(u8, 1, 8);
packed_buffer!(u16, 2, 16);
packed_buffer!(u32, 4, 32);
packed_buffer!(u64, 8, 64);
pub(crate) const fn extract_bits(word: u64, offset: usize, bits: usize) -> u64 {
if bits >= 64 {
word
} else {
(word >> offset) & ((1 << bits) - 1)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::finite_field::FiniteField;
use crate::field::{AES, Polynomial};
type Aes8 = FiniteField<2, 8, Polynomial<AES>>;
type Gf4 = FiniteField<2, 2, Polynomial<7>>;
type FanPaar4Row = FiniteField<2, 4, crate::field::Extension<crate::field::FanPaar4, 2>>;
type FanPaar8Row = FiniteField<2, 8, crate::field::Extension<crate::field::FanPaar8, 2>>;
type Rijndael16Row = FiniteField<2, 16, crate::field::Extension<crate::field::Rijndael16, 2>>;
type Rijndael32Row = FiniteField<2, 32, crate::field::Extension<crate::field::Rijndael32, 2>>;
macro_rules! packed_case {
($storage:ident, $word:ty, $base:ty, $low:expr, $high:expr, $word_value:expr) => {{
let components = [
Elem::<$base>::from_raw($low).raw,
Elem::<$base>::from_raw($high).raw,
];
let joined = <$storage<$word> as StorageRepr<$base, 2>>::join(components);
assert_eq!(joined, $word_value);
assert_eq!(
<$storage<$word> as StorageRepr<$base, 2>>::split(joined),
components
);
assert_eq!(<$storage<$word> as StorageRepr<$base, 2>>::ONE, 1);
assert_eq!(<$storage<$word> as StorageRepr<$base, 2>>::ZERO, 0);
assert_eq!(
<$storage<$word> as StorageRepr<$base, 2>>::canonical(joined),
joined
);
}};
}
#[test]
fn packed_storages_round_trip() {
packed_case!(Packed, u8, FanPaar4Row, 0b1001, 0b0101, 0b0101_1001);
packed_case!(Packed, u16, Aes8, 0x53, 0xC1, 0xC153);
packed_case!(Packed, u32, Rijndael16Row, 0x00FF, 0x1234, 0x1234_00FF);
packed_case!(
Packed,
u64,
Rijndael32Row,
0x00FF_FFFF,
0x1234_5678,
0x1234_5678_00FF_FFFF
);
packed_case!(PackedPadded, u8, Gf4, 0b10, 0b01, 0b0110);
packed_case!(PackedPadded, u16, FanPaar4Row, 0b1001, 0b0101, 0b0101_1001);
packed_case!(PackedPadded, u32, FanPaar8Row, 0x53, 0xC1, 0xC153);
packed_case!(PackedPadded, u64, FanPaar8Row, 0x53, 0xC1, 0xC153);
}
#[test]
fn padded_canonical_clears_high_bits() {
let components = [
Elem::<Gf4>::from_raw(0b10).raw,
Elem::<Gf4>::from_raw(0b01).raw,
];
let joined = <PackedPadded<u8> as StorageRepr<Gf4, 2>>::join(components);
assert_eq!(joined, 0b0110);
assert_eq!(
<PackedPadded<u8> as StorageRepr<Gf4, 2>>::canonical(0xF6),
joined
);
}
#[test]
fn packed_bytes_round_trip_every_word() {
macro_rules! buffer_case {
($word:ty, $value:expr) => {{
let mut bytes = [0u8; core::mem::size_of::<$word>()];
<Packed<$word> as StorageBuffer<Aes8, 2>>::encode(&mut bytes, &$value);
assert_eq!(bytes, $value.to_le_bytes());
assert_eq!(
<Packed<$word> as StorageBuffer<Aes8, 2>>::decode(&bytes),
$value
);
}};
}
buffer_case!(u8, 0x53u8);
buffer_case!(u16, 0xC153u16);
buffer_case!(u32, 0x0000_C153u32);
buffer_case!(u64, 0x0000_0000_0000_C153u64);
}
#[test]
fn components_storage_round_trip() {
let components = [
Elem::<Aes8>::from_raw(0x53).raw,
Elem::<Aes8>::from_raw(0xC1).raw,
];
assert_eq!(
<Components as StorageRepr<Aes8, 2>>::split(
<Components as StorageRepr<Aes8, 2>>::join(components)
),
components
);
assert_eq!(<Components as StorageRepr<Aes8, 2>>::ZERO, [0, 0]);
assert_eq!(<Components as StorageRepr<Aes8, 2>>::ONE, [1, 0]);
assert_eq!(
<Components as StorageRepr<FanPaar4Row, 2>>::ONE,
[Elem::<FanPaar4Row>::ONE.raw, 0]
);
}
#[test]
fn components_bytes_concatenate_base_encodings() {
assert_eq!(<Components as StorageBuffer<Aes8, 2>>::BYTES, 2);
assert_eq!(<Components as StorageBuffer<Aes8, 2>>::STORAGE_BITS, 16);
let components = [
Elem::<Aes8>::from_raw(0x53).raw,
Elem::<Aes8>::from_raw(0xC1).raw,
];
let mut bytes = [0u8; 2];
<Components as StorageBuffer<Aes8, 2>>::encode(&mut bytes, &components);
assert_eq!(bytes, [0x53, 0xC1]);
assert_eq!(
<Components as StorageBuffer<Aes8, 2>>::decode(&bytes),
components
);
}
}