1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
//! Append-only object storage
//!
//! So, why a custom data structure? Well, for two reasons:
//!
//! 1. No limitations on performance.
//! 2. Best possible convenience.
//!
//! Please note that I'm deliberately saying "no limitations" on performance. So
//! far, performance has not been a priority, so this might not be that fast.
//! But by having a custom data structure, we should be able to make performance
//! as good as we need it, within the limits of the practical.
//!
//! The second point, best possible convenience, is already realized.
//! [`Handle`]s can be owned, cloned, and dereference to the object they are
//! referencing. This is made possible by the append-only nature of our object
//! storage, and our immutable objects.
//!
//! There are other append-only data structures on `crates.io`. Some of them
//! look interesting, but none of them quite fit our needs and possibilities, so
//! a custom development seemed justified.
//!
//! But in any case, this was fun to write, and not that much work.
use std::{marker::PhantomData, sync::Arc};
use parking_lot::RwLock;
use super::{
blocks::{Blocks, Index},
Handle,
};
/// Append-only object storage
#[derive(Debug)]
pub struct Store<T> {
inner: StoreInner<T>,
}
impl<T> Store<T> {
/// Construct a new instance of `Store`
///
/// Equivalent to calling [`Store::with_block_size`] with a default block
/// size.
pub fn new() -> Self {
Self::with_block_size(16384)
}
/// Construct a new instance of `Store` using the provided block size
pub fn with_block_size(block_size: usize) -> Self {
let inner = Arc::new(RwLock::new(StoreInnerInner {
blocks: Blocks::new(block_size),
}));
Self { inner }
}
/// Reserve a slot for an object in the store
///
/// This method returns a handle to the reserved slot. That handle does not
/// refer to an object yet! Attempting to dereference the handle before it
/// has been used to insert an object into the store, will result in a
/// panic.
///
/// Usually, you'd acquire one handle, then immediately use it to insert an
/// object using [`Store::insert`]. The methods are separate to support more
/// advanced use cases, like inserting objects that refer to each other.
pub fn reserve(&self) -> Handle<T> {
let mut inner = self.inner.write();
let (index, ptr) = inner.blocks.reserve();
Handle {
store: self.inner.clone(),
index,
ptr,
}
}
/// Insert an object into the store
///
/// # Panics
///
/// Panics, if the passed `Handle` does not refer to a reserved slot. This
/// can only be the case, if the handle has been used to insert an object
/// before.
pub fn insert(&mut self, handle: Handle<T>, object: T) {
let mut inner = self.inner.write();
inner.blocks.insert(handle.index, object);
}
/// Iterate over all objects in this store
pub fn iter(&self) -> Iter<T> {
Iter {
store: self.inner.clone(),
next_index: Index::zero(),
_a: PhantomData,
}
}
}
impl<T> Default for Store<T> {
fn default() -> Self {
Self::new()
}
}
impl<'a, T> IntoIterator for &'a Store<T> {
type Item = Handle<T>;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
/// An iterator over objects in a [`Store`]
pub struct Iter<'a, T> {
store: StoreInner<T>,
next_index: Index,
_a: PhantomData<&'a ()>,
}
impl<'a, T: 'a> Iterator for Iter<'a, T> {
type Item = Handle<T>;
fn next(&mut self) -> Option<Self::Item> {
let inner = self.store.read();
loop {
let index = self.next_index;
let ptr = inner.blocks.get_and_inc(&mut self.next_index)?;
if ptr.is_none() {
// This is a reserved slot.
continue;
}
return Some(Handle {
store: self.store.clone(),
index,
ptr,
});
}
}
}
pub type StoreInner<T> = Arc<RwLock<StoreInnerInner<T>>>;
#[derive(Debug)]
pub struct StoreInnerInner<T> {
blocks: Blocks<T>,
}
#[cfg(test)]
mod tests {
use crate::storage::Handle;
use super::Store;
#[test]
fn insert_and_handle() {
let mut store = Store::with_block_size(1);
let handle: Handle<i32> = store.reserve();
let object = 0;
store.insert(handle.clone(), object);
assert_eq!(*handle, object);
}
#[test]
fn insert_and_iter() {
let mut store = Store::with_block_size(1);
let a: Handle<i32> = store.reserve();
let b = store.reserve();
store.insert(a.clone(), 0);
store.insert(b.clone(), 1);
let objects = store.iter().collect::<Vec<_>>();
assert_eq!(objects, [a, b]);
}
}