use super::*;
use arrayvec::ArrayVec;
use core::mem::uninitialized;
trait MarkerInternal: Marker {
fn index(&self) -> usize;
}
impl<'scratchpad, BufferT, TrackingT> MarkerInternal
for MarkerFront<'scratchpad, BufferT, TrackingT>
where
BufferT: Buffer + 'scratchpad,
TrackingT: Tracking + 'scratchpad,
{
fn index(&self) -> usize {
self.index
}
}
impl<'scratchpad, BufferT, TrackingT> MarkerInternal
for MarkerBack<'scratchpad, BufferT, TrackingT>
where
BufferT: Buffer + 'scratchpad,
TrackingT: Tracking + 'scratchpad,
{
fn index(&self) -> usize {
self.scratchpad.markers.borrow().data.capacity() - self.index - 1
}
}
impl<T, U, TrackingT> PartialEq<(T, U)> for MarkerStacks<TrackingT>
where
T: AsRef<[usize]>,
U: AsRef<[usize]>,
TrackingT: Tracking,
{
fn eq(&self, other: &(T, U)) -> bool {
let front_slice = other.0.as_ref();
if self.front != front_slice.len() {
return false;
}
let capacity = self.data.capacity();
let mut back = self.back;
if back == core::usize::MAX {
back = capacity;
}
let back_len = capacity - back;
let back_slice = other.1.as_ref();
if back_len != back_slice.len() {
return false;
}
for (index, &other_element) in front_slice.iter().enumerate() {
if self.data.get(index) != other_element {
return false;
}
}
let last_index = capacity - 1;
for (index, &other_element) in back_slice.iter().enumerate() {
if self.data.get(last_index - index) != other_element {
return false;
}
}
true
}
}
const BUFFER_SIZE: usize = 32;
const MAX_MARKERS: usize = 4;
type SimpleBuffer = array_type_for_bytes!(u64, BUFFER_SIZE);
type SimpleTracking = array_type_for_markers!(usize, MAX_MARKERS);
type SimpleScratchpad = Scratchpad<SimpleBuffer, SimpleTracking>;
fn validate_basic_operations<'scratchpad, MF, CF, M>(
scratchpad: &'scratchpad SimpleScratchpad,
mark: MF,
conv: CF,
) where
MF: Fn(&'scratchpad SimpleScratchpad) -> Result<M, Error>,
CF: for<'a> Fn(
&'a [usize]
) -> (
ArrayVec<SimpleTracking>,
ArrayVec<SimpleTracking>,
),
M: MarkerInternal + fmt::Debug,
{
assert_eq!(*scratchpad.markers.borrow(), conv(&[][..]));
unsafe {
assert_eq!(
(*scratchpad.buffer.get()).as_ptr() as usize & 0x7,
0,
);
}
let a = mark(scratchpad).unwrap();
assert_eq!(a.index(), 0);
assert_eq!(*scratchpad.markers.borrow(), conv(&[0][..]));
let a_padding = {
let a0 = a.allocate(5u32).unwrap();
assert_eq!(*a0, 5u32);
assert_eq!(*scratchpad.markers.borrow(), conv(&[4][..]));
let a1_padding = align_of::<f64>() - 4;
let a1 = a.allocate(12.5f64).unwrap();
assert_eq!(*a0, 5u32);
assert_eq!(*a1, 12.5f64);
assert_eq!(
*scratchpad.markers.borrow(),
conv(&[12 + a1_padding][..]),
);
a1_padding
};
let b = mark(scratchpad).unwrap();
assert_eq!(b.index(), 1);
assert_eq!(
*scratchpad.markers.borrow(),
conv(&[12 + a_padding, 12 + a_padding][..]),
);
assert_eq!(
a.allocate(3u8).unwrap_err(),
Error::MarkerLocked,
);
{
let b0 = b.allocate(3u8).unwrap();
assert_eq!(*b0, 3u8);
assert_eq!(
*scratchpad.markers.borrow(),
conv(&[12 + a_padding, 13 + a_padding][..]),
);
let b1 = b.allocate(9u16).unwrap();
assert_eq!(*b0, 3u8);
assert_eq!(*b1, 9u16);
assert_eq!(
*scratchpad.markers.borrow(),
conv(&[12 + a_padding, 16 + a_padding][..]),
);
}
assert_eq!(
b.allocate_array(20, 24u8).unwrap_err(),
Error::InsufficientMemory,
);
let c = mark(scratchpad).unwrap();
assert_eq!(c.index(), 2);
assert_eq!(
*scratchpad.markers.borrow(),
conv(&[12 + a_padding, 16 + a_padding, 16 + a_padding][..]),
);
{
let c0 = c.allocate_array(12, 17u8).unwrap();
assert_eq!(
*c0,
[17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17],
);
assert_eq!(
*scratchpad.markers.borrow(),
conv(&[12 + a_padding, 16 + a_padding, 28 + a_padding][..]),
);
}
let d = mark(scratchpad).unwrap();
assert_eq!(d.index(), 3);
assert_eq!(
*scratchpad.markers.borrow(),
conv(
&[
12 + a_padding,
16 + a_padding,
28 + a_padding,
28 + a_padding,
][..],
),
);
assert_eq!(
mark(scratchpad).unwrap_err(),
Error::MarkerLimit,
);
drop(a);
assert_eq!(
*scratchpad.markers.borrow(),
conv(
&[
core::usize::MAX,
16 + a_padding,
28 + a_padding,
28 + a_padding,
][..],
),
);
assert_eq!(
mark(scratchpad).unwrap_err(),
Error::MarkerLimit,
);
drop(c);
assert_eq!(
*scratchpad.markers.borrow(),
conv(
&[
core::usize::MAX,
16 + a_padding,
core::usize::MAX,
28 + a_padding,
][..],
),
);
drop(d);
assert_eq!(
*scratchpad.markers.borrow(),
conv(&[core::usize::MAX, 16 + a_padding][..]),
);
let e = mark(scratchpad).unwrap();
assert_eq!(e.index(), 2);
assert_eq!(
*scratchpad.markers.borrow(),
conv(&[core::usize::MAX, 16 + a_padding, 16 + a_padding][..]),
);
drop(e);
assert_eq!(
*scratchpad.markers.borrow(),
conv(&[core::usize::MAX, 16 + a_padding][..]),
);
drop(b);
assert_eq!(*scratchpad.markers.borrow(), ([], []));
}
#[test]
fn validate_front_operations() {
let scratchpad = unsafe {
Scratchpad::new(
uninitialized::<SimpleBuffer>(),
uninitialized::<SimpleTracking>(),
)
};
validate_basic_operations(
&scratchpad,
|scratchpad| scratchpad.mark_front(),
|stack| {
(
stack.iter().map(|x| *x).collect(),
ArrayVec::new(),
)
},
);
}
#[test]
fn validate_back_operations() {
let scratchpad = unsafe {
Scratchpad::new(
uninitialized::<SimpleBuffer>(),
uninitialized::<SimpleTracking>(),
)
};
validate_basic_operations(
&scratchpad,
|scratchpad| scratchpad.mark_back(),
|stack| {
let flipped_stack = stack
.iter()
.map(|x| {
if *x == core::usize::MAX {
*x
} else {
BUFFER_SIZE - *x
}
})
.collect();
(ArrayVec::new(), flipped_stack)
},
);
}
fn validate_memory_limits<'scratchpad, MF, OF, M, O>(
scratchpad: &'scratchpad SimpleScratchpad,
mark: MF,
mark_opposite: OF,
) where
MF: Fn(&'scratchpad SimpleScratchpad) -> Result<M, Error>,
OF: Fn(&'scratchpad SimpleScratchpad) -> Result<O, Error>,
M: Marker + fmt::Debug,
O: Marker + fmt::Debug,
{
let test_allocation = |bytes_available| {
let marker = mark(scratchpad).unwrap();
let result = marker.allocate_array(bytes_available + 1, 0u8);
assert_eq!(result.unwrap_err(), Error::InsufficientMemory);
let result = marker.allocate_array(bytes_available, 0u8);
assert!(result.is_ok());
};
let opposite_marker = mark_opposite(scratchpad).unwrap();
for i in 0..BUFFER_SIZE {
test_allocation(BUFFER_SIZE - i);
opposite_marker.allocate(0u8).unwrap();
}
test_allocation(0);
}
#[test]
fn validate_front_memory_limits() {
let scratchpad =
unsafe { Scratchpad::new(uninitialized(), uninitialized()) };
validate_memory_limits(
&scratchpad,
|scratchpad| scratchpad.mark_front(),
|scratchpad| scratchpad.mark_back(),
);
}
#[test]
fn validate_back_memory_limits() {
let scratchpad =
unsafe { Scratchpad::new(uninitialized(), uninitialized()) };
validate_memory_limits(
&scratchpad,
|scratchpad| scratchpad.mark_back(),
|scratchpad| scratchpad.mark_front(),
);
}
fn validate_marker_limits<'scratchpad, MF, OF, M, O>(
scratchpad: &'scratchpad SimpleScratchpad,
mark: MF,
mark_opposite: OF,
) where
MF: Fn(&'scratchpad SimpleScratchpad) -> Result<M, Error>,
OF: Fn(&'scratchpad SimpleScratchpad) -> Result<O, Error>,
M: Marker + fmt::Debug,
O: Marker + fmt::Debug,
{
let test_marker_allocation = |markers_available| {
let mut markers = ArrayVec::<[M; MAX_MARKERS]>::new();
for _ in 0..markers_available {
markers.push(mark(scratchpad).unwrap());
}
let result = mark(scratchpad);
assert_eq!(result.unwrap_err(), Error::MarkerLimit);
};
let mut opposite_markers = ArrayVec::<[O; MAX_MARKERS]>::new();
for i in 0..MAX_MARKERS {
test_marker_allocation(MAX_MARKERS - i);
opposite_markers.push(mark_opposite(scratchpad).unwrap());
}
test_marker_allocation(0);
}
#[test]
fn validate_front_marker_limits() {
let scratchpad =
unsafe { Scratchpad::new(uninitialized(), uninitialized()) };
validate_marker_limits(
&scratchpad,
|scratchpad| scratchpad.mark_front(),
|scratchpad| scratchpad.mark_back(),
);
}
#[test]
fn validate_back_marker_limits() {
let scratchpad =
unsafe { Scratchpad::new(uninitialized(), uninitialized()) };
validate_marker_limits(
&scratchpad,
|scratchpad| scratchpad.mark_back(),
|scratchpad| scratchpad.mark_front(),
);
}
#[test]
fn allocation_drop_test() {
struct DropTest<'a> {
is_active: &'a mut bool,
}
impl<'a> DropTest<'a> {
fn check(&self) {
assert!(*self.is_active);
}
}
impl<'a> Drop for DropTest<'a> {
fn drop(&mut self) {
assert!(*self.is_active);
*self.is_active = false;
}
}
let mut is_active = true;
{
let data = {
let scratchpad = Scratchpad::new([0usize; 1], [0usize; 1]);
let marker = scratchpad.mark_front().unwrap();
let allocation = marker
.allocate(DropTest {
is_active: &mut is_active,
})
.unwrap();
allocation.check();
allocation.unwrap()
};
data.check();
}
assert!(!is_active);
}