use std::collections::HashMap;
use std::path::PathBuf;
use fjall::{Config, Keyspace, PartitionCreateOptions, PartitionHandle, PersistMode};
use nebu::Goldilocks;
use super::{deserialize_goldilocks, dim, hash_dirty, serialize_goldilocks, ShardStore};
pub struct FjallStore {
keyspace: Keyspace,
parts: [PartitionHandle; 14],
cache: HashMap<(u8, [u8; 32]), Vec<Goldilocks>>,
dirty: Vec<(u8, [u8; 32], Vec<Goldilocks>)>,
}
impl FjallStore {
pub fn open(path: impl Into<PathBuf>) -> fjall::Result<Self> {
let ks = Config::new(path.into()).open()?;
let p = |name| ks.open_partition(name, PartitionCreateOptions::default());
let parts = [
p("particles")?, p("axons_out")?, p("axons_in")?, p("neurons")?,
p("locations")?, p("coins")?, p("cards")?, p("files")?,
p("time")?, p("signals")?, p("commitments")?, p("nullifiers")?,
p("intents")?, p("ephemeral")?,
];
Ok(Self { keyspace: ks, parts, cache: HashMap::new(), dirty: Vec::new() })
}
}
impl ShardStore for FjallStore {
fn get(&self, dimension: u8, key: &[u8; 32]) -> Option<&[Goldilocks]> {
self.cache.get(&(dimension, *key)).map(Vec::as_slice)
}
fn put(&mut self, dimension: u8, key: [u8; 32], value: Vec<Goldilocks>) {
if dimension != dim::EPHEMERAL {
self.dirty.push((dimension, key, value.clone()));
}
self.cache.insert((dimension, key), value);
}
fn dirty_entries(&self) -> &[(u8, [u8; 32], Vec<Goldilocks>)] {
&self.dirty
}
fn commit(&mut self) -> [u8; 32] {
let out = hash_dirty(&self.dirty);
for (d, k, v) in &self.dirty {
let _ = self.parts[*d as usize].insert(k, serialize_goldilocks(v));
}
let _ = self.keyspace.persist(PersistMode::SyncAll);
self.dirty.clear();
out
}
fn get_mut(&mut self, _dimension: u8, _key: &[u8; 32]) -> Option<&mut [Goldilocks]> {
None
}
fn mark_dirty(&mut self, _dimension: u8, _key: [u8; 32]) {
}
fn remove(&mut self, dimension: u8, key: &[u8; 32]) -> Option<Vec<Goldilocks>> {
let val = self.cache.remove(&(dimension, *key))?;
self.dirty.retain(|(d, k, _)| !(*d == dimension && k == key));
if dimension != dim::EPHEMERAL {
let _ = self.parts[dimension as usize].remove(*key);
}
Some(val)
}
fn iter(&self, dimension: u8) -> Box<dyn Iterator<Item = (&[u8; 32], &[Goldilocks])> + '_> {
Box::new(
self.cache.iter()
.filter(move |(k, _)| k.0 == dimension)
.map(|(k, v)| (&k.1, v.as_slice()))
)
}
}
impl FjallStore {
pub fn load(&self, dimension: u8, key: &[u8; 32]) -> Option<Vec<Goldilocks>> {
let bytes = self.parts[dimension as usize].get(key).ok()??;
Some(deserialize_goldilocks(bytes.as_ref()))
}
}