clawmates: phase work
Mission: 01a00c41-bac0-7eb3-a8c8-8b7044f3086d Phase: 01a00c41-bac2-71e3-a58b-c473421200ee Committed by the ClawMates delivery pipeline from the agents' working tree. Authored by agents, not by the named committer.
This commit is contained in:
@@ -59,11 +59,16 @@ pub trait AsyncHDF5Read: Send + Sync {
|
||||
|
||||
/// Async file-backed reader using tokio for non-blocking I/O.
|
||||
///
|
||||
/// Opens a file and reads it asynchronously. The file is read into memory
|
||||
/// on first access, making subsequent operations fast.
|
||||
/// Opens a file and reads it asynchronously. The underlying file handle is
|
||||
/// opened once (lazily, on first access) and cached for the lifetime of this
|
||||
/// reader, so repeated granular `read_at` calls reuse the open descriptor
|
||||
/// and cached length instead of paying an open+stat syscall pair every time.
|
||||
/// The handle is guarded by a mutex, which also correctly serializes the
|
||||
/// seek-then-read pairs of concurrent callers sharing the one file position.
|
||||
#[derive(Debug)]
|
||||
pub struct AsyncFileReader {
|
||||
path: std::path::PathBuf,
|
||||
handle: tokio::sync::Mutex<Option<(tokio::fs::File, u64)>>,
|
||||
}
|
||||
|
||||
impl AsyncFileReader {
|
||||
@@ -73,6 +78,7 @@ impl AsyncFileReader {
|
||||
pub fn new<P: AsRef<Path>>(path: P) -> Self {
|
||||
Self {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
handle: tokio::sync::Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,23 +95,33 @@ impl AsyncFileReader {
|
||||
|
||||
impl AsyncHDF5Read for AsyncFileReader {
|
||||
async fn read_at(&self, offset: u64, len: usize) -> io::Result<Vec<u8>> {
|
||||
let mut file = tokio::fs::File::open(&self.path).await?;
|
||||
let metadata = file.metadata().await?;
|
||||
let file_len = metadata.len();
|
||||
let mut guard = self.handle.lock().await;
|
||||
if guard.is_none() {
|
||||
let file = tokio::fs::File::open(&self.path).await?;
|
||||
let file_len = file.metadata().await?.len();
|
||||
*guard = Some((file, file_len));
|
||||
}
|
||||
let (file, file_len) = guard.as_mut().expect("just populated above");
|
||||
let file_len = *file_len;
|
||||
if offset >= file_len {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let available = (file_len - offset) as usize;
|
||||
let to_read = len.min(available);
|
||||
tokio::io::AsyncSeekExt::seek(&mut file, io::SeekFrom::Start(offset)).await?;
|
||||
tokio::io::AsyncSeekExt::seek(file, io::SeekFrom::Start(offset)).await?;
|
||||
let mut buf = vec![0u8; to_read];
|
||||
file.read_exact(&mut buf).await?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
async fn len(&self) -> io::Result<u64> {
|
||||
let metadata = tokio::fs::metadata(&self.path).await?;
|
||||
Ok(metadata.len())
|
||||
let mut guard = self.handle.lock().await;
|
||||
if guard.is_none() {
|
||||
let file = tokio::fs::File::open(&self.path).await?;
|
||||
let file_len = file.metadata().await?.len();
|
||||
*guard = Some((file, file_len));
|
||||
}
|
||||
Ok(guard.as_ref().expect("just populated above").1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user