Skip to content

Commit

Permalink
Add can_push variants
Browse files Browse the repository at this point in the history
Separate implementation to clean up return type signature.

Signed-off-by: Tin Svagelj <tin.svagelj@live.com>
  • Loading branch information
Caellian committed Sep 17, 2023
1 parent e1bc0f7 commit b78ade9
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 82 deletions.
40 changes: 11 additions & 29 deletions src/details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,8 @@ pub trait StorageDetails: ImplBase {

/// Returns whether a given layout can be stored or returns an error if
/// [`AllocationTracker`] can't be stored.
fn peek_next(
state: &Self::StorageState,
layout: Layout,
) -> Result<Option<ByteRange>, ContiguousMemoryError>;
fn peek_next(state: &Self::StorageState, layout: Layout)
-> Self::LockResult<Option<ByteRange>>;
}

impl StorageDetails for ImplConcurrent {
Expand Down Expand Up @@ -246,7 +244,7 @@ impl StorageDetails for ImplConcurrent {
fn peek_next(
state: &Self::StorageState,
layout: Layout,
) -> Result<Option<ByteRange>, ContiguousMemoryError> {
) -> Result<Option<ByteRange>, LockingError> {
let lock = state.tracker.lock_named(LockSource::AllocationTracker)?;
Ok(lock.peek_next(layout))
}
Expand Down Expand Up @@ -314,11 +312,7 @@ impl StorageDetails for ImplDefault {
state: &mut Self::StorageState,
new_capacity: usize,
) -> Result<(), ContiguousMemoryError> {
state
.tracker
.try_borrow_mut()
.map_err(|_| ContiguousMemoryError::TrackerInUse)?
.resize(new_capacity)
state.tracker.borrow_mut().resize(new_capacity)
}

fn shrink_tracker(state: &mut Self::StorageState) -> Option<usize> {
Expand All @@ -330,22 +324,13 @@ impl StorageDetails for ImplDefault {
layout: Layout,
) -> Result<ByteRange, ContiguousMemoryError> {
let base = Self::get_base(&state.base) as usize;
let mut tracker = state
.tracker
.try_borrow_mut()
.map_err(|_| ContiguousMemoryError::TrackerInUse)?;
let mut tracker = state.tracker.borrow_mut();
tracker.take_next(base, layout)
}

fn peek_next(
state: &Self::StorageState,
layout: Layout,
) -> Result<Option<ByteRange>, ContiguousMemoryError> {
let tracker = state
.tracker
.try_borrow()
.map_err(|_| ContiguousMemoryError::TrackerInUse)?;
Ok(tracker.peek_next(layout))
fn peek_next(state: &Self::StorageState, layout: Layout) -> Option<ByteRange> {
let tracker = state.tracker.borrow();
tracker.peek_next(layout)
}
}

Expand Down Expand Up @@ -425,11 +410,8 @@ impl StorageDetails for ImplUnsafe {
state.tracker.take_next(base, layout)
}

fn peek_next(
state: &Self::StorageState,
layout: Layout,
) -> Result<Option<ByteRange>, ContiguousMemoryError> {
Ok(state.tracker.peek_next(layout))
fn peek_next(state: &Self::StorageState, layout: Layout) -> Option<ByteRange> {
state.tracker.peek_next(layout)
}
}

Expand Down Expand Up @@ -714,7 +696,7 @@ impl StoreDataDetails for ImplUnsafe {
unsafe {
core::ptr::copy_nonoverlapping(data as *mut u8, found, layout.size());
}

(found, taken)
}
Err(other) => return Err(other),
Expand Down
7 changes: 0 additions & 7 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,6 @@ pub enum ContiguousMemoryError {
},
/// Indicates that a mutex wasn't lockable.
Lock(LockingError),
/// Attempted to borrow the
/// [`AllocationTracker`](crate::tracker::AllocationTracker) which is
/// already in use.
TrackerInUse,
/// Indicates that the provided [`Layout`](core::alloc::Layout) is invalid.
Layout(
/// The underlying error that caused the [`Layout`](core::alloc::Layout)
Expand Down Expand Up @@ -189,9 +185,6 @@ impl Display for ContiguousMemoryError {
min_required
),
ContiguousMemoryError::Lock(it) => write!(f, "Poison error: {}", it),
ContiguousMemoryError::TrackerInUse => {
write!(f, "Cannot borrow AllocationTracker: it is already in use")
}
ContiguousMemoryError::Layout(it) => write!(f, "Layout error: {}", it),
ContiguousMemoryError::BorrowMut(it) => write!(f, "Borrow mutable error: {}", it),
}
Expand Down
152 changes: 106 additions & 46 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,45 +150,6 @@ impl<Impl: ImplDetails> ContiguousMemoryStorage<Impl> {
Ok(moved)
}

/// Returns `true` if the provided value can be stored without growing the
/// container.
///
/// It's usually better to try storing the value directly and then handle
/// the case where it wasn't stored (for unsafe implementation).
///
/// # Example
///
/// ```rust
/// # use contiguous_mem::UnsafeContiguousMemory;
/// # use core::mem::size_of_val;
/// let mut storage = UnsafeContiguousMemory::new(0);
/// let value = [2, 4, 8, 16];
///
/// # assert_eq!(storage.can_push::<Vec<i32>>().unwrap(), false);
/// if !storage.can_push::<Vec<i32>>().unwrap() {
/// storage.resize(storage.get_capacity() + size_of_val(&value));
///
/// // ...update old pointers...
/// }
///
/// let stored_value =
/// storage.push(value).expect("unable to store after growing the container");
/// ```
///
/// # Errors
///
/// If the `AllocationTracker` can't be immutably accesed, a
/// [`ContiguousMemoryError::TrackerInUse`] error is returned.
///
/// For concurrent implementation a [`ContiguousMemoryError::Lock`] is
/// returned under same conditions.
///
/// Unsafe implementation never fails.
pub fn can_push<T: StoreRequirements>(&self) -> Result<bool, ContiguousMemoryError> {
let layout = Layout::new::<T>();
Ok(Impl::peek_next(&self.inner, layout)?.is_some())
}

/// Stores a `value` of type `T` in the contiguous memory block and returns
/// a reference or a pointer pointing to it.
///
Expand All @@ -197,7 +158,7 @@ impl<Impl: ImplDetails> ContiguousMemoryStorage<Impl> {
///
/// Returned value is implementation specific:
///
/// |Implementation | Result | Alias |
/// | Implementation | Result | Alias |
/// |-|:-:|:-:|
/// |[Default](ImplDefault)|[`ContiguousEntryRef<T>`](refs::ContiguousEntryRef)|[`CERef`](refs::CERef)|
/// |[Concurrent](ImplConcurrent)|[`SyncContiguousEntryRef<T>`](refs::SyncContiguousEntryRef)|[`SCERef`](refs::SCERef)|
Expand Down Expand Up @@ -269,10 +230,6 @@ impl<Impl: ImplDetails> ContiguousMemoryStorage<Impl> {
/// Assumes value is stored at the provided _relative_ `position` in
/// managed memory and returns a pointer or a reference to it.
///
/// This functions isn't unsafe because creating an invalid pointer isn't
/// considered unsafe. Responsibility for guaranteeing safety falls on
/// code that's dereferencing the pointer.
///
/// # Example
///
/// ```rust
Expand All @@ -292,6 +249,12 @@ impl<Impl: ImplDetails> ContiguousMemoryStorage<Impl> {
/// assert_eq!(*new_position, 278u32);
/// }
/// ```
///
/// # Safety
///
/// This functions isn't unsafe because creating an invalid pointer isn't
/// considered unsafe. Responsibility for guaranteeing safety falls on
/// code that's dereferencing the pointer.
pub fn assume_stored<T: StoreRequirements>(
&self,
position: usize,
Expand Down Expand Up @@ -322,6 +285,26 @@ impl<Impl: ImplDetails> ContiguousMemoryStorage<Impl> {
}

impl ContiguousMemoryStorage<ImplDefault> {
/// Returns `true` if provided generic type `T` can be stored without
/// growing the container.
pub fn can_push<T: StoreRequirements>(&self) -> bool {
let layout = Layout::new::<T>();
ImplDefault::peek_next(&self.inner, layout).is_some()
}

/// Returns `true` if the provided `value` can be stored without growing the
/// container.
pub fn can_push_value<T: StoreRequirements>(&self, value: &T) -> bool {
let layout = Layout::for_value(value);
ImplDefault::peek_next(&self.inner, layout).is_some()
}

/// Returns `true` if the provided `layout` can be stored without growing
/// the container.
pub fn can_push_layout(&self, layout: Layout) -> bool {
ImplDefault::peek_next(&self.inner, layout).is_some()
}

/// Shrinks the allocated memory to fit the currently stored data and
/// returns the new capacity.
pub fn shrink_to_fit(&mut self) -> usize {
Expand All @@ -335,8 +318,43 @@ impl ContiguousMemoryStorage<ImplDefault> {
}

impl ContiguousMemoryStorage<ImplConcurrent> {
/// Returns `true` if provided generic type `T` can be stored without
/// growing the container or a [`LockingError::Poisoned`] error if
/// allocation tracker mutex has been poisoned.
///
/// This function will block the current tread until internal allocation
/// tracked doesn't become available.
pub fn can_push<T: StoreRequirements>(&self) -> Result<bool, LockingError> {
let layout = Layout::new::<T>();
ImplConcurrent::peek_next(&self.inner, layout).map(|it| it.is_some())
}

/// Returns `true` if the provided `value` can be stored without growing the
/// container or a [`LockingError::Poisoned`] error if allocation tracker
/// mutex has been poisoned.
///
/// This function will block the current tread until internal allocation
/// tracked doesn't become available.
pub fn can_push_value<T: StoreRequirements>(&self, value: &T) -> Result<bool, LockingError> {
let layout = Layout::for_value(value);
ImplConcurrent::peek_next(&self.inner, layout).map(|it| it.is_some())
}

/// Returns `true` if the provided `layout` can be stored without growing
/// the container or a [`LockingError::Poisoned`] error if allocation
/// tracker mutex has been poisoned.
///
/// This function will block the current tread until internal allocation
/// tracked doesn't become available.
pub fn can_push_layout(&self, layout: Layout) -> Result<bool, LockingError> {
ImplConcurrent::peek_next(&self.inner, layout).map(|it| it.is_some())
}

/// Shrinks the allocated memory to fit the currently stored data and
/// returns the new capacity.
///
/// This function will block the current tread until internal allocation
/// tracked doesn't become available.
pub fn shrink_to_fit(&mut self) -> Result<usize, LockingError> {
if let Some(shrunk) = ImplConcurrent::shrink_tracker(&mut self.inner)? {
self.resize(shrunk).expect("unable to shrink container");
Expand All @@ -348,6 +366,48 @@ impl ContiguousMemoryStorage<ImplConcurrent> {
}

impl ContiguousMemoryStorage<ImplUnsafe> {
/// Returns `true` if the provided value can be stored without growing the
/// container.
///
/// It's usually clearer to try storing the value directly and then handle
/// the case where it wasn't stored through error matching.
///
/// # Example
///
/// ```rust
/// # use contiguous_mem::UnsafeContiguousMemory;
/// # use core::mem::size_of_val;
/// let mut storage = UnsafeContiguousMemory::new(0);
/// let value = [2, 4, 8, 16];
///
/// # assert_eq!(storage.can_push::<Vec<i32>>().unwrap(), false);
/// if !storage.can_push::<Vec<i32>>().unwrap() {
/// storage.resize(storage.get_capacity() + size_of_val(&value));
///
/// // ...update old pointers...
/// }
///
/// let stored_value =
/// storage.push(value).expect("unable to store after growing the container");
/// ```
pub fn can_push<T: StoreRequirements>(&self) -> bool {
let layout = Layout::new::<T>();
ImplUnsafe::peek_next(&self.inner, layout).is_some()
}

/// Returns `true` if the provided `value` can be stored without growing the
/// container.
pub fn can_push_value<T: StoreRequirements>(&self, value: &T) -> bool {
let layout = Layout::for_value(value);
ImplUnsafe::peek_next(&self.inner, layout).is_some()
}

/// Returns `true` if the provided `layout` can be stored without growing
/// the container.
pub fn can_push_layout(&self, layout: Layout) -> bool {
ImplUnsafe::peek_next(&self.inner, layout).is_some()
}

/// Shrinks the allocated memory to fit the currently stored data and
/// returns the new capacity.
pub fn shrink_to_fit(&mut self) -> usize {
Expand Down Expand Up @@ -660,7 +720,7 @@ mod test {
let _a = memory.push(1u32);
let _b = memory.push(2u32);
let _c = memory.push(3u32);
assert_eq!(memory.can_push::<u32>().unwrap(), false);
assert_eq!(memory.can_push::<u32>(), false);
let _d = memory.push(4u32);
assert_eq!(memory.get_capacity(), 24);
}
Expand All @@ -669,7 +729,7 @@ mod test {
{
let _a = memory.push(1u16);
let _b = memory.push(2u16);
assert_eq!(memory.can_push::<u64>().unwrap(), false);
assert_eq!(memory.can_push::<u64>(), false);
let _c = memory.push(3u64);
// expecting 12, but due to alignment we're skipping two u16 slots
// and then double the size as remaining (aligned) 4 bytes aren't
Expand Down

0 comments on commit b78ade9

Please sign in to comment.