Skip to content

Commit

Permalink
Add reference downcasting.
Browse files Browse the repository at this point in the history
Add copy_storage utility method for unsafe implementation.
Rename RegionBorrowed to RegionBorrowedError.
Improve docs.

Signed-off-by: Tin Svagelj <tin.svagelj@live.com>
  • Loading branch information
Caellian committed Aug 31, 2023
1 parent b503611 commit 30388d7
Show file tree
Hide file tree
Showing 5 changed files with 415 additions and 168 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "contiguous-mem"
version = "0.3.0"
version = "0.3.1"
edition = "2021"
description = "A contiguous memory storage"
authors = ["Tin Švagelj <tin.svagelj@live.com>"]
Expand All @@ -26,3 +26,6 @@ debug = []
leak_data = []
ptr_metadata = []
error_in_core = []

[package.metadata.docs.rs]
all-features = true
224 changes: 214 additions & 10 deletions src/details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! End-users aren't meant to interact with traits defined in this module
//! directly and they exist solely to simplify implementation of
//! [`ContiguousMemoryStorage`](crate::ContiguousMemoryStorage) by erasing
//! [`ContiguousMemoryStorage`](ContiguousMemoryStorage) by erasing
//! type details of different implementations.
//!
//! Any changes to these traits aren't considered a breaking change and won't
Expand All @@ -11,6 +11,7 @@
use core::{
alloc::{Layout, LayoutError},
cell::{Cell, RefCell},
mem::size_of,
ptr::null_mut,
};

Expand All @@ -27,7 +28,7 @@ use crate::{
refs::{sealed::*, ContiguousMemoryRef, SyncContiguousMemoryRef},
tracker::AllocationTracker,
types::*,
BaseLocation, ContiguousMemoryState,
BaseLocation, ContiguousMemoryState, ContiguousMemoryStorage,
};

