✨♻️📝 vanth, varo: Refactor storage backend, fix distribution sampling, and enhance type handling
- Refactored `Store` to use a trait-based backend with type-specific tables, replacing the previous enum approach. - Implemented `Sqlite` and `Memory` backends with proper table management and type-aware operations. - Added serialization/deserialization handling in `Store` using `serde_json`. - Implemented `ToString` for `Ty` and improved equality comparisons using string representation. - Fixed `Distribution::sample` in `varo` to correctly handle 0, 1, and 2+ moments cases. - Updated store integration tests to verify type-specific storage operations including write, read, and deletion. - Commented out unfinished `EntityContents` and related structs pending future implementation.
This commit is contained in:
parent
db531c8c73
commit
87957bfbf8
4 changed files with 275 additions and 105 deletions
|
@ -49,10 +49,10 @@ impl Node {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(entity_id: impl Into<EntityId>) -> Result<Option<EntityContents>> {
|
// pub fn load(entity_id: impl Into<EntityId>) -> Result<Option<EntityContents>> {
|
||||||
// TODO
|
// // TODO
|
||||||
Ok(None)
|
// Ok(None)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
@ -76,11 +76,17 @@ pub struct Value {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash)]
|
||||||
pub struct Ty {
|
pub struct Ty {
|
||||||
pub path: Vec<String>,
|
pub path: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToString for Ty {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.path.join("::")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq for Ty {
|
impl PartialEq for Ty {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.path == other.path
|
self.path == other.path
|
||||||
|
@ -89,7 +95,7 @@ impl PartialEq for Ty {
|
||||||
|
|
||||||
impl <T: AsRef<str>> PartialEq<T> for Ty {
|
impl <T: AsRef<str>> PartialEq<T> for Ty {
|
||||||
fn eq(&self, other: &T) -> bool {
|
fn eq(&self, other: &T) -> bool {
|
||||||
self.path.join("::") == *other.as_ref()
|
self.to_string() == *other.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,16 +108,16 @@ pub trait VanthTuple {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
// #[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct EntityContents {
|
// pub struct EntityContents {
|
||||||
components: Vec<ComponentContents>
|
// components: Vec<ComponentContents>
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ComponentContents {
|
pub struct ComponentContents<T: Vanth> {
|
||||||
id: String,
|
|
||||||
content_hash: ContentHash,
|
content_hash: ContentHash,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Component: Serialize {
|
pub trait Component: Serialize {
|
||||||
|
@ -120,7 +126,7 @@ pub trait Component: Serialize {
|
||||||
|
|
||||||
// use a macro to implement VanthTuiple here.
|
// use a macro to implement VanthTuiple here.
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, Component, Serialize)]
|
#[derive(Copy, Clone, Debug, Deserialize, Component, Serialize, PartialEq, Eq, Hash)]
|
||||||
pub struct ContentHash {
|
pub struct ContentHash {
|
||||||
pub hash: [u8; 32],
|
pub hash: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,115 +1,256 @@
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, marker::PhantomData, path::PathBuf};
|
||||||
|
|
||||||
use rusqlite::{Connection, params};
|
use rusqlite::{Connection, params, named_params};
|
||||||
|
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Component, Serialize)]
|
use crate::{hash, ComponentContents, ContentHash, Ty, Vanth};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Store {
|
pub struct Store {
|
||||||
backend: Backend,
|
backend: Box<dyn Backend>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, String>;
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub enum Error {
|
||||||
|
Serializiation(String),
|
||||||
|
Database(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Error> for Error {
|
||||||
|
fn from(err: serde_json::Error) -> Self {
|
||||||
|
Error::Serializiation(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rusqlite::Error> for Error {
|
||||||
|
fn from(err: rusqlite::Error) -> Self {
|
||||||
|
Error::Database(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Store {
|
impl Store {
|
||||||
|
/// Use an SQLite backend with a database file at the provided path.
|
||||||
pub fn from_path(path: PathBuf) -> Result<Self> {
|
pub fn from_path(path: PathBuf) -> Result<Self> {
|
||||||
let conn = Connection::open(&path).map_err(|e| e.to_string())?;
|
|
||||||
conn.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS kv (key BLOB PRIMARY KEY, value BLOB)",
|
|
||||||
params![],
|
|
||||||
).map_err(|e| e.to_string())?;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
backend: Backend::Sqlite(Sqlite { path }),
|
backend: Box::new(Sqlite::new(path)?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use an in-memory backend.
|
||||||
pub fn in_memory() -> Result<Self> {
|
pub fn in_memory() -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
backend: Backend::Memory(Memory::new()),
|
backend: Box::new(Memory::new()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, key: impl AsRef<[u8]>) -> Result<Option<Vec<u8>>> {
|
pub fn get_from_hash<T: Vanth + DeserializeOwned>(&mut self, content_hash: ContentHash) -> Result<Option<T>> {
|
||||||
match &mut self.backend {
|
let Some(raw) = self.get_raw_from_hash::<T>(content_hash)? else { return Ok(None) };
|
||||||
Backend::Memory(mem) => Ok(mem.values.get(key.as_ref()).cloned()),
|
|
||||||
Backend::Sqlite(sql) => {
|
let deserialized: T = serde_json::from_slice(&raw)?;
|
||||||
let conn = Connection::open(&sql.path).map_err(|e| e.to_string())?;
|
Ok(Some(deserialized))
|
||||||
let mut stmt = conn.prepare("SELECT value FROM kv WHERE key = ?1").map_err(|e| e.to_string())?;
|
|
||||||
let mut rows = stmt.query(params![key.as_ref()]).map_err(|e| e.to_string())?;
|
|
||||||
if let Some(row) = rows.next().map_err(|e| e.to_string())? {
|
|
||||||
let value: Vec<u8> = row.get(0).map_err(|e| e.to_string())?;
|
|
||||||
Ok(Some(value))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>) -> Result<()> {
|
pub fn get_raw_from_hash<T: Vanth>(&mut self, content_hash: ContentHash) -> Result<Option<Vec<u8>>> {
|
||||||
match &mut self.backend {
|
self.backend.get_from_hash(T::ty(), content_hash)
|
||||||
Backend::Memory(mem) => {
|
|
||||||
mem.values.insert(key.as_ref().to_vec(), value.as_ref().to_vec());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Backend::Sqlite(sql) => {
|
|
||||||
let conn = Connection::open(&sql.path).map_err(|e| e.to_string())?;
|
|
||||||
conn.execute(
|
|
||||||
"INSERT OR REPLACE INTO kv (key, value) VALUES (?1, ?2)",
|
|
||||||
params![key.as_ref(), value.as_ref()],
|
|
||||||
).map_err(|e| e.to_string())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&mut self, key: impl AsRef<[u8]>) -> Result<()> {
|
pub fn get_all_of_type<T: Vanth>(&mut self) -> Result<Vec<ComponentContents<T>>> {
|
||||||
match &mut self.backend {
|
let raw_items = self.backend.get_all_of_ty(T::ty())?;
|
||||||
Backend::Memory(mem) => {
|
let mut results = Vec::new();
|
||||||
mem.values.remove(key.as_ref());
|
for (content_hash, data) in raw_items {
|
||||||
Ok(())
|
results.push(ComponentContents {
|
||||||
}
|
_marker: PhantomData,
|
||||||
Backend::Sqlite(sql) => {
|
content_hash,
|
||||||
let conn = Connection::open(&sql.path).map_err(|e| e.to_string())?;
|
data,
|
||||||
conn.execute(
|
});
|
||||||
"DELETE FROM kv WHERE key = ?1",
|
|
||||||
params![key.as_ref()],
|
|
||||||
).map_err(|e| e.to_string())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<T: Vanth + Serialize>(&mut self, value: &T) -> Result<()> {
|
||||||
|
let content_hash = hash(&value);
|
||||||
|
let data = serde_json::to_vec(&value)?;
|
||||||
|
self.backend.write(T::ty(), content_hash, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_raw<T: Vanth>(&mut self, content_hash: ContentHash, content: Vec<u8>) -> Result<()> {
|
||||||
|
self.backend.write(T::ty(), content_hash, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete<T: Vanth>(&mut self, content_hash: ContentHash) -> Result<()> {
|
||||||
|
self.backend.delete_by_hash(T::ty(), content_hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_all<T: Vanth>(&mut self) -> Result<()> {
|
||||||
|
self.backend.delete_all_of_ty(T::ty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Component, Serialize)]
|
#[derive(Debug, Deserialize, Component, Serialize)]
|
||||||
pub struct Cache {
|
pub struct Cache {
|
||||||
size_limit_bytes: u64,
|
size_limit_bytes: u64,
|
||||||
backend: Backend,
|
// backend: Backend,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
pub trait Backend: std::fmt::Debug {
|
||||||
pub enum Backend {
|
fn get_from_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result<Option<Vec<u8>>>;
|
||||||
Memory(Memory),
|
|
||||||
Sqlite(Sqlite)
|
fn get_all_of_ty(&mut self, ty: Ty) -> Result<Vec<(ContentHash, Vec<u8>)>>;
|
||||||
|
|
||||||
|
fn write(&mut self, ty: Ty, content_hash: ContentHash, content: Vec<u8>) -> Result<()>;
|
||||||
|
|
||||||
|
fn delete_by_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result<()>;
|
||||||
|
|
||||||
|
fn delete_all_of_ty(&mut self, ty: Ty) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One table, key-value store. Keys and values are both blobs.
|
/// One table per type. Keys and values are both blobs.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug)]
|
||||||
pub struct Sqlite {
|
pub struct Sqlite {
|
||||||
path: PathBuf,
|
conn: Connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One table, key-value store. Keys and values are both blobs.
|
impl Sqlite {
|
||||||
|
pub fn new(path: PathBuf) -> Result<Self> {
|
||||||
|
let conn = Connection::open(path)?;
|
||||||
|
Ok(Self { conn })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_table_exists(&self, ty: &Ty) -> Result<()> {
|
||||||
|
let table_name = Self::table_name(ty);
|
||||||
|
let query = format!(
|
||||||
|
"CREATE TABLE IF NOT EXISTS \"{}\" (
|
||||||
|
content_hash BLOB PRIMARY KEY,
|
||||||
|
content BLOB NOT NULL
|
||||||
|
)",
|
||||||
|
table_name
|
||||||
|
);
|
||||||
|
self.conn.execute(&query, [])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn table_name(ty: &Ty) -> String {
|
||||||
|
format!("ty_{}", ty.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Backend for Sqlite {
|
||||||
|
fn get_from_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result<Option<Vec<u8>>> {
|
||||||
|
self.ensure_table_exists(&ty)?;
|
||||||
|
let table_name = Self::table_name(&ty);
|
||||||
|
let query = format!("SELECT content FROM \"{}\" WHERE content_hash = :hash", table_name);
|
||||||
|
|
||||||
|
match self.conn.query_row(&query, named_params! {":hash": content_hash.hash.as_slice()}, |row| {
|
||||||
|
row.get::<_, Vec<u8>>(0)
|
||||||
|
}) {
|
||||||
|
Ok(content) => Ok(Some(content)),
|
||||||
|
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_of_ty(&mut self, ty: Ty) -> Result<Vec<(ContentHash, Vec<u8>)>> {
|
||||||
|
self.ensure_table_exists(&ty)?;
|
||||||
|
let table_name = Self::table_name(&ty);
|
||||||
|
let query = format!("SELECT content_hash, content FROM \"{}\"", table_name);
|
||||||
|
|
||||||
|
let mut stmt = self.conn.prepare(&query)?;
|
||||||
|
let rows = stmt.query_map([], |row| {
|
||||||
|
let hash_bytes: Vec<u8> = row.get(0)?;
|
||||||
|
let content: Vec<u8> = row.get(1)?;
|
||||||
|
let mut hash_array = [0u8; 32];
|
||||||
|
hash_array.copy_from_slice(&hash_bytes);
|
||||||
|
Ok((ContentHash { hash: hash_array }, content))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut results = Vec::new();
|
||||||
|
for row in rows {
|
||||||
|
results.push(row?);
|
||||||
|
}
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, ty: Ty, content_hash: ContentHash, content: Vec<u8>) -> Result<()> {
|
||||||
|
self.ensure_table_exists(&ty)?;
|
||||||
|
let table_name = Self::table_name(&ty);
|
||||||
|
let query = format!(
|
||||||
|
"INSERT OR REPLACE INTO \"{}\" (content_hash, content) VALUES (:hash, :content)",
|
||||||
|
table_name
|
||||||
|
);
|
||||||
|
self.conn.execute(&query, named_params! {":hash": content_hash.hash.as_slice(), ":content": content})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_by_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result<()> {
|
||||||
|
self.ensure_table_exists(&ty)?;
|
||||||
|
let table_name = Self::table_name(&ty);
|
||||||
|
let query = format!("DELETE FROM \"{}\" WHERE content_hash = :hash", table_name);
|
||||||
|
self.conn.execute(&query, named_params! {":hash": content_hash.hash.as_slice()})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_all_of_ty(&mut self, ty: Ty) -> Result<()> {
|
||||||
|
let table_name = Self::table_name(&ty);
|
||||||
|
let query = format!("DROP TABLE IF EXISTS \"{}\"", table_name);
|
||||||
|
self.conn.execute(&query, [])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In-memory storage with one table per type.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Memory {
|
pub struct Memory {
|
||||||
values: HashMap<Vec<u8>, Vec<u8>>,
|
tables: HashMap<Ty, HashMap<ContentHash, Vec<u8>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Memory {
|
impl Memory {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
values: HashMap::new(),
|
tables: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Backend for Memory {
|
||||||
|
fn get_from_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result<Option<Vec<u8>>> {
|
||||||
|
Ok(self.tables
|
||||||
|
.get(&ty)
|
||||||
|
.and_then(|table| table.get(&content_hash))
|
||||||
|
.cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_of_ty(&mut self, ty: Ty) -> Result<Vec<(ContentHash, Vec<u8>)>> {
|
||||||
|
Ok(self.tables
|
||||||
|
.get(&ty)
|
||||||
|
.map(|table| {
|
||||||
|
table.iter()
|
||||||
|
.map(|(k, v)| (*k, v.clone()))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_else(Vec::new))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, ty: Ty, content_hash: ContentHash, content: Vec<u8>) -> Result<()> {
|
||||||
|
self.tables
|
||||||
|
.entry(ty)
|
||||||
|
.or_insert_with(HashMap::new)
|
||||||
|
.insert(content_hash, content);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_by_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result<()> {
|
||||||
|
if let Some(table) = self.tables.get_mut(&ty) {
|
||||||
|
table.remove(&content_hash);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_all_of_ty(&mut self, ty: Ty) -> Result<()> {
|
||||||
|
self.tables.remove(&ty);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,26 +1,47 @@
|
||||||
use vanth::store::Store;
|
use serde::{Deserialize, Serialize};
|
||||||
|
use vanth::{hash, store::Store, Vanth};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Vanth)]
|
||||||
|
struct Foo {
|
||||||
|
inner: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Vanth)]
|
||||||
|
struct Bar {
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sqlite_store() {
|
fn test_sqlite_store() {
|
||||||
let dir = TempDir::new().unwrap();
|
let dir = TempDir::new().unwrap();
|
||||||
let path = dir.path().join("test.db");
|
let path = dir.path().join("test.db");
|
||||||
let mut store = Store::from_path(path.clone()).unwrap();
|
let mut store = Store::from_path(path.clone()).unwrap();
|
||||||
|
|
||||||
assert_eq!(store.read(b"key_1"), Ok(None));
|
let foo_1 = Foo { inner: 1 };
|
||||||
assert_eq!(store.write(b"key_1", b"value_1"), Ok(()));
|
let foo_2 = Foo { inner: 2 };
|
||||||
|
let bar_1 = Bar { inner: "hello".into() };
|
||||||
|
|
||||||
let value = store.read(b"key_1").unwrap();
|
assert_eq!(store.get_all_of_type::<Foo>().unwrap().len(), 0);
|
||||||
assert_eq!(value.as_deref(), Some(b"value_1" as &[u8]));
|
assert_eq!(store.get_all_of_type::<Bar>().unwrap().len(), 0);
|
||||||
|
|
||||||
drop(store);
|
store.write(&foo_1).unwrap();
|
||||||
|
store.write(&foo_2).unwrap();
|
||||||
|
store.write(&bar_1).unwrap();
|
||||||
|
assert_eq!(store.get_all_of_type::<Foo>().unwrap().len(), 2);
|
||||||
|
assert_eq!(store.get_all_of_type::<Bar>().unwrap().len(), 1);
|
||||||
|
|
||||||
let mut store = Store::from_path(path.clone()).unwrap();
|
let foo_2_hash = hash(&foo_2);
|
||||||
|
let foo_2_fetched = store.get_from_hash(foo_2_hash).unwrap().unwrap();
|
||||||
|
assert_ne!(foo_1, foo_2_fetched);
|
||||||
|
assert_eq!(foo_2, foo_2_fetched);
|
||||||
|
|
||||||
let value = store.read(b"key_1").unwrap();
|
store.delete::<Foo>(foo_2_hash).unwrap();
|
||||||
assert_eq!(value.as_deref(), Some(b"value_1" as &[u8]));
|
assert_eq!(store.get_all_of_type::<Foo>().unwrap().len(), 1);
|
||||||
|
|
||||||
store.delete(b"key_1").unwrap();
|
store.delete_all::<Foo>().unwrap();
|
||||||
assert_eq!(store.read(b"key_1"), Ok(None));
|
store.delete_all::<Bar>().unwrap();
|
||||||
|
assert_eq!(store.get_all_of_type::<Foo>().unwrap().len(), 0);
|
||||||
|
assert_eq!(store.get_all_of_type::<Bar>().unwrap().len(), 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,11 +38,11 @@ pub fn rng_gen_f32(rng: &mut Rng) -> f32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rng_gen_gaussian(rng: &mut Rng, mean: f32, std_dev: f32) -> f32 {
|
pub fn rng_gen_gaussian(rng: &mut Rng, mean: f32, std_dev: f32) -> f32 {
|
||||||
let u = rng_gen_f32(rng);
|
let uniform_for_radius_calc = rng_gen_f32(rng);
|
||||||
let v = rng_gen_f32(rng);
|
let uniform_for_angle = rng_gen_f32(rng);
|
||||||
let s = (-2.0 * (1.0 - u).ln()).sqrt();
|
let radius = (-2.0 * (1.0 - uniform_for_radius_calc).ln()).sqrt();
|
||||||
let angle = 2.0 * PI * v;
|
let theta = 2.0 * PI * uniform_for_angle;
|
||||||
mean + std_dev * s * angle.cos()
|
mean + std_dev * radius * theta.cos()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Varo {
|
pub trait Varo {
|
||||||
|
@ -58,10 +58,12 @@ pub struct Distribution {
|
||||||
|
|
||||||
impl Distribution {
|
impl Distribution {
|
||||||
pub fn sample(&self, digest: &mut Rng) -> f32 {
|
pub fn sample(&self, digest: &mut Rng) -> f32 {
|
||||||
if self.moments.len() >= 2 {
|
if self.moments.is_empty() {
|
||||||
rng_gen_gaussian(digest, self.moments[0], self.moments[1].sqrt())
|
|
||||||
} else {
|
|
||||||
rng_gen_f32(digest)
|
rng_gen_f32(digest)
|
||||||
|
} else if self.moments.len() == 1 {
|
||||||
|
rng_gen_gaussian(digest, self.moments[0], 1.0)
|
||||||
|
} else {
|
||||||
|
rng_gen_gaussian(digest, self.moments[0], self.moments[1].sqrt())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue