-
-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move
Handle
and ObjectId
to dedicated module
- Loading branch information
1 parent
7769599
commit 2d8241e
Showing
3 changed files
with
162 additions
and
157 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
use std::{any::type_name, fmt, hash::Hash, ops::Deref}; | ||
|
||
use super::store::StoreInner; | ||
|
||
/// A handle for an object | ||
/// | ||
/// You can get an instance of `Handle` by inserting an object into a store. A | ||
/// handle dereferences to the object it points to, via its [`Deref`] | ||
/// implementation. | ||
/// | ||
/// # Equality and Identity | ||
/// | ||
/// Equality of `Handle`s is defined via the objects they reference. If those | ||
/// objects are equal, the `Handle`s are considered equal. | ||
/// | ||
/// This is distinct from the *identity* of the referenced objects. Two objects | ||
/// might be equal, but they might be have been created at different times, for | ||
/// different reasons, and thus live in different slots in the storage. This is | ||
/// a relevant distinction when validating objects, as equal but not identical | ||
/// objects might be a sign of a bug. | ||
/// | ||
/// You can compare the identity of two objects through their `Handle`s, by | ||
/// comparing the values returned by [`Handle::id`]. | ||
pub struct Handle<T> { | ||
pub(super) store: StoreInner<T>, | ||
pub(super) ptr: *const Option<T>, | ||
} | ||
|
||
impl<T> Handle<T> { | ||
/// Access this pointer's unique id | ||
pub fn id(&self) -> ObjectId { | ||
ObjectId(self.ptr as u64) | ||
} | ||
|
||
/// Return a clone of the object this handle refers to | ||
pub fn clone_object(&self) -> T | ||
where | ||
T: Clone, | ||
{ | ||
self.deref().clone() | ||
} | ||
} | ||
|
||
impl<T> Deref for Handle<T> { | ||
type Target = T; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
// `Handle` keeps a reference to `StoreInner`. Since that is an `Arc` | ||
// under the hood, we know that as long as an instance of `Handle` | ||
// exists, the `StoreInner` its data lives in is still alive. Even if | ||
// the `Store` was dropped. | ||
// | ||
// The `Store` API ensures two things: | ||
// | ||
// 1. That no `Handle` is ever created, until the object it references | ||
// has at least been reserved. | ||
// 2. That the memory objects live in is never deallocated. | ||
// | ||
// That means that as long as a `Handle` exists, the object is | ||
// references has at least been reserved, and has not been deallocated. | ||
// | ||
// Given all this, we know that the following must be true: | ||
// | ||
// - The pointer is not null. | ||
// - The pointer is properly aligned. | ||
// - The pointer is dereferenceable. | ||
// - The pointer points to an initialized instance of `T`. | ||
// | ||
// Further, there is no way to (safely) get a `&mut` reference to any | ||
// object in a `Store`/`Block`. So we know that the aliasing rules for | ||
// the reference we return here are enforced. | ||
// | ||
// Furthermore, all of the code mentioned here is covered by unit tests, | ||
// which I've run successfully run under Miri. | ||
let cell = unsafe { &*self.ptr }; | ||
|
||
// Can only happen, if the object has been reserved, but the reservation | ||
// was never completed. | ||
cell.as_ref() | ||
.expect("Handle references non-existing object") | ||
} | ||
} | ||
|
||
impl<T> Clone for Handle<T> { | ||
fn clone(&self) -> Self { | ||
Self { | ||
store: self.store.clone(), | ||
ptr: self.ptr, | ||
} | ||
} | ||
} | ||
|
||
impl<T> Eq for Handle<T> where T: Eq {} | ||
|
||
impl<T> PartialEq for Handle<T> | ||
where | ||
T: PartialEq, | ||
{ | ||
fn eq(&self, other: &Self) -> bool { | ||
self.deref().eq(other.deref()) | ||
} | ||
} | ||
|
||
impl<T> Hash for Handle<T> | ||
where | ||
T: Hash, | ||
{ | ||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | ||
self.deref().hash(state) | ||
} | ||
} | ||
|
||
impl<T> Ord for Handle<T> | ||
where | ||
T: Ord, | ||
{ | ||
fn cmp(&self, other: &Self) -> std::cmp::Ordering { | ||
self.deref().cmp(other.deref()) | ||
} | ||
} | ||
|
||
impl<T> PartialOrd for Handle<T> | ||
where | ||
T: PartialOrd, | ||
{ | ||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | ||
self.deref().partial_cmp(other.deref()) | ||
} | ||
} | ||
|
||
impl<T> fmt::Debug for Handle<T> { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
let name = { | ||
let type_name = type_name::<T>(); | ||
match type_name.rsplit_once("::") { | ||
Some((_, name)) => name, | ||
None => type_name, | ||
} | ||
}; | ||
let id = self.id().0; | ||
|
||
write!(f, "{name} @ {id:#x}")?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
unsafe impl<T> Send for Handle<T> {} | ||
unsafe impl<T> Sync for Handle<T> {} | ||
|
||
/// Represents the ID of an object | ||
/// | ||
/// See [`Handle::id`]. | ||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] | ||
pub struct ObjectId(u64); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters