Add AppendOnlyGraph, to allow safe appends without invalidating pointers.
This commit is contained in:
parent
bbb9be1ae6
commit
2f63b80e36
98
src/lib.rs
98
src/lib.rs
@ -3,11 +3,13 @@ pub mod mutators;
|
|||||||
pub mod nav;
|
pub mod nav;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
|
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use base::{EdgeId, RawEdge, RawVertex, VertexId};
|
use base::{EdgeId, RawEdge, RawVertex, VertexId};
|
||||||
use mutators::{MutEdge, MutNode};
|
use mutators::{MutEdge, MutNode};
|
||||||
use nav::Node;
|
use nav::{Edge, Node};
|
||||||
use symbol_map::indexing::{Indexing, Insertion};
|
use symbol_map::indexing::{Indexing, Insertion};
|
||||||
use symbol_map::SymbolId;
|
use symbol_map::SymbolId;
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ use symbol_map::SymbolId;
|
|||||||
///
|
///
|
||||||
/// Vertices are addressed by content. To examine graph contents, obtain a node
|
/// 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
|
/// 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>
|
pub struct Graph<T, S, A>
|
||||||
where
|
where
|
||||||
T: Hash + Eq + Clone,
|
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.
|
/// data and returns a mutable handle for it.
|
||||||
///
|
///
|
||||||
/// If `state` is already known, returns a mutable handle to that state,
|
/// If `state` is already known, returns a mutable handle to that state,
|
||||||
/// ignoring the `data` parameter. As a result, this method is guaranteed to
|
/// 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
|
/// return a handle for a root vertex only when `state` is a novel game
|
||||||
/// state.
|
/// 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()) {
|
let node_id = match self.state_ids.get_or_insert(state).map(|s| s.id().clone()) {
|
||||||
Insertion::Present(id) => id,
|
Insertion::Present(id) => id,
|
||||||
Insertion::New(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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crossbeam_utils::thread;
|
use crossbeam_utils::thread;
|
||||||
|
@ -290,9 +290,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn mark_roots_ok() {
|
fn mark_roots_ok() {
|
||||||
let mut g = empty_graph();
|
let mut g = empty_graph();
|
||||||
g.add_root("0", "");
|
g.add_node("0", "");
|
||||||
g.add_root("1", "");
|
g.add_node("1", "");
|
||||||
g.add_root("2", "");
|
g.add_node("2", "");
|
||||||
assert_eq!(3, g.vertex_count());
|
assert_eq!(3, g.vertex_count());
|
||||||
assert_eq!(0, g.edge_count());
|
assert_eq!(0, g.edge_count());
|
||||||
let root_ids = [VertexId(0), VertexId(1), VertexId(2)];
|
let root_ids = [VertexId(0), VertexId(1), VertexId(2)];
|
||||||
|
@ -345,7 +345,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn instantiation_ok() {
|
fn instantiation_ok() {
|
||||||
let mut g = Graph::new();
|
let mut g = Graph::new();
|
||||||
let root = g.add_root("root", "root");
|
let root = g.add_node("root", "root");
|
||||||
|
|
||||||
let path = Stack::new(root);
|
let path = Stack::new(root);
|
||||||
assert_eq!(1, path.len());
|
assert_eq!(1, path.len());
|
||||||
@ -355,7 +355,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn push_no_children_ok() {
|
fn push_no_children_ok() {
|
||||||
let mut g = Graph::new();
|
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);
|
let mut path = Stack::new(root);
|
||||||
assert_eq!(1, path.len());
|
assert_eq!(1, path.len());
|
||||||
@ -377,7 +377,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn push_no_children_err() {
|
fn push_no_children_err() {
|
||||||
let mut g = Graph::new();
|
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);
|
let mut path = Stack::new(root);
|
||||||
assert_eq!(1, path.len());
|
assert_eq!(1, path.len());
|
||||||
@ -478,7 +478,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn push_no_parents_ok() {
|
fn push_no_parents_ok() {
|
||||||
let mut g = Graph::new();
|
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);
|
let mut path = Stack::new(root);
|
||||||
assert_eq!(1, path.len());
|
assert_eq!(1, path.len());
|
||||||
@ -500,7 +500,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn push_no_parents_err() {
|
fn push_no_parents_err() {
|
||||||
let mut g = Graph::new();
|
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);
|
let mut path = Stack::new(root);
|
||||||
assert_eq!(1, path.len());
|
assert_eq!(1, path.len());
|
||||||
@ -633,9 +633,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn search_path_iter_empty_ok() {
|
fn search_path_iter_empty_ok() {
|
||||||
let mut g = Graph::new();
|
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!(1, path.len());
|
||||||
assert_eq!("root", *path.head().get_data());
|
assert_eq!("root", *path.head().get_data());
|
||||||
|
|
||||||
@ -651,7 +651,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn search_path_iter_items_ok() {
|
fn search_path_iter_items_ok() {
|
||||||
let mut g = Graph::new();
|
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, "root", "A");
|
||||||
add_edge(&mut g, "A", "B");
|
add_edge(&mut g, "A", "B");
|
||||||
|
|
||||||
@ -706,7 +706,7 @@ mod test {
|
|||||||
fn pop_empty_is_none_ok() {
|
fn pop_empty_is_none_ok() {
|
||||||
let mut g = Graph::new();
|
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_eq!(1, path.len());
|
||||||
assert!(path.pop().is_none());
|
assert!(path.pop().is_none());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user