pub mod rev;
use crate::bitnum::Modnum;
use crate::checksum::{CheckBuilderErr, Digest, LinearCheck};
use crate::keyval::KeyValIter;
use std::fmt::Display;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct ModSumBuilder<S: Modnum> {
width: Option<usize>,
module: Option<S>,
init: Option<S>,
check: Option<S>,
name: Option<String>,
}
impl<S: Modnum> ModSumBuilder<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 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<ModSum<S>, CheckBuilderErr> {
let width = self
.width
.ok_or(CheckBuilderErr::MissingParameter("width"))?;
let mut module = self.module.unwrap_or_else(S::zero);
if module == S::zero() && width < module.bits() {
module = S::one() << width
};
let mut init = self.init.unwrap_or_else(S::zero);
if module != S::zero() {
init = init % module
};
let s = ModSum {
width,
module,
init,
name: self.name.clone(),
};
match self.check {
Some(c) => {
if s.digest(&b"123456789"[..]).unwrap() == c {
Ok(s)
} else {
Err(CheckBuilderErr::CheckFail)
}
}
None => Ok(s),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ModSum<S: Modnum> {
width: usize,
module: S,
init: S,
name: Option<String>,
}
impl<S: Modnum> ModSum<S> {
pub fn with_options() -> ModSumBuilder<S> {
ModSumBuilder {
width: None,
module: None,
init: None,
check: None,
name: None,
}
}
}
impl<Sum: Modnum> Display for ModSum<Sum> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.name {
Some(n) => write!(f, "{}", n),
None => write!(
f,
"modsum width={} module={:#x} init={:#x}",
self.width, self.module, self.init
),
}
}
}
impl<Sum: Modnum> FromStr for ModSumBuilder<Sum> {
fn from_str(s: &str) -> Result<ModSumBuilder<Sum>, CheckBuilderErr> {
let mut sum = ModSum::<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 crc_op = match current_key.as_str() {
"width" => usize::from_str(¤t_val).ok().map(|x| sum.width(x)),
"module" => Sum::from_hex(¤t_val).ok().map(|x| sum.module(x)),
"init" => Sum::from_hex(¤t_val).ok().map(|x| sum.init(x)),
"name" => Some(sum.name(¤t_val)),
_ => return Err(CheckBuilderErr::UnknownKey(current_key)),
};
match crc_op {
Some(c) => sum = c.clone(),
None => return Err(CheckBuilderErr::MalformedString(current_key)),
}
}
Ok(sum)
}
type Err = CheckBuilderErr;
}
impl<Sum: Modnum> FromStr for ModSum<Sum> {
fn from_str(s: &str) -> Result<ModSum<Sum>, CheckBuilderErr> {
ModSumBuilder::from_str(s)?.build()
}
type Err = CheckBuilderErr;
}
impl<S: Modnum> Digest for ModSum<S> {
type Sum = S;
fn init(&self) -> Self::Sum {
self.init
}
fn dig_byte(&self, sum: Self::Sum, byte: u8) -> Self::Sum {
sum.add_mod(&S::from(byte), &self.module)
}
fn finalize(&self, sum: Self::Sum) -> Self::Sum {
sum
}
}
impl<S: Modnum> LinearCheck for ModSum<S> {
type Shift = ();
fn init_shift(&self) -> Self::Shift {}
fn inc_shift(&self, _: Self::Shift) -> Self::Shift {}
fn shift(&self, sum: Self::Sum, _: &Self::Shift) -> Self::Sum {
sum
}
fn shift_n(&self, _: usize) -> Self::Shift {}
fn add(&self, sum_a: Self::Sum, sum_b: &Self::Sum) -> Self::Sum {
sum_a.add_mod(sum_b, &self.module)
}
fn negate(&self, sum: Self::Sum) -> Self::Sum {
if sum == S::zero() {
sum
} else if self.module == S::zero() {
!sum + S::one()
} else {
self.module - sum
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::checksum::tests::{test_prop, test_shifts};
use crate::checksum::{RelativeIndex, Relativity};
#[test]
fn screw() {
let s = ModSum::<u8>::with_options()
.width(8)
.check(0xdd)
.build()
.unwrap();
test_shifts(&s);
test_prop(&s);
let s = ModSum::<u16>::with_options()
.width(8)
.check(0xdd)
.build()
.unwrap();
test_shifts(&s);
test_prop(&s);
}
#[test]
fn this() {
let s = ModSum::<u8>::with_options()
.width(5)
.module(17)
.check(1)
.build()
.unwrap();
test_shifts(&s);
test_prop(&s);
}
#[test]
fn checksum_type() {
let chk = ModSum::<u16>::with_options()
.width(16)
.init(0xff00)
.module(0xffff)
.check(0xde)
.build()
.unwrap();
test_shifts(&chk);
test_prop(&chk);
let many_255: Vec<_> = std::iter::repeat(0xffu8).take(0x101).collect();
assert_eq!(chk.digest(many_255.as_slice()).unwrap(), 0xff00);
let x = Vec::from("implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR");
let y = Vec::from("This program comes with ABSOLUTELY NO WARRANTY; for details typ");
let merchantibility = chk.digest(b"MERCHANTABILITY".as_ref()).unwrap();
let ith_absolutely_ = chk.digest(b"ith ABSOLUTELY ".as_ref()).unwrap();
assert_eq!(
chk.find_segments(
&[x, y],
&[merchantibility, ith_absolutely_],
Relativity::Start
),
vec![(vec![20], vec![RelativeIndex::FromStart(35)])]
);
}
}