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,
|
is_write: bool,
|
||||||
versioning: Option<Arc<WebDavVersioning>>,
|
versioning: Option<Arc<WebDavVersioning>>,
|
||||||
flushed: bool,
|
flushed: bool,
|
||||||
|
read_cache: Vec<u8>,
|
||||||
|
read_cache_offset: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for VfsDavFile {
|
impl std::fmt::Debug for VfsDavFile {
|
||||||
@@ -359,6 +361,8 @@ impl VfsDavFile {
|
|||||||
is_write: false,
|
is_write: false,
|
||||||
versioning: None,
|
versioning: None,
|
||||||
flushed: true,
|
flushed: true,
|
||||||
|
read_cache: Vec::new(),
|
||||||
|
read_cache_offset: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,6 +390,8 @@ impl VfsDavFile {
|
|||||||
is_write: true,
|
is_write: true,
|
||||||
versioning,
|
versioning,
|
||||||
flushed: false,
|
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> {
|
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 {
|
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() {
|
if let Ok(mut vfs_file) = vfs_file_mutex.lock() {
|
||||||
let mut buf = vec![0u8; count];
|
// Seek to current position if needed
|
||||||
match vfs_file.read(&mut buf) {
|
let current_pos = vfs_file.seek(std::io::SeekFrom::Current(0)).unwrap_or(self.position);
|
||||||
Ok(0) => return Box::pin(std::future::ready(Ok(Bytes::new()))),
|
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) => {
|
Ok(n) => {
|
||||||
buf.truncate(n);
|
self.read_cache.truncate(n);
|
||||||
self.position += n as u64;
|
// Return requested portion
|
||||||
return Box::pin(std::future::ready(Ok(Bytes::from(buf))));
|
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(_) => {}
|
Err(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box::pin(std::future::ready(Err(FsError::NotImplemented)))
|
Box::pin(std::future::ready(Err(FsError::NotImplemented)))
|
||||||
} else {
|
} else {
|
||||||
|
// Write mode - read from self.data buffer
|
||||||
let start = self.position as usize;
|
let start = self.position as usize;
|
||||||
let end = std::cmp::min(start + count, self.data.len());
|
let end = std::cmp::min(start + count, self.data.len());
|
||||||
|
|
||||||
@@ -489,6 +533,8 @@ impl DavFile for VfsDavFile {
|
|||||||
match vfs_file.seek(pos) {
|
match vfs_file.seek(pos) {
|
||||||
Ok(new_pos) => {
|
Ok(new_pos) => {
|
||||||
self.position = 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)));
|
return Box::pin(std::future::ready(Ok(new_pos)));
|
||||||
}
|
}
|
||||||
Err(_) => {}
|
Err(_) => {}
|
||||||
|
|||||||
Reference in New Issue
Block a user