use std::fmt::Write;
use nom::{IResult,
bytes::complete::{take, take_until, tag},
error::{context, ErrorKind, FromExternalError},
};
use crate::data::{Header, Hunk, Trailer};
pub type Error<'a> = nom::error::VerboseError<&'a [u8]>;
type Res<'a, T> = IResult<&'a [u8], T, Error<'a>>;
pub fn header(i: &[u8]) -> Res<Header> {
let (i, _) = context("magic", tag("UPS1"))(i)?;
let (i, src_len) = context("src_len", vlq)(i)?;
let (i, dst_len) = context("dst_len", vlq)(i)?;
Ok((i, Header {
src_len,
dst_len,
}))
}
pub fn hunk(i: &[u8]) -> Res<Hunk> {
context("hunk", hunk1)(i)
}
fn hunk1(i: &[u8]) -> Res<Hunk> {
let (i, skip) = context("skip", vlq)(i)?;
let (i, xor) = context("data", take_until(&b"\x00"[..]))(i)?;
let (i, _) = context("terminator", tag(&b"\x00"[..]))(i)?;
Ok((i, Hunk {
skip,
xor,
}))
}
pub fn trailer(i: &[u8]) -> Res<Trailer> {
let (i, src_crc) = context("src_crc", crc)(i)?;
let (i, dst_crc) = context("dst_crc", crc)(i)?;
let (i, ups_crc) = context("ups_crc", crc)(i)?;
Ok((i, Trailer {
src_crc,
dst_crc,
ups_crc,
}))
}
fn vlq(i: &[u8]) -> Res<usize> {
let mut res = 0usize;
let mut i = i;
let mut bs: &[u8];
let mut shift = 0;
loop {
(i, bs) = take(1u8)(i)?;
let octet: usize = bs[0] as usize;
if (bs[0] & 0x80) != 0 {
res = res.checked_add((octet & 0x7f) << shift)
.ok_or_else(|| fail(i, "VLQ overflow"))?;
break;
}
res = res.checked_add((octet | 0x80) << shift)
.ok_or_else(|| fail(i, "VLQ overflow"))?;
shift += 7;
if shift > 56 {
return Err(fail(i, "VLQ shift overflow"));
}
}
Ok((i, res))
}
fn crc(i: &[u8]) -> Res<u32> {
let (i, crc) = take(4u8)(i)?;
Ok((i, u32::from_le_bytes(crc.try_into().unwrap())))
}
fn fail<'a>(input: &'a [u8], msg: &'static str) -> nom::Err<Error<'a>> {
nom::Err::Error(Error::from_external_error(input, ErrorKind::Fail, msg))
}
pub fn format_error(err: Error) -> String {
let mut result = String::new();
for error in err.errors.iter() {
let mut slice = error.0;
if slice.len() > 16 {
slice = &slice[0..16];
}
writeln!(result, "At {:?}", slice).unwrap();
match error.1 {
nom::error::VerboseErrorKind::Context(c) => writeln!(result, " In {}", c),
nom::error::VerboseErrorKind::Char(c) => writeln!(result, " Expected '{}'", c),
nom::error::VerboseErrorKind::Nom(n) => writeln!(result, " Error: {:?}", n),
}.unwrap();
}
result
}
#[test]
fn vlq_test() {
assert_eq!(vlq(&[0x80]), Ok((&[][..], 0)));
assert_eq!(vlq(&[0x81]), Ok((&[][..], 1)));
assert_eq!(vlq(&[0x81, 0x48]), Ok((&[0x48][..], 1)));
assert_eq!(vlq(&[0x10, 0x20, 0x30, 0x81, 0x48]), Ok((&[0x48][..], 0x4c5090)));
assert_eq!(vlq(&[0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x48]).is_ok(), false);
}
#[test]
fn header_test() {
assert_eq!(header(&b"UPS1\x81\x82"[..]), Ok((&[][..], Header {
src_len: 1,
dst_len: 2,
})));
assert_eq!(header(&b"HELO\x81\x82"[..]).is_ok(), false);
}
#[test]
fn hunk_test() {
assert_eq!(hunk(&b"\x81\xaa\xbb\xcc\x00"[..]), Ok((&[][..], Hunk {
skip: 1,
xor: &[0xaa, 0xbb, 0xcc][..],
})));
assert_eq!(hunk(&b"\x81\x82\x83\x84"[..]).is_ok(), false);
assert_eq!(hunk(&b"\x7f"[..]).is_ok(), false);
}
#[test]
fn trailer_test() {
assert_eq!(trailer(&b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"[..]), Ok((&[][..], Trailer {
src_crc: 0x04030201,
dst_crc: 0x08070605,
ups_crc: 0x0c0b0a09,
})));
assert_eq!(trailer(&b"\x81\x82\x83\x84\x81\x82\x83\x84"[..]).is_ok(), false);
}
#[test]
fn patch_test() {
let i = &b"UPS1\xe0\xf0\x85FOO\x00\x8aBAR\x00abcdEFGHijkl"[..];
let (i, header) = header(i).expect("parsing header failed");
let (i, hunk1) = hunk(i).expect("parsing hunk 1 failed");
let (i, hunk2) = hunk(i).expect("parsing hunk 2 failed");
let (i, trailer) = trailer(i).expect("parsing trailer failed");
assert_eq!(header, Header {
src_len: 0x60,
dst_len: 0x70,
});
assert_eq!(hunk1, Hunk {
skip: 5,
xor: &b"FOO"[..],
});
assert_eq!(hunk2, Hunk {
skip: 10,
xor: &b"BAR"[..],
});
assert_eq!(trailer, Trailer {
src_crc: 1684234849,
dst_crc: 1212630597,
ups_crc: 1818978921
});
assert_eq!(i.len(), 0);
}