mod bitnum;
pub mod checksum;
mod factor;
mod keyval;
pub mod utils;
pub mod crc;
pub(crate) mod endian;
pub mod fletcher;
pub mod modsum;
use bitnum::BitNum;
use checksum::{CheckBuilderErr, CheckReverserError};
use checksum::{Digest, LinearCheck, RangePair};
use crc::{reverse_crc, CrcBuilder, CRC};
use fletcher::{reverse_fletcher, Fletcher, FletcherBuilder};
use modsum::{reverse_modsum, ModSum, ModSumBuilder};
use std::cmp::Ordering;
use std::str::FromStr;
use utils::SignedInclRange;
#[cfg(feature = "parallel")]
use {crc::reverse_crc_para, fletcher::reverse_fletcher_para, rayon::prelude::*};
#[cfg(test)]
#[macro_use(quickcheck)]
extern crate quickcheck_macros;
fn find_prefix_width(s: &str) -> Result<(&str, usize, &str), CheckBuilderErr> {
let stripped = s.trim_start();
let pref = stripped.split_whitespace().next();
let (prefix, rest) = match PREFIXES.iter().find(|x| Some(**x) == pref) {
Some(p) => (*p, &stripped[p.len()..]),
None => return Err(CheckBuilderErr::MalformedString("algorithm".to_owned())),
};
for x in keyval::KeyValIter::new(rest) {
match x {
Err(k) => return Err(CheckBuilderErr::MalformedString(k)),
Ok((k, v)) => {
if &k == "width" {
return v
.parse()
.map_err(|_| CheckBuilderErr::MalformedString(k))
.map(|width| (prefix, width, rest));
}
}
}
}
Err(CheckBuilderErr::MissingParameter("width"))
}
fn find_segment_str<L>(
spec: &str,
bytes: &[Vec<u8>],
sum: &[Vec<u8>],
start_range: SignedInclRange,
end_range: SignedInclRange,
) -> Result<Vec<RangePair>, CheckBuilderErr>
where
L: LinearCheck + FromStr<Err = CheckBuilderErr>,
L::Sum: BitNum,
{
let spec = L::from_str(spec)?;
let sum_array: Vec<_> = sum
.iter()
.map(|x| spec.wordspec().bytes_to_output(x))
.collect();
Ok(spec.find_segments_range(bytes, &sum_array, start_range, end_range))
}
static PREFIXES: &[&str] = &["fletcher", "crc", "modsum"];
pub fn find_checksum_segments(
strspec: &str,
bytes: &[Vec<u8>],
sum: &[Vec<u8>],
start_range: SignedInclRange,
end_range: SignedInclRange,
) -> Result<Vec<RangePair>, CheckBuilderErr> {
let (prefix, width, rest) = find_prefix_width(strspec)?;
match (width, prefix) {
(1..=8, "crc") => find_segment_str::<CRC<u8>>(rest, bytes, sum, start_range, end_range),
(9..=16, "crc") => find_segment_str::<CRC<u16>>(rest, bytes, sum, start_range, end_range),
(17..=32, "crc") => find_segment_str::<CRC<u32>>(rest, bytes, sum, start_range, end_range),
(33..=64, "crc") => find_segment_str::<CRC<u64>>(rest, bytes, sum, start_range, end_range),
(65..=128, "crc") => {
find_segment_str::<CRC<u128>>(rest, bytes, sum, start_range, end_range)
}
(1..=8, "modsum") => {
find_segment_str::<ModSum<u8>>(rest, bytes, sum, start_range, end_range)
}
(9..=16, "modsum") => {
find_segment_str::<ModSum<u16>>(rest, bytes, sum, start_range, end_range)
}
(17..=32, "modsum") => {
find_segment_str::<ModSum<u32>>(rest, bytes, sum, start_range, end_range)
}
(33..=64, "modsum") => {
find_segment_str::<ModSum<u64>>(rest, bytes, sum, start_range, end_range)
}
(1..=16, "fletcher") => {
find_segment_str::<Fletcher<u8>>(rest, bytes, sum, start_range, end_range)
}
(17..=32, "fletcher") => {
find_segment_str::<Fletcher<u16>>(rest, bytes, sum, start_range, end_range)
}
(33..=64, "fletcher") => {
find_segment_str::<Fletcher<u32>>(rest, bytes, sum, start_range, end_range)
}
(65..=128, "fletcher") => {
find_segment_str::<Fletcher<u64>>(rest, bytes, sum, start_range, end_range)
}
_ => Err(CheckBuilderErr::ValueOutOfRange("width")),
}
}
fn get_checksums<A>(
strspec: &str,
files: &[&[u8]],
width: usize,
) -> Result<Vec<Vec<u8>>, CheckBuilderErr>
where
A: Digest + FromStr<Err = CheckBuilderErr>,
A::Sum: crate::bitnum::BitNum,
{
let algo = A::from_str(strspec)?;
let mut sums = Vec::new();
for file in files {
sums.push(
algo.wordspec()
.output_to_bytes(algo.digest(file).unwrap(), width),
);
}
Ok(sums)
}
pub fn find_checksum(strspec: &str, bytes: &[&[u8]]) -> Result<Vec<Vec<u8>>, CheckBuilderErr> {
let (prefix, width, rest) = find_prefix_width(strspec)?;
match (width, prefix) {
(1..=8, "crc") => get_checksums::<CRC<u8>>(rest, bytes, width),
(9..=16, "crc") => get_checksums::<CRC<u16>>(rest, bytes, width),
(17..=32, "crc") => get_checksums::<CRC<u32>>(rest, bytes, width),
(33..=64, "crc") => get_checksums::<CRC<u64>>(rest, bytes, width),
(65..=128, "crc") => get_checksums::<CRC<u128>>(rest, bytes, width),
(1..=8, "modsum") => get_checksums::<ModSum<u8>>(rest, bytes, width),
(9..=16, "modsum") => get_checksums::<ModSum<u16>>(rest, bytes, width),
(17..=32, "modsum") => get_checksums::<ModSum<u32>>(rest, bytes, width),
(33..=64, "modsum") => get_checksums::<ModSum<u64>>(rest, bytes, width),
(1..=16, "fletcher") => get_checksums::<Fletcher<u8>>(rest, bytes, width),
(17..=32, "fletcher") => get_checksums::<Fletcher<u16>>(rest, bytes, width),
(33..=64, "fletcher") => get_checksums::<Fletcher<u32>>(rest, bytes, width),
(65..=128, "fletcher") => get_checksums::<Fletcher<u64>>(rest, bytes, width),
_ => Err(CheckBuilderErr::ValueOutOfRange("width")),
}
}
enum BuilderEnum {
Crc(CrcBuilder<u128>),
ModSum(ModSumBuilder<u64>),
Fletcher(FletcherBuilder<u64>),
}
pub struct AlgorithmFinder<'a> {
pairs: Vec<(&'a [u8], Vec<u8>)>,
spec: BuilderEnum,
verbosity: u64,
extended_search: bool,
}
impl<'a> AlgorithmFinder<'a> {
pub fn find_all(&'_ self) -> impl Iterator<Item = Result<String, CheckReverserError>> + '_ {
let maybe_crc = if let BuilderEnum::Crc(crc) = &self.spec {
Some(
reverse_crc(
crc,
self.pairs.as_slice(),
self.verbosity,
self.extended_search,
)
.map(|x| x.map(|y| y.to_string())),
)
} else {
None
};
let maybe_modsum = if let BuilderEnum::ModSum(modsum) = &self.spec {
Some(
reverse_modsum(
modsum,
self.pairs.as_slice(),
self.verbosity,
self.extended_search,
)
.map(|x| x.map(|y| y.to_string())),
)
} else {
None
};
let maybe_fletcher = if let BuilderEnum::Fletcher(fletcher) = &self.spec {
Some(
reverse_fletcher(
fletcher,
self.pairs.as_slice(),
self.verbosity,
self.extended_search,
)
.map(|x| x.map(|y| y.to_string())),
)
} else {
None
};
maybe_crc
.into_iter()
.flatten()
.chain(maybe_modsum.into_iter().flatten())
.chain(maybe_fletcher.into_iter().flatten())
}
#[cfg(feature = "parallel")]
pub fn find_all_para(
&'_ self,
) -> impl ParallelIterator<Item = Result<String, CheckReverserError>> + '_ {
let maybe_crc = if let BuilderEnum::Crc(crc) = &self.spec {
Some(
reverse_crc_para(
crc,
self.pairs.as_slice(),
self.verbosity,
self.extended_search,
)
.map(|x| x.map(|y| y.to_string())),
)
} else {
None
};
let maybe_modsum = if let BuilderEnum::ModSum(modsum) = &self.spec {
Some(
reverse_modsum(
modsum,
self.pairs.as_slice(),
self.verbosity,
self.extended_search,
)
.map(|x| x.map(|y| y.to_string()))
.par_bridge(),
)
} else {
None
};
let maybe_fletcher = if let BuilderEnum::Fletcher(fletcher) = &self.spec {
Some(
reverse_fletcher_para(
fletcher,
self.pairs.as_slice(),
self.verbosity,
self.extended_search,
)
.map(|x| x.map(|y| y.to_string())),
)
} else {
None
};
maybe_crc
.into_par_iter()
.flatten()
.chain(maybe_modsum.into_par_iter().flatten())
.chain(maybe_fletcher.into_par_iter().flatten())
}
}
pub fn find_algorithm<'a>(
strspec: &str,
bytes: &'a [&[u8]],
sums: &[Vec<u8>],
verbosity: u64,
extended_search: bool,
) -> Result<AlgorithmFinder<'a>, CheckBuilderErr> {
let (prefix, _, rest) = find_prefix_width(strspec)?;
let prefix = prefix.to_ascii_lowercase();
let spec = match prefix.as_str() {
"crc" => BuilderEnum::Crc(CrcBuilder::<u128>::from_str(rest)?),
"modsum" => BuilderEnum::ModSum(ModSumBuilder::<u64>::from_str(rest)?),
"fletcher" => BuilderEnum::Fletcher(FletcherBuilder::<u64>::from_str(rest)?),
_ => unimplemented!(),
};
match sums.len().cmp(&bytes.len()) {
Ordering::Greater => {
return Err(CheckBuilderErr::MissingParameter(
"not enough files for checksums given",
))
}
Ordering::Less => {
return Err(CheckBuilderErr::MissingParameter(
"not enough checksums for files given",
))
}
Ordering::Equal => (),
};
let pairs: Vec<_> = bytes.iter().cloned().zip(sums.iter().cloned()).collect();
Ok(AlgorithmFinder {
pairs,
spec,
verbosity,
extended_search,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn multibyte_part_range() {
assert_eq!(
find_checksum_segments(
"modsum width=16 wordsize=24 module=0",
&[vec![0u8; 15]],
&[vec![0, 0]],
SignedInclRange::new(0, 5).unwrap(),
SignedInclRange::new(-5, -2).unwrap()
),
Ok(vec![
(vec![0, 3], vec![-4]),
(vec![1, 4], vec![-3]),
(vec![2, 5], vec![-5, -2])
])
);
assert_eq!(
find_checksum_segments(
"modsum width=16 wordsize=16 module=0 wordsize=16",
&[vec![0u8; 15], vec![0u8; 12], vec![0u8; 9]],
&[vec![0, 0], vec![0, 0], vec![0, 0]],
SignedInclRange::new(0, 8).unwrap(),
SignedInclRange::new(-9, -1).unwrap(),
),
Ok(vec![])
);
assert_eq!(
find_checksum_segments(
"modsum width=16 wordsize=16 module=0 wordsize=24",
&[vec![0u8; 15], vec![0u8; 12], vec![0u8; 9]],
&[vec![0, 0], vec![0, 0], vec![0, 0]],
SignedInclRange::new(0, 8).unwrap(),
SignedInclRange::new(-9, -1).unwrap(),
),
Ok(vec![
(vec![0, 3, 6], vec![-7, -4, -1]),
(vec![1, 4], vec![-6, -3]),
(vec![2, 5], vec![-5, -2]),
])
);
assert_eq!(
find_checksum_segments(
"crc width=16 poly=1 wordsize=16 out_endian=little",
&[
vec![0x6d, 0x79, 0x72, 0x3f, 0x00, 0x5d],
vec![0x75, 0x2d, 0xf4, 0xd4, 0xf5, 0xcf, 0xd8, 0x35]
],
&[vec![0x72, 0x3f], vec![0x01, 0x1b]],
SignedInclRange::new(0, 5).unwrap(),
SignedInclRange::new(-7, -1).unwrap(),
),
Ok(vec![(vec![2], vec![-3])])
)
}
}