mod aligned_storage;
mod castable;
use crate::aligned_storage::AlignedStorage;
#[cfg(feature = "inventory")]
pub use crate::castable::RegisterCast;
pub use crate::castable::{assert_implements_castable, Castable, Casts};
use std::any::TypeId;
use std::collections::{BTreeSet, HashMap};
use std::sync::Mutex;
pub mod tag {
pub struct ThreadLocal(std::marker::PhantomData<*const ()>);
pub struct Send(std::marker::PhantomData<*const ()>);
pub struct SendSync;
unsafe impl std::marker::Send for Send {}
pub unsafe trait Satisfies<Tag> {}
unsafe impl<T> Satisfies<ThreadLocal> for T {}
unsafe impl<T> Satisfies<Send> for T where T: std::marker::Send {}
unsafe impl<T> Satisfies<SendSync> for T where T: std::marker::Send + Sync {}
}
pub struct ObjectStore<Tag = tag::Send> {
_marker: std::marker::PhantomData<Tag>,
inner: ObjectStoreInner,
}
pub struct UninitObjectStore<Tag = tag::Send> {
_marker: std::marker::PhantomData<Tag>,
inner: ObjectStoreInner,
unwritten_objects: HashMap<*const (), UninitObjectInfo>,
}
struct ObjectStoreInner {
id: u32,
metas: Vec<ObjectMeta>,
casts: Casts,
metas_by_type: HashMap<TypeId, u32>,
metas_by_cast_target: BTreeSet<(TypeId, u32)>,
object_keys: BTreeSet<ObjectKey>,
buffers: Vec<AlignedStorage>,
}
pub struct Handle<T: ?Sized> {
store_id: u32,
meta_ix: u32,
ptr: *const T,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct DynamicHandle {
inner: Handle<()>,
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
struct ObjectKey {
buffer_ix: u32,
meta_ix: u32,
offset: (u32, u32),
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub struct UninitObjectInfo {
pub buffer_ix: u32,
meta_ix: u32,
}
struct ObjectMeta {
name: String,
type_id: TypeId,
destroy: fn(*mut ()),
}
impl ObjectStore<tag::ThreadLocal> {
pub fn new_thread_local() -> Self {
Self::new()
}
}
impl ObjectStore<tag::Send> {
pub fn new_send() -> Self {
Self::new()
}
}
impl ObjectStore<tag::SendSync> {
pub fn new_sync() -> Self {
Self::new()
}
}
impl<Tag> ObjectStore<Tag> {
pub fn new() -> Self {
ObjectStore {
_marker: std::marker::PhantomData,
inner: ObjectStoreInner::new(),
}
}
pub fn push<T>(&mut self, buffer_ix: u32, item: T) -> Handle<T>
where
T: Castable + tag::Satisfies<Tag>,
{
self.inner.push(buffer_ix, item)
}
pub fn find<'a, T>(&'a self, buffer_ix: u32) -> impl Iterator<Item = Handle<T>> + 'a
where
T: ?Sized + 'static,
{
self.inner.find::<T>(buffer_ix)
}
pub fn find_dynamic<'a>(
&'a self,
buffer_ix: u32,
type_id: TypeId,
) -> impl Iterator<Item = DynamicHandle> + 'a {
self.inner.find_dynamic(buffer_ix, type_id)
}
pub fn cast_from_dynamic<T>(&self, handle: DynamicHandle) -> Option<Handle<T>>
where
T: ?Sized + 'static,
{
self.inner.cast_from_dynamic(handle)
}
pub fn cast_to_dynamic<T>(&self, handle: Handle<T>) -> DynamicHandle
where
T: ?Sized,
{
self.inner.cast_to_dynamic(handle)
}
pub fn cast<T, U>(&self, handle: Handle<T>) -> Option<Handle<U>>
where
T: ?Sized + 'static,
U: ?Sized + 'static,
{
self.inner.cast(handle)
}
pub fn get<T>(&self, handle: Handle<T>) -> &T
where
T: ?Sized + 'static,
{
self.inner.get(handle)
}
pub fn get_mut<T>(&mut self, handle: Handle<T>) -> &mut T
where
T: ?Sized + 'static,
{
self.inner.get_mut(handle)
}
pub fn get_type_id<T>(&self, handle: Handle<T>) -> TypeId
where
T: ?Sized,
{
self.inner.get_type_id(handle)
}
pub fn get_type_id_dynamic(&self, handle: DynamicHandle) -> TypeId {
self.inner.get_type_id_dynamic(handle)
}
pub fn get_type_name<T>(&self, handle: Handle<T>) -> &str
where
T: ?Sized,
{
self.inner.get_type_name(handle)
}
pub fn get_type_name_dynamic(&self, handle: DynamicHandle) -> &str {
self.inner.get_type_name_dynamic(handle)
}
}
unsafe impl<Tag> Send for ObjectStore<Tag> where Tag: Send {}
unsafe impl<Tag> Sync for ObjectStore<Tag> where Tag: Sync {}
impl UninitObjectStore<tag::ThreadLocal> {
pub fn new_thread_local() -> Self {
Self::new()
}
}
impl UninitObjectStore<tag::Send> {
pub fn new_send() -> Self {
Self::new()
}
}
impl UninitObjectStore<tag::SendSync> {
pub fn new_sync() -> Self {
Self::new()
}
}
impl<Tag> UninitObjectStore<Tag> {
pub fn new() -> Self {
UninitObjectStore {
_marker: std::marker::PhantomData,
inner: ObjectStoreInner::new(),
unwritten_objects: Default::default(),
}
}
pub fn try_init(self) -> Result<ObjectStore<Tag>, UninitObjectStore<Tag>> {
if self.unwritten_objects.is_empty() {
Ok(ObjectStore {
_marker: self._marker,
inner: self.inner,
})
} else {
Err(self)
}
}
pub fn iter_unwritten_handles<'a>(
&'a self,
) -> impl Iterator<Item = (DynamicHandle, UninitObjectInfo)> + 'a {
self.unwritten_objects.iter().map(|(&ptr, &info)| {
(
DynamicHandle {
inner: Handle {
store_id: self.inner.id,
meta_ix: info.meta_ix,
ptr,
},
},
info,
)
})
}
pub fn count_unwritten_handles(&self) -> usize {
self.unwritten_objects.len()
}
pub fn can_init(&self) -> bool {
self.count_unwritten_handles() == 0
}
pub fn reserve<T>(&mut self, buffer_ix: u32) -> Handle<T>
where
T: Castable + tag::Satisfies<Tag>,
{
let handle = self.inner.reserve(buffer_ix);
self.unwritten_objects.insert(
handle.ptr as *const (),
UninitObjectInfo {
buffer_ix,
meta_ix: handle.meta_ix,
},
);
handle
}
pub fn write<T>(&mut self, handle: Handle<T>, value: T)
where
T: Sized,
{
if !self.try_write(handle, value) {
panic!("writing to the same handle twice");
}
}
pub fn try_write<T>(&mut self, handle: Handle<T>, value: T) -> bool
where
T: Sized,
{
self.unwritten_objects
.remove(&(handle.ptr as *const ()))
.and_then(|_| {
unsafe {
self.inner.write(handle, value);
}
Some(())
})
.is_some()
}
pub fn push<T>(&mut self, buffer_ix: u32, item: T) -> Handle<T>
where
T: Castable + tag::Satisfies<Tag>,
{
self.inner.push(buffer_ix, item)
}
pub fn find<'a, T>(&'a self, buffer_ix: u32) -> impl Iterator<Item = Handle<T>> + 'a
where
T: ?Sized + 'static,
{
self.inner.find::<T>(buffer_ix)
}
pub fn find_dynamic<'a>(
&'a self,
buffer_ix: u32,
type_id: TypeId,
) -> impl Iterator<Item = DynamicHandle> + 'a {
self.inner.find_dynamic(buffer_ix, type_id)
}
pub fn cast_from_dynamic<T>(&self, handle: DynamicHandle) -> Option<Handle<T>>
where
T: ?Sized + 'static,
{
self.inner.cast_from_dynamic(handle)
}
pub fn cast_to_dynamic<T>(&self, handle: Handle<T>) -> DynamicHandle
where
T: ?Sized,
{
self.inner.cast_to_dynamic(handle)
}
pub fn cast<T, U>(&self, handle: Handle<T>) -> Option<Handle<U>>
where
T: ?Sized + 'static,
U: ?Sized + 'static,
{
self.inner.cast(handle)
}
pub fn get_type_id<T>(&self, handle: Handle<T>) -> TypeId
where
T: ?Sized,
{
self.inner.get_type_id(handle)
}
pub fn get_type_id_dynamic(&self, handle: DynamicHandle) -> TypeId {
self.inner.get_type_id_dynamic(handle)
}
pub fn get_type_name<T>(&self, handle: Handle<T>) -> &str
where
T: ?Sized,
{
self.inner.get_type_name(handle)
}
pub fn get_type_name_dynamic(&self, handle: DynamicHandle) -> &str {
self.inner.get_type_name_dynamic(handle)
}
}
unsafe impl<Tag> Send for UninitObjectStore<Tag> where Tag: Send {}
unsafe impl<Tag> Sync for UninitObjectStore<Tag> {}
impl ObjectMeta {
fn new<T>() -> Self
where
T: Castable,
{
Self {
name: T::name().to_string(),
type_id: TypeId::of::<T>(),
destroy: |data: *mut ()| unsafe { destroy::<T>(data) },
}
}
}
unsafe impl<T: ?Sized + 'static> Sync for Handle<T> {}
unsafe impl<T: ?Sized + 'static> Send for Handle<T> {}
impl<T> Clone for Handle<T>
where
T: ?Sized,
{
fn clone(&self) -> Self {
Handle {
store_id: self.store_id,
meta_ix: self.meta_ix,
ptr: self.ptr,
}
}
}
impl<T> Copy for Handle<T> where T: ?Sized {}
impl<T> PartialEq for Handle<T>
where
T: ?Sized,
{
fn eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
}
impl<T> Eq for Handle<T> where T: ?Sized {}
impl<T> PartialOrd for Handle<T>
where
T: ?Sized,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.ptr.partial_cmp(&other.ptr)
}
}
impl<T> Ord for Handle<T>
where
T: ?Sized,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.ptr.cmp(&other.ptr)
}
}
impl<T> std::hash::Hash for Handle<T>
where
T: ?Sized,
{
fn hash<H>(&self, hasher: &mut H)
where
H: std::hash::Hasher,
{
self.ptr.hash(hasher)
}
}
impl<T> std::fmt::Debug for Handle<T>
where
T: ?Sized + 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
f.debug_struct("Handle")
.field("type", &TypeId::of::<T>())
.field("store_id", &self.store_id)
.field("meta_ix", &self.meta_ix)
.field("ptr", &self.ptr)
.finish()
}
}
fn new_store_id() -> u32 {
static NEXT_ID: once_cell::sync::OnceCell<Mutex<u32>> = once_cell::sync::OnceCell::new();
let mut next_id = NEXT_ID.get_or_init(|| Mutex::new(0)).lock().unwrap();
if *next_id == std::u32::MAX {
panic!(
"total number of [`ObjectStore`] instances over the life of the program reached {} so
no more stores can be created",
*next_id
);
}
let id = *next_id;
*next_id += 1;
id
}
impl ObjectStoreInner {
fn new() -> Self {
ObjectStoreInner {
id: new_store_id(),
casts: Default::default(),
metas: Default::default(),
metas_by_type: Default::default(),
metas_by_cast_target: Default::default(),
object_keys: Default::default(),
buffers: Default::default(),
}
}
fn reserve<T>(&mut self, buffer_ix: u32) -> Handle<T>
where
T: Castable,
{
let meta_ix = *self
.metas_by_type
.entry(TypeId::of::<T>())
.or_insert_with(|| {
let meta = ObjectMeta::new::<T>();
let ix = self.metas.len() as u32;
self.metas.push(meta);
T::collect_casts(&mut self.casts);
for (dst, _) in self.casts.find_keys_by_src(TypeId::of::<T>()) {
self.metas_by_cast_target.insert((dst, ix));
}
ix
});
while self.buffers.len() <= buffer_ix as usize {
self.buffers.push(Default::default());
}
let buffer = &mut self.buffers[buffer_ix as usize];
let (ptr, offset) = buffer.reserve::<T>();
self.object_keys.insert(ObjectKey {
buffer_ix,
meta_ix,
offset,
});
Handle {
store_id: self.id,
meta_ix,
ptr,
}
}
unsafe fn write<T: Sized>(&mut self, handle: Handle<T>, item: T) {
let addr = handle.ptr as *mut T;
std::ptr::write(addr, item);
}
fn push<T>(&mut self, buffer_ix: u32, item: T) -> Handle<T>
where
T: Castable,
{
let handle = self.reserve::<T>(buffer_ix);
unsafe {
self.write(handle, item);
}
handle
}
fn find<'a, T>(&'a self, buffer_ix: u32) -> impl Iterator<Item = Handle<T>> + 'a
where
T: ?Sized + 'static,
{
self.find_dynamic(buffer_ix, TypeId::of::<T>())
.flat_map(|handle| self.cast_from_dynamic::<T>(handle))
}
fn find_dynamic<'a>(
&'a self,
buffer_ix: u32,
type_id: TypeId,
) -> impl Iterator<Item = DynamicHandle> + 'a {
self.metas_by_cast_target
.range((type_id, 0)..=(type_id, std::u32::MAX))
.flat_map(move |&(_, meta_ix)| {
self.object_keys
.range(
ObjectKey {
buffer_ix,
meta_ix,
offset: (0, 0),
}..ObjectKey {
buffer_ix,
meta_ix: 1 + meta_ix,
offset: (0, 0),
},
)
.map(move |&key| self.key_to_handle(key))
})
}
fn key_to_handle(&self, key: ObjectKey) -> DynamicHandle {
DynamicHandle {
inner: Handle {
store_id: self.id,
meta_ix: key.meta_ix,
ptr: self.buffers[key.buffer_ix as usize].offset(key.offset),
},
}
}
fn cast_from_dynamic<T>(&self, handle: DynamicHandle) -> Option<Handle<T>>
where
T: ?Sized + 'static,
{
let meta = &self.metas[handle.inner.meta_ix as usize];
let cast_ix = self.casts.find_key(meta.type_id, TypeId::of::<T>())?;
Some(Handle {
store_id: handle.inner.store_id,
meta_ix: handle.inner.meta_ix,
ptr: self.casts.cast(cast_ix, handle.inner.ptr),
})
}
fn cast_to_dynamic<T>(&self, handle: Handle<T>) -> DynamicHandle
where
T: ?Sized,
{
assert_eq!(handle.store_id, self.id);
DynamicHandle {
inner: Handle {
meta_ix: handle.meta_ix,
store_id: self.id,
ptr: handle.ptr as *const (),
},
}
}
fn cast<T, U>(&self, handle: Handle<T>) -> Option<Handle<U>>
where
T: ?Sized + 'static,
U: ?Sized + 'static,
{
self.cast_from_dynamic(self.cast_to_dynamic(handle))
}
fn get<T>(&self, handle: Handle<T>) -> &T
where
T: ?Sized + 'static,
{
assert_eq!(handle.store_id, self.id);
unsafe { &*handle.ptr }
}
fn get_mut<T>(&mut self, handle: Handle<T>) -> &mut T
where
T: ?Sized + 'static,
{
assert_eq!(handle.store_id, self.id);
unsafe { &mut *(handle.ptr as *mut T) }
}
fn get_type_id<T>(&self, handle: Handle<T>) -> TypeId
where
T: ?Sized,
{
assert_eq!(handle.store_id, self.id);
self.metas[handle.meta_ix as usize].type_id
}
fn get_type_id_dynamic(&self, handle: DynamicHandle) -> TypeId {
assert_eq!(handle.inner.store_id, self.id);
self.metas[handle.inner.meta_ix as usize].type_id
}
fn get_type_name<T>(&self, handle: Handle<T>) -> &str
where
T: ?Sized,
{
assert_eq!(handle.store_id, self.id);
self.metas[handle.meta_ix as usize].name.as_str()
}
fn get_type_name_dynamic(&self, handle: DynamicHandle) -> &str {
assert_eq!(handle.inner.store_id, self.id);
self.metas[handle.inner.meta_ix as usize].name.as_str()
}
}
impl Drop for ObjectStoreInner {
fn drop(&mut self) {
for key in &self.object_keys {
let ptr = self.buffers[key.buffer_ix as usize].offset_mut(key.offset);
let meta = &self.metas[key.meta_ix as usize];
(meta.destroy)(ptr);
}
}
}
unsafe fn destroy<T>(ptr: *mut ())
where
T: Sized,
{
let ptr = ptr as *mut T;
std::ptr::drop_in_place(ptr);
}
#[cfg(test)]
mod tests {
use std::any::TypeId;
use super::{Handle, ObjectStore, UninitObjectStore};
trait Object {}
#[derive(PartialEq, Eq, Debug)]
struct MyObjectA {
a: String,
b: i32,
}
#[derive(PartialEq, Eq, Debug)]
struct MyObjectB {
x: u32,
}
crate::impl_castable! {
impl Castable for MyObjectA {
into dyn std::fmt::Debug;
into dyn std::any::Any;
into dyn Object;
}
}
crate::impl_castable! {
impl Castable for MyObjectB {
into dyn Object;
}
}
impl Object for MyObjectA {}
impl Object for MyObjectB {}
#[test]
fn test_object_store() {
let mut store = ObjectStore::new_send();
let handle0 = store.push(
0,
MyObjectA {
a: "test".to_string(),
b: 4,
},
);
let handle1 = store.push(0, MyObjectB { x: 0 });
let handle2 = store.push(
2,
MyObjectA {
a: "bar".to_string(),
b: 8,
},
);
let handle3 = store.push(
0,
MyObjectA {
a: "baz".to_string(),
b: 1,
},
);
check_store_contents(
&store,
TestHandles {
handle0,
handle1,
handle2,
handle3,
},
);
}
#[test]
#[should_panic]
fn test_invalid_object_store() {
let mut store1 = ObjectStore::new_send();
let store2 = ObjectStore::new_send();
let handle = store1.push(
0,
MyObjectA {
a: "test".to_string(),
b: 4,
},
);
store2.get(handle);
}
#[test]
fn test_uninit_object_store() {
let mut store = UninitObjectStore::new_thread_local();
let handle0 = store.reserve::<MyObjectA>(0);
let handle1 = store.push(0, MyObjectB { x: 0 });
let handle2 = store.reserve::<MyObjectA>(2);
let handle3 = store.reserve::<MyObjectA>(0);
assert_eq!(store.count_unwritten_handles(), 3);
let mut store = match store.try_init() {
Ok(_) => panic!("store should not yet be initializable"),
Err(store) => store,
};
store.write(
handle0,
MyObjectA {
a: "test".to_string(),
b: 4,
},
);
assert_eq!(store.count_unwritten_handles(), 2);
assert!(!store.try_write(
handle0,
MyObjectA {
a: "test".to_string(),
b: 4,
},
));
assert!(!store.try_write(handle1, MyObjectB { x: 0 }));
assert_eq!(store.count_unwritten_handles(), 2);
store.write(
handle2,
MyObjectA {
a: "bar".to_string(),
b: 8,
},
);
assert!(store.try_write(
handle3,
MyObjectA {
a: "baz".to_string(),
b: 1,
},
));
assert_eq!(store.count_unwritten_handles(), 0);
assert!(store.can_init());
let store = match store.try_init() {
Ok(store) => store,
Err(_) => {
panic!("store should be initializable after all handles have been written to")
}
};
check_store_contents(
&store,
TestHandles {
handle0,
handle1,
handle2,
handle3,
},
);
}
struct TestHandles {
handle0: Handle<MyObjectA>,
handle1: Handle<MyObjectB>,
handle2: Handle<MyObjectA>,
handle3: Handle<MyObjectA>,
}
fn check_store_contents<Tag>(store: &ObjectStore<Tag>, handles: TestHandles) {
let TestHandles {
handle0,
handle1,
handle2,
handle3,
} = handles;
assert_eq!(
store.get(handle0),
&MyObjectA {
a: "test".to_string(),
b: 4
}
);
assert_eq!(store.get(handle1), &MyObjectB { x: 0 });
assert_eq!(
store.get(handle2),
&MyObjectA {
a: "bar".to_string(),
b: 8
}
);
assert_eq!(
store.get(handle3),
&MyObjectA {
a: "baz".to_string(),
b: 1
}
);
assert_eq!(
store
.find_dynamic(0, TypeId::of::<MyObjectA>())
.next()
.unwrap(),
store.cast_to_dynamic(handle0)
);
assert_eq!(
store
.find_dynamic(2, TypeId::of::<MyObjectA>())
.next()
.unwrap(),
store.cast_to_dynamic(handle2)
);
{
let objects: Vec<_> = store.find::<MyObjectA>(0).collect();
assert_eq!(objects, vec![handle0, handle3]);
}
{
let objects: Vec<_> = store.find::<MyObjectA>(2).collect();
assert_eq!(objects, vec![handle2]);
}
{
let objects: Vec<_> = store.find::<MyObjectB>(0).collect();
assert_eq!(objects, vec![handle1]);
}
{
let objects: Vec<_> = store.find::<MyObjectB>(2).collect();
assert_eq!(objects, vec![]);
}
{
let debug_handles: Vec<_> = store.find::<dyn std::fmt::Debug>(0).collect();
assert_eq!(debug_handles.len(), 2);
assert_eq!(
format!("{:?}", store.get(debug_handles[0])),
"MyObjectA { a: \"test\", b: 4 }"
);
assert_eq!(
format!("{:?}", store.get(debug_handles[1])),
"MyObjectA { a: \"baz\", b: 1 }"
);
}
{
let handles: Vec<_> = store.find::<dyn Object>(0).collect();
assert_eq!(handles.len(), 3);
assert_eq!(
store.cast_to_dynamic(handles[0]),
store.cast_to_dynamic(handle0)
);
assert_eq!(
store.cast_to_dynamic(handles[1]),
store.cast_to_dynamic(handle3)
);
assert_eq!(
store.cast_to_dynamic(handles[2]),
store.cast_to_dynamic(handle1)
);
}
}
}