#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
use contracts::*;
#[cfg(not(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
)))]
use disabled_contracts::*;
#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
use std::ffi::c_void;
use std::mem::MaybeUninit;
use std::ptr::NonNull;
use crate::linear_ref::LinearRef;
#[cfg(not(feature = "test_only_small_constants"))]
pub const MAGAZINE_SIZE: u32 = 30;
#[cfg(feature = "test_only_small_constants")]
pub const MAGAZINE_SIZE: u32 = 6;
#[repr(C)]
pub struct MagazineStorage {
allocations: [MaybeUninit<LinearRef>; MAGAZINE_SIZE as usize],
pub(crate) link: Option<NonNull<MagazineStorage>>,
pub(crate) num_allocated_slow: u32,
}
#[repr(C)]
pub struct MagazineImpl<const PUSH_MAG: bool> {
top_of_stack: isize,
inner: Option<&'static mut MagazineStorage>,
}
impl<const PUSH_MAG: bool> MagazineImpl<PUSH_MAG> {
#[inline(always)]
pub fn new(maybe_inner: Option<&'static mut MagazineStorage>) -> Self {
if let Some(inner) = maybe_inner {
#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
assert!(inner.link.is_none());
if PUSH_MAG {
Self {
top_of_stack: inner.num_allocated_slow as isize - MAGAZINE_SIZE as isize,
inner: Some(inner),
}
} else {
Self {
top_of_stack: inner.num_allocated_slow as isize,
inner: Some(inner),
}
}
} else {
Self {
top_of_stack: 0,
inner: None,
}
}
}
#[inline(always)]
pub fn has_storage(&self) -> bool {
self.inner.is_some()
}
#[inline(always)]
pub fn storage(self) -> Option<&'static mut MagazineStorage> {
#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
assert!(self.check_rep());
let inner = self.inner?;
if PUSH_MAG {
inner.num_allocated_slow = (MAGAZINE_SIZE as isize + self.top_of_stack) as u32;
} else {
inner.num_allocated_slow = self.top_of_stack as u32;
}
#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
assert!(inner.link.is_none());
Some(inner)
}
#[invariant(self.check_rep())]
#[inline]
pub fn is_full(&self) -> bool {
if PUSH_MAG {
self.top_of_stack == 0
} else {
self.top_of_stack == MAGAZINE_SIZE as isize
}
}
#[invariant(self.check_rep())]
#[inline]
pub fn is_empty(&self) -> bool {
if PUSH_MAG {
self.top_of_stack == -(MAGAZINE_SIZE as isize)
} else {
self.top_of_stack == 0
}
}
#[cfg(any(test, feature = "check_contracts"))]
pub fn len(&self) -> usize {
if PUSH_MAG {
(self.top_of_stack + MAGAZINE_SIZE as isize) as usize
} else {
self.top_of_stack as usize
}
}
#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
pub fn nth(&self, index: usize) -> Option<&LinearRef> {
Some(unsafe { &*self.inner.as_ref()?.allocations[index].as_ptr() })
}
#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
pub(crate) fn check_rep(&self) -> bool {
let inner;
if let Some(storage) = &self.inner {
inner = storage;
} else {
return self.top_of_stack == 0;
};
if inner.link.is_some() {
return false;
}
if PUSH_MAG {
if self.top_of_stack < -(MAGAZINE_SIZE as isize) || self.top_of_stack > 0 {
return false;
}
} else {
if self.top_of_stack < 0 || self.top_of_stack > MAGAZINE_SIZE as isize {
return false;
}
}
inner
.allocations
.iter()
.take(self.len())
.all(|entry| !entry.as_ptr().is_null())
}
}
impl<const PUSH_MAG: bool> Default for MagazineImpl<PUSH_MAG> {
#[inline(always)]
fn default() -> Self {
Self::new(None)
}
}
impl MagazineImpl<true> {
#[cfg(test)]
pub fn into_pop(self) -> MagazineImpl<false> {
MagazineImpl::new(self.storage())
}
#[invariant(self.check_rep())]
#[ensures(ret.is_none() -> self.top_of_stack == old(self.top_of_stack) + 1,
"We add one element on success.")]
#[ensures(ret.is_some() -> self.top_of_stack == old(self.top_of_stack),
"We don't change the stack on failure.")]
#[ensures(ret.is_some() -> old(freed.get().as_ptr()) == ret.as_ref().unwrap().get().as_ptr(),
"On failure, we return `freed`.")]
#[ensures(ret.is_none() -> old(freed.get().as_ptr()) == self.peek(),
"On success, `freed` is in the magazine.")]
#[ensures(old(self.is_full()) == ret.is_some(),
"We only fail to push to full magazines.")]
#[inline(always)]
pub fn put(&mut self, freed: LinearRef) -> Option<LinearRef> {
#[cfg(features = "c_fast_path")]
const C_FAST_PATH: bool = true;
#[cfg(not(features = "c_fast_path"))]
const C_FAST_PATH: bool = false;
if C_FAST_PATH {
extern "C" {
fn slitter__magazine_put(
mag: &mut MagazineImpl<true>,
freed: LinearRef,
) -> Option<LinearRef>;
}
return unsafe { slitter__magazine_put(self, freed) };
}
let index = self.top_of_stack;
if index == 0 {
return Some(freed);
}
self.top_of_stack += 1;
unsafe {
self.inner
.as_mut()
.expect("non-zero top_of_stack must have a storage")
.allocations[(MAGAZINE_SIZE as isize + index) as usize]
.as_mut_ptr()
.write(freed);
}
None
}
#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
fn peek(&self) -> *mut c_void {
if self.top_of_stack == -(MAGAZINE_SIZE as isize) {
std::ptr::null::<c_void>() as *mut _
} else {
unsafe {
self.inner
.as_ref()
.expect("non-zero top_of_stack must have a storage")
.allocations[(MAGAZINE_SIZE as isize + self.top_of_stack) as usize - 1]
.as_ptr()
.as_ref()
}
.unwrap()
.get()
.as_ptr()
}
}
}
impl MagazineImpl<false> {
#[cfg(test)]
pub fn into_push(self) -> MagazineImpl<true> {
MagazineImpl::new(self.storage())
}
#[invariant(self.check_rep(), "Representation makes sense.")]
#[ensures(old(self.is_empty()) == ret.is_none(),
"We only fail to pop from empty magazines.")]
#[ensures(ret.is_none() -> self.top_of_stack == old(self.top_of_stack),
"We don't change the stack size on failure.")]
#[ensures(ret.is_some() -> self.top_of_stack == old(self.top_of_stack) - 1,
"Must remove one element on success.")]
#[ensures(ret.is_some() -> ret.as_ref().unwrap().get().as_ptr() == old(self.peek()),
"Must return the top of stack on success.")]
#[inline(always)]
pub fn get(&mut self) -> Option<LinearRef> {
#[cfg(features = "c_fast_path")]
const C_FAST_PATH: bool = true;
#[cfg(not(features = "c_fast_path"))]
const C_FAST_PATH: bool = false;
if C_FAST_PATH {
extern "C" {
fn slitter__magazine_get(mag: &mut MagazineImpl<false>) -> Option<LinearRef>;
}
return unsafe { slitter__magazine_get(self) };
}
if self.top_of_stack == 0 {
return None;
}
self.top_of_stack -= 1;
let mut old = MaybeUninit::uninit();
std::mem::swap(
&mut old,
&mut self
.inner
.as_mut()
.expect("non-zero top_of_stack must have a storage")
.allocations[self.top_of_stack as usize],
);
Some(unsafe { old.assume_init() })
}
#[inline(always)]
pub fn get_populated(&self) -> &[MaybeUninit<LinearRef>] {
if let Some(inner) = &self.inner {
&inner.allocations[0..self.top_of_stack as usize]
} else {
&[]
}
}
#[inline(always)]
pub fn get_unpopulated(&mut self) -> &mut [MaybeUninit<LinearRef>] {
if let Some(inner) = &mut self.inner {
&mut inner.allocations[self.top_of_stack as usize..]
} else {
&mut []
}
}
#[invariant(self.check_rep())]
#[requires(count <= MAGAZINE_SIZE as usize - self.top_of_stack as usize)]
#[inline(always)]
pub fn commit_populated(&mut self, count: usize) {
self.top_of_stack += count as isize;
}
#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
fn peek(&self) -> *mut c_void {
if self.top_of_stack == 0 {
std::ptr::null::<c_void>() as *mut _
} else {
unsafe {
self.inner
.as_ref()
.expect("non-zero top_of_stack must have a storage")
.allocations[self.top_of_stack as usize - 1]
.as_ptr()
.as_ref()
}
.unwrap()
.get()
.as_ptr()
}
}
}
impl Default for MagazineStorage {
fn default() -> Self {
#[allow(dead_code)]
extern "C" fn unused(
_mag: MagazineStorage,
_ref: Option<LinearRef>,
_link: Option<Box<MagazineStorage>>,
) {
}
Self {
allocations: unsafe { MaybeUninit::uninit().assume_init() },
link: None,
num_allocated_slow: 0,
}
}
}
impl Drop for MagazineStorage {
#[requires(self.num_allocated_slow == 0,
"Only empty magazines can be dropped.")]
fn drop(&mut self) {}
}
#[test]
fn smoke_test_magazine() {
let rack = crate::rack::get_default_rack();
let mut mag = rack.allocate_empty_magazine::<false>().0;
assert_eq!(mag.get(), None);
assert_eq!(mag.get(), None);
let mut mag2 = mag.into_push();
assert_eq!(mag2.put(LinearRef::from_address(1)), None); assert_eq!(mag2.put(LinearRef::from_address(2)), None);
let mut mag3 = mag2.into_pop();
{
let popped = mag3.get().expect("should have a value");
assert_eq!(popped.get().as_ptr() as usize, 2);
std::mem::forget(popped);
}
let mut mag4 = mag3.into_push();
assert_eq!(mag4.put(LinearRef::from_address(3)), None);
let mut mag5 = mag4.into_pop();
{
let popped = mag5.get().expect("should have a value");
assert_eq!(popped.get().as_ptr() as usize, 3); std::mem::forget(popped);
}
{
let popped = mag5.get().expect("should have a value");
assert_eq!(popped.get().as_ptr() as usize, 1); std::mem::forget(popped);
}
rack.release_empty_magazine(crate::magazine::Magazine(mag5));
}
#[test]
fn magazine_fill_up() {
let rack = crate::rack::get_default_rack();
let mut mag = rack.allocate_empty_magazine::<true>().0;
for i in 1..=MAGAZINE_SIZE as usize {
assert_eq!(mag.len(), i - 1);
assert_eq!(mag.put(LinearRef::from_address(i)), None);
assert_eq!(mag.len(), i);
}
let failed_insert = mag
.put(LinearRef::from_address(usize::MAX))
.expect("should fail");
assert_eq!(failed_insert.get().as_ptr() as usize, usize::MAX);
std::mem::forget(failed_insert);
assert_eq!(mag.len(), MAGAZINE_SIZE as usize);
let mut pop_mag = mag.into_pop();
for i in (1..=MAGAZINE_SIZE as usize).rev() {
assert_eq!(pop_mag.len(), i);
let popped = pop_mag.get().expect("has value");
assert_eq!(popped.get().as_ptr() as usize, i as usize);
std::mem::forget(popped);
assert_eq!(pop_mag.len(), i - 1);
}
assert_eq!(pop_mag.len(), 0);
assert_eq!(pop_mag.get(), None);
assert_eq!(pop_mag.get(), None);
assert_eq!(pop_mag.len(), 0);
rack.release_empty_magazine(crate::magazine::Magazine(pop_mag));
}