use std::{mem::MaybeUninit, task::{Context, Poll, Waker}, usize};
struct Branch {
waker: Option<Waker>,
strong: usize,
}
struct Node {
next: usize,
version: u64,
branch: MaybeUninit<Branch>,
}
impl Node {
pub fn new_free_chain(len: usize) -> Box<[Node]> {
(0..len).map(|n| Node {
next: n + 1,
version: 0,
branch: MaybeUninit::uninit()
}).collect()
}
fn log_weak_ref(&mut self) -> u64 {
self.version |= 1;
self.version
}
fn flush_weak_refs(&mut self) {
self.version += 1;
self.version &= -2_i64 as u64;
}
}
pub const END: usize = usize::MAX;
pub struct Graph {
free: usize,
nodes: Box<[Node]>
}
impl Graph {
pub fn new() -> Graph {
Graph::with_capacity(7)
}
pub fn with_capacity(capacity: usize) -> Graph {
Graph {
free: 0,
nodes: Node::new_free_chain(capacity),
}
}
fn alloc(&mut self, branch: Branch) -> &mut Node {
if self.free == self.nodes.len() {
let next_len = (self.nodes.len() << 1) + 1;
let allocated = std::mem::replace(&mut self.nodes, Node::new_free_chain(next_len));
for (i, node) in allocated.into_vec().into_iter().enumerate() {
*unsafe { self.nodes.get_unchecked_mut(i) } = node;
}
}
let node = unsafe { self.nodes.get_unchecked_mut(self.free) };
std::mem::swap(&mut node.next, &mut self.free);
node.branch.write(branch);
node
}
unsafe fn free(free: &mut usize, node: &mut Node, index: usize) -> usize {
node.branch.assume_init_drop();
node.flush_weak_refs();
let parent = std::mem::replace(&mut node.next, index);
std::mem::swap(free, &mut node.next);
parent
}
pub fn share(&mut self) -> usize {
self.forward(END)
}
pub fn forward(&mut self, parent: usize) -> usize {
let node = self.alloc(Branch {
waker: Some(futures::task::noop_waker()),
strong: 1,
});
std::mem::replace(&mut node.next, parent)
}
pub unsafe fn track_weak_unchecked(&mut self, index: usize) -> u64 {
self.nodes.get_unchecked_mut(index).log_weak_ref()
}
pub unsafe fn try_upgrade_weak_unchecked(&mut self, index: usize, version: u64) -> bool {
let node = self.nodes.get_unchecked_mut(index);
if node.version != version {
return false;
}
let branch = node.branch.assume_init_mut();
branch.strong += 1;
true
}
pub unsafe fn track_borrow_unchecked(&mut self, index: usize) {
let branch = self.nodes.get_unchecked_mut(index).branch.assume_init_mut();
branch.strong += 1;
}
pub unsafe fn untrack_borrow_unchecked(&mut self, mut index: usize) -> bool {
loop {
if index == END {
break true;
}
let node = self.nodes.get_unchecked_mut(index);
let branch = node.branch.assume_init_mut();
branch.strong -= 1;
if branch.strong == 0 {
match branch.waker.take() {
Some(waker) => {
waker.wake();
break false;
},
None => {
index = Graph::free(&mut self.free, node, index);
continue;
},
}
} else {
break false;
}
#[allow(unreachable_code)]
{
unreachable!()
}
}
}
pub unsafe fn poll_unchecked(&mut self, index: usize, cx: &mut Context<'_>) -> Poll<usize> {
let node = self.nodes.get_unchecked_mut(index);
let branch = node.branch.assume_init_mut();
match branch.waker.as_mut() {
Some(waker) => {
if !waker.will_wake(cx.waker()) {
*waker = cx.waker().clone();
}
Poll::Pending
},
None => {
let parent = Graph::free(&mut self.free, node, index);
Poll::Ready(parent)
},
}
}
pub unsafe fn close_future_unchecked(&mut self, index: usize) -> bool {
let node = self.nodes.get_unchecked_mut(index);
let branch = node.branch.assume_init_mut();
match branch.waker.take() {
Some(..) => false,
None => {
let parent = Graph::free(&mut self.free, node, index);
unsafe { self.untrack_borrow_unchecked(parent) }
},
}
}
}