112 lines
3.2 KiB
Rust
112 lines
3.2 KiB
Rust
// 独立 Rust 参照保留文件的跨线程别名及同步流的共享游标,不调用 Dever I/O。
|
|
use std::{
|
|
cell::RefCell,
|
|
fs,
|
|
io::Read,
|
|
rc::Rc,
|
|
sync::{Arc, Mutex},
|
|
};
|
|
|
|
#[derive(Clone)]
|
|
struct ReferenceFile(Arc<Mutex<Option<fs::File>>>);
|
|
|
|
impl ReferenceFile {
|
|
fn open(path: &str) -> Result<Self, String> {
|
|
fs::File::open(path)
|
|
.map(|file| Self(Arc::new(Mutex::new(Some(file)))))
|
|
.map_err(|error| error.to_string())
|
|
}
|
|
|
|
fn read(&self, limit: i64) -> Result<Option<Vec<u8>>, String> {
|
|
let limit = usize::try_from(limit)
|
|
.ok()
|
|
.filter(|limit| *limit > 0)
|
|
.ok_or("read limit must be positive")?;
|
|
let mut guard = self.0.lock().map_err(|_| "file lock poisoned")?;
|
|
let file = guard.as_mut().ok_or("file is closed")?;
|
|
let mut bytes = Vec::new();
|
|
bytes
|
|
.try_reserve_exact(limit)
|
|
.map_err(|error| error.to_string())?;
|
|
bytes.resize(limit, 0);
|
|
let count = file.read(&mut bytes).map_err(|error| error.to_string())?;
|
|
bytes.truncate(count);
|
|
Ok((count != 0).then_some(bytes))
|
|
}
|
|
|
|
fn close(&self) -> Result<(), String> {
|
|
drop(
|
|
self.0
|
|
.lock()
|
|
.map_err(|_| "file lock poisoned")?
|
|
.take()
|
|
.ok_or("file is closed")?,
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct ReferenceStream(Rc<RefCell<Option<(ReferenceFile, i64)>>>);
|
|
|
|
impl ReferenceStream {
|
|
fn new(file: &ReferenceFile, limit: i64) -> Result<Self, String> {
|
|
usize::try_from(limit)
|
|
.ok()
|
|
.filter(|limit| *limit > 0)
|
|
.ok_or("read limit must be positive")?;
|
|
if file.0.lock().map_err(|_| "file lock poisoned")?.is_none() {
|
|
return Err("file is closed".into());
|
|
}
|
|
Ok(Self(Rc::new(RefCell::new(Some((file.clone(), limit))))))
|
|
}
|
|
|
|
fn next(&self) -> Option<Result<Vec<u8>, String>> {
|
|
let mut source = self.0.borrow_mut();
|
|
let (file, limit) = source.as_ref()?;
|
|
match file.read(*limit) {
|
|
Ok(Some(bytes)) => Some(Ok(bytes)),
|
|
Ok(None) => {
|
|
*source = None;
|
|
None
|
|
}
|
|
Err(error) => {
|
|
*source = None;
|
|
Some(Err(error))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn close(&self) {
|
|
*self.0.borrow_mut() = None;
|
|
}
|
|
}
|
|
|
|
fn reference_count(bytes: &[u8], total: i64) -> Result<i64, String> {
|
|
bytes.iter().try_fold(total, |sum, byte| {
|
|
sum.checked_add(i64::from(*byte))
|
|
.ok_or_else(|| "integer overflow".into())
|
|
})
|
|
}
|
|
|
|
fn reference_consume(path: &str, limit: i64, partial: bool) -> Result<i64, String> {
|
|
let file = ReferenceFile::open(path)?;
|
|
let stream = ReferenceStream::new(&file, limit)?;
|
|
let alias = partial.then(|| stream.clone());
|
|
let mut total = 0;
|
|
if partial {
|
|
if let Some(bytes) = stream.next() {
|
|
total = reference_count(&bytes?, total)?;
|
|
}
|
|
}
|
|
while let Some(bytes) = alias.as_ref().unwrap_or(&stream).next() {
|
|
total = reference_count(&bytes?, total)?;
|
|
}
|
|
if partial {
|
|
stream.close();
|
|
alias.as_ref().unwrap().close();
|
|
}
|
|
file.close()?;
|
|
Ok(total)
|
|
}
|