pub mod rev;
use crate::bitnum::BitNum;
use crate::checksum::{CheckBuilderErr, Digest, LinearCheck};
use crate::keyval::KeyValIter;
use std::fmt::Display;
use std::str::FromStr;
#[derive(Clone, Debug)]
pub struct FletcherBuilder<Sum: BitNum> {
width: Option<usize>,
module: Option<Sum>,
init: Option<Sum>,
addout: Option<Sum>,
swap: Option<bool>,
check: Option<Sum>,
name: Option<String>,
}
impl<S: BitNum> FletcherBuilder<S> {
pub fn width(&mut self, w: usize) -> &mut Self {
self.width = Some(w);
self
}
pub fn module(&mut self, m: S) -> &mut Self {
self.module = Some(m);
self
}
pub fn init(&mut self, i: S) -> &mut Self {
self.init = Some(i);
self
}
pub fn addout(&mut self, o: S) -> &mut Self {
self.addout = Some(o);
self
}
pub fn swap(&mut self, s: bool) -> &mut Self {
self.swap = Some(s);
self
}
pub fn check(&mut self, c: S) -> &mut Self {
self.check = Some(c);
self
}
pub fn name(&mut self, n: &str) -> &mut Self {
self.name = Some(String::from(n));
self
}
pub fn build(&self) -> Result<Fletcher<S>, CheckBuilderErr> {
let init = self.init.unwrap_or_else(S::zero);
let addout = self.addout.unwrap_or_else(S::zero);
let hwidth = match self.width {
None => return Err(CheckBuilderErr::MissingParameter("width")),
Some(w) => {
if w % 2 != 0 || w > init.bits() {
return Err(CheckBuilderErr::ValueOutOfRange("width"));
} else {
w / 2
}
}
};
let mask = (S::one() << hwidth) - S::one();
let module = if self.module.unwrap_or_else(S::zero) == S::zero() {
S::one() << hwidth
} else {
self.module.unwrap_or_else(S::zero)
};
let mut fletch = Fletcher {
hwidth,
module,
init,
addout,
swap: self.swap.unwrap_or(false),
mask,
name: self.name.clone(),
};
fletch.init = init % module;
let (mut s, mut c) = fletch.from_compact(addout);
s = s % module;
c = c % module;
fletch.addout = fletch.to_compact((s, c));
match self.check {
Some(chk) => {
if fletch.digest(&b"123456789"[..]).unwrap() != chk {
println!("{:x?}", fletch.digest(&b"123456789"[..]).unwrap());
Err(CheckBuilderErr::CheckFail)
} else {
Ok(fletch)
}
}
None => Ok(fletch),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Fletcher<Sum: BitNum> {
hwidth: usize,
module: Sum,
init: Sum,
addout: Sum,
swap: bool,
mask: Sum,
name: Option<String>,
}
impl<Sum: BitNum> Display for Fletcher<Sum> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.name {
Some(n) => write!(f, "{}", n),
None => write!(
f,
"fletcher width={} module={:#x} init={:#x} addout={:#x} swap={}",
2 * self.hwidth,
self.module,
self.init,
self.addout,
self.swap
),
}
}
}
impl<Sum: BitNum> Fletcher<Sum> {
pub fn with_options() -> FletcherBuilder<Sum> {
FletcherBuilder {
width: None,
module: None,
init: None,
addout: None,
swap: None,
check: None,
name: None,
}
}
fn from_compact(&self, x: Sum) -> (Sum, Sum) {
let l = x & self.mask;
let h = (x >> self.hwidth) & self.mask;
if self.swap {
(h, l)
} else {
(l, h)
}
}
fn to_compact(&self, (s, c): (Sum, Sum)) -> Sum {
let (l, h) = if self.swap { (c, s) } else { (s, c) };
(l & self.mask) ^ (h & self.mask) << self.hwidth
}
}
impl<Sum: BitNum> FromStr for FletcherBuilder<Sum> {
fn from_str(s: &str) -> Result<FletcherBuilder<Sum>, CheckBuilderErr> {
let mut fletch = Fletcher::<Sum>::with_options();
for x in KeyValIter::new(s) {
let (current_key, current_val) = match x {
Err(key) => return Err(CheckBuilderErr::MalformedString(key)),
Ok(s) => s,
};
let fletch_op = match current_key.as_str() {
"width" => usize::from_str(¤t_val).ok().map(|x| fletch.width(x)),
"module" => Sum::from_hex(¤t_val).ok().map(|x| fletch.module(x)),
"init" => Sum::from_hex(¤t_val).ok().map(|x| fletch.init(x)),
"addout" => Sum::from_hex(¤t_val).ok().map(|x| fletch.addout(x)),
"swap" => bool::from_str(¤t_val).ok().map(|x| fletch.swap(x)),
"check" => Sum::from_hex(¤t_val).ok().map(|x| fletch.check(x)),
"name" => Some(fletch.name(¤t_val)),
_ => return Err(CheckBuilderErr::UnknownKey(current_key)),
};
match fletch_op {
Some(f) => fletch = f.clone(),
None => return Err(CheckBuilderErr::MalformedString(current_key)),
}
}
Ok(fletch)
}
type Err = CheckBuilderErr;
}
impl<Sum: BitNum> FromStr for Fletcher<Sum> {
fn from_str(s: &str) -> Result<Fletcher<Sum>, CheckBuilderErr> {
FletcherBuilder::<Sum>::from_str(s)?.build()
}
type Err = CheckBuilderErr;
}
impl<S: BitNum> Digest for Fletcher<S> {
type Sum = S;
fn init(&self) -> Self::Sum {
self.to_compact((self.init, S::zero()))
}
fn dig_byte(&self, sum: Self::Sum, byte: u8) -> Self::Sum {
let (mut s, mut c) = self.from_compact(sum);
s = (s + S::from(byte) % self.module) % self.module;
c = (c + s) % self.module;
self.to_compact((s, c))
}
fn finalize(&self, sum: Self::Sum) -> Self::Sum {
self.add(sum, &self.addout)
}
}
impl<S: BitNum> LinearCheck for Fletcher<S> {
type Shift = S;
fn init_shift(&self) -> Self::Shift {
S::zero()
}
fn inc_shift(&self, shift: Self::Shift) -> Self::Shift {
(shift + S::one()) % self.module
}
fn shift(&self, sum: Self::Sum, shift: &Self::Shift) -> Self::Sum {
let (s, mut c) = self.from_compact(sum);
c = (c + s * *shift) % self.module;
self.to_compact((s, c))
}
fn add(&self, sum_a: Self::Sum, sum_b: &Self::Sum) -> Self::Sum {
let (sa, ca) = self.from_compact(sum_a);
let (sb, cb) = self.from_compact(*sum_b);
self.to_compact(((sa + sb) % self.module, (ca + cb) % self.module))
}
fn negate(&self, sum: Self::Sum) -> Self::Sum {
let (mut s, mut c) = self.from_compact(sum);
if s != S::zero() {
s = self.module - s;
}
if c != S::zero() {
c = self.module - c;
}
self.to_compact((s, c))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::checksum::tests::{check_example, test_find, test_prop, test_shifts};
use std::str::FromStr;
#[test]
fn adler32() {
let adel = Fletcher::<u32>::with_options()
.width(32)
.init(1)
.module(65521)
.check(0x091e01de)
.build()
.unwrap();
test_shifts(&adel);
test_find(&adel);
test_prop(&adel);
check_example(&adel, 0x81bfd25f);
let nobel = Fletcher::with_options()
.width(32)
.init(1u64)
.module(65521)
.check(0x091e01de)
.build()
.unwrap();
test_shifts(&nobel);
test_find(&nobel);
test_prop(&adel);
check_example(&nobel, 0x81bfd25f);
}
#[test]
fn fletcher16() {
let f16 = Fletcher::with_options()
.width(16)
.module(0xffu16)
.check(0x1ede)
.build()
.unwrap();
test_shifts(&f16);
test_find(&f16);
test_prop(&f16);
check_example(&f16, 0x7815);
}
#[test]
fn fletcher8() {
let f8 = Fletcher::<u8>::from_str("width=8 module=f init=0 addout=0 swap=false check=0xc")
.unwrap();
test_shifts(&f8);
test_prop(&f8);
check_example(&f8, 0x6);
}
}