129 lines
3.9 KiB
Rust
129 lines
3.9 KiB
Rust
//! 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<u32>;
|
|
}
|
|
|
|
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<E: Elem>(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> {
|
|
let old_head = head.get();
|
|
// Case 1: empty head, replace it.
|
|
if old_head.is_null() {
|
|
return AddToSllResult::EmptyHead(head);
|
|
}
|
|
unsafe {
|
|
// 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<E: Elem>(elem: &E, from: u32, by: Delta<u32>) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|