use std::alloc::{alloc, dealloc, Layout};
use std::mem::MaybeUninit;
use std::ops::Range;
use std::pin::Pin;
use std::ptr::{drop_in_place, NonNull};
use crate::internal::{In, Out};
#[repr(C)]
struct Node<T> {
next: Option<NonNull<Node<T>>>,
len: usize,
data: [MaybeUninit<T>],
}
#[repr(C)]
struct Head<T> {
next: Option<NonNull<Node<T>>>,
len: usize,
}
union FatPtr<T> {
raw: (NonNull<Head<T>>, usize),
fat: NonNull<Node<T>>,
}
pub struct PageList<T> {
first: Option<NonNull<Node<T>>>,
}
impl<T> Node<T> {
const MIN_PAGE_SIZE: usize = {
if std::mem::size_of::<T>() == 0 {
usize::MAX
} else {
let n = 512 / std::mem::size_of::<T>();
if n == 0 {
1
} else {
n
}
}
};
fn new(cap: usize) -> NonNull<Self> {
let cap = std::cmp::max(cap, Self::MIN_PAGE_SIZE);
debug_assert_eq!(
std::mem::size_of::<(NonNull<Head<T>>, usize)>(),
std::mem::size_of::<NonNull<Node<T>>>()
);
Vec::<u32>::new().into_boxed_slice();
unsafe {
let head = alloc(Self::layout(cap)) as *mut Head<T>;
head.write(Head { next: None, len: 0 });
FatPtr {
raw: (NonNull::new_unchecked(head), cap),
}
.fat
}
}
fn dealloc(ptr: NonNull<Self>) {
unsafe { dealloc(ptr.as_ptr().cast(), Layout::for_value(ptr.as_ref())) }
}
fn capacity(&self) -> usize {
self.data.len()
}
unsafe fn assume_slice(&mut self, range: Range<usize>) -> &mut [T] {
&mut *(self.data.get_unchecked_mut(range) as *mut _ as *mut [T])
}
fn slice(&mut self) -> &mut [T] {
unsafe { &mut *(self.data.get_unchecked_mut(..self.len) as *mut _ as *mut [T]) }
}
fn as_mut<'a>(ptr: NonNull<Self>) -> &'a mut Self {
unsafe { &mut *ptr.as_ptr() }
}
const fn layout(cap: usize) -> Layout {
use std::mem::{align_of, size_of};
let mut align = align_of::<Head<T>>();
let mut pad = 0;
if align_of::<T>() > align {
pad = align_of::<T>() - align;
align = align_of::<T>();
}
unsafe {
Layout::from_size_align_unchecked(
size_of::<Head<T>>() + pad + cap * size_of::<T>(),
align,
)
}
}
}
impl<T> PageList<T> {
pub fn build<I, U, F>(iter: I, constructor: F) -> Self
where
I: IntoIterator<Item = U>,
F: FnMut(U, In<T>) -> Out<T>,
{
let mut first = None;
Tail { cur: &mut first }.extend(iter, constructor);
PageList { first }
}
pub fn cursor(&mut self) -> Cursor<T> {
Cursor {
idx: 0,
cur: &mut self.first,
}
}
}
impl<T> Drop for PageList<T> {
fn drop(&mut self) {
unsafe {
while let Some(node) = self.first {
let node = Node::as_mut(node);
drop_in_place(node.slice());
self.first = node.next;
Node::dealloc(node.into());
}
}
}
}
pub struct Cursor<'cur, T> {
idx: usize,
cur: &'cur mut Option<NonNull<Node<T>>>,
}
pub struct Tail<'cur, T> {
cur: &'cur mut Option<NonNull<Node<T>>>,
}
impl<'cur, T> Tail<'cur, T> {
pub fn extend<I, U, F>(self, iter: I, mut constructor: F)
where
I: IntoIterator<Item = U>,
F: FnMut(U, In<T>) -> Out<T>,
{
let mut iter = iter.into_iter();
let mut next = self.cur;
while let Some(item) = iter.next() {
let node = Node::as_mut(*next.get_or_insert_with(|| Node::new(iter.size_hint().0 + 1)));
In::pinned(
unsafe { Pin::new_unchecked(node.data.get_unchecked_mut(node.len)) },
|p| constructor(item, p),
);
node.len += 1;
if node.len == node.capacity() {
next = &mut node.next;
}
}
}
}
impl<'cur, T> Cursor<'cur, T>
where
T: 'cur,
{
pub fn truncate_rest(self) -> Tail<'cur, T> {
let node = match self.cur {
Some(node) => Node::as_mut(*node),
None => return Tail { cur: self.cur },
};
drop(PageList {
first: node.next.take(),
});
let len = node.len;
unsafe {
drop_in_place(node.assume_slice(self.idx..len));
}
node.len = self.idx;
Tail { cur: self.cur }
}
pub fn zip_each<I, F, U>(&mut self, iter: I, mut each: F)
where
I: IntoIterator<Item = U>,
F: FnMut(&mut T, U),
{
let mut iter = iter.into_iter();
while let Some(cur) = self.cur.map(Node::as_mut) {
if let Some(slot) = cur.slice().get_mut(self.idx) {
if let Some(item) = iter.next() {
each(slot, item);
self.idx += 1;
if self.idx == cur.len {
self.idx = 0;
self.cur = &mut cur.next;
}
continue;
}
}
break;
}
}
}
#[cfg(test)]
impl<'cur, T> Iterator for Cursor<'cur, T>
where
T: 'cur,
{
type Item = &'cur mut T;
fn next(&mut self) -> Option<&'cur mut T> {
let cur = match self.cur.map(Node::as_mut) {
Some(node) if self.idx < node.len => node,
_ => return None,
};
let item = unsafe { cur.data.get_unchecked_mut(self.idx).assume_init_mut() };
self.idx += 1;
if self.idx == cur.len {
self.idx = 0;
self.cur = &mut cur.next;
}
Some(item)
}
}
#[cfg(test)]
mod test {
use super::*;
struct NoHint<I>(I);
impl<I> Iterator for NoHint<I>
where
I: Iterator,
{
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
self.0.next()
}
}
#[test]
fn empty_list() {
let list = PageList::build([], |n: usize, p| p.put(n));
assert!(list.first.is_none());
}
#[test]
fn one_node() {
let list = PageList::build(0..128, |n, p| p.put(n));
let first = Node::as_mut(list.first.unwrap());
unsafe {
assert_eq!(first.data[0].assume_init_read(), 0);
assert_eq!(first.data[127].assume_init_read(), 127);
}
}
#[test]
fn one_node_alloc() {
let list = PageList::build(0..20, |n, p| p.put(Box::new(n)));
unsafe {
let first = Node::as_mut(list.first.unwrap());
assert_eq!(**first.data[0].assume_init_ref(), 0);
assert_eq!(**first.data[19].assume_init_ref(), 19);
}
}
#[test]
fn many_nodes() {
let mut list = PageList::build(NoHint(0..256), |n, p| p.put(n));
let first = Node::as_mut(list.first.unwrap());
assert!(first.capacity() < 256);
list.cursor().zip_each(0..256, |left, right| {
assert_eq!(*left, right);
});
}
#[test]
fn cursor_iter() {
let mut list = PageList::build(0..100, |n, p| p.put(n));
list.cursor().zip_each(0..100, |left, right| {
assert_eq!(*left, right);
});
}
#[test]
fn cursor_truncate_unaligned() {
let mut list = PageList::build(NoHint(0..300), |n, p| p.put(Box::new(n)));
let mut cur = list.cursor();
cur.by_ref().take(100).count();
cur.truncate_rest();
list.cursor().zip_each(0..200, |left, right| {
assert_eq!(**left, right);
});
}
#[test]
fn cursor_truncate_extend_unaligned() {
let mut list = PageList::build(NoHint(0..300), |n, p| p.put(Box::new(n)));
let mut cur = list.cursor();
cur.by_ref().take(100).count();
cur.truncate_rest()
.extend(200..300, |n, p| p.put(Box::new(n)));
list.cursor()
.zip_each((0..100).chain(200..300), |left, right| {
assert_eq!(**left, right);
});
}
#[test]
fn cursor_truncate_extend_empty() {
let mut list = PageList::build(NoHint(0..300), |n, p| p.put(Box::new(n)));
let mut cur = list.cursor();
cur.by_ref().take(100).count();
cur.truncate_rest().extend([], |n, p| p.put(Box::new(n)));
list.cursor().zip_each(0..100, |left, right| {
assert_eq!(**left, right);
});
}
#[test]
fn cursor_truncate_extend_aligned() {
let mut list = PageList::build(NoHint(0..512), |n, p| p.put(Box::new(n)));
let mut cur = list.cursor();
cur.by_ref().take(256).count();
cur.truncate_rest()
.extend(512..640, |n, p| p.put(Box::new(n)));
list.cursor()
.zip_each((0..256).chain(512..640), |left, right| {
assert_eq!(**left, right);
});
}
}