From 3cdc21832bbad905f5833ff7bffa0f99b4c58888 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Tue, 28 Jun 2022 20:46:55 +0200 Subject: [PATCH] :pencil: More tests and some docs --- src/lib.rs | 367 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 334 insertions(+), 33 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3cf405f..bb1c2cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,68 @@ +//! # `VecCell`, a variant of `Vec` with interior mutability +//! +//! This crate contains the [`VecCell`](VecCell) type, which implements a contiguous, +//! growable array (similar to [`Vec`](Vec)) with interior mutability (similar to [`RefCell`](std::cell::RefCell)). +//! +//! You would use this crate if: +//! - You need a `Vec` with [interior mutability](std::cell#when-to-choose-interior-mutability) +//! - You only want mutable access to one element at a time +//! - You need a constant memory cost +//! +//! You wouldn't need this crate if: +//! - You don't need interior mutability (use `Vec`) +//! - You want mutable access to multiple elements at a time (use `Vec>`) +//! - You need to share the array across multiple threads (use `Vec>` or `Arc>>`) + #![feature(type_alias_impl_trait)] use std::cell::{Cell, UnsafeCell}; use std::fmt::{self, Debug}; use std::ops::{Deref, DerefMut}; +/// A contiguous, growable array type with interior mutability. +/// +/// This type allows to get a mutable reference ([`VecRefMut`]) to an index `i`, while letting you access immutably ([`VecRef`]) elements at indices `j != i`. +/// New mutable references may only be created if there are no active mutable *or immutable* references. +/// New immutable references may only be created for indices that aren't mutably borrowed. +/// +/// - To borrow an item immutably, use [`VecCell::get`]. +/// - To borrow an item mutably, use [`VecCell::borrow_mut`]. +/// - If you have a mutable reference to the `VecCell`, then you may also use [`VecCell::get_mut`]. +/// - You may access the internal `Vec>` using [`VecCell::inner`] and [`VecCell::inner_mut`]. +/// +/// This type is essentially a more restricted version of `Vec>`, +/// with the benefit of having a lower memory footprint since we only need +/// `sizeof!(usize)+sizeof!(Option)` bytes instead of `N*sizeof!(usize)` bytes of memory to prevent aliasing. +/// +/// # Examples +/// +/// ``` +/// use veccell::VecCell; +/// +/// fn update(current: &mut usize, prev: &usize) { +/// *current += *prev; +/// } +/// +/// // Create an empty vec +/// let mut vec: VecCell = VecCell::new(); +/// +/// // Add the numbers from 0 to 9 in vec +/// for n in 0..10 { +/// vec.push(n); +/// } +/// +/// for index in 1..10 { +/// // Get a mutable reference *then* an immutable reference +/// // (would fail if the order was reversed) +/// let mut current = vec.borrow_mut(index).unwrap(); +/// let prev = vec.get(index - 1).unwrap(); +/// +/// // Give both references to update +/// update(current.get_mut(), prev.get()); +/// } +/// +/// assert_eq!(vec, vec![0, 1, 3, 6, 10, 15, 21, 28, 36, 45]); +/// ``` /// /// # Safety /// @@ -15,7 +74,6 @@ use std::ops::{Deref, DerefMut}; /// - (IV) a mutable reference may only be created if: /// - (IV-a) exclusive access to the `VecCell` is guaranteed (`&mut VecCell`) /// - (IV-b) it is created from a mutable guard, and the mutable guard's lifetime does not exceed that of the mutable guard -#[derive(Debug)] pub struct VecCell { mut_borrow: Cell>, borrows: Cell, @@ -23,6 +81,7 @@ pub struct VecCell { } impl VecCell { + /// Creates a new `VecCell`. pub fn new() -> Self { Self { mut_borrow: Cell::new(None), @@ -31,6 +90,7 @@ impl VecCell { } } + /// Creates a new `VecCell` with `capacity` as capacity. pub fn with_capacity(capacity: usize) -> Self { Self { mut_borrow: Cell::new(None), @@ -39,57 +99,109 @@ impl VecCell { } } + /// Returns the raw parts of a `VecCell` in the format `(inner_vec, mut_borrow, borrows)`. + /// If no reference was [forgotten](std::mem::forget), then `mut_borrow == None` and `borrows == 0`. pub fn into_raw_parts(self) -> (Vec>, Option, usize) { (self.inner, self.mut_borrow.into_inner(), self.borrows.into_inner()) } - pub unsafe fn from_raw_parts(inner: Vec>, mut_borrow: Option, borrows: usize) -> Self { - Self { - inner, - borrows: Cell::new(borrows), - mut_borrow: Cell::new(mut_borrow), - } - } - + /// Returns the length of the `VecCell`, ie. the number of elements in the array. + /// + /// See [`Vec::len()`] for more information. #[inline] pub fn len(&self) -> usize { self.inner.len() } + /// Returns the capacity of the `VecCell`, ie. the number of elements that can be added before we need to realloc. + /// The following holds true at any time: `vec.len() <= vec.capacity()`. + /// + /// See [`Vec::capacity()`] for more information. #[inline] pub fn capacity(&self) -> usize { self.inner.capacity() } + /// Reserves space in the buffer for at least `additional` more elements. + /// + /// See [`Vec::reserve()`] for more information. #[inline] pub fn reserve(&mut self, additional: usize) { self.inner.reserve(additional); } + /// Removes the element at index `index`, shifting the elements after it and returning that index. + /// + /// See [`Vec::remove()`] for more information. + /// + /// # Panics + /// + /// Panics if `index >= self.len()`. #[inline] pub fn remove(&mut self, index: usize) -> T { self.inner.remove(index).into_inner() } + /// Removes the element at index `index`, replacing it with the last element. + /// + /// See [`Vec::swap_remove()`] for more information. + /// + /// # Panics + /// + /// Panics if `index >= self.len()`. #[inline] pub fn swap_remove(&mut self, index: usize) -> T { self.inner.swap_remove(index).into_inner() } - #[inline] - pub fn inner(&self) -> &Vec> { - &self.inner - } - - #[inline] - pub fn inner_mut(&mut self) -> &mut Vec> { - &mut self.inner - } - + /// Returns the number of immutable borrows to elements of the `VecCell`. + /// + /// # Example + /// + /// ``` + /// # use veccell::*; + /// let mut vec: VecCell = VecCell::new(0); + /// + /// vec.push(2); + /// vec.push(5); + /// vec.push(9); + /// + /// // There are no borrows at this point + /// assert_eq!(x.borrows(), 0); + /// + /// let x = vec.get(1); + /// + /// // There is exactly one borrow at this point + /// assert_eq!(x.borrows(), 1); + /// + /// std::mem::drop(x); // x lives up to here + /// ``` pub fn borrows(&self) -> usize { self.borrows.get() } + /// Returns the index of the mutable borrows of the `VecCell`, if any. + /// + /// # Example + /// + /// ``` + /// # use veccell::*; + /// let mut vec: VecCell = VecCell::new(0); + /// + /// vec.push(2); + /// vec.push(5); + /// vec.push(9); + /// + /// // There is no mutable borrow at this point + /// assert_eq!(x.mut_borrow(), None); + /// + /// let x = vec.get_mut(2); + /// + /// // There is a mutable borrow of element 2 at this point + /// assert_eq!(x.mut_borrow(), Some(2)); + /// + /// std::mem::drop(x); // x lives up to here + /// ``` pub fn mut_borrow(&self) -> Option { self.mut_borrow.get() } @@ -99,7 +211,6 @@ impl VecCell { } pub fn pop(&mut self) -> Option { - debug_assert!(self.mut_borrow.get().is_none()); self.inner.pop().map(UnsafeCell::into_inner) } @@ -107,22 +218,16 @@ impl VecCell { VecRef::new(self, index) } - pub unsafe fn get_unchecked(&self, index: usize) -> &T { - &*self.inner.get_unchecked(index).get() - } - pub fn borrow_mut<'b>(&'b self, index: usize) -> Option> { VecRefMut::new(self, index) } + // TODO: reborrow_mut? + pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { self.inner.get_mut(index).map(UnsafeCell::get_mut) } - pub unsafe fn get_mut_unchecked(&self, index: usize) -> &mut T { - &mut *self.inner.get_unchecked(index).get() - } - pub fn iter<'b>(&'b self) -> impl Iterator> { self.into_iter() } @@ -130,6 +235,84 @@ impl VecCell { pub fn iter_mut<'b>(&'b mut self) -> impl Iterator { self.inner.iter_mut().map(UnsafeCell::get_mut) } + + pub fn try_iter<'b>(&'b self) -> impl Iterator>> { + (0..self.len()).map(|index| { + self.get(index) + }) + } + + /// Returns a reference to the inner buffer. + /// You may safely perform immutable operations on it. + #[inline] + pub fn inner(&self) -> &Vec> { + &self.inner + } + + /// Returns a mutable reference to the inner buffer. + /// You may safely perform mutable operations on it. + #[inline] + pub fn inner_mut(&mut self) -> &mut Vec> { + &mut self.inner + } + + // == Unsafe functions section == + + /// Alternative to `get`, which skips all checks and returns a mutable reference. + /// Neither the `mut_borrow`, nor the `borrows` buffer will be updated or read, + /// so make sure that no exclusive reference to the element at `index` is made. + /// + /// If `index >= len`, then calling this function is undefined behavior. + pub unsafe fn get_unchecked(&self, index: usize) -> &T { + &*self.inner.get_unchecked(index).get() + } + + /// Alternative to `borrow_mut`, which skips all checks and returns a mutable reference. + /// Neither the `mut_borrow`, nor the `borrows` buffer will be updated or read, + /// so make sure that this is the only reference to the element at `index`. + /// + /// If `index >= len`, then calling this function is undefined behavior. + pub unsafe fn get_mut_unchecked(&self, index: usize) -> &mut T { + &mut *self.inner.get_unchecked(index).get() + } + + /// Constructs a `VecCell` from its raw parts. + pub unsafe fn from_raw_parts(inner: Vec>, mut_borrow: Option, borrows: usize) -> Self { + Self { + inner, + borrows: Cell::new(borrows), + mut_borrow: Cell::new(mut_borrow), + } + } +} + +impl fmt::Debug for VecCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + enum BorrowStatus { + Ok(T), + Borrowed, + } + + impl fmt::Debug for BorrowStatus> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BorrowStatus::Ok(x) => fmt::Debug::fmt(x.get(), f), + BorrowStatus::Borrowed => write!(f, "(borrowed)"), + } + } + } + + f.debug_struct("VecCell") + .field("borrows", &self.borrows.get()) + .field("mut_borrow", &self.mut_borrow.get()) + .field("inner", &self.try_iter().map(|x| { + match x { + Some(y) => BorrowStatus::Ok(y), + None => BorrowStatus::Borrowed, + } + }).collect::>()) + .finish() + } } impl<'a, T: 'a> IntoIterator for &'a VecCell { @@ -141,7 +324,10 @@ impl<'a, T: 'a> IntoIterator for &'a VecCell { /// Panics if a value is currently mutably borrowed fn into_iter(self) -> Self::IntoIter { (0..self.len()).map(|index| { - self.get(index).expect("An element of VecCell is currently mutably borrowed") + match self.get(index) { + Some(x) => x, + None => panic!("Error while borrowing immutably element {} of VecCell: already mutably borrowed", index), + } }) } } @@ -219,6 +405,7 @@ impl From> for VecCell { } } +/// Wraps a borrowed reference from a [`VecCell`]. pub struct VecRef<'a, T: ?Sized> { borrows: &'a Cell, value: &'a T, @@ -248,6 +435,15 @@ impl<'a, T: ?Sized> VecRef<'a, T> { }) } + fn from(value: &'a T, borrows: &'a Cell) -> Option { + borrows.set(borrows.get().checked_add(1)?); + + Some(Self { + borrows, + value + }) + } + pub fn get(&self) -> &T { &*self.value } @@ -256,10 +452,8 @@ impl<'a, T: ?Sized> VecRef<'a, T> { where F: FnOnce(&T) -> &U { - VecRef { - value: f(original.value), - borrows: original.borrows - } + VecRef::from(f(original.value), original.borrows).expect("Error creating a new VecRef: integer overflow") + // original is dropped here } } @@ -299,6 +493,13 @@ impl<'a, T: PartialEq + ?Sized> PartialEq for VecRef<'a, T> { } } +impl<'a, T: ?Sized> Clone for VecRef<'a, T> { + fn clone(&self) -> Self { + Self::from(self.value, self.borrows).expect("Error creating a new VecRef: integer overflow") + } +} + +/// Wraps a mutably borrowed value from a [`VecCell`]. pub struct VecRefMut<'a, T: ?Sized> { mut_borrow: &'a Cell>, value: &'a mut T, @@ -517,4 +718,104 @@ mod test { assert!(vec.get(0).is_some()); assert!(vec.get(1).is_none()); } + + #[test] + fn test_borrow_clone() { + let mut vec: VecCell = VecCell::new(); + + vec.push(1); + vec.push(2); + + let x = vec.get(0); + assert_eq!(vec.borrows(), 1); + assert_eq!(vec.borrows(), 1); + assert_eq!(vec.mut_borrow(), None); + + let y = x.clone(); + + assert_eq!(vec.borrows(), 2); + assert_eq!(vec.borrows(), 2); + assert_eq!(vec.mut_borrow(), None); + + assert_eq!(x, y); + + std::mem::drop(x); + assert_eq!(vec.borrows(), 1); + std::mem::drop(y); + assert_eq!(vec.borrows(), 0); + + let x = vec.get(0); + assert_eq!(vec.borrows(), 1); + let y = vec.get(0); + assert_eq!(vec.borrows(), 2); + let z = x.clone(); + assert_eq!(vec.borrows(), 3); + + std::mem::drop(z); + assert_eq!(vec.borrows(), 2); + std::mem::drop(y); + assert_eq!(vec.borrows(), 1); + std::mem::drop(x); + assert_eq!(vec.borrows(), 0); + } + + #[test] + fn test_borrow_mut() { + let mut vec: VecCell = VecCell::new(); + + vec.push(1); + vec.push(2); + + let x = vec.get(0); + assert!(x.is_some()); + + let y = vec.borrow_mut(0); + assert!(y.is_none()); + + let y = vec.borrow_mut(1); + assert!(y.is_none()); + + std::mem::drop(x); + + let y = vec.borrow_mut(0); + assert!(y.is_some()); + + let z = vec.borrow_mut(1); + assert!(z.is_none()); + + std::mem::drop(y); + } + + #[test] + fn test_iter() { + let mut vec: Vec = Vec::new(); + let mut vec2: VecCell = VecCell::new(); + + for x in 0..10 { + vec.push(x); + vec2.push(x); + } + + let vec3 = vec2.iter().map(|x| *x).collect::>(); + + assert_eq!(vec, vec3); + } + + #[test] + #[should_panic] + fn test_iter_panic() { + let mut vec: VecCell = VecCell::new(); + + for x in 0..10 { + vec.push(x); + } + + let y = vec.borrow_mut(3); + + for _x in vec.iter() { + // noop + } + + std::mem::drop(y); + } }