macro_rules! flat_gf8 {
($field:ident, $reduction_poly:literal, $reduction_low:literal, $generator:literal, $name:literal) => {
pub const REDUCTION_POLY: u16 = $reduction_poly;
pub(crate) const REDUCTION_LOW: u8 = $reduction_low;
pub const GENERATOR: Elem = Elem($generator);
#[doc = concat!("Marker type for ", $name, ".")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub struct $field;
/// An element of this GF(2^8) field, stored as its polynomial
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub struct Elem(pub(crate) u8);
impl Elem {
pub const ZERO: Self = Self(0);
pub const ONE: Self = Self(1);
#[inline]
#[must_use]
pub const fn from_raw(value: u8) -> Self {
Self(value)
}
#[inline]
#[must_use]
pub const fn to_raw(self) -> u8 {
self.0
}
#[inline]
#[must_use]
pub const fn from_bytes(bytes: [u8; 1]) -> Self {
Self(bytes[0])
}
#[inline]
#[must_use]
pub const fn to_bytes(self) -> [u8; 1] {
[self.0]
}
#[inline]
#[must_use]
pub const fn add(self, rhs: Self) -> Self {
Self(self.0 ^ rhs.0)
}
#[inline]
#[must_use]
pub const fn sub(self, rhs: Self) -> Self {
self.add(rhs)
}
#[inline]
#[must_use]
pub const fn neg(self) -> Self {
self
}
#[must_use]
pub(crate) const fn mul_xtime(self, rhs: Self) -> Self {
let mut a = self.0;
let b = rhs.0;
let mut acc: u8 = 0;
let mut i = 0;
while i < 8 {
if (b >> i) & 1 == 1 {
acc ^= a;
}
let overflow = a & 0x80 != 0;
a <<= 1;
if overflow {
a ^= REDUCTION_LOW;
}
i += 1;
}
Self(acc)
}
#[must_use]
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) const fn inv_xtime(self) -> Self {
if self.0 == 0 {
return Self::ZERO;
}
let mut result = Self::ONE;
let mut i = 0;
while i < 8 {
result = result.mul_xtime(result);
if (254u32 >> (7 - i)) & 1 == 1 {
result = result.mul_xtime(self);
}
i += 1;
}
result
}
#[inline]
#[must_use]
pub const fn mul(self, rhs: Self) -> Self {
if self.0 == 0 || rhs.0 == 0 {
return Self::ZERO;
}
let la = LOG[self.0 as usize] as usize;
let lb = LOG[rhs.0 as usize] as usize;
Self(EXP[(la + lb) % 255])
}
#[inline]
#[must_use]
pub const fn square(self) -> Self {
self.mul(self)
}
#[inline]
#[must_use]
pub const fn inv(self) -> Self {
if self.0 == 0 {
return Self::ZERO;
}
let l = LOG[self.0 as usize] as usize;
Self(EXP[(255 - l) % 255])
}
#[inline]
#[must_use]
pub const fn div(self, rhs: Self) -> Self {
if self.0 == 0 || rhs.0 == 0 {
return Self::ZERO;
}
self.mul(rhs.inv())
}
#[must_use]
pub const fn pow(self, mut exponent: u64) -> Self {
let mut base = self;
let mut result = Self::ONE;
while exponent != 0 {
if exponent & 1 != 0 {
result = result.mul(base);
}
base = base.square();
exponent >>= 1;
}
result
}
}
impl crate::field::Elem for Elem {
const ZERO: Self = Self::ZERO;
const ONE: Self = Self::ONE;
#[inline]
fn add(self, rhs: Self) -> Self {
Elem::add(self, rhs)
}
#[inline]
fn sub(self, rhs: Self) -> Self {
Elem::sub(self, rhs)
}
#[inline]
fn mul(self, rhs: Self) -> Self {
Elem::mul(self, rhs)
}
#[inline]
fn square(self) -> Self {
Elem::square(self)
}
#[inline]
fn inv(self) -> Self {
Elem::inv(self)
}
#[inline]
fn div(self, rhs: Self) -> Self {
Elem::div(self, rhs)
}
#[inline]
fn pow(self, exponent: u64) -> Self {
Elem::pow(self, exponent)
}
}
impl crate::field::Field for $field {
type Elem = Elem;
const NAME: &'static str = $name;
const BITS: u32 = 8;
const BYTES: usize = 1;
const ORDER: u128 = 256;
const CHARACTERISTIC: u64 = 2;
const GENERATOR: Elem = GENERATOR;
#[inline]
fn decode(bytes: &[u8]) -> Elem {
let bytes: [u8; 1] = bytes
.try_into()
.expect(concat!($name, " element has the wrong byte width"));
Elem::from_bytes(bytes)
}
#[inline]
fn encode(bytes: &mut [u8], value: Elem) {
assert_eq!(
bytes.len(),
1,
concat!($name, " element has the wrong byte width")
);
bytes.copy_from_slice(&value.to_bytes());
}
}
impl core::fmt::Debug for Elem {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, concat!(stringify!($field), "({:#04x})"), self.0)
}
}
impl core::fmt::Display for Elem {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:02x}", self.0)
}
}
impl core::ops::Add for Elem {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Elem::add(self, rhs)
}
}
impl core::ops::Sub for Elem {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Elem::sub(self, rhs)
}
}
impl core::ops::Neg for Elem {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Elem::neg(self)
}
}
impl core::ops::Mul for Elem {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Elem::mul(self, rhs)
}
}
impl core::ops::Div for Elem {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
Elem::div(self, rhs)
}
}
impl core::ops::AddAssign for Elem {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = Elem::add(*self, rhs);
}
}
impl core::ops::SubAssign for Elem {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = Elem::sub(*self, rhs);
}
}
impl core::ops::MulAssign for Elem {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = Elem::mul(*self, rhs);
}
}
impl core::ops::DivAssign for Elem {
#[inline]
fn div_assign(&mut self, rhs: Self) {
*self = Elem::div(*self, rhs);
}
}
impl core::iter::Sum for Elem {
#[inline]
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ZERO, Elem::add)
}
}
impl<'a> core::iter::Sum<&'a Elem> for Elem {
#[inline]
fn sum<I: Iterator<Item = &'a Elem>>(iter: I) -> Self {
iter.fold(Self::ZERO, |acc, &x| Elem::add(acc, x))
}
}
impl core::iter::Product for Elem {
#[inline]
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ONE, Elem::mul)
}
}
impl<'a> core::iter::Product<&'a Elem> for Elem {
#[inline]
fn product<I: Iterator<Item = &'a Elem>>(iter: I) -> Self {
iter.fold(Self::ONE, |acc, &x| Elem::mul(acc, x))
}
}
pub(crate) const EXP: [u8; 255] = build_exp();
pub(crate) const LOG: [u8; 256] = build_log();
const fn build_exp() -> [u8; 255] {
let mut table = [0u8; 255];
let mut value: u8 = 1;
let mut i = 0;
while i < 255 {
table[i] = value;
value = Elem(value).mul_xtime(GENERATOR).0;
i += 1;
}
table
}
#[allow(clippy::cast_possible_truncation)]
const fn build_log() -> [u8; 256] {
let mut table = [0u8; 256];
let mut i = 0;
while i < 255 {
table[EXP[i] as usize] = i as u8;
i += 1;
}
table
}
#[cfg(test)]
mod table_builders {
use super::*;
#[test]
fn const_tables_match_their_runtime_builders() {
assert_eq!(build_exp(), EXP);
assert_eq!(build_log(), LOG);
for (i, &g) in EXP.iter().enumerate() {
assert_eq!(LOG[g as usize] as usize, i, "log/exp inverse at {i}");
}
assert_eq!(
Elem(1).mul_xtime(GENERATOR),
GENERATOR,
"the generator generates"
);
for raw in 1..=255u8 {
assert_eq!(
Elem(raw).inv_xtime(),
Elem(raw).inv(),
"inv_xtime disagrees with the table inverse at {raw:#04x}"
);
}
}
}
};
}
pub mod gf8b {
flat_gf8!(Gf8B, 0x11B, 0x1B, 0x03, "GF(2^8)");
}
pub mod gf8d {
flat_gf8!(Gf8D, 0x11D, 0x1D, 0x02, "GF(2^8)/0x11D");
}