optimize push and pop for StackWithLimit

This commit is contained in:
Andrew Dirksen 2018-11-19 19:44:13 -08:00
parent 1913702a03
commit 6f9b0a2b96
2 changed files with 66 additions and 18 deletions

View File

@ -1,4 +1,5 @@
use core::marker::PhantomData;
use core::ptr;
use core::usize;
#[allow(unused_imports)]
@ -77,24 +78,49 @@ impl<T> StackWithLimit<T> {
/// # Errors
///
/// Returns Err(StackOverflow) if stack is already full.
#[inline]
pub(crate) fn push(&mut self, value: T) -> Result<(), StackOverflow> {
let ret = if self.stack.len() < self.limit {
self.stack.push(value);
Ok(())
} else {
Err(StackOverflow)
};
debug_assert!(
self.stack.len() <= self.limit,
"Stack length should never be larger than stack limit."
);
ret
if self.stack.len() == self.stack.capacity() {
if self.stack.len() == self.limit {
return Err(StackOverflow);
}
// grows exponentially, just like Vec normally does
// https://doc.rust-lang.org/1.26.0/src/alloc/raw_vec.rs.html#462
let desired_len = self
.stack
.len()
.checked_mul(2)
.unwrap_or(usize::MAX)
.min(self.limit)
.max(1);
let additional_len = desired_len - self.stack.len();
self.stack.reserve_exact(additional_len);
}
debug_assert!(self.stack.len() < self.limit);
debug_assert!(self.stack.len() < self.stack.capacity());
let len = self.stack.len();
unsafe {
ptr::write(self.stack.get_unchecked_mut(len), value);
self.stack.set_len(len + 1);
}
Ok(())
}
pub(crate) fn pop(&mut self) -> Option<T> {
self.stack.pop()
}
/// Remove and Return top element. Does not check for emptyness.
/// If this is called on a zero length stack, bad things will happen.
/// Do not call this method unless you can prove the stack has length.
#[inline]
pub(crate) unsafe fn pop_unchecked(&mut self) -> T {
debug_assert!(self.stack.len() > 0);
let len = self.stack.len();
self.stack.set_len(len - 1);
ptr::read(self.stack.get_unchecked(self.stack.len()))
}
/// Return optional reference to item `depth` distance away from top
///
/// `bstack.get_relative_to_top(0)` gets the top of the stack
@ -360,12 +386,34 @@ mod test {
StackSizeInitial(StackSize::from_element_count(0)),
StackSizeLimit(StackSize::from_element_count(usize::MAX)),
);
println!(
"{}",
StackSizeLimit(StackSize::<i32>::from_element_count(usize::MAX))
.0
.element_count()
);
exersize(bstack);
}
// Make sure the stack resizes properly.
#[test]
fn must_resize() {
let mut bstack = StackWithLimit::<i32>::new(
StackSizeInitial(StackSize::from_element_count(2)),
StackSizeLimit(StackSize::from_element_count(4)),
);
bstack.push(2).unwrap();
bstack.push(4).unwrap();
bstack.push(8).unwrap();
bstack.push(16).unwrap();
bstack.push(16).unwrap_err();
assert_eq!(bstack.pop(), Some(16));
assert_eq!(bstack.pop(), Some(8));
assert_eq!(bstack.pop(), Some(4));
assert_eq!(bstack.pop(), Some(2));
}
#[test]
fn pop_unchecked() {
let mut bstack = StackWithLimit::<i32>::with_size(StackSize::from_element_count(20));
bstack.push(8).unwrap();
bstack.push(0).unwrap();
assert_eq!(unsafe { bstack.pop_unchecked() }, 0);
assert_eq!(unsafe { bstack.pop_unchecked() }, 8);
}
}

View File

@ -1321,7 +1321,7 @@ impl ValueStack {
#[inline]
fn pop(&mut self) -> RuntimeValueInternal {
self.0.pop().expect("pre-validated")
unsafe { self.0.pop_unchecked() }
}
#[inline]