use ekv::{Database, flash::Flash};
use embassy_sync::blocking_mutex::raw::RawMutex;
use heapless::String;
use crate::error::Error;
use crate::key::{self, KEY_BUF_CAP, KEY_SUFFIX_OVERHEAD};
use crate::meta::FileMeta;
pub struct EkvFile<'a, F: Flash, M: RawMutex, const MAX_PATH_LEN: usize, const CHUNK_SIZE: usize> {
db: &'a Database<F, M>,
path: String<MAX_PATH_LEN>,
meta: FileMeta,
current_chunk: usize,
chunk_offset: usize,
cache: [u8; CHUNK_SIZE],
cache_valid: bool,
}
impl<'a, F: Flash, M: RawMutex, const MAX_PATH_LEN: usize, const CHUNK_SIZE: usize>
EkvFile<'a, F, M, MAX_PATH_LEN, CHUNK_SIZE>
{
const _KEY_LEN_OK: () = assert!(MAX_PATH_LEN + KEY_SUFFIX_OVERHEAD <= KEY_BUF_CAP);
pub(crate) fn new(db: &'a Database<F, M>, path: String<MAX_PATH_LEN>, meta: FileMeta) -> Self {
Self {
db,
path,
meta,
current_chunk: 0,
chunk_offset: 0,
cache: [0; CHUNK_SIZE],
cache_valid: false,
}
}
pub const fn meta(&self) -> &FileMeta {
&self.meta
}
pub const fn size(&self) -> usize {
self.meta.size
}
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
if self.current_chunk >= self.meta.chunks {
return Ok(0);
}
if !self.cache_valid {
let mut key_buf = String::<KEY_BUF_CAP>::new();
key::format_chunk_key(&mut key_buf, &self.path, self.current_chunk)?;
let tx = self.db.read_transaction().await;
tx.read(key_buf.as_bytes(), &mut self.cache)
.await
.map_err(Error::from_db)?;
self.cache_valid = true;
}
let total_read_so_far = (self.current_chunk * CHUNK_SIZE) + self.chunk_offset;
let bytes_left_in_file = self.meta.size.saturating_sub(total_read_so_far);
let bytes_left_in_chunk = CHUNK_SIZE - self.chunk_offset;
let to_copy = core::cmp::min(buf.len(), bytes_left_in_chunk);
let to_copy = core::cmp::min(to_copy, bytes_left_in_file);
if to_copy == 0 {
return Ok(0);
}
buf[..to_copy].copy_from_slice(&self.cache[self.chunk_offset..self.chunk_offset + to_copy]);
self.chunk_offset += to_copy;
if self.chunk_offset == CHUNK_SIZE || total_read_so_far + to_copy == self.meta.size {
self.current_chunk += 1;
self.chunk_offset = 0;
self.cache_valid = false;
}
Ok(to_copy)
}
}