use std::path::Path;
use crate::common::generic_consts::Random;
use crate::common::mmap::AdviceSetting;
use crate::common::universal_io::{
OpenOptions, Populate, ReadRange, UniversalAppend, UniversalFlush as _, UniversalIoError,
UniversalRead as _, UniversalReadFs, UniversalWriteFileOps,
};
pub fn open_options(writeable: bool) -> OpenOptions {
OpenOptions {
writeable,
need_sequential: false,
populate: Populate::No,
advice: AdviceSetting::Global,
}
}
pub fn run_append_conformance<Fs>(fs: &Fs, dir: &Path)
where
Fs: UniversalReadFs + UniversalWriteFileOps,
Fs::File: UniversalAppend,
Fs::OpenExtra: Default,
{
let path = dir.join("append.dat");
fs.create(&path, 0).unwrap();
let mut file = fs
.open(&path, open_options(true), Fs::OpenExtra::default())
.unwrap();
file.append::<u8>(0, &[]).unwrap();
assert_eq!(file.len::<u8>().unwrap(), 0);
file.append(0, b"hello ".as_slice()).unwrap();
file.append(6, b"world".as_slice()).unwrap();
assert_eq!(file.len::<u8>().unwrap(), 11);
assert_eq!(
file.read_whole::<u8>().unwrap().as_ref(),
b"hello world".as_slice(),
);
assert_eq!(
file.read::<_, u8>(ReadRange::new(6, 5), Random)
.unwrap()
.as_ref(),
b"world".as_slice(),
);
let err = file.append(6, b"world".as_slice()).unwrap_err();
assert!(matches!(err, UniversalIoError::AppendOffsetConflict { .. }));
let err = file.append(100, b"x".as_slice()).unwrap_err();
assert!(matches!(err, UniversalIoError::AppendOffsetConflict { .. }));
assert_eq!(file.len::<u8>().unwrap(), 11);
assert_eq!(
file.read_whole::<u8>().unwrap().as_ref(),
b"hello world".as_slice(),
);
let batch: [&[u8]; 4] = [b"ab", b"", b"cde", b"f"];
file.append_batch(11, batch).unwrap();
assert_eq!(file.len::<u8>().unwrap(), 17);
assert_eq!(
file.read::<_, u8>(ReadRange::new(11, 6), Random)
.unwrap()
.as_ref(),
b"abcdef".as_slice(),
);
file.append_batch::<u8>(17, std::iter::empty()).unwrap();
let buffers: Vec<Vec<u8>> = (0..1500u32).map(|i| i.to_le_bytes().to_vec()).collect();
let expected: Vec<u8> = buffers.concat();
file.append_batch(17, buffers.iter().map(Vec::as_slice))
.unwrap();
assert_eq!(
file.read::<_, u8>(ReadRange::new(17, expected.len() as u64), Random)
.unwrap()
.as_ref(),
expected.as_slice(),
);
let eof = 17 + expected.len() as u64;
let mut reader = fs
.open(&path, open_options(false), Fs::OpenExtra::default())
.unwrap();
assert_eq!(reader.len::<u8>().unwrap(), eof);
file.append(eof, b"tail".as_slice()).unwrap();
reader.reopen().unwrap();
assert_eq!(reader.len::<u8>().unwrap(), eof + 4);
assert_eq!(
reader
.read::<_, u8>(ReadRange::new(eof, 4), Random)
.unwrap()
.as_ref(),
b"tail".as_slice(),
);
(file.flusher())().unwrap();
let conflict_path = dir.join("append_conflict.dat");
fs.create(&conflict_path, 0).unwrap();
let mut first = fs
.open(&conflict_path, open_options(true), Fs::OpenExtra::default())
.unwrap();
let mut second = fs
.open(&conflict_path, open_options(true), Fs::OpenExtra::default())
.unwrap();
first.append(0, b"aaa".as_slice()).unwrap();
second.append(3, b"bbb".as_slice()).unwrap();
let err = first.append(3, b"ccc".as_slice()).unwrap_err();
assert!(matches!(
err,
UniversalIoError::AppendOffsetConflict { offset: 3, .. }
));
first.reopen().unwrap();
let eof = first.len::<u8>().unwrap();
assert_eq!(eof, 6);
first.append(eof, b"ccc".as_slice()).unwrap();
assert_eq!(
first.read_whole::<u8>().unwrap().as_ref(),
b"aaabbbccc".as_slice(),
);
run_open_append_conformance(fs, dir);
}
pub fn run_open_append_conformance<Fs>(fs: &Fs, dir: &Path)
where
Fs: UniversalReadFs + UniversalWriteFileOps,
Fs::OpenExtra: Default,
{
let path = dir.join("open_append.dat");
fs.create(&path, 0).unwrap();
let mut file = fs.open_append(&path, open_options(false)).unwrap();
file.append(0, b"one".as_slice()).unwrap();
file.append_batch(3, [b"two".as_slice(), b"three".as_slice()])
.unwrap();
(file.flusher())().unwrap();
let err = file.append(0, b"x".as_slice()).unwrap_err();
assert!(matches!(err, UniversalIoError::AppendOffsetConflict { .. }));
let reader = fs
.open(&path, open_options(false), Fs::OpenExtra::default())
.unwrap();
assert_eq!(
reader.read_whole::<u8>().unwrap().as_ref(),
b"onetwothree".as_slice(),
);
}