use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ptr;
#[cfg(all(test, not(miri)))]
use std::collections::BTreeSet;
#[cfg(all(test, not(miri)))]
use std::sync::atomic::{AtomicUsize, Ordering};
#[cfg(all(test, not(miri)))]
use std::sync::Mutex;
#[cfg(all(test, not(miri)))]
thread_local!(static LL_NODE_COUNTER: AtomicUsize = const { AtomicUsize::new(1) });
#[cfg(all(test, not(miri)))]
thread_local!(static LL_ALLOC_LIST: Mutex<BTreeSet<usize>> = const { Mutex::new(BTreeSet::new()) });
#[cfg(all(test, not(miri)))]
fn alloc_nid() -> usize {
let nid: usize = LL_NODE_COUNTER.with(|nc| nc.fetch_add(1, Ordering::AcqRel));
#[cfg(not(feature = "dhat-heap"))]
{
LL_ALLOC_LIST.with(|llist| llist.lock().unwrap().insert(nid));
}
nid
}
#[cfg(all(test, not(miri), not(feature = "dhat-heap")))]
fn release_nid(nid: usize) {
{
let r = LL_ALLOC_LIST.with(|llist| llist.lock().unwrap().remove(&nid));
assert!(r);
}
}
#[cfg(test)]
pub fn assert_released() {
#[cfg(all(test, not(miri), not(feature = "dhat-heap")))]
{
let is_empt = LL_ALLOC_LIST.with(|llist| {
let x = llist.lock().unwrap();
eprintln!("Assert Released - Remaining -> {:?}", x);
x.is_empty()
});
assert!(is_empt);
}
}
pub trait LLWeight {
fn ll_weight(&self) -> usize;
}
#[derive(Clone, Debug)]
pub(crate) struct LL<K>
where
K: LLWeight + Clone + Debug,
{
head: *mut LLNode<K>,
tail: *mut LLNode<K>,
size: usize,
}
#[derive(Debug)]
struct LLNode<K>
where
K: LLWeight + Clone + Debug,
{
pub(crate) k: MaybeUninit<K>,
next: *mut LLNode<K>,
prev: *mut LLNode<K>,
#[cfg(all(test, not(miri)))]
pub(crate) nid: usize,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct LLNodeRef<K>
where
K: LLWeight + Clone + Debug,
{
inner: *mut LLNode<K>,
}
impl<K> LLNodeRef<K>
where
K: LLWeight + Clone + Debug,
{
pub fn is_null(&self) -> bool {
self.inner.is_null()
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn make_mut(&self) -> &mut K {
&mut *(*self.inner).k.as_mut_ptr()
}
}
impl<K> AsRef<K> for LLNodeRef<K>
where
K: LLWeight + Clone + Debug,
{
fn as_ref(&self) -> &K {
unsafe { &*(*self.inner).k.as_ptr() }
}
}
impl<K> PartialEq<&LLNodeOwned<K>> for &LLNodeRef<K>
where
K: LLWeight + Clone + Debug,
{
fn eq(&self, other: &&LLNodeOwned<K>) -> bool {
self.inner == other.inner
}
}
#[derive(Debug)]
pub(crate) struct LLNodeOwned<K>
where
K: LLWeight + Clone + Debug,
{
inner: *mut LLNode<K>,
}
impl<K> LLNodeOwned<K>
where
K: LLWeight + Clone + Debug,
{
fn into_inner(mut self) -> *mut LLNode<K> {
let x = self.inner;
self.inner = ptr::null_mut();
x
}
pub fn is_null(&self) -> bool {
self.inner.is_null()
}
}
impl<K> PartialEq for &LLNodeOwned<K>
where
K: LLWeight + Clone + Debug,
{
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<K> AsRef<K> for LLNodeOwned<K>
where
K: LLWeight + Clone + Debug,
{
fn as_ref(&self) -> &K {
unsafe { &*(*self.inner).k.as_ptr() }
}
}
impl<K> AsMut<K> for LLNodeOwned<K>
where
K: LLWeight + Clone + Debug,
{
fn as_mut(&mut self) -> &mut K {
unsafe { &mut *(*self.inner).k.as_mut_ptr() }
}
}
impl<K> Drop for LLNodeOwned<K>
where
K: LLWeight + Clone + Debug,
{
fn drop(&mut self) {
if !self.inner.is_null() {
unsafe {
debug_assert!((*self.inner).next.is_null());
debug_assert!((*self.inner).prev.is_null());
}
panic!("dropping LLNodeOwned<K> which has valid content, this should never happen!");
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct LLIterMut<'a, K>
where
K: LLWeight + Clone + Debug,
{
next: *mut LLNode<K>,
end: *mut LLNode<K>,
phantom: PhantomData<&'a K>,
}
impl<K> LL<K>
where
K: LLWeight + Clone + Debug,
{
pub(crate) fn new( ) -> Self {
let (head, tail) = LLNode::create_markers();
LL {
head,
tail,
size: 0,
}
}
#[allow(dead_code)]
pub(crate) fn iter_mut(&self) -> LLIterMut<'_, K> {
LLIterMut {
next: unsafe { (*self.head).next },
end: self.tail,
phantom: PhantomData,
}
}
pub(crate) fn append_k(&mut self, k: K) -> LLNodeRef<K> {
let n = LLNode::new(k);
self.append_n(n)
}
pub(crate) fn append_n(&mut self, owned: LLNodeOwned<K>) -> LLNodeRef<K> {
let n = owned.into_inner();
unsafe {
self.size += (*(*n).k.as_ptr()).ll_weight();
debug_assert!((*self.tail).next.is_null());
debug_assert!(!(*self.tail).prev.is_null());
let pred = (*self.tail).prev;
debug_assert!(!pred.is_null());
debug_assert!((*pred).next == self.tail);
(*n).prev = pred;
(*n).next = self.tail;
(*pred).next = n;
(*self.tail).prev = n;
debug_assert!(!(*n).prev.is_null());
debug_assert!(!(*n).next.is_null());
debug_assert!(!(*(*n).prev).next.is_null());
debug_assert!(!(*(*n).next).prev.is_null());
debug_assert!((*(*n).prev).next == n);
debug_assert!((*(*n).next).prev == n);
};
LLNodeRef { inner: n }
}
pub(crate) fn touch(&mut self, n: LLNodeRef<K>) {
debug_assert!(self.size > 0);
if n.inner == unsafe { (*self.tail).prev } {
} else {
let previous_size = self.size;
let owned = self.extract(n);
self.append_n(owned);
let after_size = self.size;
debug_assert_eq!(previous_size, after_size);
}
}
pub(crate) fn pop(&mut self) -> Option<LLNodeOwned<K>> {
let next = unsafe { (*self.head).next };
if next == self.tail {
None
} else {
let n = unsafe { (*self.head).next };
let owned = self.extract(LLNodeRef { inner: n });
debug_assert!(!owned.is_null());
debug_assert!(owned.inner != self.head);
debug_assert!(owned.inner != self.tail);
Some(owned)
}
}
pub(crate) fn pop_n_free(&mut self) -> Option<K> {
if let Some(owned) = self.pop() {
let ll_node = owned.into_inner();
let k = LLNode::into_inner(ll_node);
Some(k)
} else {
None
}
}
pub(crate) fn extract(&mut self, n: LLNodeRef<K>) -> LLNodeOwned<K> {
assert!(!n.is_null());
assert!(self.size > 0);
unsafe {
debug_assert!(!(*n.inner).prev.is_null());
debug_assert!(!(*n.inner).next.is_null());
debug_assert!(!(*(*n.inner).prev).next.is_null());
debug_assert!(!(*(*n.inner).next).prev.is_null());
debug_assert!((*(*n.inner).prev).next == n.inner);
debug_assert!((*(*n.inner).next).prev == n.inner);
self.size -= (*(*n.inner).k.as_ptr()).ll_weight();
}
unsafe {
let prev = (*n.inner).prev;
let next = (*n.inner).next;
(*next).prev = prev;
(*prev).next = next;
if cfg!(test) || cfg!(debug_assertions) {
(*n.inner).prev = ptr::null_mut();
(*n.inner).next = ptr::null_mut();
}
}
LLNodeOwned { inner: n.inner }
}
pub(crate) fn len(&self) -> usize {
self.size
}
pub(crate) fn drop_head(&mut self) {
if let Some(owned) = self.pop() {
let n = owned.into_inner();
LLNode::free(n);
}
}
pub(crate) fn peek_head(&self) -> Option<&K> {
debug_assert!(!self.head.is_null());
let next = unsafe { (*self.head).next };
if next == self.tail {
None
} else {
let l = unsafe {
let ptr = (*next).k.as_ptr();
&(*ptr) as &K
};
Some(l)
}
}
#[cfg(test)]
pub(crate) fn peek_tail(&self) -> Option<&K> {
debug_assert!(!self.tail.is_null());
let prev = unsafe { (*self.tail).prev };
if prev == self.head {
None
} else {
let l = unsafe {
let ptr = (*prev).k.as_ptr();
&(*ptr) as &K
};
Some(l)
}
}
#[cfg(test)]
pub(crate) fn verify(&self) {
let head = self.head;
let tail = self.tail;
let expect_size = self.size;
let mut size = 0;
assert_ne!(head, tail);
unsafe {
assert!((*head).prev.is_null());
assert!(!(*head).next.is_null());
assert!((*tail).next.is_null());
assert!(!(*tail).prev.is_null());
}
let mut n = unsafe { (*head).next };
unsafe {
assert_eq!((*n).prev, head);
}
while n != tail {
unsafe {
let next = (*n).next;
size += (*(*n).k.as_ptr()).ll_weight();
assert!(!(*n).prev.is_null());
assert!(!(*n).next.is_null());
assert!(!(*(*n).prev).next.is_null());
assert!((*(*n).prev).next == n);
assert!(!(*(*n).next).prev.is_null());
assert!((*(*n).next).prev == n);
n = next;
}
}
assert_eq!(expect_size, size);
}
}
impl<K> Drop for LL<K>
where
K: LLWeight + Clone + Debug,
{
fn drop(&mut self) {
let head = self.head;
let tail = self.tail;
debug_assert!(head != tail);
let mut n = unsafe { (*head).next };
while n != tail {
unsafe {
let next = (*n).next;
debug_assert!((*next).prev == n);
debug_assert!(!(*n).k.as_mut_ptr().is_null());
LLNode::free(n);
n = next;
}
}
LLNode::free_marker(head);
LLNode::free_marker(tail);
}
}
impl<K> LLNode<K>
where
K: LLWeight + Clone + Debug,
{
#[inline]
pub(crate) fn create_markers() -> (*mut Self, *mut Self) {
let head = Box::into_raw(Box::new(LLNode {
k: MaybeUninit::uninit(),
next: ptr::null_mut(),
prev: ptr::null_mut(),
#[cfg(all(test, not(miri)))]
nid: alloc_nid(),
}));
let tail = Box::into_raw(Box::new(LLNode {
k: MaybeUninit::uninit(),
next: ptr::null_mut(),
prev: ptr::null_mut(),
#[cfg(all(test, not(miri)))]
nid: alloc_nid(),
}));
unsafe {
(*head).next = tail;
(*tail).prev = head;
}
(head, tail)
}
#[inline]
#[allow(clippy::new_ret_no_self)]
pub(crate) fn new(
k: K,
) -> LLNodeOwned<K> {
let b = Box::new(LLNode {
k: MaybeUninit::new(k),
next: ptr::null_mut(),
prev: ptr::null_mut(),
#[cfg(all(test, not(miri)))]
nid: alloc_nid(),
});
let inner = Box::into_raw(b);
LLNodeOwned { inner }
}
#[inline]
fn into_inner(v: *mut Self) -> K {
debug_assert!(!v.is_null());
let llnode = unsafe { Box::from_raw(v) };
let k = unsafe { llnode.k.assume_init() };
#[cfg(all(test, not(miri), not(feature = "dhat-heap")))]
release_nid(llnode.nid);
k
}
#[inline]
fn free(v: *mut Self) {
let _ = Self::into_inner(v);
}
#[inline]
fn free_marker(v: *mut Self) {
debug_assert!(!v.is_null());
let _llnode = unsafe { Box::from_raw(v) };
#[cfg(all(test, not(miri), not(feature = "dhat-heap")))]
release_nid(_llnode.nid)
}
}
impl<K> AsRef<K> for LLNode<K>
where
K: LLWeight + Clone + Debug,
{
fn as_ref(&self) -> &K {
unsafe {
let ptr = self.k.as_ptr();
&(*ptr) as &K
}
}
}
impl<K> AsMut<K> for LLNode<K>
where
K: LLWeight + Clone + Debug,
{
fn as_mut(&mut self) -> &mut K {
unsafe {
let ptr = self.k.as_mut_ptr();
&mut (*ptr) as &mut K
}
}
}
impl<'a, K> Iterator for LLIterMut<'a, K>
where
K: LLWeight + Clone + Debug,
{
type Item = &'a mut K;
fn next(&mut self) -> Option<Self::Item> {
debug_assert!(!self.next.is_null());
if self.next == self.end {
None
} else {
let r = Some(unsafe { (*self.next).as_mut() });
self.next = unsafe { (*self.next).next };
r
}
}
}
#[cfg(test)]
mod tests {
use super::{assert_released, LLWeight, LL};
impl LLWeight for Box<usize> {
#[inline]
fn ll_weight(&self) -> usize {
1
}
}
#[test]
fn test_cache_arc_ll_basic() {
let mut ll: LL<Box<usize>> = LL::new();
assert!(ll.len() == 0);
let n1 = ll.append_k(Box::new(1));
let n2 = ll.append_k(Box::new(2));
let _n3 = ll.append_k(Box::new(3));
let n4 = ll.append_k(Box::new(4));
assert!(ll.len() == 4);
assert!(ll.peek_head().unwrap().as_ref() == &1);
assert!(ll.peek_tail().unwrap().as_ref() == &4);
ll.touch(n2.clone());
assert!(ll.len() == 4);
assert!(ll.peek_head().unwrap().as_ref() == &1);
assert!(ll.peek_tail().unwrap().as_ref() == &2);
ll.touch(n1.clone());
assert!(ll.len() == 4);
assert!(ll.peek_head().unwrap().as_ref() == &3);
assert!(ll.peek_tail().unwrap().as_ref() == &1);
ll.touch(n1.clone());
assert!(ll.len() == 4);
assert!(ll.peek_head().unwrap().as_ref() == &3);
assert!(ll.peek_tail().unwrap().as_ref() == &1);
let n3 = ll.pop().unwrap();
assert!(ll.len() == 3);
assert!(ll.peek_head().unwrap().as_ref() == &4);
assert!(ll.peek_tail().unwrap().as_ref() == &1);
let n2 = ll.extract(n2);
assert!(ll.len() == 2);
assert!(ll.peek_head().unwrap().as_ref() == &4);
assert!(ll.peek_tail().unwrap().as_ref() == &1);
let n1 = ll.extract(n1);
assert!(ll.len() == 1);
assert!(ll.peek_head().unwrap().as_ref() == &4);
assert!(ll.peek_tail().unwrap().as_ref() == &4);
ll.touch(n4);
assert!(ll.len() == 1);
assert!(ll.peek_head().unwrap().as_ref() == &4);
assert!(ll.peek_tail().unwrap().as_ref() == &4);
let n4 = ll.pop().unwrap();
assert!(ll.len() == 0);
assert!(ll.peek_head().is_none());
assert!(ll.peek_tail().is_none());
ll.append_n(n1);
ll.append_n(n2);
ll.append_n(n3);
ll.append_n(n4);
drop(ll);
assert_released();
}
#[derive(Clone, Debug)]
struct Weighted {
_i: u64,
}
impl LLWeight for Weighted {
fn ll_weight(&self) -> usize {
8
}
}
#[test]
fn test_cache_arc_ll_weighted() {
let mut ll: LL<Weighted> = LL::new();
assert!(ll.len() == 0);
let _n1 = ll.append_k(Weighted { _i: 1 });
assert!(ll.len() == 8);
let _n2 = ll.append_k(Weighted { _i: 2 });
assert!(ll.len() == 16);
let n1 = ll.pop().unwrap();
assert!(ll.len() == 8);
let n2 = ll.pop().unwrap();
assert!(ll.len() == 0);
ll.append_n(n1);
ll.append_n(n2);
drop(ll);
assert_released();
}
}