// 独立 Rust 参照保留文件的跨线程别名及同步流的共享游标,不调用 Dever I/O。 use std::{ cell::RefCell, fs, io::Read, rc::Rc, sync::{Arc, Mutex}, }; #[derive(Clone)] struct ReferenceFile(Arc>>); impl ReferenceFile { fn open(path: &str) -> Result { 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>, 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>>); impl ReferenceStream { fn new(file: &ReferenceFile, limit: i64) -> Result { 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, 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 { 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 { 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) }