#![warn(rust_2018_idioms, rust_2024_compatibility)]
#![deny(
missing_docs,
missing_debug_implementations,
unsafe_op_in_unsafe_fn,
clippy::missing_safety_doc,
clippy::missing_errors_doc,
clippy::missing_inline_in_public_items,
clippy::missing_const_for_fn,
rustdoc::missing_crate_level_docs
)]
#![doc(html_playground_url = "https://play.rust-lang.org")]
#![no_std]
#[cfg(feature = "std")]
extern crate std;
mod iter;
use core::{fmt, hash, mem, ptr};
pub use iter::StackIntoIter;
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! count_args {
($_:expr,) => { 1 };
($_:expr, $($others:expr,)+) => {
count_args!($_,) + count_args!($($others,)*)
}
}
#[macro_export(local_inner_macros)]
macro_rules! stack {
($T:ty; $N:expr) => {
$crate::Stack::<$T, $N>::new()
};
([$T:ty; $N:expr] = [$($expr:expr),+ $(,)?]) => {{
const _: () = core::prelude::rust_2021::assert!(
count_args!($($expr,)*) <= $N,
core::prelude::rust_2021::concat!(
"The number of items exceeds the capacity specified, which is ",
$N
)
);
let mut stack = stack![$T; $N];
$( unsafe { stack.push_unchecked($expr) }; )*
stack
}};
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum StackError {
Overflow,
Underflow,
}
impl fmt::Display for StackError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[cfg(feature = "std")]
impl std::error::Error for StackError {}
pub struct Stack<T, const N: usize> {
items: [mem::MaybeUninit<T>; N],
len: usize,
}
impl<T, const N: usize> Stack<T, N> {
#[inline]
pub const fn new() -> Self {
Self {
items: unsafe { mem::MaybeUninit::uninit().assume_init() },
len: 0,
}
}
#[inline]
pub const fn fill_with_copy(item: T) -> Self
where
T: Copy,
{
Self {
items: [mem::MaybeUninit::new(item); N],
len: N,
}
}
#[inline]
pub fn fill_with_fn<F>(mut f: F) -> Self
where
F: FnMut(usize) -> T,
{
Self {
items: core::array::from_fn(|i| mem::MaybeUninit::new(f(i))),
len: N,
}
}
#[inline]
pub fn fill_with_default() -> Self
where
T: Default,
{
Self::fill_with_fn(|_| T::default())
}
#[inline]
pub const unsafe fn from_raw_parts(items: [mem::MaybeUninit<T>; N], len: usize) -> Self {
debug_assert!(len <= N);
Self { items, len }
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub const fn is_full(&self) -> bool {
self.len >= N
}
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub const fn capacity(&self) -> usize {
N
}
#[inline]
pub const fn capacity_remaining(&self) -> usize {
N - self.len
}
#[inline]
pub const fn tos(&self) -> Option<&T> {
if self.is_empty() {
None
} else {
Some(unsafe { self.tos_unchecked() })
}
}
#[inline]
pub const fn tos_panicking(&self) -> &T {
assert!(
!self.is_empty(),
"Called Stack::tos_panicking on an empty stack"
);
unsafe { self.tos_unchecked() }
}
#[inline]
pub const unsafe fn tos_unchecked(&self) -> &T {
debug_assert!(!self.is_empty());
unsafe { (*self.items.as_ptr().add(self.len - 1)).assume_init_ref() }
}
#[inline]
pub fn tos_mut(&mut self) -> Option<&mut T> {
if self.is_empty() {
None
} else {
Some(unsafe { self.tos_mut_unchecked() })
}
}
#[inline]
pub fn tos_mut_panicking(&mut self) -> &mut T {
assert!(
!self.is_empty(),
"Called Stack::tos_mut_panicking on an empty stack"
);
unsafe { self.tos_mut_unchecked() }
}
#[inline]
pub unsafe fn tos_mut_unchecked(&mut self) -> &mut T {
debug_assert!(!self.is_empty());
unsafe { (*self.items.as_mut_ptr().add(self.len - 1)).assume_init_mut() }
}
#[inline]
pub fn push(&mut self, item: T) -> Result<(), StackError> {
if self.is_full() {
Err(StackError::Overflow)
} else {
unsafe { self.push_unchecked(item) };
Ok(())
}
}
#[inline]
pub fn push_panicking(&mut self, item: T) {
assert!(
!self.is_full(),
"Called Stack::push_panicking but the stack is full"
);
unsafe { self.push_unchecked(item) };
}
#[inline]
pub unsafe fn push_unchecked(&mut self, item: T) {
debug_assert!(!self.is_full());
unsafe {
let end = self.items.as_mut_ptr().add(self.len);
ptr::write(end, mem::MaybeUninit::new(item));
}
self.len += 1;
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
if self.is_empty() {
None
} else {
Some(unsafe { self.pop_unchecked() })
}
}
#[inline]
pub fn pop_panicking(&mut self) -> T {
assert!(
!self.is_empty(),
"Called Stack::pop_panicking but the stack is empty"
);
unsafe { self.pop_unchecked() }
}
#[inline]
pub unsafe fn pop_unchecked(&mut self) -> T {
debug_assert!(!self.is_empty());
self.len -= 1;
unsafe { ptr::read(self.items.as_ptr().add(self.len)).assume_init() }
}
#[inline]
pub fn extend_array<const M: usize>(&mut self, arr: [T; M]) -> Result<(), StackError> {
if self.len + M > N {
Err(StackError::Overflow)
} else {
unsafe { self.extend_array_unchecked(arr) };
Ok(())
}
}
#[inline]
pub fn extend_array_panicking<const M: usize>(&mut self, arr: [T; M]) {
assert!(
self.len + M <= N,
"Called Stack::push_array_panicking but self.len + M > N"
);
unsafe { self.extend_array_unchecked(arr) };
}
#[inline]
pub unsafe fn extend_array_unchecked<const M: usize>(&mut self, arr: [T; M]) {
debug_assert!(self.len + M <= N);
let mut arr = arr.map(|item| mem::MaybeUninit::new(item));
unsafe {
ptr::swap_nonoverlapping(
self.items[self.len..(self.len + M)].as_mut_ptr(),
arr[..].as_mut_ptr(),
M,
);
}
self.len += M;
}
#[inline]
pub fn extend<I>(&mut self, items: I) -> Result<(), StackError>
where
I: IntoIterator<Item = T>,
{
items.into_iter().try_for_each(|item| self.push(item))
}
#[inline]
pub fn extend_panicking<I>(&mut self, items: I)
where
I: IntoIterator<Item = T>,
{
items.into_iter().for_each(|item| self.push_panicking(item));
}
#[inline]
pub unsafe fn extend_unchecked<I>(&mut self, items: I)
where
I: IntoIterator<Item = T>,
{
items
.into_iter()
.for_each(|item| unsafe { self.push_unchecked(item) });
}
#[inline]
#[must_use = "If you want to discard the result, see `Stack::truncate`"]
pub fn cut<const M: usize>(&mut self) -> Result<[T; M], StackError> {
if M > self.len {
Err(StackError::Underflow)
} else {
Ok(unsafe { self.cut_unchecked() })
}
}
#[inline]
#[must_use = "If you want to discard the result, see `Stack::truncate_panicking`"]
pub fn cut_panicking<const M: usize>(&mut self) -> [T; M] {
assert!(
M <= self.len,
"Called Stack::cut_panicking but M > self.len"
);
unsafe { self.cut_unchecked() }
}
#[inline]
#[must_use = "If you want to discard the result, see `Stack::truncate_unchecked`"]
pub unsafe fn cut_unchecked<const M: usize>(&mut self) -> [T; M] {
debug_assert!(M <= self.len);
unsafe {
let mut arr = mem::MaybeUninit::<[mem::MaybeUninit<T>; M]>::uninit().assume_init();
ptr::swap_nonoverlapping(
self.items[(self.len - M)..].as_mut_ptr(),
arr[..].as_mut_ptr(),
M,
);
self.len -= M;
arr.map(|item| item.assume_init())
}
}
#[inline]
pub fn swap_tos(&mut self, item: T) -> Result<T, StackError> {
if self.is_empty() {
Err(StackError::Underflow)
} else {
Ok(unsafe { self.swap_tos_unchecked(item) })
}
}
#[inline]
pub fn swap_tos_panicking(&mut self, item: T) -> T {
assert!(
!self.is_empty(),
"Called Stack::swap_tos_panicking but the stack is empty"
);
unsafe { self.swap_tos_unchecked(item) }
}
#[inline]
pub unsafe fn swap_tos_unchecked(&mut self, item: T) -> T {
debug_assert!(!self.is_empty());
unsafe {
let item = ptr::replace(
self.items.as_mut_ptr().add(self.len - 1),
mem::MaybeUninit::new(item),
);
item.assume_init()
}
}
#[inline]
pub fn rotate_tos(&mut self) -> Result<(), StackError> {
if self.len < 2 {
Err(StackError::Underflow)
} else {
unsafe { self.rotate_tos_unchecked() };
Ok(())
}
}
#[inline]
pub fn rotate_tos_panicking(&mut self) {
assert!(
self.len >= 2,
"Called Stack::rotate_tos_panicking but the stack contains 1 or 0 items"
);
unsafe { self.rotate_tos_unchecked() };
}
#[inline]
pub unsafe fn rotate_tos_unchecked(&mut self) {
debug_assert!(self.len >= 2);
let ptr = self.items.as_mut_ptr();
unsafe {
ptr::swap(ptr.add(self.len - 1), ptr.add(self.len - 2));
}
}
#[inline]
pub fn truncate(&mut self, new_len: usize) -> Result<(), StackError> {
if new_len > self.len {
Err(StackError::Underflow)
} else {
unsafe { self.truncate_unchecked(new_len) };
Ok(())
}
}
#[inline]
pub fn truncate_panicking(&mut self, new_len: usize) {
assert!(
new_len <= self.len,
"Called Stack::truncate_panicking but `len` ({}) is greater than stack's len ({})",
new_len,
self.len
);
unsafe { self.truncate_unchecked(new_len) };
}
#[inline]
pub unsafe fn truncate_unchecked(&mut self, new_len: usize) {
debug_assert!(new_len <= self.len);
let remaining_len = self.len - new_len;
unsafe {
self.drop_items(remaining_len);
}
self.len = new_len;
}
#[inline]
pub fn apply_tos<F>(&mut self, f: F) -> Result<(), StackError>
where
F: Fn(&mut T),
{
let Some(tos) = self.tos_mut() else {
return Err(StackError::Underflow);
};
f(tos);
Ok(())
}
#[inline]
pub fn apply_tos_panicking<F>(&mut self, f: F)
where
F: Fn(&mut T),
{
assert!(
!self.is_empty(),
"Called Stack::apply_tos_panicking on empty stack"
);
unsafe {
self.apply_tos_unchecked(f);
}
}
#[inline]
pub unsafe fn apply_tos_unchecked<F>(&mut self, f: F)
where
F: Fn(&mut T),
{
debug_assert!(!self.is_empty());
f(unsafe { self.tos_mut_unchecked() });
}
#[inline]
pub fn clear(&mut self) {
unsafe { self.drop_items(0) };
self.len = 0;
}
#[inline]
unsafe fn drop_items(&mut self, start: usize) {
unsafe {
let items = self.init_items_as_mut_ptr(start);
ptr::drop_in_place(items);
}
}
#[inline]
unsafe fn init_items_as_ptr(&self, start: usize) -> *const [T] {
debug_assert!(start <= self.len);
unsafe { self.items.get_unchecked(start..self.len) as *const [mem::MaybeUninit<T>] as _ }
}
#[inline]
unsafe fn init_items_as_mut_ptr(&mut self, start: usize) -> *mut [T] {
debug_assert!(start <= self.len);
unsafe { self.items.get_unchecked_mut(start..self.len) as *mut [mem::MaybeUninit<T>] as _ }
}
}
impl<T, const N: usize> Default for Stack<T, N> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> Clone for Stack<T, N>
where
T: Clone,
{
#[inline]
fn clone(&self) -> Self {
unsafe {
let mut items = mem::MaybeUninit::<[mem::MaybeUninit<T>; N]>::uninit().assume_init();
self.items
.get_unchecked(..self.len)
.iter()
.zip(items.get_unchecked_mut(..self.len))
.for_each(|(src, dst)| {
dst.write(src.assume_init_ref().clone());
});
Self {
items,
len: self.len,
}
}
}
}
impl<T, U, const N: usize> PartialEq<Stack<U, N>> for Stack<T, N>
where
T: PartialEq<U>,
{
#[inline]
fn eq(&self, other: &Stack<U, N>) -> bool {
unsafe {
let items1 = self.init_items_as_ptr(0);
let items2 = other.init_items_as_ptr(0);
*items1 == *items2
}
}
}
impl<T, U, const N: usize> PartialEq<[U]> for Stack<T, N>
where
T: PartialEq<U>,
{
#[inline]
fn eq(&self, other: &[U]) -> bool {
unsafe {
let items = self.init_items_as_ptr(0);
*items == *other
}
}
}
impl<T, U, const N: usize, const M: usize> PartialEq<[U; M]> for Stack<T, N>
where
T: PartialEq<U>,
{
#[inline]
fn eq(&self, other: &[U; M]) -> bool {
unsafe {
let items = self.init_items_as_ptr(0);
*items == *other
}
}
}
impl<T, const N: usize> hash::Hash for Stack<T, N>
where
T: hash::Hash,
{
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
ptr::hash(unsafe { self.init_items_as_ptr(0) }, state);
}
}
impl<T, const N: usize> fmt::Debug for Stack<T, N>
where
T: fmt::Debug,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct DebugUninit;
impl fmt::Debug for DebugUninit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(uninit)")
}
}
let mut list = f.debug_list();
unsafe {
list.entries(
self.items
.get_unchecked(..self.len)
.iter()
.map(|item| item.assume_init_ref()),
);
}
list.entries((self.len..N).map(|_| &DebugUninit));
list.finish()
}
}
impl<T, const N: usize> Drop for Stack<T, N> {
#[inline]
fn drop(&mut self) {
unsafe { self.drop_items(0) };
}
}
#[cfg(feature = "std")]
impl<T, const N: usize> From<Stack<T, N>> for std::vec::Vec<T> {
#[inline]
fn from(stack: Stack<T, N>) -> Self {
let mut v = std::vec::Vec::with_capacity(stack.len());
v.extend(stack.into_iter().rev());
v
}
}
#[cfg(feature = "std")]
impl<T, const N: usize> TryFrom<std::vec::Vec<T>> for Stack<T, N> {
type Error = StackError;
#[inline]
fn try_from(v: std::vec::Vec<T>) -> Result<Self, Self::Error> {
try_extend_into_iter(v.len(), v)
}
}
#[cfg(feature = "std")]
impl<T, const N: usize> TryFrom<std::collections::LinkedList<T>> for Stack<T, N> {
type Error = StackError;
#[inline]
fn try_from(list: std::collections::LinkedList<T>) -> Result<Self, Self::Error> {
try_extend_into_iter(list.len(), list)
}
}
#[cfg(feature = "std")]
#[inline]
fn try_extend_into_iter<T, const N: usize>(
len: usize,
it: impl IntoIterator<Item = T>,
) -> Result<Stack<T, N>, StackError> {
if len > N {
return Err(StackError::Overflow);
}
let mut stack = Stack::new();
unsafe {
stack.extend_unchecked(it);
}
Ok(stack)
}
impl<T, const N: usize> IntoIterator for Stack<T, N> {
type IntoIter = StackIntoIter<T, N>;
type Item = T;
#[inline]
fn into_iter(mut self) -> Self::IntoIter {
let items = mem::replace(&mut self.items, unsafe {
mem::MaybeUninit::uninit().assume_init()
});
let top_len = mem::replace(&mut self.len, 0);
unsafe { StackIntoIter::new(items, top_len) }
}
}