From 191bc824c27fe26736b6a25bc07bbbe2e44ce8f8 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Sat, 12 Oct 2024 19:15:13 -0400 Subject: [PATCH] add: Fast Stack This is an implementation of a stack that does minimal bookeeping, has zero heap allocations, and is guaranteed to not panic --- crates/core/src/util/fast_stack.rs | 60 ++++++++++++++++++++++++++++++ crates/core/src/util/gurantee.rs | 11 ++++++ crates/core/src/util/mod.rs | 8 ++++ 3 files changed, 79 insertions(+) create mode 100644 crates/core/src/util/fast_stack.rs create mode 100644 crates/core/src/util/gurantee.rs create mode 100644 crates/core/src/util/mod.rs diff --git a/crates/core/src/util/fast_stack.rs b/crates/core/src/util/fast_stack.rs new file mode 100644 index 000000000..c78cae18a --- /dev/null +++ b/crates/core/src/util/fast_stack.rs @@ -0,0 +1,60 @@ +use super::gurantee; + +pub struct FastStack { + storage: [u8; 256], + pos: usize +} + +impl FastStack { + #[inline(always)] + pub fn new() -> FastStack { + FastStack { + storage: [0; 256], + pos: 0 + } + } + + #[inline(always)] + pub fn push(&mut self, value: u8) { + gurantee(!self.overgrown()); + + self.storage[self.pos] = value; + self.pos += 1; + } + + #[inline(always)] + pub fn peek(&self) -> u8 { + gurantee(!self.overgrown()); + + return self.storage[self.pos - 1]; + } + + #[inline(always)] + pub fn last(&self) -> Option { + if self.is_empty() || self.overgrown() { + return None; + } + + return Some(self.peek()); + } + + #[inline(always)] + pub fn pop(&mut self) { + // SAFETY: The buffer does not need to be mutated because the stack is + // only ever read from or written to its current position. Its current + // position is only ever incremented after writing to it. Meaning that + // the buffer can be dirty for the next use and still be correct since + // reading/writing always starts at position `0`. + self.pos = self.pos.saturating_sub(1); + } + + #[inline(always)] + pub fn is_empty(&self) -> bool { + return self.pos == 0; + } + + #[inline(always)] + pub fn overgrown(&self) -> bool { + self.pos > 256 + } +} diff --git a/crates/core/src/util/gurantee.rs b/crates/core/src/util/gurantee.rs new file mode 100644 index 000000000..e49e0b962 --- /dev/null +++ b/crates/core/src/util/gurantee.rs @@ -0,0 +1,11 @@ +#[inline(always)] +pub const fn gurantee(expr: bool) { + #[cfg(debug_assertions)] + if !expr { + panic!("gurantee failed") + } + + unsafe { + std::hint::assert_unchecked(expr) + } +} diff --git a/crates/core/src/util/mod.rs b/crates/core/src/util/mod.rs new file mode 100644 index 000000000..9d12c9626 --- /dev/null +++ b/crates/core/src/util/mod.rs @@ -0,0 +1,8 @@ +mod fast_stack; +mod gurantee; + +#[allow(unused_imports)] +pub use crate::util::fast_stack::*; + +#[allow(unused_imports)] +pub use crate::util::gurantee::*;