use cfb::CompoundFile;
use std::io::{Cursor, Read, Write};
fn write_streams(
comp: &mut CompoundFile<Cursor<Vec<u8>>>,
count: u32,
len: usize,
) {
for i in 0..count {
let data = vec![(i % 251) as u8; len];
let mut stream = comp.create_stream(format!("/s{i}")).unwrap();
stream.write_all(&data).unwrap();
}
}
fn assert_stream(
comp: &mut CompoundFile<Cursor<Vec<u8>>>,
i: u32,
len: usize,
) {
let mut data = Vec::new();
comp.open_stream(format!("/s{i}"))
.unwrap()
.read_to_end(&mut data)
.unwrap();
assert_eq!(data.len(), len, "stream s{} length", i);
assert!(
data.iter().all(|&b| b == (i % 251) as u8),
"stream s{} contents",
i
);
}
#[test]
fn many_mini_streams_grow_shrink_and_regrow() {
let mut comp = CompoundFile::create(Cursor::new(Vec::new())).unwrap();
write_streams(&mut comp, 800, 300);
for i in 0..800 {
assert_stream(&mut comp, i, 300);
}
for i in (0..800).step_by(3) {
comp.remove_stream(format!("/s{i}")).unwrap();
}
for i in 800..1000 {
let data = vec![(i % 251) as u8; 300];
let mut stream = comp.create_stream(format!("/s{i}")).unwrap();
stream.write_all(&data).unwrap();
}
comp.flush().unwrap();
let check = |comp: &mut CompoundFile<Cursor<Vec<u8>>>| {
for i in 0..1000u32 {
if i < 800 && i % 3 == 0 {
assert!(
!comp.is_stream(format!("/s{i}")),
"s{} was removed",
i
);
} else {
assert_stream(comp, i, 300);
}
}
};
check(&mut comp);
let bytes = comp.into_inner().into_inner();
let mut reopened = CompoundFile::open(Cursor::new(bytes)).unwrap();
check(&mut reopened);
}
#[test]
fn a_stream_crossing_the_mini_cutoff_keeps_its_neighbours_intact() {
let mut comp = CompoundFile::create(Cursor::new(Vec::new())).unwrap();
write_streams(&mut comp, 100, 200);
let mut big = comp.create_stream("/big").unwrap();
big.write_all(&[7u8; 100]).unwrap();
big.write_all(&vec![7u8; 10_000]).unwrap();
big.set_len(50).unwrap();
drop(big);
for i in 0..100 {
assert_stream(&mut comp, i, 200);
}
let mut data = Vec::new();
comp.open_stream("/big").unwrap().read_to_end(&mut data).unwrap();
assert_eq!(data, vec![7u8; 50]);
}
#[test]
fn mini_streams_written_whole_in_pieces_and_into_reused_sectors() {
let mut comp = CompoundFile::create(Cursor::new(Vec::new())).unwrap();
let whole: Vec<u8> = (0..3000).map(|i| (i % 253) as u8).collect();
comp.create_stream("/whole").unwrap().write_all(&whole).unwrap();
let mut pieces = Vec::new();
{
let mut stream = comp.create_stream("/pieces").unwrap();
for (i, len) in
[10usize, 60, 100, 1, 63, 64, 65, 500].iter().enumerate()
{
let piece = vec![i as u8 + 1; *len];
stream.write_all(&piece).unwrap();
pieces.extend_from_slice(&piece);
}
}
write_streams(&mut comp, 20, 200);
for i in (0..20).step_by(2) {
comp.remove_stream(format!("/s{i}")).unwrap();
}
let reused: Vec<u8> = (0..2500).map(|i| (i % 7) as u8).collect();
comp.create_stream("/reused").unwrap().write_all(&reused).unwrap();
let check = |comp: &mut CompoundFile<Cursor<Vec<u8>>>| {
let mut read = |path: &str| {
let mut data = Vec::new();
comp.open_stream(path).unwrap().read_to_end(&mut data).unwrap();
data
};
assert_eq!(read("/whole"), whole);
assert_eq!(read("/pieces"), pieces);
assert_eq!(read("/reused"), reused);
for i in (1..20).step_by(2) {
assert_stream(comp, i, 200);
}
};
check(&mut comp);
let bytes = comp.into_inner().into_inner();
let mut comp = CompoundFile::open_strict(Cursor::new(bytes)).unwrap();
check(&mut comp);
}
#[test]
fn mini_streams_beyond_one_minifat_sector() {
let mut comp = CompoundFile::create(Cursor::new(Vec::new())).unwrap();
write_streams(&mut comp, 40, 4000);
let bytes = comp.into_inner().into_inner();
let mut comp = CompoundFile::open_strict(Cursor::new(bytes)).unwrap();
for i in 0..40 {
assert_stream(&mut comp, i, 4000);
}
}