P2: Streaming read optimization (64KB chunk cache)
- Add read_cache + read_cache_offset fields to VfsDavFile - Read-ahead 64KB chunks to reduce VFS calls - Serve from cache when data is available - Invalidate cache on seek() - Reduces memory allocations and VFS syscall overhead Tests: 289 passed, 0 failed
This commit is contained in:
@@ -333,6 +333,8 @@ pub struct VfsDavFile {
|
||||
is_write: bool,
|
||||
versioning: Option<Arc<WebDavVersioning>>,
|
||||
flushed: bool,
|
||||
read_cache: Vec<u8>,
|
||||
read_cache_offset: u64,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for VfsDavFile {
|
||||
@@ -359,6 +361,8 @@ impl VfsDavFile {
|
||||
is_write: false,
|
||||
versioning: None,
|
||||
flushed: true,
|
||||
read_cache: Vec::new(),
|
||||
read_cache_offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,6 +390,8 @@ impl VfsDavFile {
|
||||
is_write: true,
|
||||
versioning,
|
||||
flushed: false,
|
||||
read_cache: Vec::new(),
|
||||
read_cache_offset: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -455,21 +461,59 @@ impl DavFile for VfsDavFile {
|
||||
}
|
||||
|
||||
fn read_bytes(&'_ mut self, count: usize) -> FsFuture<'_, Bytes> {
|
||||
const CHUNK_SIZE: usize = 64 * 1024; // 64KB read-ahead
|
||||
|
||||
if let Some(vfs_file_mutex) = &self.vfs_file {
|
||||
// Check if requested data is in cache
|
||||
let cache_start = self.read_cache_offset as usize;
|
||||
let cache_end = cache_start + self.read_cache.len();
|
||||
let req_start = self.position as usize;
|
||||
let req_end = req_start + count;
|
||||
|
||||
if req_start >= cache_start && req_end <= cache_end {
|
||||
// Data in cache - return directly
|
||||
let offset_in_cache = req_start - cache_start;
|
||||
let bytes = Bytes::copy_from_slice(
|
||||
&self.read_cache[offset_in_cache..offset_in_cache + count.min(self.read_cache.len() - offset_in_cache)]
|
||||
);
|
||||
self.position += bytes.len() as u64;
|
||||
return Box::pin(std::future::ready(Ok(bytes)));
|
||||
}
|
||||
|
||||
// Need to read new chunk
|
||||
if let Ok(mut vfs_file) = vfs_file_mutex.lock() {
|
||||
let mut buf = vec![0u8; count];
|
||||
match vfs_file.read(&mut buf) {
|
||||
Ok(0) => return Box::pin(std::future::ready(Ok(Bytes::new()))),
|
||||
// Seek to current position if needed
|
||||
let current_pos = vfs_file.seek(std::io::SeekFrom::Current(0)).unwrap_or(self.position);
|
||||
if current_pos != self.position {
|
||||
if vfs_file.seek(std::io::SeekFrom::Start(self.position)).is_err() {
|
||||
return Box::pin(std::future::ready(Err(FsError::GeneralFailure)));
|
||||
}
|
||||
}
|
||||
|
||||
// Read larger chunk
|
||||
let read_size = std::cmp::max(count, CHUNK_SIZE);
|
||||
self.read_cache.resize(read_size, 0);
|
||||
self.read_cache_offset = self.position;
|
||||
|
||||
match vfs_file.read(&mut self.read_cache) {
|
||||
Ok(0) => {
|
||||
self.read_cache.clear();
|
||||
return Box::pin(std::future::ready(Ok(Bytes::new())));
|
||||
}
|
||||
Ok(n) => {
|
||||
buf.truncate(n);
|
||||
self.position += n as u64;
|
||||
return Box::pin(std::future::ready(Ok(Bytes::from(buf))));
|
||||
self.read_cache.truncate(n);
|
||||
// Return requested portion
|
||||
let result_len = count.min(n);
|
||||
let bytes = Bytes::copy_from_slice(&self.read_cache[..result_len]);
|
||||
self.position += result_len as u64;
|
||||
return Box::pin(std::future::ready(Ok(bytes)));
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
Box::pin(std::future::ready(Err(FsError::NotImplemented)))
|
||||
} else {
|
||||
// Write mode - read from self.data buffer
|
||||
let start = self.position as usize;
|
||||
let end = std::cmp::min(start + count, self.data.len());
|
||||
|
||||
@@ -489,6 +533,8 @@ impl DavFile for VfsDavFile {
|
||||
match vfs_file.seek(pos) {
|
||||
Ok(new_pos) => {
|
||||
self.position = new_pos;
|
||||
self.read_cache.clear(); // Invalidate cache on seek
|
||||
self.read_cache_offset = 0;
|
||||
return Box::pin(std::future::ready(Ok(new_pos)));
|
||||
}
|
||||
Err(_) => {}
|
||||
|
||||
Reference in New Issue
Block a user