//! Sorted Linked List use std::{cell::Cell, cmp::Ordering, ptr}; use crate::utility_types::Delta; pub(crate) unsafe trait Elem { fn prev(&self) -> &Cell<*const Self>; fn next(&self) -> &Cell<*const Self>; fn key(&self) -> &Cell; } pub(crate) enum AddToSllResult<'a, E: Elem> { NoHead, EmptyHead(&'a Cell<*const E>), SmallerThanHead(&'a Cell<*const E>), SmallerThanNotHead(*const E), AlreadyInSll(*const E), } impl<'a, E: Elem> AddToSllResult<'a, E> { pub(crate) fn add_to_sll(&self, elem_ptr: *const E) { unsafe { (*elem_ptr).prev().set(elem_ptr); (*elem_ptr).next().set(elem_ptr); match self { // Case 1: empty head, replace it. AddToSllResult::EmptyHead(head) => head.set(elem_ptr), // Case 2: we are smaller than the head, replace it. AddToSllResult::SmallerThanHead(head) => { let old_head = head.get(); let prev = (*old_head).prev().replace(elem_ptr); (*prev).next().set(elem_ptr); (*elem_ptr).next().set(old_head); (*elem_ptr).prev().set(prev); head.set(elem_ptr); } // Case 3: insert in place found by looping AddToSllResult::SmallerThanNotHead(curr) => { let next = (**curr).next().replace(elem_ptr); (*next).prev().set(elem_ptr); (*elem_ptr).prev().set(*curr); (*elem_ptr).next().set(next); } AddToSllResult::NoHead | AddToSllResult::AlreadyInSll(_) => (), } } } } #[cold] pub(crate) fn init<'a, E: Elem>( head: Option<&'a Cell<*const E>>, elem: &E, ) -> AddToSllResult<'a, E> { if let Some(head) = head { link(head, elem) } else { AddToSllResult::NoHead } } #[cold] pub(crate) fn unlink(head: &Cell<*const E>, elem: &E) { debug_assert!(!head.get().is_null(), "invalid linked list head"); let elem_ptr: *const E = elem; let prev = elem.prev().replace(elem_ptr); let next = elem.next().replace(elem_ptr); unsafe { debug_assert_eq!((*prev).next().get(), elem_ptr, "invalid linked list links"); debug_assert_eq!((*next).prev().get(), elem_ptr, "invalid linked list links"); (*prev).next().set(next); (*next).prev().set(prev); } if head.get() == elem_ptr { head.set(if next == elem_ptr { ptr::null() } else { next }) } } #[cold] pub(crate) fn link<'a, E: Elem>(head: &'a Cell<*const E>, elem: &E) -> AddToSllResult<'a, E> { unsafe { let old_head = head.get(); // Case 1: empty head, replace it. if old_head.is_null() { return AddToSllResult::EmptyHead(head); } // Case 2: we are smaller than the head, replace it. if elem.key() < (*old_head).key() { return AddToSllResult::SmallerThanHead(head); } // Case 3: loop *backward* until we find insertion place. Because of // Case 2, we can't loop beyond the head. let mut curr = (*old_head).prev().get(); loop { match (*curr).key().cmp(elem.key()) { Ordering::Less => return AddToSllResult::SmallerThanNotHead(curr), Ordering::Equal => return AddToSllResult::AlreadyInSll(curr), Ordering::Greater => curr = (*curr).prev().get(), } } } } pub(crate) fn adjust(elem: &E, from: u32, by: Delta) { let elem_ptr: *const E = elem; unsafe { let mut curr = elem_ptr; loop { let mut key = (*curr).key().get(); if key >= from { key += by; (*curr).key().set(key); } curr = (*curr).next().get(); if curr == elem_ptr { break; } } } }