mod config;
mod snapshot;
pub mod snapshot_set; mod store;
mod types;
use std::{
borrow::{Borrow, Cow},
error::Error,
};
use snapshot_set::FileSnapshotSet;
use store::{FixedLengthKey64Bit, Store, StoreImpl, VariableLengthKey};
pub use config::{Config, SyncMode};
pub type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync + 'static>>;
pub struct PersistentKeyValueStore<K, V> {
store: StoreImpl,
phantom: std::marker::PhantomData<(K, V)>,
}
unsafe impl<K, V> Sync for PersistentKeyValueStore<K, V> {}
unsafe impl<K, V> Send for PersistentKeyValueStore<K, V> {}
pub trait Deserializable {
fn from_bytes(bytes: &[u8]) -> Self;
}
pub trait Serializable {
const IS_FIXED_SIZE: bool;
fn serialize(&self) -> Cow<'_, [u8]>;
fn serialize_fixed_size(&self) -> Option<[u8; 8]>;
}
impl<K, V> PersistentKeyValueStore<K, V>
where
K: Serializable,
{
pub fn new(path: impl AsRef<std::path::Path>, config: Config) -> Result<Self> {
let snapshot_set = FileSnapshotSet::new(path.as_ref())?;
Ok(Self {
store: if <K as Serializable>::IS_FIXED_SIZE {
StoreImpl::FixedKey(Store::new(snapshot_set, config)?)
} else {
StoreImpl::VariableKey(Store::new(snapshot_set, config)?)
},
phantom: std::marker::PhantomData,
})
}
pub fn unset<Q>(&self, key: &Q) -> Result<()>
where
K: Borrow<Q>,
Q: ?Sized + Serializable,
{
match &self.store {
StoreImpl::FixedKey(store) => store
.unset(key.serialize_fixed_size().unwrap().borrow())
.map(|_| ()),
StoreImpl::VariableKey(store) => store.unset(key.serialize().borrow()).map(|_| ()),
}
}
fn get_<Q, F, V2>(&self, key: &Q, c: F) -> Option<V2>
where
K: Borrow<Q>,
Q: ?Sized + Serializable,
F: FnOnce(Option<&[u8]>) -> Option<V2>,
{
match &self.store {
StoreImpl::VariableKey(store) => store.get_convert(&key.serialize(), c),
StoreImpl::FixedKey(store) => {
store.get_convert(key.serialize_fixed_size().unwrap().borrow(), c)
}
}
}
fn set_(&self, key: K, value: Vec<u8>) -> Result<()> {
match &self.store {
StoreImpl::FixedKey(store) => store
.set(
FixedLengthKey64Bit(key.serialize_fixed_size().unwrap()),
value,
)
.map(|_| ()),
StoreImpl::VariableKey(store) => store
.set(VariableLengthKey(key.serialize().into_owned()), value)
.map(|_| ()),
}
}
}
impl<K, V> PersistentKeyValueStore<K, V>
where
K: Serializable,
V: Deserializable + Serializable,
{
pub fn set(&self, key: impl Into<K>, value: impl Into<V>) -> Result<()> {
self.set_(key.into(), value.into().serialize().into_owned())
}
pub fn get<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: ?Sized + Serializable,
{
self.get_(key, |bytes| bytes.map(|bytes| V::from_bytes(bytes)))
}
}
impl<K, V> PersistentKeyValueStore<K, V>
where
K: Serializable,
V: prost::Message + Default,
{
pub fn set_proto(&self, key: impl Into<K>, value: impl prost::Message) -> Result<()> {
self.set_(key.into(), value.encode_to_vec())
}
pub fn get_proto<Q>(&self, key: &Q) -> std::result::Result<Option<V>, prost::DecodeError>
where
K: Borrow<Q>,
Q: ?Sized + Serializable,
{
self.get_(key, |bytes| bytes.map(|bytes| V::decode(bytes)))
.transpose()
}
}
impl<K, V> std::fmt::Debug for PersistentKeyValueStore<K, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (num_elements, num_bytes) = match &self.store {
StoreImpl::FixedKey(store) => store.compute_size_info(),
StoreImpl::VariableKey(store) => store.compute_size_info(),
};
write!(
f,
"PersistentKeyValueStore({} elements, {} KiB total size)",
num_elements,
num_bytes / 1024
)?;
Ok(())
}
}
impl<K, V> IntoIterator for PersistentKeyValueStore<K, V>
where
K: Deserializable + Serializable,
V: Deserializable + Serializable,
{
type Item = (K, V);
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'static>;
fn into_iter(self) -> Self::IntoIter {
match self.store {
StoreImpl::FixedKey(store) => Box::new(
store
.into_iter()
.map(|(k, v)| (K::from_bytes(&k.0), V::from_bytes(&v))),
) as Box<dyn Iterator<Item = Self::Item>>,
StoreImpl::VariableKey(store) => Box::new(
store
.into_iter()
.map(|(k, v)| (K::from_bytes(&k.0[..]), V::from_bytes(&v))),
) as Box<dyn Iterator<Item = Self::Item>>,
}
}
}
impl<'a, K, V> IntoIterator for &'a PersistentKeyValueStore<K, V>
where
K: Deserializable + Serializable,
V: Deserializable + Serializable,
{
type Item = (K, V);
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
fn into_iter(self) -> Self::IntoIter {
match self.store {
StoreImpl::FixedKey(ref store) => Box::new(
store
.into_iter()
.map(|(k, v)| (K::from_bytes(&k.0), V::from_bytes(&v))),
) as Box<dyn Iterator<Item = Self::Item>>,
StoreImpl::VariableKey(ref store) => Box::new(
store
.into_iter()
.map(|(k, v)| (K::from_bytes(&k.0[..]), V::from_bytes(&v))),
)
as Box<dyn Iterator<Item = Self::Item>>,
}
}
}
impl<K, V> PersistentKeyValueStore<K, V>
where
K: Deserializable + Serializable,
V: Deserializable + Serializable,
{
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (K, V)> + 'a> {
<&Self as IntoIterator>::into_iter(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<PersistentKeyValueStore<u32, u32>>();
}
#[test]
fn setget_string_string() {
let tmp_dir = TempDir::new().unwrap();
let store: PersistentKeyValueStore<String, String> =
PersistentKeyValueStore::new(tmp_dir.path(), Config::default()).unwrap();
store.set("foo", "1").unwrap();
assert_eq!(store.get("foo"), Some("1".to_string()));
store.unset("foo").unwrap();
assert_eq!(store.get("foo"), None);
}
#[test]
fn setget_u64_u64() {
let tmp_dir = TempDir::new().unwrap();
let store: PersistentKeyValueStore<u64, u64> =
PersistentKeyValueStore::new(tmp_dir.path(), Config::default()).unwrap();
store.set(35293853295u64, 1139131311u64).unwrap();
assert_eq!(store.get(&35293853295u64), Some(1139131311u64));
store.unset(&35293853295u64).unwrap();
assert_eq!(store.get(&35293853295u64), None);
}
#[test]
fn setget_i32_i32() {
let tmp_dir = TempDir::new().unwrap();
let store: PersistentKeyValueStore<i32, i32> =
PersistentKeyValueStore::new(tmp_dir.path(), Config::default()).unwrap();
store.set(352938539, 113913131).unwrap();
assert_eq!(store.get(&352938539), Some(113913131));
store.unset(&352938539).unwrap();
assert_eq!(store.get(&352938539), None);
}
#[test]
fn debug_trait() {
let tmp_dir = TempDir::new().unwrap();
let store: PersistentKeyValueStore<String, String> =
PersistentKeyValueStore::new(tmp_dir.path(), Config::default()).unwrap();
store.set("foo", "1".repeat(2048)).unwrap();
assert_eq!(
format!("{store:?}"),
"PersistentKeyValueStore(1 elements, 2 KiB total size)"
);
}
#[test]
fn into_iter() {
let tmp_dir = TempDir::new().unwrap();
let store: PersistentKeyValueStore<String, String> =
PersistentKeyValueStore::new(tmp_dir.path(), Config::default()).unwrap();
store.set("foo", "1").unwrap();
store.set("bar", "2").unwrap();
let mut iter = store.into_iter();
assert_eq!(iter.next(), Some(("foo".to_string(), "1".to_string())));
assert_eq!(iter.next(), Some(("bar".to_string(), "2".to_string())));
assert_eq!(iter.next(), None);
let store: PersistentKeyValueStore<String, String> =
PersistentKeyValueStore::new(tmp_dir.path(), Config::default()).unwrap();
assert_eq!(store.get("foo"), Some("1".to_string()));
}
#[test]
fn ref_iter() {
let tmp_dir = TempDir::new().unwrap();
let store: PersistentKeyValueStore<String, String> =
PersistentKeyValueStore::new(tmp_dir.path(), Config::default()).unwrap();
store.set("foo", "1").unwrap();
store.set("bar", "2").unwrap();
let mut iter = store.iter();
assert_eq!(iter.next(), Some(("foo".to_string(), "1".to_string())));
assert_eq!(iter.next(), Some(("bar".to_string(), "2".to_string())));
assert_eq!(iter.next(), None);
}
}