diff --git a/src/lib.rs b/src/lib.rs index ed15ad6..faf1731 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,11 +3,13 @@ pub mod mutators; pub mod nav; pub mod search; +use std::cell::UnsafeCell; use std::hash::Hash; +use std::ops::Deref; use base::{EdgeId, RawEdge, RawVertex, VertexId}; use mutators::{MutEdge, MutNode}; -use nav::Node; +use nav::{Edge, Node}; use symbol_map::indexing::{Indexing, Insertion}; use symbol_map::SymbolId; @@ -26,7 +28,7 @@ use symbol_map::SymbolId; /// /// Vertices are addressed by content. To examine graph contents, obtain a node /// handle with `get_node`. To modify graph contents, add new root vertices with -/// `add_root` and retrieve extant vertices with `get_node_mut`. +/// `add_node` and retrieve extant vertices with `get_node_mut`. pub struct Graph<T, S, A> where T: Hash + Eq + Clone, @@ -123,14 +125,14 @@ where } } - /// Adds a root vertex (one with no parents) for the given game state and + /// Adds a vertex (with no parents or children) for the given game state and /// data and returns a mutable handle for it. /// /// If `state` is already known, returns a mutable handle to that state, /// ignoring the `data` parameter. As a result, this method is guaranteed to /// return a handle for a root vertex only when `state` is a novel game /// state. - pub fn add_root<'s>(&'s mut self, state: T, data: S) -> MutNode<'s, T, S, A> { + pub fn add_node<'s>(&'s mut self, state: T, data: S) -> MutNode<'s, T, S, A> { let node_id = match self.state_ids.get_or_insert(state).map(|s| s.id().clone()) { Insertion::Present(id) => id, Insertion::New(id) => { @@ -214,6 +216,94 @@ where } } +/// Allows both read-only references into a `Graph` and operations that modify +/// the graph but do not invalidate family of types defined in the `nav` +/// module. +/// +/// All methods on `Graph` that take a `&self` parameter may also be called on +/// an `AppendOnlyGraph`. The `add_node` and `add_edge` methods may also be +/// called, because they do not invalidate `Node`, `Edge`, or other such smart +/// pointers into the underlying graph. Unlike the analogous methods on `Graph`, +/// these methods return a read-only view of the graph topology, instead of +/// granting read-write access. +/// +/// For example: +/// +/// ```rust +/// # use search_graph::{AppendOnlyGraph, Graph}; +/// # use search_graph::nav::{Node, Edge}; +/// # fn main() { +/// let appendable: AppendOnlyGraph<u32, String, ()> = Graph::new().into(); +/// let root1 = appendable.add_node(0, "data1".to_string()); +/// // If appendable were a Graph, we could not call add_node while root1 is alive. +/// let root2 = appendable.add_node(1, "data2".to_string()); +/// assert_eq!("data1", appendable.get_node(&0).unwrap().get_data()); +/// # } +/// ``` +pub struct AppendOnlyGraph<T, S, A> +where + T: Hash + Eq + Clone, +{ + graph: UnsafeCell<Graph<T, S, A>>, +} + +impl<T, S, A> AppendOnlyGraph<T, S, A> +where + T: Hash + Eq + Clone, +{ + pub fn add_node<'s>(&'s self, state: T, data: S) -> Node<'s, T, S, A> { + unsafe { (*self.graph.get()).add_node(state, data).to_node() } + } + + pub fn add_edge<'s, F, G>( + &'s mut self, + source: T, + source_data: F, + dest: T, + dest_data: G, + edge_data: A, + ) -> Edge<'s, T, S, A> + where + F: for<'b> FnOnce(Node<'b, T, S, A>) -> S, + G: for<'b> FnOnce(Node<'b, T, S, A>) -> S, + { + unsafe { + (*self.graph.get()) + .add_edge(source, source_data, dest, dest_data, edge_data) + .to_edge() + } + } +} + +impl<T, S, A> Deref for AppendOnlyGraph<T, S, A> +where + T: Hash + Eq + Clone, +{ + type Target = Graph<T, S, A>; + + fn deref(&self) -> &Graph<T, S, A> { + unsafe { &*self.graph.get() } + } +} + +impl<T, S, A> From<AppendOnlyGraph<T, S, A>> for Graph<T, S, A> +where + T: Hash + Eq + Clone, +{ + fn from(graph: AppendOnlyGraph<T, S, A>) -> Self { + graph.graph.into_inner() + } +} + +impl<T, S, A> From<Graph<T, S, A>> for AppendOnlyGraph<T, S, A> +where + T: Hash + Eq + Clone, +{ + fn from(graph: Graph<T, S, A>) -> Self { + AppendOnlyGraph { graph: UnsafeCell::new(graph) } + } +} + #[cfg(test)] mod test { use crossbeam_utils::thread; diff --git a/src/mutators/mark_compact.rs b/src/mutators/mark_compact.rs index 506b1b4..aff7e2b 100644 --- a/src/mutators/mark_compact.rs +++ b/src/mutators/mark_compact.rs @@ -290,9 +290,9 @@ mod test { #[test] fn mark_roots_ok() { let mut g = empty_graph(); - g.add_root("0", ""); - g.add_root("1", ""); - g.add_root("2", ""); + g.add_node("0", ""); + g.add_node("1", ""); + g.add_node("2", ""); assert_eq!(3, g.vertex_count()); assert_eq!(0, g.edge_count()); let root_ids = [VertexId(0), VertexId(1), VertexId(2)]; diff --git a/src/search.rs b/src/search.rs index 8773055..809765c 100644 --- a/src/search.rs +++ b/src/search.rs @@ -345,7 +345,7 @@ mod test { #[test] fn instantiation_ok() { let mut g = Graph::new(); - let root = g.add_root("root", "root"); + let root = g.add_node("root", "root"); let path = Stack::new(root); assert_eq!(1, path.len()); @@ -355,7 +355,7 @@ mod test { #[test] fn push_no_children_ok() { let mut g = Graph::new(); - let root = g.add_root("root", "root"); + let root = g.add_node("root", "root"); let mut path = Stack::new(root); assert_eq!(1, path.len()); @@ -377,7 +377,7 @@ mod test { #[test] fn push_no_children_err() { let mut g = Graph::new(); - let root = g.add_root("root", "root"); + let root = g.add_node("root", "root"); let mut path = Stack::new(root); assert_eq!(1, path.len()); @@ -478,7 +478,7 @@ mod test { #[test] fn push_no_parents_ok() { let mut g = Graph::new(); - let root = g.add_root("root", "root"); + let root = g.add_node("root", "root"); let mut path = Stack::new(root); assert_eq!(1, path.len()); @@ -500,7 +500,7 @@ mod test { #[test] fn push_no_parents_err() { let mut g = Graph::new(); - let root = g.add_root("root", "root"); + let root = g.add_node("root", "root"); let mut path = Stack::new(root); assert_eq!(1, path.len()); @@ -633,9 +633,9 @@ mod test { #[test] fn search_path_iter_empty_ok() { let mut g = Graph::new(); - g.add_root("root", "root"); + g.add_node("root", "root"); - let path = Stack::new(g.add_root("root", "root")); + let path = Stack::new(g.add_node("root", "root")); assert_eq!(1, path.len()); assert_eq!("root", *path.head().get_data()); @@ -651,7 +651,7 @@ mod test { #[test] fn search_path_iter_items_ok() { let mut g = Graph::new(); - g.add_root("root", "root"); + g.add_node("root", "root"); add_edge(&mut g, "root", "A"); add_edge(&mut g, "A", "B"); @@ -706,7 +706,7 @@ mod test { fn pop_empty_is_none_ok() { let mut g = Graph::new(); - let mut path = Stack::new(g.add_root("root", "root")); + let mut path = Stack::new(g.add_node("root", "root")); assert_eq!(1, path.len()); assert!(path.pop().is_none()); }