use core::{mem, ops};
use crate::primitives::gf32::Fe32;
use crate::primitives::hrp::Hrp;
pub trait Checksum {
type MidstateRepr: PackedFe32;
const CODE_LENGTH: usize;
const CHECKSUM_LENGTH: usize;
const GENERATOR_SH: [Self::MidstateRepr; 5];
const TARGET_RESIDUE: Self::MidstateRepr;
fn sanity_check() {
assert!(Self::CHECKSUM_LENGTH <= Self::MidstateRepr::WIDTH);
for i in 1..5 {
for j in 0..Self::MidstateRepr::WIDTH {
let last = Self::GENERATOR_SH[i - 1].unpack(j);
let curr = Self::GENERATOR_SH[i].unpack(j);
assert_eq!(
curr,
(last << 1) ^ if last & 0x10 == 0x10 { 41 } else { 0 },
"Element {} of generator << 2^{} was incorrectly computed. (Should have been {} << 1)",
j, i, last,
);
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Engine<Ck: Checksum> {
residue: Ck::MidstateRepr,
}
impl<Ck: Checksum> Default for Engine<Ck> {
fn default() -> Self { Self::new() }
}
impl<Ck: Checksum> Engine<Ck> {
#[inline]
pub fn new() -> Self { Engine { residue: Ck::MidstateRepr::ONE } }
#[inline]
pub fn input_hrp(&mut self, hrp: Hrp) {
for fe in HrpFe32Iter::new(&hrp) {
self.input_fe(fe)
}
}
#[inline]
pub fn input_fe(&mut self, e: Fe32) {
let xn = self.residue.mul_by_x_then_add(Ck::CHECKSUM_LENGTH, e.into());
for i in 0..5 {
if xn & (1 << i) != 0 {
self.residue = self.residue ^ Ck::GENERATOR_SH[i];
}
}
}
#[inline]
pub fn input_target_residue(&mut self) {
for i in 0..Ck::CHECKSUM_LENGTH {
self.input_fe(Fe32(Ck::TARGET_RESIDUE.unpack(Ck::CHECKSUM_LENGTH - i - 1)));
}
}
#[inline]
pub fn residue(&self) -> &Ck::MidstateRepr { &self.residue }
}
pub trait PackedFe32: Copy + PartialEq + Eq + ops::BitXor<Self, Output = Self> {
const ONE: Self;
const WIDTH: usize = mem::size_of::<Self>() * 8 / 5;
fn unpack(&self, n: usize) -> u8;
fn mul_by_x_then_add(&mut self, degree: usize, add: u8) -> u8;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PackedNull;
impl ops::BitXor<PackedNull> for PackedNull {
type Output = PackedNull;
#[inline]
fn bitxor(self, _: PackedNull) -> PackedNull { PackedNull }
}
impl PackedFe32 for PackedNull {
const ONE: Self = PackedNull;
#[inline]
fn unpack(&self, _: usize) -> u8 { 0 }
#[inline]
fn mul_by_x_then_add(&mut self, _: usize, _: u8) -> u8 { 0 }
}
macro_rules! impl_packed_fe32 {
($ty:ident) => {
impl PackedFe32 for $ty {
const ONE: Self = 1;
#[inline]
fn unpack(&self, n: usize) -> u8 {
debug_assert!(n < Self::WIDTH);
(*self >> (n * 5)) as u8 & 0x1f
}
#[inline]
fn mul_by_x_then_add(&mut self, degree: usize, add: u8) -> u8 {
debug_assert!(degree > 0);
debug_assert!(degree <= Self::WIDTH);
debug_assert!(add < 32);
let ret = self.unpack(degree - 1);
*self &= !(0x1f << ((degree - 1) * 5));
*self <<= 5;
*self |= Self::from(add);
ret
}
}
};
}
impl_packed_fe32!(u32);
impl_packed_fe32!(u64);
impl_packed_fe32!(u128);
pub struct HrpFe32Iter<'hrp> {
high_iter: Option<crate::primitives::hrp::LowercaseByteIter<'hrp>>,
low_iter: Option<crate::primitives::hrp::LowercaseByteIter<'hrp>>,
}
impl<'hrp> HrpFe32Iter<'hrp> {
#[inline]
pub fn new(hrp: &'hrp Hrp) -> Self {
let high_iter = hrp.lowercase_byte_iter();
let low_iter = hrp.lowercase_byte_iter();
Self { high_iter: Some(high_iter), low_iter: Some(low_iter) }
}
}
impl<'hrp> Iterator for HrpFe32Iter<'hrp> {
type Item = Fe32;
#[inline]
fn next(&mut self) -> Option<Fe32> {
if let Some(ref mut high_iter) = &mut self.high_iter {
match high_iter.next() {
Some(high) => return Some(Fe32(high >> 5)),
None => {
self.high_iter = None;
return Some(Fe32::Q);
}
}
}
if let Some(ref mut low_iter) = &mut self.low_iter {
match low_iter.next() {
Some(low) => return Some(Fe32(low & 0x1f)),
None => self.low_iter = None,
}
}
None
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let high = match &self.high_iter {
Some(high_iter) => {
let (min, max) = high_iter.size_hint();
(min + 1, max.map(|max| max + 1)) }
None => (0, Some(0)),
};
let low = match &self.low_iter {
Some(low_iter) => low_iter.size_hint(),
None => (0, Some(0)),
};
let min = high.0 + 1 + low.0;
let max = high.1.zip(low.1).map(|(high, low)| high + 1 + low);
(min, max)
}
}