use super::*;
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
IntoBytes,
FromBytes,
Unaligned,
KnownLayout,
Immutable,
)]
#[repr(transparent)]
pub struct ChecksumKind(pub u8);
impl ChecksumKind {
pub const NONE: ChecksumKind = ChecksumKind(0);
pub const MD5: ChecksumKind = ChecksumKind(1);
pub const SHA_1: ChecksumKind = ChecksumKind(2);
pub const SHA_256: ChecksumKind = ChecksumKind(3);
}
impl std::fmt::Debug for ChecksumKind {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
static NAMES: [&str; 4] = ["NONE", "MD5", "SHA_1", "SHA_256"];
if let Some(name) = NAMES.get(self.0 as usize) {
f.write_str(name)
} else {
write!(f, "??({})", self.0)
}
}
}
#[test]
fn checksum_kind_debug() {
assert_eq!(format!("{:?}", ChecksumKind::SHA_256), "SHA_256");
assert_eq!(format!("{:?}", ChecksumKind(42)), "??(42)");
}
pub struct FileChecksumsSubsection<'a> {
#[allow(missing_docs)]
pub bytes: &'a [u8],
}
impl<'a> FileChecksumsSubsection<'a> {
#[allow(missing_docs)]
pub fn new(bytes: &'a [u8]) -> Self {
Self { bytes }
}
pub fn iter(&self) -> FileChecksumIter<'a> {
FileChecksumIter { bytes: self.bytes }
}
pub fn get_file(&self, file_index: u32) -> anyhow::Result<FileChecksum<'a>> {
if let Some(b) = self.bytes.get(file_index as usize..) {
if let Some(c) = FileChecksumIter::new(b).next() {
Ok(c)
} else {
bail!("failed to decode FileChecksum record");
}
} else {
bail!("file index is out of range of file checksums subsection");
}
}
}
pub struct FileChecksumsSubsectionMut<'a> {
#[allow(missing_docs)]
pub bytes: &'a mut [u8],
}
impl<'a> HasRestLen for FileChecksumsSubsectionMut<'a> {
fn rest_len(&self) -> usize {
self.bytes.len()
}
}
impl<'a> FileChecksumsSubsectionMut<'a> {
#[allow(missing_docs)]
pub fn new(bytes: &'a mut [u8]) -> Self {
Self { bytes }
}
pub fn iter_mut(&mut self) -> FileChecksumMutIter<'_> {
FileChecksumMutIter { bytes: self.bytes }
}
pub fn get_file_mut(&mut self, file_index: u32) -> anyhow::Result<FileChecksumMut<'_>> {
if let Some(b) = self.bytes.get_mut(file_index as usize..) {
if let Some(c) = FileChecksumMutIter::new(b).next() {
Ok(c)
} else {
bail!("failed to decode FileChecksum record");
}
} else {
bail!("file index is out of range of file checksums subsection");
}
}
}
pub struct FileChecksum<'a> {
pub header: &'a FileChecksumHeader,
pub checksum_data: &'a [u8],
}
pub struct FileChecksumMut<'a> {
pub header: &'a mut FileChecksumHeader,
pub checksum_data: &'a mut [u8],
}
impl<'a> FileChecksum<'a> {
pub fn name(&self) -> NameIndex {
NameIndex(self.header.name.get())
}
}
#[derive(IntoBytes, FromBytes, KnownLayout, Immutable, Unaligned, Clone, Debug)]
#[repr(C)]
pub struct FileChecksumHeader {
pub name: U32<LE>,
pub checksum_size: u8,
pub checksum_kind: ChecksumKind,
}
pub struct FileChecksumIter<'a> {
pub bytes: &'a [u8],
}
impl<'a> HasRestLen for FileChecksumIter<'a> {
fn rest_len(&self) -> usize {
self.bytes.len()
}
}
pub struct FileChecksumMutIter<'a> {
pub bytes: &'a mut [u8],
}
impl<'a> HasRestLen for FileChecksumMutIter<'a> {
fn rest_len(&self) -> usize {
self.bytes.len()
}
}
impl<'a> FileChecksumIter<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self { bytes }
}
}
impl<'a> Iterator for FileChecksumIter<'a> {
type Item = FileChecksum<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.bytes.is_empty() {
return None;
}
let mut p = Parser::new(self.bytes);
let len_before = p.len();
let header: &FileChecksumHeader = p.get().ok()?;
let checksum_data = p.bytes(header.checksum_size as usize).ok()?;
let record_len = len_before - p.len();
let _ = p.skip((4 - (record_len & 3)) & 3);
self.bytes = p.into_rest();
Some(FileChecksum {
header,
checksum_data,
})
}
}
impl<'a> FileChecksumMutIter<'a> {
pub fn new(bytes: &'a mut [u8]) -> Self {
Self { bytes }
}
}
impl<'a> Iterator for FileChecksumMutIter<'a> {
type Item = FileChecksumMut<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.bytes.is_empty() {
return None;
}
let mut p = ParserMut::new(take(&mut self.bytes));
let len_before = p.len();
let header: &mut FileChecksumHeader = p.get_mut().ok()?;
let checksum_data = p.bytes_mut(header.checksum_size as usize).ok()?;
let record_len = len_before - p.len();
let _ = p.skip((4 - (record_len & 3)) & 3);
self.bytes = p.into_rest();
Some(FileChecksumMut {
header,
checksum_data,
})
}
}
#[test]
fn iter_ranges() {
const PAD: u8 = 0xaa;
#[rustfmt::skip]
let data = &[
200, 0, 0, 0, 0, 0, PAD, PAD,
42, 0, 0, 0, 16, 1, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb,
0xcc, 0xcd, 0xce, 0xcf,
PAD, PAD,
];
let sums = FileChecksumsSubsection::new(data);
let mut iter = sums.iter().with_ranges();
let (sub0_range, _) = iter.next().unwrap();
assert_eq!(sub0_range, 0..8);
let (sub1_range, _) = iter.next().unwrap();
assert_eq!(sub1_range, 8..32);
assert!(iter.next().is_none());
}
#[test]
fn iter_mut() {
const PAD: u8 = 0xaa;
#[rustfmt::skip]
let data = &[
42, 0, 0, 0, 16, 1, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb,
0xcc, 0xcd, 0xce, 0xcf,
PAD, PAD,
];
let mut data_mut = data.to_vec();
let mut sums = FileChecksumsSubsectionMut::new(&mut data_mut);
let mut iter = sums.iter_mut();
assert_eq!(iter.rest_len(), 24); let sum0 = iter.next().unwrap();
assert_eq!(iter.rest_len(), 0); assert_eq!(sum0.header.name.get(), 42);
assert_eq!(
sum0.checksum_data,
&[
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
0xce, 0xcf
]
);
sum0.header.name = U32::new(0xcafef00d);
sum0.checksum_data[4] = 0xff;
#[rustfmt::skip]
let expected_new_data = &[
0x0d, 0xf0, 0xfe, 0xca, 16, 1, 0xc0, 0xc1, 0xc2, 0xc3, 0xff, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
0xcc, 0xcd, 0xce, 0xcf,
PAD, PAD,
];
assert_eq!(data_mut.as_slice(), expected_new_data);
}
#[test]
fn basic_iter() {
const PAD: u8 = 0xaa;
#[rustfmt::skip]
let data = &[
42, 0, 0, 0, 16, 1, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb,
0xcc, 0xcd, 0xce, 0xcf,
PAD, PAD,
0, 1, 0, 0, 16, 1, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb,
0xdc, 0xdd, 0xde, 0xdf,
PAD, PAD,
];
{
let mut iter = FileChecksumIter::new(data);
assert_eq!(iter.rest_len(), 48); let sum0 = iter.next().unwrap();
assert_eq!(sum0.name(), NameIndex(42));
assert_eq!(
sum0.checksum_data,
&[
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
0xce, 0xcf
]
);
assert_eq!(iter.rest_len(), 24); let sum1 = iter.next().unwrap();
assert_eq!(sum1.name(), NameIndex(0x100));
assert_eq!(
sum1.checksum_data,
&[
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd,
0xde, 0xdf,
]
);
assert_eq!(iter.rest_len(), 0); assert!(iter.next().is_none());
}
{
let mut data_mut = data.to_vec();
let mut iter = FileChecksumMutIter::new(&mut data_mut);
assert_eq!(iter.rest_len(), 48); let sum0 = iter.next().unwrap();
assert_eq!(sum0.header.name.get(), 42);
assert_eq!(
sum0.checksum_data,
&[
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
0xce, 0xcf
]
);
assert_eq!(iter.rest_len(), 24); let sum1 = iter.next().unwrap();
assert_eq!(sum1.header.name.get(), 0x100);
assert_eq!(
sum1.checksum_data,
&[
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd,
0xde, 0xdf,
]
);
assert_eq!(iter.rest_len(), 0); assert!(iter.next().is_none());
}
}
#[test]
fn test_get_file() {
const PAD: u8 = 0xaa;
#[rustfmt::skip]
let data = &[
42, 0, 0, 0, 0, 0, PAD, PAD, 0xee, 0, 0, 0, 0, 0, PAD, PAD, 0, 0xcc, 0, 0, 0, 0, PAD, PAD, ];
{
let sums = FileChecksumsSubsection::new(data);
let sum0 = sums.get_file(0).unwrap();
assert_eq!(sum0.name(), NameIndex(42));
let sum1 = sums.get_file(8).unwrap();
assert_eq!(sum1.name(), NameIndex(0xee));
let sum2 = sums.get_file(0x10).unwrap();
assert_eq!(sum2.name(), NameIndex(0xcc00));
assert!(sums.get_file(0x1000).is_err());
assert!(sums.get_file(0x16).is_err());
}
{
let mut data_mut = data.to_vec();
let mut sums = FileChecksumsSubsectionMut::new(&mut data_mut);
let sum0 = sums.get_file_mut(0).unwrap();
assert_eq!(sum0.header.name.get(), 42);
let sum1 = sums.get_file_mut(8).unwrap();
assert_eq!(sum1.header.name.get(), 0xee);
let sum2 = sums.get_file_mut(0x10).unwrap();
assert_eq!(sum2.header.name.get(), 0xcc00);
sum2.header.name = U32::new(0xcafe);
assert!(sums.get_file_mut(0x1000).is_err());
assert!(sums.get_file_mut(0x16).is_err());
#[rustfmt::skip]
let expected_data = &[
42, 0, 0, 0, 0, 0, PAD, PAD, 0xee, 0, 0, 0, 0, 0, PAD, PAD, 0xfe, 0xca, 0, 0, 0, 0, PAD, PAD, ];
assert_eq!(data_mut.as_slice(), expected_data);
}
}