use core::fmt;
use super::*;
use arrayvec::ArrayVec;
use core::cell::Cell;
use core::mem::{align_of, uninitialized};
#[cfg(any(feature = "std", feature = "unstable"))]
use super::{Box, Vec};
#[derive(Clone, Debug)]
struct DropCounter<'a> {
count: &'a Cell<usize>,
}
impl<'a> DropCounter<'a> {
#[inline]
fn new(count: &'a Cell<usize>) -> Self {
Self { count }
}
}
impl<'a> Drop for DropCounter<'a> {
#[inline]
fn drop(&mut self) {
self.count.set(self.count.get() + 1);
}
}
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().kind(), ErrorKind::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().kind(),
ErrorKind::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().kind(), ErrorKind::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().kind(), ErrorKind::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().kind(), ErrorKind::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().kind(), ErrorKind::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_unwrap_test() {
let drop_count = Cell::new(0);
{
let _data = {
let scratchpad =
Scratchpad::<[usize; 1], [usize; 1]>::static_new();
let marker = scratchpad.mark_front().unwrap();
let allocation =
marker.allocate(DropCounter::new(&drop_count)).unwrap();
assert_eq!(drop_count.get(), 0);
allocation.unwrap()
};
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 1);
}
#[test]
fn marker_allocate_test() {
let drop_count = Cell::new(0);
{
let scratchpad = Scratchpad::<[usize; 1], [usize; 1]>::static_new();
let marker = scratchpad.mark_front().unwrap();
let _allocation =
marker.allocate(DropCounter::new(&drop_count)).unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 1);
}
#[test]
fn marker_allocate_array_test() {
let drop_count = Cell::new(0);
{
let scratchpad = Scratchpad::<[usize; 10], [usize; 1]>::static_new();
let marker = scratchpad.mark_front().unwrap();
let _allocation = marker
.allocate_array(10, DropCounter::new(&drop_count))
.unwrap();
assert!(drop_count.get() <= 1);
drop_count.set(0);
}
assert_eq!(drop_count.get(), 10);
}
#[test]
fn marker_allocate_array_with_test() {
let drop_count = Cell::new(0);
{
let scratchpad = Scratchpad::<[usize; 10], [usize; 1]>::static_new();
let marker = scratchpad.mark_front().unwrap();
let _allocation = marker
.allocate_array_with(10, |_| DropCounter::new(&drop_count))
.unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 10);
}
#[test]
fn marker_extend_test() {
let drop_count = Cell::new(0);
{
let scratchpad = Scratchpad::<[usize; 8], [usize; 1]>::static_new();
let marker = scratchpad.mark_back().unwrap();
let allocation =
marker.allocate(DropCounter::new(&drop_count)).unwrap();
let allocation = marker
.extend(allocation, [DropCounter::new(&drop_count)])
.unwrap();
#[cfg(any(feature = "std", feature = "unstable"))]
let allocation = {
let mut v = Vec::with_capacity(2);
v.push(DropCounter::new(&drop_count));
v.push(DropCounter::new(&drop_count));
let mut bs = v.clone().into_boxed_slice();
let ba = Box::new([
DropCounter::new(&drop_count),
DropCounter::new(&drop_count),
]);
let allocation = marker.extend(allocation, v).unwrap();
let allocation = marker.extend(allocation, bs).unwrap();
let allocation =
marker.extend(allocation, ba as Box<[DropCounter]>).unwrap();
allocation
};
assert_eq!(drop_count.get(), 0);
let _ = allocation; }
if cfg!(any(feature = "std", feature = "unstable")) {
assert_eq!(drop_count.get(), 8);
} else {
assert_eq!(drop_count.get(), 2);
}
}
#[test]
fn marker_extend_clone_test() {
let drop_count = Cell::new(0);
{
let scratchpad = Scratchpad::<[usize; 2], [usize; 1]>::static_new();
let marker = scratchpad.mark_back().unwrap();
let allocation =
marker.allocate(DropCounter::new(&drop_count)).unwrap();
let _allocation = marker
.extend_clone(allocation, &[DropCounter::new(&drop_count)][..])
.unwrap();
assert_eq!(drop_count.get(), 1);
}
assert_eq!(drop_count.get(), 3);
}
#[test]
fn allocation_concat_string_test() {
let scratchpad = Scratchpad::<[u8; 32], [usize; 1]>::static_new();
let marker = scratchpad.mark_front().unwrap();
let foo = marker.allocate_slice_copy("foo").unwrap();
let bar = marker.allocate_slice_copy("bar").unwrap();
let foobar: Allocation<str> = foo.concat(bar).unwrap();
assert_eq!(&*foobar, "foobar");
}
#[test]
fn marker_extend_string_test() {
let scratchpad = Scratchpad::<[u8; 32], [usize; 1]>::static_new();
let marker = scratchpad.mark_front().unwrap();
let foo = marker.allocate_slice_copy("foo").unwrap();
let foobar = marker.extend(foo, "bar").unwrap();
assert_eq!(&*foobar, "foobar");
}
#[test]
fn slice_move_source_collection_tuple_test() {
let scratchpad = Scratchpad::<[usize; 6], [usize; 1]>::static_new();
let drop_count = Cell::new(0);
{
let marker = scratchpad.mark_back().unwrap();
let a = [DropCounter::new(&drop_count)];
#[cfg(any(feature = "std", feature = "unstable"))]
let (b, c) = {
let mut c = Vec::new();
c.push(DropCounter::new(&drop_count));
c.push(DropCounter::new(&drop_count));
c.push(DropCounter::new(&drop_count));
(
Box::new([
DropCounter::new(&drop_count),
DropCounter::new(&drop_count),
]) as Box<[_]>,
c,
)
};
#[cfg(not(any(feature = "std", feature = "unstable")))]
let (b, c) = (
[DropCounter::new(&drop_count), DropCounter::new(&drop_count)],
[
DropCounter::new(&drop_count),
DropCounter::new(&drop_count),
DropCounter::new(&drop_count),
],
);
let _allocation = marker.concat_slices((a, b, c)).unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 6);
}
#[test]
fn slice_move_source_collection_array_test() {
let scratchpad = Scratchpad::<[usize; 3], [usize; 1]>::static_new();
let drop_count = Cell::new(0);
{
let marker = scratchpad.mark_back().unwrap();
let a = [DropCounter::new(&drop_count)];
let b = [DropCounter::new(&drop_count)];
let c = [DropCounter::new(&drop_count)];
let _allocation = marker.concat_slices([a, b, c]).unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 3);
drop_count.set(0);
#[cfg(any(feature = "std", feature = "unstable"))]
{
{
let marker = scratchpad.mark_back().unwrap();
let a = Box::new([DropCounter::new(&drop_count)]) as Box<[_]>;
let b = Box::new([DropCounter::new(&drop_count)]) as Box<[_]>;
let c = Box::new([DropCounter::new(&drop_count)]) as Box<[_]>;
let _allocation = marker.concat_slices([a, b, c]).unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 3);
drop_count.set(0);
{
let marker = scratchpad.mark_back().unwrap();
let mut a = Vec::new();
a.push(DropCounter::new(&drop_count));
let mut b = Vec::new();
b.push(DropCounter::new(&drop_count));
let mut c = Vec::new();
c.push(DropCounter::new(&drop_count));
let _allocation = marker.concat_slices([a, b, c]).unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 3);
}
}
#[test]
#[cfg(any(feature = "std", feature = "unstable"))]
fn slice_move_source_collection_vec_test() {
let scratchpad = Scratchpad::<[usize; 3], [usize; 1]>::static_new();
let drop_count = Cell::new(0);
{
let marker = scratchpad.mark_back().unwrap();
let a = [DropCounter::new(&drop_count)];
let b = [DropCounter::new(&drop_count)];
let c = [DropCounter::new(&drop_count)];
let mut sources = Vec::new();
sources.push(a);
sources.push(b);
sources.push(c);
let _allocation = marker.concat_slices(sources).unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 3);
drop_count.set(0);
{
let marker = scratchpad.mark_back().unwrap();
let a = Box::new([DropCounter::new(&drop_count)]) as Box<[_]>;
let b = Box::new([DropCounter::new(&drop_count)]) as Box<[_]>;
let c = Box::new([DropCounter::new(&drop_count)]) as Box<[_]>;
let mut sources = Vec::new();
sources.push(a);
sources.push(b);
sources.push(c);
let _allocation = marker.concat_slices(sources).unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 3);
drop_count.set(0);
{
let marker = scratchpad.mark_back().unwrap();
let mut a = Vec::new();
a.push(DropCounter::new(&drop_count));
let mut b = Vec::new();
b.push(DropCounter::new(&drop_count));
let mut c = Vec::new();
c.push(DropCounter::new(&drop_count));
let mut sources = Vec::new();
sources.push(a);
sources.push(b);
sources.push(c);
let _allocation = marker.concat_slices(sources).unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 3);
}
#[test]
#[cfg(any(feature = "std", feature = "unstable"))]
fn slice_move_source_collection_boxed_slice_test() {
let scratchpad = Scratchpad::<[usize; 3], [usize; 1]>::static_new();
let drop_count = Cell::new(0);
{
let marker = scratchpad.mark_back().unwrap();
let a = [DropCounter::new(&drop_count)];
let b = [DropCounter::new(&drop_count)];
let c = [DropCounter::new(&drop_count)];
let _allocation = marker
.concat_slices(Box::new([a, b, c]) as Box<[[_; 1]]>)
.unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 3);
drop_count.set(0);
{
let marker = scratchpad.mark_back().unwrap();
let a = Box::new([DropCounter::new(&drop_count)]) as Box<[_]>;
let b = Box::new([DropCounter::new(&drop_count)]) as Box<[_]>;
let c = Box::new([DropCounter::new(&drop_count)]) as Box<[_]>;
let _allocation = marker
.concat_slices(Box::new([a, b, c]) as Box<[Box<[_]>]>)
.unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 3);
drop_count.set(0);
{
let marker = scratchpad.mark_back().unwrap();
let mut a = Vec::new();
a.push(DropCounter::new(&drop_count));
let mut b = Vec::new();
b.push(DropCounter::new(&drop_count));
let mut c = Vec::new();
c.push(DropCounter::new(&drop_count));
let _allocation = marker
.concat_slices(Box::new([a, b, c]) as Box<[Vec<_>]>)
.unwrap();
assert_eq!(drop_count.get(), 0);
}
assert_eq!(drop_count.get(), 3);
}
#[test]
fn zst_test() {
let scratchpad = Scratchpad::<[u8; 1], [usize; 2]>::static_new();
let front_marker = scratchpad.mark_front().unwrap();
let _byte = front_marker.allocate(0u8).unwrap();
assert_eq!(*scratchpad.markers.borrow(), ([1usize], []));
let _unit = front_marker.allocate(()).unwrap();
assert_eq!(*scratchpad.markers.borrow(), ([1usize], []));
#[derive(Clone, Copy, Debug)]
struct UnitStruct;
let back_marker = scratchpad.mark_back().unwrap();
assert_eq!(*scratchpad.markers.borrow(), ([1usize], [1usize]));
let _unit = back_marker.allocate_array(12, UnitStruct).unwrap();
assert_eq!(*scratchpad.markers.borrow(), ([1usize], [1usize]));
}