feat(storage-server): add StorageServer<F: FileSystem> with BlockDevice trait, mock adapters, and tests
This commit is contained in:
46
crates/servers/storage-server/src/adapters/mock_block.rs
Normal file
46
crates/servers/storage-server/src/adapters/mock_block.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use crate::block_device::BlockDevice;
|
||||
|
||||
const SECTOR_SIZE: usize = 512;
|
||||
const MAX_SECTORS: usize = 64;
|
||||
|
||||
pub struct MockBlockDevice {
|
||||
data: [[u8; SECTOR_SIZE]; MAX_SECTORS],
|
||||
sectors: usize,
|
||||
}
|
||||
|
||||
impl MockBlockDevice {
|
||||
pub fn new(sectors: usize) -> Self {
|
||||
Self {
|
||||
data: [[0; SECTOR_SIZE]; MAX_SECTORS],
|
||||
sectors: sectors.min(MAX_SECTORS),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDevice for MockBlockDevice {
|
||||
fn sector_size(&self) -> usize {
|
||||
SECTOR_SIZE
|
||||
}
|
||||
|
||||
fn sector_count(&self) -> usize {
|
||||
self.sectors
|
||||
}
|
||||
|
||||
fn read_sector(&mut self, sector: u32, buf: &mut [u8]) -> bool {
|
||||
let idx = sector as usize;
|
||||
if idx >= self.sectors || buf.len() < SECTOR_SIZE {
|
||||
return false;
|
||||
}
|
||||
buf[..SECTOR_SIZE].copy_from_slice(&self.data[idx]);
|
||||
true
|
||||
}
|
||||
|
||||
fn write_sector(&mut self, sector: u32, buf: &[u8]) -> bool {
|
||||
let idx = sector as usize;
|
||||
if idx >= self.sectors || buf.len() < SECTOR_SIZE {
|
||||
return false;
|
||||
}
|
||||
self.data[idx].copy_from_slice(&buf[..SECTOR_SIZE]);
|
||||
true
|
||||
}
|
||||
}
|
||||
127
crates/servers/storage-server/src/adapters/mock_fs.rs
Normal file
127
crates/servers/storage-server/src/adapters/mock_fs.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
use crate::filesystem::{DirEntry, FileHandle, FileSystem};
|
||||
|
||||
const MAX_FILES: usize = 16;
|
||||
const MAX_FILE_SIZE: usize = 256;
|
||||
|
||||
struct FileEntry {
|
||||
name: [u8; 32],
|
||||
name_len: u8,
|
||||
data: [u8; MAX_FILE_SIZE],
|
||||
size: usize,
|
||||
open: bool,
|
||||
}
|
||||
|
||||
impl FileEntry {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
name: [0; 32],
|
||||
name_len: 0,
|
||||
data: [0; MAX_FILE_SIZE],
|
||||
size: 0,
|
||||
open: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MockFileSystem {
|
||||
files: [Option<FileEntry>; MAX_FILES],
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl MockFileSystem {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
files: [const { None }; MAX_FILES],
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_by_name(&self, path: &[u8]) -> Option<usize> {
|
||||
for i in 0..self.count {
|
||||
if let Some(entry) = &self.files[i] {
|
||||
if &entry.name[..entry.name_len as usize] == path {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl FileSystem for MockFileSystem {
|
||||
fn open(&mut self, path: &[u8]) -> Option<FileHandle> {
|
||||
let idx = self.find_by_name(path)?;
|
||||
if let Some(entry) = &mut self.files[idx] {
|
||||
entry.open = true;
|
||||
Some(FileHandle::new(idx as u16))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, handle: FileHandle, buf: &mut [u8]) -> Option<usize> {
|
||||
let entry = self.files[handle.raw() as usize].as_ref()?;
|
||||
let len = buf.len().min(entry.size);
|
||||
buf[..len].copy_from_slice(&entry.data[..len]);
|
||||
Some(len)
|
||||
}
|
||||
|
||||
fn write(&mut self, handle: FileHandle, buf: &[u8]) -> Option<usize> {
|
||||
let entry = self.files[handle.raw() as usize].as_mut()?;
|
||||
let len = buf.len().min(MAX_FILE_SIZE - entry.size);
|
||||
entry.data[entry.size..entry.size + len].copy_from_slice(&buf[..len]);
|
||||
entry.size += len;
|
||||
Some(len)
|
||||
}
|
||||
|
||||
fn close(&mut self, handle: FileHandle) {
|
||||
if let Some(entry) = &mut self.files[handle.raw() as usize] {
|
||||
entry.open = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn list_dir(&mut self, _path: &[u8], entries: &mut [DirEntry]) -> Option<usize> {
|
||||
let mut count = 0;
|
||||
for i in 0..self.count {
|
||||
if count >= entries.len() { break; }
|
||||
if let Some(file) = &self.files[i] {
|
||||
let mut entry = DirEntry::empty();
|
||||
let len = file.name_len as usize;
|
||||
entry.name[..len].copy_from_slice(&file.name[..len]);
|
||||
entry.name_len = file.name_len;
|
||||
entry.size = file.size as u32;
|
||||
entry.is_dir = false;
|
||||
entries[count] = entry;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Some(count)
|
||||
}
|
||||
|
||||
fn create(&mut self, path: &[u8]) -> Option<FileHandle> {
|
||||
if self.count >= MAX_FILES || path.len() > 32 {
|
||||
return None;
|
||||
}
|
||||
let idx = self.count;
|
||||
let mut entry = FileEntry::empty();
|
||||
entry.name[..path.len()].copy_from_slice(path);
|
||||
entry.name_len = path.len() as u8;
|
||||
entry.open = true;
|
||||
self.files[idx] = Some(entry);
|
||||
self.count += 1;
|
||||
Some(FileHandle::new(idx as u16))
|
||||
}
|
||||
|
||||
fn delete(&mut self, path: &[u8]) -> bool {
|
||||
if let Some(idx) = self.find_by_name(path) {
|
||||
self.files[idx] = None;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn file_size(&mut self, handle: FileHandle) -> Option<usize> {
|
||||
self.files[handle.raw() as usize].as_ref().map(|e| e.size)
|
||||
}
|
||||
}
|
||||
2
crates/servers/storage-server/src/adapters/mod.rs
Normal file
2
crates/servers/storage-server/src/adapters/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod mock_block;
|
||||
pub mod mock_fs;
|
||||
Reference in New Issue
Block a user