From: Ian Jackson Date: Thu, 14 Nov 2024 21:48:56 +0000 (+0000) Subject: code motion X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=9613b23c460f1e8d6f6a2c58f07dc12739c9dcd7;p=manually-boxed code motion --- diff --git a/src/lib.rs b/src/lib.rs index 2f07b8f..6b886fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,7 @@ +#[cfg(test)] +mod test; + use std::collections::HashSet; use std::fmt::{self, Debug}; use std::marker::PhantomData; @@ -365,219 +368,3 @@ impl<'a> MultiRuntime<'a> { Ok(()) } } - -#[cfg(test)] -mod tests { - use super::*; - use fmt::Write as _; - - mod list { - use super::*; - use std::hint::unreachable_unchecked; - use std::iter; - - #[derive(Debug)] - pub struct Node { - back: Option>>, - next: Option>>, - pub data: T, - } - - #[derive(Debug)] - pub struct List { - head: Option>>, - tail: Option>>, - noalias: NoAliasSingleton, - } - - #[derive(Debug, Eq, PartialEq)] - pub enum HeadAndTail { - None, - One(T), - Both(T, T), - } - - type P = Ptr>; - - impl List { - pub fn new() -> Self { - let noalias = unsafe { NoAliasSingleton::init() }; - List { head: None, tail: None, noalias } - } - - pub fn check_consistency(&self) { - dbg!(self); - let tok = self.noalias.ref_token(); - let mut last: Option> = None; - let mut node = self.head; - while let Some(node_) = node { - dbg!(node_); - let node__ = node_.borrow(&tok); - dbg!(node__); - assert_eq!(last.as_ptr(), node__.back.as_ptr()); - last = Some(node_); - node = node__.next; - } - assert_eq!(self.tail.as_ptr(), last.as_ptr()); - } - - pub fn append(&mut self, data: T) { - let node = Node { - back: self.head, - next: None, - data, - }; - let node = Ptr::new_heap(node); - if let Some(old_tail) = self.tail { - old_tail.borrow_mut(&mut self.noalias).next = Some(node); - } else { - self.head = Some(node); - }; - self.tail = Some(node); - } - - pub fn front(&self) -> Option<&T> { - let tok = self.noalias.ref_token(); - let head = self.head?; - Some(&head.borrow(tok).data) - } - - pub fn front_mut(&mut self) -> Option<&mut T> { - let tok = self.noalias.mut_token(); - let head = self.head?; - Some(&mut head.borrow_mut(tok).data) - } - - pub fn pop_front(&mut self) -> Option { - let tok = self.noalias.mut_token(); - Self::pop_front_inner( - tok, - &mut self.head, - &mut self.tail, - ) - } - - pub fn head_and_tail_mut(&mut self) -> HeadAndTail<&mut T> { - let Some(head) = self.head - else { return HeadAndTail::None }; - - let multi = self.noalias.mut_token().multi_static(); - - let Ok(multi) = multi.borrow_mut(head) - else { unsafe { unreachable_unchecked() } }; - - let tail = unsafe { self.tail.unwrap_unchecked() }; - - match multi.borrow_mut(tail) { - Ok(y) => { - let (((), head), tail) = y.finish(); - HeadAndTail::Both(&mut head.data, &mut tail.data) - }, - Err(n) => { - let ((), head) = n.finish(); - HeadAndTail::One(&mut head.data) - } - } - } - - pub fn all(&self) -> impl Iterator { - let tok = self.noalias.ref_token(); - Self::iter_ptrs(self.head, move |node| { - let node = node.borrow(tok); - (&node.data, node.next) - }) - } - - pub fn all_mut_safe(&mut self) -> impl Iterator { - let mut multi = self.noalias.multi_runtime(); - Self::iter_ptrs(self.head, move |node| { - let node = multi.borrow_mut(node).expect("untangled!"); - (&mut node.data, node.next) - }) - } - - pub fn all_mut_fast(&mut self) -> impl Iterator { - Self::iter_ptrs(self.head, |node| { - let tok = unsafe { MutToken::new_unchecked() }; - let node = node.borrow_mut(tok); - (&mut node.data, node.next) - }) - } - - // We wouldn't do this, since it's daft, but - // it lets us demonstrate passing a token. - fn pop_front_inner<'a>( - mut tok: MutToken<'a>, - head: &mut Option>, - tail: &mut Option>, - ) -> Option { - let deleting = (*head)?; - let new_head = deleting.borrow(&tok).next; - *head = new_head; - if let Some(new_head) = new_head { - new_head.borrow_mut(&mut tok).back = None; - } else { - *tail = None; - } - let deleted = unsafe { deleting.free_heap(&mut tok) }; - Some(deleted.data) - } - - fn iter_ptrs<'i, I: 'i>( - head: Option>, - mut node_map: impl FnMut(P) -> (I, Option>) + 'i, - ) -> impl Iterator + 'i - where T: 'i - { - let mut next = head; - iter::from_fn(move || { - let node = next?; - let item; - (item, next) = node_map(node); - Some(item) - }) - } - } - - impl Drop for List { - fn drop(&mut self) { - while let Some(_) = Self::pop_front_inner( - self.noalias.mut_token(), - &mut self.head, - &mut self.tail, - ) { } - } - } - } - - #[test] - fn demo() { - use list::{List, HeadAndTail}; - use HeadAndTail as H; - - let s = |s: &str| format!("{s}"); - - let l = List::::new(); - l.check_consistency(); - assert_eq!(l.front(), None); - drop(l); - - let mut l = List::new(); l.check_consistency(); - assert_eq!(l.head_and_tail_mut(), H::None); - - l.append(s("hi")); l.check_consistency(); - assert_eq!(l.head_and_tail_mut(), H::One(&mut s("hi"))); - - l.append(s("ho")); l.check_consistency(); - assert_eq!(l.head_and_tail_mut(), H::Both(&mut s("hi"), &mut s("ho"))); - - write!(l.front_mut().unwrap(), "!").unwrap(); - assert_eq!(l.pop_front(), Some(s("hi!"))); l.check_consistency(); - assert_eq!(l.front(), Some(&s("ho"))); l.check_consistency(); - - drop(l); - } -} - - - diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..55ca991 --- /dev/null +++ b/src/test.rs @@ -0,0 +1,33 @@ + +use super::*; +use fmt::Write as _; + +mod list; +use list::{List, HeadAndTail}; + +#[test] +fn demo() { + use HeadAndTail as H; + + let s = |s: &str| format!("{s}"); + + let l = List::::new(); + l.check_consistency(); + assert_eq!(l.front(), None); + drop(l); + + let mut l = List::new(); l.check_consistency(); + assert_eq!(l.head_and_tail_mut(), H::None); + + l.append(s("hi")); l.check_consistency(); + assert_eq!(l.head_and_tail_mut(), H::One(&mut s("hi"))); + + l.append(s("ho")); l.check_consistency(); + assert_eq!(l.head_and_tail_mut(), H::Both(&mut s("hi"), &mut s("ho"))); + + write!(l.front_mut().unwrap(), "!").unwrap(); + assert_eq!(l.pop_front(), Some(s("hi!"))); l.check_consistency(); + assert_eq!(l.front(), Some(&s("ho"))); l.check_consistency(); + + drop(l); +} diff --git a/src/test/list.rs b/src/test/list.rs new file mode 100644 index 0000000..5514e75 --- /dev/null +++ b/src/test/list.rs @@ -0,0 +1,177 @@ +use super::*; +use std::hint::unreachable_unchecked; +use std::iter; + +#[derive(Debug)] +pub struct Node { + back: Option>>, + next: Option>>, + pub data: T, +} + +#[derive(Debug)] +pub struct List { + head: Option>>, + tail: Option>>, + noalias: NoAliasSingleton, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum HeadAndTail { + None, + One(T), + Both(T, T), +} + +type P = Ptr>; + +impl List { + pub fn new() -> Self { + let noalias = unsafe { NoAliasSingleton::init() }; + List { head: None, tail: None, noalias } + } + + pub fn check_consistency(&self) { + dbg!(self); + let tok = self.noalias.ref_token(); + let mut last: Option> = None; + let mut node = self.head; + while let Some(node_) = node { + dbg!(node_); + let node__ = node_.borrow(&tok); + dbg!(node__); + assert_eq!(last.as_ptr(), node__.back.as_ptr()); + last = Some(node_); + node = node__.next; + } + assert_eq!(self.tail.as_ptr(), last.as_ptr()); + } + + pub fn append(&mut self, data: T) { + let node = Node { + back: self.head, + next: None, + data, + }; + let node = Ptr::new_heap(node); + if let Some(old_tail) = self.tail { + old_tail.borrow_mut(&mut self.noalias).next = Some(node); + } else { + self.head = Some(node); + }; + self.tail = Some(node); + } + + pub fn front(&self) -> Option<&T> { + let tok = self.noalias.ref_token(); + let head = self.head?; + Some(&head.borrow(tok).data) + } + + pub fn front_mut(&mut self) -> Option<&mut T> { + let tok = self.noalias.mut_token(); + let head = self.head?; + Some(&mut head.borrow_mut(tok).data) + } + + pub fn pop_front(&mut self) -> Option { + let tok = self.noalias.mut_token(); + Self::pop_front_inner( + tok, + &mut self.head, + &mut self.tail, + ) + } + + pub fn head_and_tail_mut(&mut self) -> HeadAndTail<&mut T> { + let Some(head) = self.head + else { return HeadAndTail::None }; + + let multi = self.noalias.mut_token().multi_static(); + + let Ok(multi) = multi.borrow_mut(head) + else { unsafe { unreachable_unchecked() } }; + + let tail = unsafe { self.tail.unwrap_unchecked() }; + + match multi.borrow_mut(tail) { + Ok(y) => { + let (((), head), tail) = y.finish(); + HeadAndTail::Both(&mut head.data, &mut tail.data) + }, + Err(n) => { + let ((), head) = n.finish(); + HeadAndTail::One(&mut head.data) + } + } + } + + pub fn all(&self) -> impl Iterator { + let tok = self.noalias.ref_token(); + Self::iter_ptrs(self.head, move |node| { + let node = node.borrow(tok); + (&node.data, node.next) + }) + } + + pub fn all_mut_safe(&mut self) -> impl Iterator { + let mut multi = self.noalias.multi_runtime(); + Self::iter_ptrs(self.head, move |node| { + let node = multi.borrow_mut(node).expect("untangled!"); + (&mut node.data, node.next) + }) + } + + pub fn all_mut_fast(&mut self) -> impl Iterator { + Self::iter_ptrs(self.head, |node| { + let tok = unsafe { MutToken::new_unchecked() }; + let node = node.borrow_mut(tok); + (&mut node.data, node.next) + }) + } + + // We wouldn't do this, since it's daft, but + // it lets us demonstrate passing a token. + fn pop_front_inner<'a>( + mut tok: MutToken<'a>, + head: &mut Option>, + tail: &mut Option>, + ) -> Option { + let deleting = (*head)?; + let new_head = deleting.borrow(&tok).next; + *head = new_head; + if let Some(new_head) = new_head { + new_head.borrow_mut(&mut tok).back = None; + } else { + *tail = None; + } + let deleted = unsafe { deleting.free_heap(&mut tok) }; + Some(deleted.data) + } + + fn iter_ptrs<'i, I: 'i>( + head: Option>, + mut node_map: impl FnMut(P) -> (I, Option>) + 'i, + ) -> impl Iterator + 'i + where T: 'i + { + let mut next = head; + iter::from_fn(move || { + let node = next?; + let item; + (item, next) = node_map(node); + Some(item) + }) + } +} + +impl Drop for List { + fn drop(&mut self) { + while let Some(_) = Self::pop_front_inner( + self.noalias.mut_token(), + &mut self.head, + &mut self.tail, + ) { } + } +} +