/// Implementation details shared between [storage](StorageDetails) and
Expand All @@ -51,6 +52,8 @@ pub trait ImplBase: Sized {
/// A marker struct representing the behavior specialization that does not
/// require thread-safety. This implementation skips mutexes, making it faster
/// but unsuitable for concurrent usage.
///
/// For example usage of default implementation see: [`ContiguousMemory`](crate::ContiguousMemory)
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ImplDefault;
Expand All @@ -64,6 +67,9 @@ impl ImplBase for ImplDefault {
/// operations. This implementation ensures that the container's operations can
/// be used safely in asynchronous contexts, utilizing mutexes to prevent data
/// races.
///
/// For example usage of default implementation see:
/// [`SyncContiguousMemory`](crate::SyncContiguousMemory)
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ImplConcurrent;
Expand All @@ -78,6 +84,9 @@ impl ImplBase for ImplConcurrent {
/// A marker struct representing the behavior specialization for unsafe
/// implementation. Should be used when the container is guaranteed to outlive
/// any pointers to data contained in represented memory block.
///
/// For example usage of default implementation see:
/// [`UnsafeContiguousMemory`](crate::UnsafeContiguousMemory)
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ImplUnsafe;
Expand All @@ -88,7 +97,7 @@ impl ImplBase for ImplUnsafe {
}

/// Implementation details of
/// [`ContiguousMemoryStorage`](crate::ContiguousMemoryStorage).
/// [`ContiguousMemoryStorage`](ContiguousMemoryStorage).
pub trait StorageDetails: ImplBase {
/// The type representing the base memory and allocation tracking.
type Base;
Expand Down Expand Up @@ -426,7 +435,7 @@ pub trait ReferenceDetails: ImplBase {
fn build_ref<T: StoreRequirements>(
state: &Self::StorageState,
addr: *mut T,
range: &ByteRange,
range: ByteRange,
) -> Self::ReferenceType<T>;

/// Marks reference state as no longer being borrowed.
Expand Down Expand Up @@ -456,7 +465,7 @@ impl ReferenceDetails for ImplConcurrent {
fn build_ref<T: StoreRequirements>(
state: &Self::StorageState,
_addr: *mut T,
range: &ByteRange,
range: ByteRange,
) -> Self::ReferenceType<T> {
SyncContiguousMemoryRef {
inner: Arc::new(ReferenceState {
Expand Down Expand Up @@ -495,7 +504,7 @@ impl ReferenceDetails for ImplDefault {
fn build_ref<T: StoreRequirements>(
state: &Self::StorageState,
_addr: *mut T,
range: &ByteRange,
range: ByteRange,
) -> Self::ReferenceType<T> {
ContiguousMemoryRef {
inner: Rc::new(ReferenceState {
Expand Down Expand Up @@ -537,13 +546,208 @@ impl ReferenceDetails for ImplUnsafe {
fn build_ref<T>(
_base: &Self::StorageState,
addr: *mut T,
_range: &ByteRange,
_range: ByteRange,
) -> Self::ReferenceType<T> {
addr
}
}

pub trait StoreDataDetails: StorageDetails {
unsafe fn store_data<T: StoreRequirements>(
state: &mut Self::StorageState,
data: *mut T,
layout: Layout,
) -> Self::StoreResult<T>;

fn assume_stored<T: StoreRequirements>(
state: &Self::StorageState,
position: usize,
) -> Self::LockResult<Self::ReferenceType<T>>;
}

impl StoreDataDetails for ImplConcurrent {
unsafe fn store_data<T: StoreRequirements>(
state: &mut Self::StorageState,
data: *mut T,
layout: Layout,
) -> Result<SyncContiguousMemoryRef<T>, LockingError> {
let (addr, range) = loop {
match ImplConcurrent::store_next(state, layout) {
Ok(taken) => {
let found = (taken.0
+ ImplConcurrent::get_base(&ImplConcurrent::deref_state(state).base)?
as usize) as *mut u8;
unsafe { core::ptr::copy_nonoverlapping(data as *mut u8, found, layout.size()) }
break (found, taken);
}
Err(ContiguousMemoryError::NoStorageLeft) => {
match ImplConcurrent::resize_container(state, ImplConcurrent::get_capacity(&ImplConcurrent::deref_state(state).capacity) * 2) {
Ok(_) => {}
Err(ContiguousMemoryError::Lock(locking_err)) => return Err(locking_err),
Err(other) => unreachable!(
"reached unexpected error while growing the container to store data: {:?}",
other
),
};
}
Err(ContiguousMemoryError::Lock(locking_err)) => return Err(locking_err),
Err(other) => unreachable!(
"reached unexpected error while looking for next region to store data: {:?}",
other
),
}
};

Ok(ImplConcurrent::build_ref(state, addr as *mut T, range))
}

fn assume_stored<T: StoreRequirements>(
state: &Self::StorageState,
position: usize,
) -> Result<SyncContiguousMemoryRef<T>, LockingError> {
let addr = unsafe {
ImplConcurrent::get_base(&ImplConcurrent::deref_state(state).base)?.add(position)
};
Ok(ImplConcurrent::build_ref(
state,
addr as *mut T,
ByteRange(position, size_of::<T>()),
))
}
}

impl StoreDataDetails for ImplDefault {
unsafe fn store_data<T: StoreRequirements>(
state: &mut Self::StorageState,
data: *mut T,
layout: Layout,
) -> ContiguousMemoryRef<T> {
let (addr, range) = loop {
match ImplDefault::store_next(state, layout) {
Ok(taken) => {
let found = (taken.0
+ ImplDefault::get_base(&ImplDefault::deref_state(state).base) as usize)
as *mut u8;
unsafe {
core::ptr::copy_nonoverlapping(data as *mut u8, found, layout.size());
}
break (found, taken);
}
Err(ContiguousMemoryError::NoStorageLeft) => {
match ImplDefault::resize_container(state, ImplDefault::get_capacity(&ImplDefault::deref_state(state).capacity) * 2) {
Ok(_) => {},
Err(err) => unreachable!(
"reached unexpected error while growing the container to store data: {:?}",
err
),
}
}
Err(other) => unreachable!(
"reached unexpected error while looking for next region to store data: {:?}",
other
),
}
};

ImplDefault::build_ref(state, addr as *mut T, range)
}

fn assume_stored<T: StoreRequirements>(
state: &Self::StorageState,
position: usize,
) -> ContiguousMemoryRef<T> {
let addr =
unsafe { ImplDefault::get_base(&ImplDefault::deref_state(state).base).add(position) };
ImplDefault::build_ref(state, addr as *mut T, ByteRange(position, size_of::<T>()))
}
}

impl StoreDataDetails for ImplUnsafe {
/// Returns a raw pointer (`*mut T`) to the stored value or
unsafe fn store_data<T: StoreRequirements>(
state: &mut Self::StorageState,
data: *mut T,
layout: Layout,
) -> Result<*mut T, ContiguousMemoryError> {
let (addr, range) = loop {
match ImplUnsafe::store_next(state, layout) {
Ok(taken) => {
let found = (taken.0
+ ImplUnsafe::get_base(&ImplUnsafe::deref_state(state).base) as usize)
as *mut u8;
unsafe {
core::ptr::copy_nonoverlapping(data as *mut u8, found, layout.size());
}
break (found, taken);
}
Err(other) => return Err(other),
}
};

Ok(ImplUnsafe::build_ref(state, addr as *mut T, range))
}

fn assume_stored<T: StoreRequirements>(state: &Self::StorageState, position: usize) -> *mut T {
let addr =
unsafe { ImplUnsafe::get_base(&ImplUnsafe::deref_state(state).base).add(position) };
ImplUnsafe::build_ref(
state,
addr as *mut T,
ByteRange(position, position + size_of::<T>()),
)
}
}

/// A deprecated trait for specializing store function across implementations.
///
/// These store functions are now available directly on ContiguousMemoryStorage
/// and implemented in a sealed module.
#[deprecated(
since = "0.3.1",
note = "Use methods available directly on ContiguousMemoryStorage"
)]
pub trait StoreData<Impl: ImplDetails> {
/// See [`ContiguousMemoryStorage::store_data`].
unsafe fn store_data<T: StoreRequirements>(
&mut self,
data: *mut T,
layout: Layout,
) -> Impl::StoreResult<T>;

/// See [`ContiguousMemoryStorage::assume_stored`].
unsafe fn assume_stored<T: StoreRequirements>(
&self,
position: usize,
) -> Impl::LockResult<Impl::ReferenceType<T>>;
}

#[allow(deprecated)]
impl<Impl: ImplDetails> StoreData<Impl> for ContiguousMemoryStorage<Impl> {
unsafe fn store_data<T: StoreRequirements>(
&mut self,
data: *mut T,
layout: Layout,
) -> Impl::StoreResult<T> {
Impl::store_data(&mut self.inner, data, layout)
}

unsafe fn assume_stored<T: StoreRequirements>(
&self,
position: usize,
) -> Impl::LockResult<Impl::ReferenceType<T>> {
Impl::assume_stored(&self.inner, position)
}
}

/// Trait representing requirements for implementation details of the
/// [`ContiguousMemoryStorage`](crate::ContiguousMemoryStorage).
pub trait ImplDetails: ImplBase + StorageDetails + ReferenceDetails + DebugReq {}
impl<Impl: ImplBase + StorageDetails + ReferenceDetails + DebugReq> ImplDetails for Impl {}
/// [`ContiguousMemoryStorage`](ContiguousMemoryStorage).
///
/// This trait is implemented by:
/// - [`ImplDefault`]
/// - [`ImplConcurrent`]
/// - [`ImplUnsafe`]
///
/// As none of the underlying traits can't be implemented, changes to this trait
/// aren't considered breaking and won't affect semver.
pub trait ImplDetails: ImplBase + StorageDetails + ReferenceDetails + StoreDataDetails {}
impl<Impl: ImplBase + StorageDetails + ReferenceDetails + StoreDataDetails> ImplDetails for Impl {}
14 changes: 10 additions & 4 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,19 @@ where
/// Error returned when concurrent mutable access is attempted to the same
/// memory region.
#[derive(Debug)]
pub struct RegionBorrowed {
pub struct RegionBorrowedError {
/// [`ByteRange`] that was attempted to be borrowed.
pub range: ByteRange,
}

#[deprecated(
since = "0.3.1",
note = "Renamed; use RegionBorrowedError"
)]
pub use RegionBorrowedError as RegionBorrowed;

#[cfg(any(feature = "std", feature = "error_in_core"))]
impl Display for RegionBorrowed {
impl Display for RegionBorrowedError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
Expand All @@ -110,7 +116,7 @@ impl Display for RegionBorrowed {
}

#[cfg(any(feature = "std", feature = "error_in_core"))]
impl Error for RegionBorrowed {
impl Error for RegionBorrowedError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
Expand Down Expand Up @@ -150,7 +156,7 @@ pub enum ContiguousMemoryError {
LayoutError,
),
/// Tried mutably borrowing already borrowed region of memory
BorrowMut(RegionBorrowed),
BorrowMut(RegionBorrowedError),
}

/// Represents possible poisoning sources for mutexes and locks.
Expand Down
Loading

0 comments on commit 30388d7

Please sign in to comment.