cl-structures: add a sized, monotype stack
This commit is contained in:
parent
ecde44910f
commit
af35dd1bb3
@ -2,6 +2,11 @@
|
|||||||
//! - [Span](struct@span::Span): Stores a start and end [Loc](struct@span::Loc)
|
//! - [Span](struct@span::Span): Stores a start and end [Loc](struct@span::Loc)
|
||||||
//! - [Loc](struct@span::Loc): Stores the index in a stream
|
//! - [Loc](struct@span::Loc): Stores the index in a stream
|
||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
|
#![feature(inline_const, dropck_eyepatch, decl_macro)]
|
||||||
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
pub mod span;
|
pub mod span;
|
||||||
|
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
|
|
||||||
|
pub mod stack;
|
||||||
|
747
cl-structures/src/stack.rs
Normal file
747
cl-structures/src/stack.rs
Normal file
@ -0,0 +1,747 @@
|
|||||||
|
//! A contiguous collection with constant capacity.
|
||||||
|
//!
|
||||||
|
//! Since the capacity of a [Stack] may be [*known at compile time*](Sized),
|
||||||
|
//! it may live on the call stack.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! Unlike a [Vec], the [Stack] doesn't grow when it reaches capacity.
|
||||||
|
//! ```should_panic
|
||||||
|
//! # use cl_structures::stack::*;
|
||||||
|
//! let mut v = stack![1];
|
||||||
|
//! v.push("This should work");
|
||||||
|
//! v.push("This will panic!");
|
||||||
|
//! ```
|
||||||
|
//! To get around this limitation, the methods [try_push](Stack::try_push) and
|
||||||
|
//! [try_insert](Stack::try_insert) are provided:
|
||||||
|
//! ```
|
||||||
|
//! # use cl_structures::stack::*;
|
||||||
|
//! let mut v = stack![1];
|
||||||
|
//! v.push("This should work");
|
||||||
|
//! v.try_push("This should produce an err").unwrap_err();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! As the name suggests, a [Stack] enforces a stack discipline:
|
||||||
|
//! ```
|
||||||
|
//! # use cl_structures::stack::*;
|
||||||
|
//! let mut v = stack![100];
|
||||||
|
//!
|
||||||
|
//! assert_eq!(100, v.capacity());
|
||||||
|
//! assert_eq!(0, v.len());
|
||||||
|
//!
|
||||||
|
//! // Elements are pushed one at a time onto the stack
|
||||||
|
//! v.push("foo");
|
||||||
|
//! v.push("bar");
|
||||||
|
//! assert_eq!(2, v.len());
|
||||||
|
//!
|
||||||
|
//! // The stack can be used anywhere a slice is expected
|
||||||
|
//! assert_eq!(Some(&"foo"), v.get(0));
|
||||||
|
//! assert_eq!(Some(&"bar"), v.last());
|
||||||
|
//!
|
||||||
|
//! // Elements are popped from the stack in reverse order
|
||||||
|
//! assert_eq!(Some("bar"), v.pop());
|
||||||
|
//! assert_eq!(Some("foo"), v.pop());
|
||||||
|
//! assert_eq!(None, v.pop());
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
// yar har! here there be unsafe code! Tread carefully.
|
||||||
|
|
||||||
|
use core::slice;
|
||||||
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
marker::PhantomData,
|
||||||
|
mem::{ManuallyDrop, MaybeUninit},
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
ptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Creates a [`stack`] containing the arguments
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Creates a *full* [`Stack`] containing a list of elements
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::stack;
|
||||||
|
/// let mut v = stack![1, 2, 3];
|
||||||
|
///
|
||||||
|
/// assert_eq!(Some(3), v.pop());
|
||||||
|
/// assert_eq!(Some(2), v.pop());
|
||||||
|
/// assert_eq!(Some(1), v.pop());
|
||||||
|
/// assert_eq!(None, v.pop());
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Creates a *full* [`Stack`] from a given element and size
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::stack;
|
||||||
|
/// let mut v = stack![1; 2];
|
||||||
|
///
|
||||||
|
/// assert_eq!(Some(1), v.pop());
|
||||||
|
/// assert_eq!(Some(1), v.pop());
|
||||||
|
/// assert_eq!(None, v.pop());
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Creates an *empty* [`Stack`] from a given size
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::{Stack, stack};
|
||||||
|
/// let mut v = stack![10];
|
||||||
|
///
|
||||||
|
/// assert_eq!(0, v.len());
|
||||||
|
/// assert_eq!(10, v.capacity());
|
||||||
|
///
|
||||||
|
/// v.push(10);
|
||||||
|
/// assert_eq!(Some(&10), v.last());
|
||||||
|
/// ```
|
||||||
|
pub macro stack {
|
||||||
|
($count:literal) => {
|
||||||
|
Stack::<_, $count>::new()
|
||||||
|
},
|
||||||
|
($value:expr ; $count:literal) => {{
|
||||||
|
let mut stack: Stack<_, $count> = Stack::new();
|
||||||
|
for _ in 0..$count {
|
||||||
|
stack.push($value)
|
||||||
|
}
|
||||||
|
stack
|
||||||
|
}},
|
||||||
|
($($values:expr),* $(,)?) => {
|
||||||
|
Stack::from([$($values),*])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A contiguous collection with constant capacity
|
||||||
|
pub struct Stack<T, const N: usize> {
|
||||||
|
_data: PhantomData<T>,
|
||||||
|
buf: [MaybeUninit<T>; N],
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone, const N: usize> Clone for Stack<T, N> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let mut new = Self::new();
|
||||||
|
for value in self.iter() {
|
||||||
|
new.push(value.clone())
|
||||||
|
}
|
||||||
|
new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug, const N: usize> Debug for Stack<T, N> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_list().entries(self.iter()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> Default for Stack<T, N> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> Deref for Stack<T, N> {
|
||||||
|
type Target = [T];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
// Safety:
|
||||||
|
// - We have ensured all elements from 0 to len have been initialized
|
||||||
|
// - self.elem[0] came from a reference, and so is aligned to T
|
||||||
|
// unsafe { &*(&self.buf[0..self.len] as *const [_] as *const [T]) }
|
||||||
|
unsafe { slice::from_raw_parts(self.buf.as_ptr().cast(), self.len) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> DerefMut for Stack<T, N> {
|
||||||
|
#[inline]
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
// Safety:
|
||||||
|
// - See Deref
|
||||||
|
unsafe { slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast(), self.len) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// requires dropck-eyepatch for elements with contravariant lifetimes
|
||||||
|
unsafe impl<#[may_dangle] T, const N: usize> Drop for Stack<T, N> {
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Safety: We have ensured that all elements in the list are
|
||||||
|
unsafe { core::ptr::drop_in_place(self.as_mut_slice()) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> Extend<T> for Stack<T, N> {
|
||||||
|
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||||
|
for value in iter {
|
||||||
|
self.push(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> From<[T; N]> for Stack<T, N> {
|
||||||
|
fn from(value: [T; N]) -> Self {
|
||||||
|
let value = ManuallyDrop::new(value);
|
||||||
|
if std::mem::size_of::<[T; N]>() == 0 {
|
||||||
|
// Safety: since [T; N] is zero-sized, and there are no other fields,
|
||||||
|
// it should be okay to interpret N as Self
|
||||||
|
unsafe { ptr::read(&N as *const _ as *const _) }
|
||||||
|
} else {
|
||||||
|
// Safety:
|
||||||
|
// - `value` is ManuallyDrop, so its destructor won't run
|
||||||
|
// - All elements are assumed to be initialized (so len is N)
|
||||||
|
Self {
|
||||||
|
buf: unsafe { ptr::read(&value as *const _ as *const _) },
|
||||||
|
len: N,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> Stack<T, N> {
|
||||||
|
/// Constructs a new, empty [Stack]
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// let mut v: Stack<_, 3> = Stack::new();
|
||||||
|
///
|
||||||
|
/// v.try_push(1).unwrap();
|
||||||
|
/// v.try_push(2).unwrap();
|
||||||
|
/// v.try_push(3).unwrap();
|
||||||
|
/// // Trying to push a 4th element will fail, and return the failed element
|
||||||
|
/// assert_eq!(4, v.try_push(4).unwrap_err());
|
||||||
|
///
|
||||||
|
/// assert_eq!(Some(3), v.pop());
|
||||||
|
/// ```
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self { buf: [const { MaybeUninit::uninit() }; N], len: 0, _data: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a new [Stack] from an array of [`MaybeUninit<T>`] and an initialized length
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// - Elements from `0..len` must be initialized
|
||||||
|
/// - len must not exceed the length of the array
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// # use core::mem::MaybeUninit;
|
||||||
|
/// let mut v = unsafe { Stack::from_raw_parts([MaybeUninit::new(100)], 1) };
|
||||||
|
///
|
||||||
|
/// assert_eq!(1, v.len());
|
||||||
|
/// assert_eq!(1, v.capacity());
|
||||||
|
/// assert_eq!(Some(100), v.pop());
|
||||||
|
/// assert_eq!(None, v.pop());
|
||||||
|
/// ```
|
||||||
|
pub const unsafe fn from_raw_parts(buf: [MaybeUninit<T>; N], len: usize) -> Self {
|
||||||
|
Self { buf, len, _data: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a [Stack] into an array of [`MaybeUninit<T>`] and the initialized length
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// let mut v: Stack<_, 10> = Stack::new();
|
||||||
|
/// v.push(0);
|
||||||
|
/// v.push(1);
|
||||||
|
///
|
||||||
|
/// let (buf, len) = v.into_raw_parts();
|
||||||
|
///
|
||||||
|
/// assert_eq!(0, unsafe { buf[0].assume_init() });
|
||||||
|
/// assert_eq!(1, unsafe { buf[1].assume_init() });
|
||||||
|
/// assert_eq!(2, len);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn into_raw_parts(self) -> ([MaybeUninit<T>; N], usize) {
|
||||||
|
let this = ManuallyDrop::new(self);
|
||||||
|
// Safety: since
|
||||||
|
(unsafe { ptr::read(&this.buf) }, this.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a raw pointer to the stack's buffer
|
||||||
|
pub const fn as_ptr(&self) -> *const T {
|
||||||
|
self.buf.as_ptr().cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an unsafe mutable pointer to the stack's buffer
|
||||||
|
pub fn as_mut_ptr(&mut self) -> *mut T {
|
||||||
|
self.buf.as_mut_ptr().cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts a slice containing the entire vector
|
||||||
|
pub fn as_slice(&self) -> &[T] {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts a mutable slice containing the entire vector
|
||||||
|
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the total number of elements the stack can hold
|
||||||
|
pub const fn capacity(&self) -> usize {
|
||||||
|
N
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves an existing stack into an allocation of a (potentially) different size,
|
||||||
|
/// truncating if necessary.
|
||||||
|
///
|
||||||
|
/// This can be used to easily construct a half-empty stack
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// You can grow a stack to fit more elements
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// let v = Stack::from([0, 1, 2, 3, 4]);
|
||||||
|
/// assert_eq!(5, v.capacity());
|
||||||
|
///
|
||||||
|
/// let mut v = v.resize::<10>();
|
||||||
|
/// assert_eq!(10, v.capacity());
|
||||||
|
///
|
||||||
|
/// v.push(5);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// You can truncate a stack, dropping elements off the end
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// let v = Stack::from([0, 1, 2, 3, 4, 5, 6, 7]);
|
||||||
|
/// assert_eq!(8, v.capacity());
|
||||||
|
///
|
||||||
|
/// let v = v.resize::<5>();
|
||||||
|
/// assert_eq!(5, v.capacity());
|
||||||
|
/// ```
|
||||||
|
pub fn resize<const M: usize>(mut self) -> Stack<T, M> {
|
||||||
|
// Drop elements until new length is reached
|
||||||
|
while self.len > M {
|
||||||
|
drop(self.pop());
|
||||||
|
}
|
||||||
|
let (old, len) = self.into_raw_parts();
|
||||||
|
let mut new: Stack<T, M> = Stack::new();
|
||||||
|
|
||||||
|
// Safety:
|
||||||
|
// - new and old are separate allocations
|
||||||
|
// - len <= M
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(old.as_ptr(), new.buf.as_mut_ptr(), len);
|
||||||
|
}
|
||||||
|
|
||||||
|
new.len = len;
|
||||||
|
new
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a new element onto the end of the stack
|
||||||
|
///
|
||||||
|
/// # May Panic
|
||||||
|
///
|
||||||
|
/// Panics if the new length exceeds capacity
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// let mut v: Stack<_, 4> = Stack::new();
|
||||||
|
///
|
||||||
|
/// v.push(0);
|
||||||
|
/// v.push(1);
|
||||||
|
/// v.push(2);
|
||||||
|
/// v.push(3);
|
||||||
|
/// assert_eq!(&[0, 1, 2, 3], v.as_slice());
|
||||||
|
/// ```
|
||||||
|
pub fn push(&mut self, value: T) {
|
||||||
|
if self.len >= N {
|
||||||
|
panic!("Attempted to push into full stack")
|
||||||
|
}
|
||||||
|
// Safety: len is confirmed to be less than capacity
|
||||||
|
unsafe { self.push_unchecked(value) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a new element onto the end of the stack
|
||||||
|
///
|
||||||
|
/// Returns [`Err(value)`](Result::Err) if the new length would exceed capacity
|
||||||
|
pub fn try_push(&mut self, value: T) -> Result<(), T> {
|
||||||
|
if self.len >= N {
|
||||||
|
return Err(value);
|
||||||
|
}
|
||||||
|
// Safety: len is confirmed to be less than capacity
|
||||||
|
unsafe { self.push_unchecked(value) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a new element onto the end of the stack, without checking capacity
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// len after push must not exceed capacity N
|
||||||
|
#[inline]
|
||||||
|
unsafe fn push_unchecked(&mut self, value: T) {
|
||||||
|
unsafe { ptr::write(self.as_mut_ptr().add(self.len), value) }
|
||||||
|
self.len += 1; // post inc
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pops the last element off the end of the stack, and returns it
|
||||||
|
///
|
||||||
|
/// Returns None if the stack is empty
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// let mut v = Stack::from([0, 1, 2, 3]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(Some(3), v.pop());
|
||||||
|
/// assert_eq!(Some(2), v.pop());
|
||||||
|
/// assert_eq!(Some(1), v.pop());
|
||||||
|
/// assert_eq!(Some(0), v.pop());
|
||||||
|
/// assert_eq!(None, v.pop());
|
||||||
|
/// ```
|
||||||
|
pub fn pop(&mut self) -> Option<T> {
|
||||||
|
if self.len == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.len -= 1;
|
||||||
|
// Safety: MaybeUninit<T> implies ManuallyDrop<T>,
|
||||||
|
// therefore should not get dropped twice
|
||||||
|
Some(unsafe { ptr::read(self.as_ptr().add(self.len).cast()) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes and returns the element at the given index,
|
||||||
|
/// shifting other elements toward the start
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// let mut v = Stack::from([0, 1, 2, 3, 4]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(2, v.remove(2));
|
||||||
|
/// assert_eq!(&[0, 1, 3, 4], v.as_slice());
|
||||||
|
/// ```
|
||||||
|
pub fn remove(&mut self, index: usize) -> T {
|
||||||
|
if index >= self.len {
|
||||||
|
panic!("Index {index} exceeded length {}", self.len)
|
||||||
|
}
|
||||||
|
let len = self.len - 1;
|
||||||
|
let base = self.as_mut_ptr();
|
||||||
|
let out = unsafe { ptr::read(base.add(index)) };
|
||||||
|
|
||||||
|
unsafe { ptr::copy(base.add(index + 1), base.add(index), len - index) };
|
||||||
|
self.len = len;
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes and returns the element at the given index,
|
||||||
|
/// swapping it with the last element.
|
||||||
|
///
|
||||||
|
/// # May Panic
|
||||||
|
///
|
||||||
|
/// Panics if `index >= len`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// let mut v = Stack::from([0, 1, 2, 3, 4]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(2, v.swap_remove(2));
|
||||||
|
///
|
||||||
|
/// assert_eq!(&[0, 1, 4, 3], v.as_slice());
|
||||||
|
/// ```
|
||||||
|
pub fn swap_remove(&mut self, index: usize) -> T {
|
||||||
|
if index >= self.len {
|
||||||
|
panic!("Index {index} exceeds length {}", self.len);
|
||||||
|
}
|
||||||
|
let len = self.len - 1;
|
||||||
|
let ptr = self.as_mut_ptr();
|
||||||
|
let out = unsafe { ptr::read(ptr.add(index)) };
|
||||||
|
|
||||||
|
unsafe { ptr::copy(ptr.add(len), ptr.add(index), 1) };
|
||||||
|
self.len = len;
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts an element at position `index` in the stack,
|
||||||
|
/// shifting all elements after it to the right.
|
||||||
|
///
|
||||||
|
/// # May Panic
|
||||||
|
///
|
||||||
|
/// Panics if `index > len` or [`self.is_full()`](Stack::is_full)
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
/// let mut v = Stack::from([0, 1, 2, 3, 4]).resize::<6>();
|
||||||
|
///
|
||||||
|
/// v.insert(3, 0xbeef);
|
||||||
|
/// assert_eq!(&[0, 1, 2, 0xbeef, 3, 4], v.as_slice());
|
||||||
|
/// ```
|
||||||
|
pub fn insert(&mut self, index: usize, data: T) {
|
||||||
|
if index > self.len {
|
||||||
|
panic!("Index {index} exceeded length {}", self.len)
|
||||||
|
}
|
||||||
|
if self.is_full() {
|
||||||
|
panic!("Attempted to insert into full stack")
|
||||||
|
}
|
||||||
|
unsafe { self.insert_unchecked(index, data) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to insert an element at position `index` in the stack,
|
||||||
|
/// shifting all elements after it to the right.
|
||||||
|
///
|
||||||
|
/// If the stack is at capacity, returns the original element and an [InsertFailed] error.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use cl_structures::stack::Stack;
|
||||||
|
/// let mut v: Stack<_, 2> = Stack::new();
|
||||||
|
///
|
||||||
|
/// assert_eq!(Ok(()), v.try_insert(0, 0));
|
||||||
|
/// ```
|
||||||
|
pub fn try_insert(&mut self, index: usize, data: T) -> Result<(), (T, InsertFailed<N>)> {
|
||||||
|
if index > self.len {
|
||||||
|
return Err((data, InsertFailed::Bounds(index)));
|
||||||
|
}
|
||||||
|
if self.is_full() {
|
||||||
|
return Err((data, InsertFailed::Full));
|
||||||
|
}
|
||||||
|
// Safety: index < self.len && !self.is_full()
|
||||||
|
unsafe { self.insert_unchecked(index, data) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety:
|
||||||
|
/// - index must be less than self.len
|
||||||
|
/// - length after insertion must be <= N
|
||||||
|
#[inline]
|
||||||
|
unsafe fn insert_unchecked(&mut self, index: usize, data: T) {
|
||||||
|
let base = self.as_mut_ptr();
|
||||||
|
|
||||||
|
unsafe { ptr::copy(base.add(index), base.add(index + 1), self.len - index) }
|
||||||
|
|
||||||
|
self.len += 1;
|
||||||
|
self.buf[index] = MaybeUninit::new(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears the stack
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::Stack;
|
||||||
|
///
|
||||||
|
/// let mut v = Stack::from([0, 1, 2, 3, 4]);
|
||||||
|
/// assert_eq!(v.as_slice(), &[0, 1, 2, 3, 4]);
|
||||||
|
///
|
||||||
|
/// v.clear();
|
||||||
|
/// assert_eq!(v.as_slice(), &[]);
|
||||||
|
/// ```
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
// Hopefully copy elision takes care of this lmao
|
||||||
|
drop(std::mem::take(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of elements in the stack
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::*;
|
||||||
|
/// let v = Stack::from([0, 1, 2, 3, 4]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(5, v.len());
|
||||||
|
/// ```
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the stack is at (or over) capacity
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::*;
|
||||||
|
/// let v = Stack::from([(); 10]);
|
||||||
|
///
|
||||||
|
/// assert!(v.is_full());
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn is_full(&self) -> bool {
|
||||||
|
self.len >= N
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the stack contains no elements
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cl_structures::stack::*;
|
||||||
|
/// let v: Stack<(), 10> = Stack::new();
|
||||||
|
///
|
||||||
|
/// assert!(v.is_empty());
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum InsertFailed<const N: usize> {
|
||||||
|
Bounds(usize),
|
||||||
|
Full,
|
||||||
|
}
|
||||||
|
impl<const N: usize> std::error::Error for InsertFailed<N> {}
|
||||||
|
impl<const N: usize> std::fmt::Display for InsertFailed<N> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
InsertFailed::Bounds(idx) => write!(f, "Index {idx} exceeded length {N}"),
|
||||||
|
InsertFailed::Full => {
|
||||||
|
write!(f, "Attempt to insert into full stack (length {N})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn zero_sized() {
|
||||||
|
let v: Stack<(), { usize::MAX }> = Stack::new();
|
||||||
|
assert_eq!(usize::MAX, v.capacity());
|
||||||
|
assert_eq!(std::mem::size_of::<usize>(), std::mem::size_of_val(&v))
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(debug_assertions, ignore = "calls ().drop() usize::MAX times")]
|
||||||
|
fn from_usize_max_zst_array() {
|
||||||
|
let mut v = Stack::from([(); usize::MAX]);
|
||||||
|
assert_eq!(v.len(), usize::MAX);
|
||||||
|
v.pop();
|
||||||
|
assert_eq!(v.len(), usize::MAX - 1);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn new() {
|
||||||
|
let v: Stack<(), 255> = Stack::new();
|
||||||
|
assert_eq!(0, v.len());
|
||||||
|
assert_eq!(255, v.capacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn push() {
|
||||||
|
let mut v: Stack<_, 64> = Stack::new();
|
||||||
|
v.push(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic = "Attempted to push into full stack"]
|
||||||
|
fn push_overflow() {
|
||||||
|
let mut v = Stack::from([]);
|
||||||
|
v.push(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pop() {
|
||||||
|
let mut v = Stack::from([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
assert_eq!(Some(9), v.pop());
|
||||||
|
assert_eq!(Some(8), v.pop());
|
||||||
|
assert_eq!(Some(7), v.pop());
|
||||||
|
assert_eq!(Some(6), v.pop());
|
||||||
|
assert_eq!(Some(5), v.pop());
|
||||||
|
assert_eq!(Some(4), v.pop());
|
||||||
|
assert_eq!(Some(3), v.pop());
|
||||||
|
assert_eq!(Some(2), v.pop());
|
||||||
|
assert_eq!(Some(1), v.pop());
|
||||||
|
assert_eq!(None, v.pop());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resize_smaller() {
|
||||||
|
let v = Stack::from([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
let mut v = v.resize::<2>();
|
||||||
|
|
||||||
|
assert_eq!(2, v.capacity());
|
||||||
|
|
||||||
|
assert_eq!(Some(2), v.pop());
|
||||||
|
assert_eq!(Some(1), v.pop());
|
||||||
|
assert_eq!(None, v.pop());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resize_bigger() {
|
||||||
|
let v = Stack::from([1, 2, 3, 4]);
|
||||||
|
let mut v: Stack<_, 10> = v.resize();
|
||||||
|
|
||||||
|
assert_eq!(Some(4), v.pop());
|
||||||
|
assert_eq!(Some(3), v.pop());
|
||||||
|
assert_eq!(Some(2), v.pop());
|
||||||
|
assert_eq!(Some(1), v.pop());
|
||||||
|
assert_eq!(None, v.pop());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn dangle() {
|
||||||
|
let mut v: Stack<_, 2> = Stack::new();
|
||||||
|
let a = 0;
|
||||||
|
let b = 1;
|
||||||
|
v.push(&a);
|
||||||
|
v.push(&b);
|
||||||
|
println!("{v:?}");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn remove() {
|
||||||
|
let mut v = Stack::from([0, 1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
|
assert_eq!(3, v.remove(3));
|
||||||
|
assert_eq!(4, v.remove(3));
|
||||||
|
assert_eq!(5, v.remove(3));
|
||||||
|
assert_eq!(Some(2), v.pop());
|
||||||
|
assert_eq!(Some(1), v.pop());
|
||||||
|
assert_eq!(Some(0), v.pop());
|
||||||
|
assert_eq!(None, v.pop());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn swap_remove() {
|
||||||
|
let mut v = Stack::from([0, 1, 2, 3, 4, 5]);
|
||||||
|
assert_eq!(3, v.swap_remove(3));
|
||||||
|
assert_eq!(&[0, 1, 2, 5, 4], v.as_slice());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn swap_remove_last() {
|
||||||
|
let mut v = Stack::from([0, 1, 2, 3, 4, 5]);
|
||||||
|
assert_eq!(5, v.swap_remove(5));
|
||||||
|
assert_eq!(&[0, 1, 2, 3, 4], v.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert() {
|
||||||
|
let mut v = Stack::from([0, 1, 2, 4, 5, 0x41414141]);
|
||||||
|
v.pop();
|
||||||
|
v.insert(3, 3);
|
||||||
|
|
||||||
|
assert_eq!(&[0, 1, 2, 3, 4, 5], v.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic = "Attempted to insert into full stack"]
|
||||||
|
fn insert_overflow() {
|
||||||
|
let mut v = Stack::from([0]);
|
||||||
|
v.insert(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drop() {
|
||||||
|
let v = Stack::from([
|
||||||
|
Box::new(0),
|
||||||
|
Box::new(1),
|
||||||
|
Box::new(2),
|
||||||
|
Box::new(3),
|
||||||
|
Box::new(4),
|
||||||
|
]);
|
||||||
|
std::mem::drop(std::hint::black_box(v));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user