Implement new mutations with native core (#630)

* work on seperating tree struture from realdom

* intial update to new mutations

* handle height

* update to taffy 0.2

* add as_varient functions to OwnedAttributeValue

* make get node parent mut optionally return a parent

* work on upward pass

* add more code for parrellel passes

* make resolve passes public

* more work on parallel passes

* fix deadlock and add more tests

* move height into the tree instead of the realdom

* make passes exicute in parallel instead of executing invidual passes in parellel

* fix some warnings

* add up dependant test

* clean up examples

* work on intigrating state with passes

* update to new mutations

* work on implementing macro

* make the macro compile

* more progress on macro

* mark cloned nodes as dirty

* update persistant_iterator utility

* fix mask generation

* update tui with new mutations

* more progress updating tui

* some basic tui examples working

* don't push template nodes onto the stack

* update hover example

* update benchmark

* update more examples

* fix root node layout

* ignore out of bounds renders

* update color picker example

* update all events example

* update remaining examples

* update tests

* tests passing

* move persistant iterator test

* update examples

* fix gaps in layout

* fix formatting

* fix memory leak
This commit is contained in:
Demonthos authored and GitHub committed 2022-12-06 15:38:04 -08:00
1 parent b9aad5e8f4
commit 67a6fa9eb8
38 files changed
+3405 -2417

No files matched your search

+21 -21
View File
@@ -343,14 +343,14 @@ fn apply_border(name: &str, value: &str, style: &mut Style) {
"border-left" => {}
"border-left-color" => {}
"border-left-style" => {
if style.border.start == Dimension::default() {
if style.border.left == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.start = v;
style.border.left = v;
}
}
"border-left-width" => {
if let Some(v) = parse_value(value) {
style.border.start = v;
style.border.left = v;
}
}
"border-radius" => {}
@@ -358,11 +358,11 @@ fn apply_border(name: &str, value: &str, style: &mut Style) {
"border-right-color" => {}
"border-right-style" => {
let v = Dimension::Points(1.0);
style.border.end = v;
style.border.right = v;
}
"border-right-width" => {
if let Some(v) = parse_value(value) {
style.border.end = v;
style.border.right = v;
}
}
"border-spacing" => {}
@@ -375,13 +375,13 @@ fn apply_border(name: &str, value: &str, style: &mut Style) {
let v = Dimension::Points(1.0);
style.border.bottom = v;
}
if style.border.start == Dimension::default() {
if style.border.left == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.start = v;
style.border.left = v;
}
if style.border.end == Dimension::default() {
if style.border.right == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.end = v;
style.border.right = v;
}
}
"border-top" => {}
@@ -404,8 +404,8 @@ fn apply_border(name: &str, value: &str, style: &mut Style) {
if values.len() == 1 {
if let Some(dim) = parse_value(values[0]) {
style.border = Rect {
start: dim,
end: dim,
right: dim,
left: dim,
top: dim,
bottom: dim,
};
@@ -414,8 +414,8 @@ fn apply_border(name: &str, value: &str, style: &mut Style) {
let border_widths = [
&mut style.border.top,
&mut style.border.bottom,
&mut style.border.start,
&mut style.border.end,
&mut style.border.left,
&mut style.border.right,
];
for (v, width) in values.into_iter().zip(border_widths) {
if let Some(w) = parse_value(v) {
@@ -512,12 +512,12 @@ fn apply_padding(name: &str, value: &str, style: &mut Style) {
"padding" => {
style.padding.top = v;
style.padding.bottom = v;
style.padding.start = v;
style.padding.end = v;
style.padding.left = v;
style.padding.right = v;
}
"padding-bottom" => style.padding.bottom = v,
"padding-left" => style.padding.start = v,
"padding-right" => style.padding.end = v,
"padding-left" => style.padding.left = v,
"padding-right" => style.padding.right = v,
"padding-top" => style.padding.top = v,
_ => {}
}
@@ -578,13 +578,13 @@ fn apply_margin(name: &str, value: &str, style: &mut Style) {
"margin" => {
style.margin.top = dim;
style.margin.bottom = dim;
style.margin.start = dim;
style.margin.end = dim;
style.margin.left = dim;
style.margin.right = dim;
}
"margin-top" => style.margin.top = dim,
"margin-bottom" => style.margin.bottom = dim,
"margin-left" => style.margin.start = dim,
"margin-right" => style.margin.end = dim,
"margin-left" => style.margin.left = dim,
"margin-right" => style.margin.right = dim,
_ => {}
}
}
+14 -42
View File
@@ -1,54 +1,26 @@
use std::cmp::Ordering;
use std::hash::BuildHasherDefault;
use dioxus_core::ElementId;
pub use node_ref::NodeMask;
pub use passes::{
AnyPass, DownwardPass, MemberMask, NodePass, Pass, PassId, PassReturn, UpwardPass,
};
use rustc_hash::FxHasher;
pub use tree::NodeId;
pub mod layout_attributes;
pub mod node;
pub mod node_ref;
pub mod passes;
pub mod real_dom;
pub mod state;
#[doc(hidden)]
pub mod traversable;
pub mod tree;
pub mod utils;
/// A id for a node that lives in the real dom.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RealNodeId {
ElementId(ElementId),
UnaccessableId(usize),
}
impl RealNodeId {
pub fn as_element_id(&self) -> ElementId {
match self {
RealNodeId::ElementId(id) => *id,
RealNodeId::UnaccessableId(_) => panic!("Expected element id"),
}
}
pub fn as_unaccessable_id(&self) -> usize {
match self {
RealNodeId::ElementId(_) => panic!("Expected unaccessable id"),
RealNodeId::UnaccessableId(id) => *id,
}
}
}
impl Ord for RealNodeId {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl PartialOrd for RealNodeId {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Self::ElementId(a), Self::ElementId(b)) => a.0.partial_cmp(&b.0),
(Self::UnaccessableId(a), Self::UnaccessableId(b)) => a.partial_cmp(b),
(Self::ElementId(_), Self::UnaccessableId(_)) => Some(Ordering::Greater),
(Self::UnaccessableId(_), Self::ElementId(_)) => Some(Ordering::Less),
}
}
}
pub type RealNodeId = NodeId;
pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
pub type SendAnyMap = anymap::Map<dyn anymap::any::Any + Send + Sync + 'static>;
/// Used in derived state macros
#[derive(Eq, PartialEq)]
+119
View File
@@ -0,0 +1,119 @@
use crate::{state::State, tree::NodeId};
use dioxus_core::ElementId;
use rustc_hash::{FxHashMap, FxHashSet};
/// The node is stored client side and stores only basic data about the node.
#[derive(Debug, Clone)]
pub struct Node<S: State> {
/// The transformed state of the node.
pub state: S,
/// The raw data for the node
pub node_data: NodeData,
}
#[derive(Debug, Clone)]
pub struct NodeData {
/// The id of the node
pub node_id: NodeId,
/// The id of the node in the vdom.
pub element_id: Option<ElementId>,
/// Additional inforation specific to the node type
pub node_type: NodeType,
}
/// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
#[derive(Debug, Clone)]
pub enum NodeType {
Text {
text: String,
},
Element {
tag: String,
namespace: Option<String>,
attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue>,
listeners: FxHashSet<String>,
},
Placeholder,
}
impl<S: State> Node<S> {
pub(crate) fn new(node_type: NodeType) -> Self {
Node {
state: S::default(),
node_data: NodeData {
element_id: None,
node_type,
node_id: NodeId(0),
},
}
}
/// get the mounted id of the node
pub fn mounted_id(&self) -> Option<ElementId> {
self.node_data.element_id
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct OwnedAttributeDiscription {
pub name: String,
pub namespace: Option<String>,
pub volatile: bool,
}
/// An attribute on a DOM node, such as `id="my-thing"` or
/// `href="https://example.com"`.
#[derive(Clone, Copy, Debug)]
pub struct OwnedAttributeView<'a> {
/// The discription of the attribute.
pub attribute: &'a OwnedAttributeDiscription,
/// The value of the attribute.
pub value: &'a OwnedAttributeValue,
}
#[derive(Clone, Debug)]
pub enum OwnedAttributeValue {
Text(String),
Float(f32),
Int(i32),
Bool(bool),
None,
}
impl OwnedAttributeValue {
pub fn as_text(&self) -> Option<&str> {
match self {
OwnedAttributeValue::Text(text) => Some(text),
_ => None,
}
}
pub fn as_float(&self) -> Option<f32> {
match self {
OwnedAttributeValue::Float(float) => Some(*float),
_ => None,
}
}
pub fn as_int(&self) -> Option<i32> {
match self {
OwnedAttributeValue::Int(int) => Some(*int),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
OwnedAttributeValue::Bool(bool) => Some(*bool),
_ => None,
}
}
pub fn as_none(&self) -> Option<()> {
match self {
OwnedAttributeValue::None => Some(()),
_ => None,
}
}
}
+42 -23
View File
@@ -1,5 +1,7 @@
use dioxus_core::ElementId;
use crate::{
real_dom::{NodeData, NodeType, OwnedAttributeView},
node::{NodeData, NodeType, OwnedAttributeView},
state::union_ordered_iter,
RealNodeId,
};
@@ -21,8 +23,13 @@ impl<'a> NodeView<'a> {
}
/// Get the id of the node
pub fn id(&self) -> RealNodeId {
self.inner.id.unwrap()
pub fn id(&self) -> Option<ElementId> {
self.inner.element_id
}
/// Get the node id of the node
pub fn node_id(&self) -> RealNodeId {
self.inner.node_id
}
/// Get the tag of the node if the tag is enabled in the mask
@@ -41,7 +48,7 @@ impl<'a> NodeView<'a> {
self.mask
.namespace
.then_some(match &self.inner.node_type {
NodeType::Element { namespace, .. } => *namespace,
NodeType::Element { namespace, .. } => namespace.as_deref(),
_ => None,
})
.flatten()
@@ -92,7 +99,7 @@ impl<'a> NodeView<'a> {
pub enum AttributeMask {
All,
/// A list of attribute names that are visible, this list must be sorted
Dynamic(Vec<&'static str>),
Dynamic(Vec<String>),
/// A list of attribute names that are visible, this list must be sorted
Static(&'static [&'static str]),
}
@@ -104,14 +111,14 @@ impl AttributeMask {
fn contains_attribute(&self, attr: &str) -> bool {
match self {
AttributeMask::All => true,
AttributeMask::Dynamic(l) => l.binary_search(&attr).is_ok(),
AttributeMask::Dynamic(l) => l.binary_search_by_key(&attr, |s| s.as_str()).is_ok(),
AttributeMask::Static(l) => l.binary_search(&attr).is_ok(),
}
}
/// Create a new dynamic attribute mask with a single attribute
pub fn single(new: &'static str) -> Self {
Self::Dynamic(vec![new])
pub fn single(new: &str) -> Self {
Self::Dynamic(vec![new.to_string()])
}
/// Ensure the attribute list is sorted.
@@ -132,15 +139,27 @@ impl AttributeMask {
/// Combine two attribute masks
pub fn union(&self, other: &Self) -> Self {
let new = match (self, other) {
(AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
),
(AttributeMask::Static(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
),
(AttributeMask::Dynamic(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
),
(AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => {
AttributeMask::Dynamic(union_ordered_iter(
s.iter().map(|s| s.as_str()),
o.iter().map(|s| s.as_str()),
s.len() + o.len(),
))
}
(AttributeMask::Static(s), AttributeMask::Dynamic(o)) => {
AttributeMask::Dynamic(union_ordered_iter(
s.iter().copied(),
o.iter().map(|s| s.as_str()),
s.len() + o.len(),
))
}
(AttributeMask::Dynamic(s), AttributeMask::Static(o)) => {
AttributeMask::Dynamic(union_ordered_iter(
s.iter().map(|s| s.as_str()),
o.iter().copied(),
s.len() + o.len(),
))
}
(AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
),
@@ -152,9 +171,9 @@ impl AttributeMask {
/// Check if two attribute masks overlap
fn overlaps(&self, other: &Self) -> bool {
fn overlaps_iter(
self_iter: impl Iterator<Item = &'static str>,
mut other_iter: impl Iterator<Item = &'static str>,
fn overlaps_iter<'a>(
self_iter: impl Iterator<Item = &'a str>,
mut other_iter: impl Iterator<Item = &'a str>,
) -> bool {
if let Some(mut other_attr) = other_iter.next() {
for self_attr in self_iter {
@@ -179,13 +198,13 @@ impl AttributeMask {
(AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(),
(AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(),
(AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => {
overlaps_iter(v1.iter().copied(), v2.iter().copied())
overlaps_iter(v1.iter().map(|s| s.as_str()), v2.iter().map(|s| s.as_str()))
}
(AttributeMask::Dynamic(v), AttributeMask::Static(s)) => {
overlaps_iter(v.iter().copied(), s.iter().copied())
overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
}
(AttributeMask::Static(s), AttributeMask::Dynamic(v)) => {
overlaps_iter(v.iter().copied(), s.iter().copied())
overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
}
(AttributeMask::Static(s1), AttributeMask::Static(s2)) => {
overlaps_iter(s1.iter().copied(), s2.iter().copied())
+906
View File
@@ -0,0 +1,906 @@
use crate::tree::{NodeId, TreeView};
use crate::{FxDashMap, FxDashSet, SendAnyMap};
use rustc_hash::{FxHashMap, FxHashSet};
use std::collections::BTreeMap;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct DirtyNodes {
map: BTreeMap<u16, FxHashSet<NodeId>>,
}
impl DirtyNodes {
pub fn insert(&mut self, depth: u16, node_id: NodeId) {
self.map
.entry(depth)
.or_insert_with(FxHashSet::default)
.insert(node_id);
}
fn pop_front(&mut self) -> Option<NodeId> {
let (&depth, values) = self.map.iter_mut().next()?;
let key = *values.iter().next()?;
let node_id = values.take(&key)?;
if values.is_empty() {
self.map.remove(&depth);
}
Some(node_id)
}
fn pop_back(&mut self) -> Option<NodeId> {
let (&depth, values) = self.map.iter_mut().rev().next()?;
let key = *values.iter().next()?;
let node_id = values.take(&key)?;
if values.is_empty() {
self.map.remove(&depth);
}
Some(node_id)
}
}
#[test]
fn dirty_nodes() {
let mut dirty_nodes = DirtyNodes::default();
dirty_nodes.insert(1, NodeId(1));
dirty_nodes.insert(0, NodeId(0));
dirty_nodes.insert(2, NodeId(3));
dirty_nodes.insert(1, NodeId(2));
assert_eq!(dirty_nodes.pop_front(), Some(NodeId(0)));
assert!(matches!(dirty_nodes.pop_front(), Some(NodeId(1 | 2))));
assert!(matches!(dirty_nodes.pop_front(), Some(NodeId(1 | 2))));
assert_eq!(dirty_nodes.pop_front(), Some(NodeId(3)));
}
#[derive(Default)]
pub struct DirtyNodeStates {
dirty: FxDashMap<NodeId, Vec<AtomicU64>>,
}
impl DirtyNodeStates {
pub fn new(starting_nodes: FxHashMap<NodeId, FxHashSet<PassId>>) -> Self {
let this = Self::default();
for (node, nodes) in starting_nodes {
for pass_id in nodes {
this.insert(pass_id, node);
}
}
this
}
pub fn insert(&self, pass_id: PassId, node_id: NodeId) {
let pass_id = pass_id.0;
let index = pass_id / 64;
let bit = pass_id % 64;
let encoded = 1 << bit;
if let Some(dirty) = self.dirty.get(&node_id) {
if let Some(atomic) = dirty.get(index as usize) {
atomic.fetch_or(encoded, Ordering::Relaxed);
} else {
drop(dirty);
let mut write = self.dirty.get_mut(&node_id).unwrap();
write.resize_with(index as usize + 1, || AtomicU64::new(0));
write[index as usize].fetch_or(encoded, Ordering::Relaxed);
}
} else {
let mut v = Vec::with_capacity(index as usize + 1);
v.resize_with(index as usize + 1, || AtomicU64::new(0));
v[index as usize].fetch_or(encoded, Ordering::Relaxed);
self.dirty.insert(node_id, v);
}
}
fn all_dirty<T>(&self, pass_id: PassId, dirty_nodes: &mut DirtyNodes, tree: &impl TreeView<T>) {
let pass_id = pass_id.0;
let index = pass_id / 64;
let bit = pass_id % 64;
let encoded = 1 << bit;
for entry in self.dirty.iter() {
let node_id = entry.key();
let dirty = entry.value();
if let Some(atomic) = dirty.get(index as usize) {
if atomic.load(Ordering::Relaxed) & encoded != 0 {
dirty_nodes.insert(tree.height(*node_id).unwrap(), *node_id);
}
}
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
pub struct PassId(pub u64);
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)]
pub struct MemberMask(pub u64);
impl MemberMask {
pub fn overlaps(&self, other: Self) -> bool {
(*self & other).0 != 0
}
}
impl BitAndAssign for MemberMask {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl BitAnd for MemberMask {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
MemberMask(self.0 & rhs.0)
}
}
impl BitOrAssign for MemberMask {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl BitOr for MemberMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
pub struct PassReturn {
pub progress: bool,
pub mark_dirty: bool,
}
pub trait Pass {
fn pass_id(&self) -> PassId;
fn dependancies(&self) -> &'static [PassId];
fn dependants(&self) -> &'static [PassId];
fn mask(&self) -> MemberMask;
}
pub trait UpwardPass<T>: Pass {
fn pass<'a>(
&self,
node: &mut T,
children: &mut dyn Iterator<Item = &'a mut T>,
ctx: &SendAnyMap,
) -> PassReturn;
}
fn resolve_upward_pass<T, P: UpwardPass<T> + ?Sized>(
tree: &mut impl TreeView<T>,
pass: &P,
mut dirty: DirtyNodes,
dirty_states: &DirtyNodeStates,
nodes_updated: &FxDashSet<NodeId>,
ctx: &SendAnyMap,
) {
while let Some(id) = dirty.pop_back() {
let (node, mut children) = tree.parent_child_mut(id).unwrap();
let result = pass.pass(node, &mut children, ctx);
drop(children);
if result.progress || result.mark_dirty {
nodes_updated.insert(id);
if let Some(id) = tree.parent_id(id) {
if result.mark_dirty {
for dependant in pass.dependants() {
dirty_states.insert(*dependant, id);
}
}
if result.progress {
let height = tree.height(id).unwrap();
dirty.insert(height, id);
}
}
}
}
}
pub trait DownwardPass<T>: Pass {
fn pass(&self, node: &mut T, parent: Option<&mut T>, ctx: &SendAnyMap) -> PassReturn;
}
fn resolve_downward_pass<T, P: DownwardPass<T> + ?Sized>(
tree: &mut impl TreeView<T>,
pass: &P,
mut dirty: DirtyNodes,
dirty_states: &DirtyNodeStates,
nodes_updated: &FxDashSet<NodeId>,
ctx: &SendAnyMap,
) {
while let Some(id) = dirty.pop_front() {
let (node, parent) = tree.node_parent_mut(id).unwrap();
let result = pass.pass(node, parent, ctx);
if result.mark_dirty {
nodes_updated.insert(id);
}
if result.mark_dirty || result.progress {
for id in tree.children_ids(id).unwrap() {
if result.mark_dirty {
for dependant in pass.dependants() {
dirty_states.insert(*dependant, *id);
}
}
if result.progress {
let height = tree.height(*id).unwrap();
dirty.insert(height, *id);
}
}
}
}
}
pub trait NodePass<T>: Pass {
fn pass(&self, node: &mut T, ctx: &SendAnyMap) -> bool;
}
fn resolve_node_pass<T, P: NodePass<T> + ?Sized>(
tree: &mut impl TreeView<T>,
pass: &P,
mut dirty: DirtyNodes,
dirty_states: &DirtyNodeStates,
nodes_updated: &FxDashSet<NodeId>,
ctx: &SendAnyMap,
) {
while let Some(id) = dirty.pop_back() {
let node = tree.get_mut(id).unwrap();
if pass.pass(node, ctx) {
nodes_updated.insert(id);
for dependant in pass.dependants() {
dirty_states.insert(*dependant, id);
}
}
}
}
pub enum AnyPass<T: 'static> {
Upward(&'static (dyn UpwardPass<T> + Send + Sync + 'static)),
Downward(&'static (dyn DownwardPass<T> + Send + Sync + 'static)),
Node(&'static (dyn NodePass<T> + Send + Sync + 'static)),
}
impl<T> AnyPass<T> {
pub fn pass_id(&self) -> PassId {
match self {
Self::Upward(pass) => pass.pass_id(),
Self::Downward(pass) => pass.pass_id(),
Self::Node(pass) => pass.pass_id(),
}
}
pub fn dependancies(&self) -> &'static [PassId] {
match self {
Self::Upward(pass) => pass.dependancies(),
Self::Downward(pass) => pass.dependancies(),
Self::Node(pass) => pass.dependancies(),
}
}
fn mask(&self) -> MemberMask {
match self {
Self::Upward(pass) => pass.mask(),
Self::Downward(pass) => pass.mask(),
Self::Node(pass) => pass.mask(),
}
}
fn resolve(
&self,
tree: &mut impl TreeView<T>,
dirty: DirtyNodes,
dirty_states: &DirtyNodeStates,
nodes_updated: &FxDashSet<NodeId>,
ctx: &SendAnyMap,
) {
match self {
Self::Downward(pass) => {
resolve_downward_pass(tree, *pass, dirty, dirty_states, nodes_updated, ctx)
}
Self::Upward(pass) => {
resolve_upward_pass(tree, *pass, dirty, dirty_states, nodes_updated, ctx)
}
Self::Node(pass) => {
resolve_node_pass(tree, *pass, dirty, dirty_states, nodes_updated, ctx)
}
}
}
}
struct RawPointer<T>(*mut T);
unsafe impl<T> Send for RawPointer<T> {}
unsafe impl<T> Sync for RawPointer<T> {}
pub fn resolve_passes<T, Tr: TreeView<T>>(
tree: &mut Tr,
dirty_nodes: DirtyNodeStates,
mut passes: Vec<&AnyPass<T>>,
ctx: SendAnyMap,
) -> FxDashSet<NodeId> {
let dirty_states = Arc::new(dirty_nodes);
let mut resolved_passes: FxHashSet<PassId> = FxHashSet::default();
let mut resolving = Vec::new();
let nodes_updated = Arc::new(FxDashSet::default());
let ctx = Arc::new(ctx);
while !passes.is_empty() {
let mut currently_borrowed = MemberMask::default();
std::thread::scope(|s| {
let mut i = 0;
while i < passes.len() {
let pass = &passes[i];
let pass_id = pass.pass_id();
let pass_mask = pass.mask();
if pass
.dependancies()
.iter()
.all(|d| resolved_passes.contains(d) || *d == pass_id)
&& !pass_mask.overlaps(currently_borrowed)
{
let pass = passes.remove(i);
resolving.push(pass_id);
currently_borrowed |= pass_mask;
let tree_mut = tree as *mut _;
let raw_ptr = RawPointer(tree_mut);
let dirty_states = dirty_states.clone();
let nodes_updated = nodes_updated.clone();
let ctx = ctx.clone();
s.spawn(move || unsafe {
// let tree_mut: &mut Tr = &mut *raw_ptr.0;
let raw = raw_ptr;
// this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass
let tree_mut: &mut Tr = &mut *raw.0;
let mut dirty = DirtyNodes::default();
dirty_states.all_dirty(pass_id, &mut dirty, tree_mut);
pass.resolve(tree_mut, dirty, &dirty_states, &nodes_updated, &ctx);
});
} else {
i += 1;
}
}
// all passes are resolved at the end of the scope
});
resolved_passes.extend(resolving.iter().copied());
resolving.clear()
}
std::sync::Arc::try_unwrap(nodes_updated).unwrap()
}
#[test]
fn node_pass() {
use crate::tree::{Tree, TreeLike};
let mut tree = Tree::new(0);
struct AddPass;
impl Pass for AddPass {
fn pass_id(&self) -> PassId {
PassId(0)
}
fn dependancies(&self) -> &'static [PassId] {
&[]
}
fn dependants(&self) -> &'static [PassId] {
&[]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl NodePass<i32> for AddPass {
fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool {
*node += 1;
true
}
}
let add_pass = AnyPass::Node(&AddPass);
let passes = vec![&add_pass];
let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
dirty_nodes.insert(PassId(0), tree.root());
resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
assert_eq!(tree.get(tree.root()).unwrap(), &1);
}
#[test]
fn dependant_node_pass() {
use crate::tree::{Tree, TreeLike};
let mut tree = Tree::new(0);
struct AddPass;
impl Pass for AddPass {
fn pass_id(&self) -> PassId {
PassId(0)
}
fn dependancies(&self) -> &'static [PassId] {
&[PassId(1)]
}
fn dependants(&self) -> &'static [PassId] {
&[]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl NodePass<i32> for AddPass {
fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool {
*node += 1;
true
}
}
struct SubtractPass;
impl Pass for SubtractPass {
fn pass_id(&self) -> PassId {
PassId(1)
}
fn dependancies(&self) -> &'static [PassId] {
&[]
}
fn dependants(&self) -> &'static [PassId] {
&[PassId(0)]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl NodePass<i32> for SubtractPass {
fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool {
*node -= 1;
true
}
}
let add_pass = AnyPass::Node(&AddPass);
let subtract_pass = AnyPass::Node(&SubtractPass);
let passes = vec![&add_pass, &subtract_pass];
let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
dirty_nodes.insert(PassId(1), tree.root());
resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
assert_eq!(*tree.get(tree.root()).unwrap(), 0);
}
#[test]
fn independant_node_pass() {
use crate::tree::{Tree, TreeLike};
let mut tree = Tree::new((0, 0));
struct AddPass1;
impl Pass for AddPass1 {
fn pass_id(&self) -> PassId {
PassId(0)
}
fn dependancies(&self) -> &'static [PassId] {
&[]
}
fn dependants(&self) -> &'static [PassId] {
&[]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl NodePass<(i32, i32)> for AddPass1 {
fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool {
node.0 += 1;
true
}
}
struct AddPass2;
impl Pass for AddPass2 {
fn pass_id(&self) -> PassId {
PassId(1)
}
fn dependancies(&self) -> &'static [PassId] {
&[]
}
fn dependants(&self) -> &'static [PassId] {
&[]
}
fn mask(&self) -> MemberMask {
MemberMask(1)
}
}
impl NodePass<(i32, i32)> for AddPass2 {
fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool {
node.1 += 1;
true
}
}
let add_pass1 = AnyPass::Node(&AddPass1);
let add_pass2 = AnyPass::Node(&AddPass2);
let passes = vec![&add_pass1, &add_pass2];
let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
dirty_nodes.insert(PassId(0), tree.root());
dirty_nodes.insert(PassId(1), tree.root());
resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
assert_eq!(tree.get(tree.root()).unwrap(), &(1, 1));
}
#[test]
fn down_pass() {
use crate::tree::{Tree, TreeLike};
let mut tree = Tree::new(1);
let parent = tree.root();
let child1 = tree.create_node(1);
tree.add_child(parent, child1);
let grandchild1 = tree.create_node(1);
tree.add_child(child1, grandchild1);
let child2 = tree.create_node(1);
tree.add_child(parent, child2);
let grandchild2 = tree.create_node(1);
tree.add_child(child2, grandchild2);
struct AddPass;
impl Pass for AddPass {
fn pass_id(&self) -> PassId {
PassId(0)
}
fn dependancies(&self) -> &'static [PassId] {
&[]
}
fn dependants(&self) -> &'static [PassId] {
&[]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl DownwardPass<i32> for AddPass {
fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn {
if let Some(parent) = parent {
*node += *parent;
}
PassReturn {
progress: true,
mark_dirty: true,
}
}
}
let add_pass = AnyPass::Downward(&AddPass);
let passes = vec![&add_pass];
let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
dirty_nodes.insert(PassId(0), tree.root());
resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
assert_eq!(tree.get(tree.root()).unwrap(), &1);
assert_eq!(tree.get(child1).unwrap(), &2);
assert_eq!(tree.get(grandchild1).unwrap(), &3);
assert_eq!(tree.get(child2).unwrap(), &2);
assert_eq!(tree.get(grandchild2).unwrap(), &3);
}
#[test]
fn dependant_down_pass() {
use crate::tree::{Tree, TreeLike};
// 0
let mut tree = Tree::new(1);
let parent = tree.root();
// 1
let child1 = tree.create_node(1);
tree.add_child(parent, child1);
// 2
let grandchild1 = tree.create_node(1);
tree.add_child(child1, grandchild1);
// 3
let child2 = tree.create_node(1);
tree.add_child(parent, child2);
// 4
let grandchild2 = tree.create_node(1);
tree.add_child(child2, grandchild2);
struct AddPass;
impl Pass for AddPass {
fn pass_id(&self) -> PassId {
PassId(0)
}
fn dependancies(&self) -> &'static [PassId] {
&[PassId(1)]
}
fn dependants(&self) -> &'static [PassId] {
&[]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl DownwardPass<i32> for AddPass {
fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn {
if let Some(parent) = parent {
*node += *parent;
} else {
}
PassReturn {
progress: true,
mark_dirty: true,
}
}
}
struct SubtractPass;
impl Pass for SubtractPass {
fn pass_id(&self) -> PassId {
PassId(1)
}
fn dependancies(&self) -> &'static [PassId] {
&[]
}
fn dependants(&self) -> &'static [PassId] {
&[PassId(0)]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl DownwardPass<i32> for SubtractPass {
fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn {
if let Some(parent) = parent {
*node -= *parent;
} else {
}
PassReturn {
progress: true,
mark_dirty: true,
}
}
}
let add_pass = AnyPass::Downward(&AddPass);
let subtract_pass = AnyPass::Downward(&SubtractPass);
let passes = vec![&add_pass, &subtract_pass];
let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
dirty_nodes.insert(PassId(1), tree.root());
resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
// Tree before:
// 1=\
// 1=\
// 1
// 1=\
// 1
// Tree after subtract:
// 1=\
// 0=\
// 1
// 0=\
// 1
// Tree after add:
// 1=\
// 1=\
// 2
// 1=\
// 2
assert_eq!(tree.get(tree.root()).unwrap(), &1);
assert_eq!(tree.get(child1).unwrap(), &1);
assert_eq!(tree.get(grandchild1).unwrap(), &2);
assert_eq!(tree.get(child2).unwrap(), &1);
assert_eq!(tree.get(grandchild2).unwrap(), &2);
}
#[test]
fn up_pass() {
use crate::tree::{Tree, TreeLike};
// Tree before:
// 0=\
// 0=\
// 1
// 0=\
// 1
// Tree after:
// 2=\
// 1=\
// 1
// 1=\
// 1
let mut tree = Tree::new(0);
let parent = tree.root();
let child1 = tree.create_node(0);
tree.add_child(parent, child1);
let grandchild1 = tree.create_node(1);
tree.add_child(child1, grandchild1);
let child2 = tree.create_node(0);
tree.add_child(parent, child2);
let grandchild2 = tree.create_node(1);
tree.add_child(child2, grandchild2);
struct AddPass;
impl Pass for AddPass {
fn pass_id(&self) -> PassId {
PassId(0)
}
fn dependancies(&self) -> &'static [PassId] {
&[]
}
fn dependants(&self) -> &'static [PassId] {
&[]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl UpwardPass<i32> for AddPass {
fn pass<'a>(
&self,
node: &mut i32,
children: &mut dyn Iterator<Item = &'a mut i32>,
_: &SendAnyMap,
) -> PassReturn {
*node += children.map(|i| *i).sum::<i32>();
PassReturn {
progress: true,
mark_dirty: true,
}
}
}
let add_pass = AnyPass::Upward(&AddPass);
let passes = vec![&add_pass];
let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
dirty_nodes.insert(PassId(0), grandchild1);
dirty_nodes.insert(PassId(0), grandchild2);
resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
assert_eq!(tree.get(tree.root()).unwrap(), &2);
assert_eq!(tree.get(child1).unwrap(), &1);
assert_eq!(tree.get(grandchild1).unwrap(), &1);
assert_eq!(tree.get(child2).unwrap(), &1);
assert_eq!(tree.get(grandchild2).unwrap(), &1);
}
#[test]
fn dependant_up_pass() {
use crate::tree::{Tree, TreeLike};
// 0
let mut tree = Tree::new(0);
let parent = tree.root();
// 1
let child1 = tree.create_node(0);
tree.add_child(parent, child1);
// 2
let grandchild1 = tree.create_node(1);
tree.add_child(child1, grandchild1);
// 3
let child2 = tree.create_node(0);
tree.add_child(parent, child2);
// 4
let grandchild2 = tree.create_node(1);
tree.add_child(child2, grandchild2);
struct AddPass;
impl Pass for AddPass {
fn pass_id(&self) -> PassId {
PassId(0)
}
fn dependancies(&self) -> &'static [PassId] {
&[PassId(1)]
}
fn dependants(&self) -> &'static [PassId] {
&[]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl UpwardPass<i32> for AddPass {
fn pass<'a>(
&self,
node: &mut i32,
children: &mut dyn Iterator<Item = &'a mut i32>,
_: &SendAnyMap,
) -> PassReturn {
*node += children.map(|i| *i).sum::<i32>();
PassReturn {
progress: true,
mark_dirty: true,
}
}
}
struct SubtractPass;
impl Pass for SubtractPass {
fn pass_id(&self) -> PassId {
PassId(1)
}
fn dependancies(&self) -> &'static [PassId] {
&[]
}
fn dependants(&self) -> &'static [PassId] {
&[PassId(0)]
}
fn mask(&self) -> MemberMask {
MemberMask(0)
}
}
impl UpwardPass<i32> for SubtractPass {
fn pass<'a>(
&self,
node: &mut i32,
children: &mut dyn Iterator<Item = &'a mut i32>,
_: &SendAnyMap,
) -> PassReturn {
*node -= children.map(|i| *i).sum::<i32>();
PassReturn {
progress: true,
mark_dirty: true,
}
}
}
let add_pass = AnyPass::Upward(&AddPass);
let subtract_pass = AnyPass::Upward(&SubtractPass);
let passes = vec![&add_pass, &subtract_pass];
let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default();
dirty_nodes.insert(PassId(1), grandchild1);
dirty_nodes.insert(PassId(1), grandchild2);
resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new());
// Tree before:
// 0=\
// 0=\
// 1
// 0=\
// 1
// Tree after subtract:
// 2=\
// -1=\
// 1
// -1=\
// 1
// Tree after add:
// 2=\
// 0=\
// 1
// 0=\
// 1
assert_eq!(tree.get(tree.root()).unwrap(), &2);
assert_eq!(tree.get(child1).unwrap(), &0);
assert_eq!(tree.get(grandchild1).unwrap(), &1);
assert_eq!(tree.get(child2).unwrap(), &0);
assert_eq!(tree.get(grandchild2).unwrap(), &1);
}
File diff suppressed because it is too large. Load diff
+92 -49
View File
@@ -1,18 +1,17 @@
use std::{cmp::Ordering, fmt::Debug};
use std::cmp::Ordering;
use crate::node::Node;
use crate::node_ref::{NodeMask, NodeView};
use crate::real_dom::NodeData;
use crate::traversable::Traversable;
use crate::RealNodeId;
use anymap::AnyMap;
use rustc_hash::FxHashSet;
use crate::passes::{resolve_passes, AnyPass, DirtyNodeStates};
use crate::tree::TreeView;
use crate::{FxDashSet, RealNodeId, SendAnyMap};
/// Join two sorted iterators
pub(crate) fn union_ordered_iter<T: Ord + Debug>(
s_iter: impl Iterator<Item = T>,
o_iter: impl Iterator<Item = T>,
pub(crate) fn union_ordered_iter<'a>(
s_iter: impl Iterator<Item = &'a str>,
o_iter: impl Iterator<Item = &'a str>,
new_len_guess: usize,
) -> Vec<T> {
) -> Vec<String> {
let mut s_peekable = s_iter.peekable();
let mut o_peekable = o_iter.peekable();
let mut v = Vec::with_capacity(new_len_guess);
@@ -23,7 +22,7 @@ pub(crate) fn union_ordered_iter<T: Ord + Debug>(
break;
}
Ordering::Less => {
v.push(o_peekable.next().unwrap());
v.push(o_peekable.next().unwrap().to_string());
}
Ordering::Equal => {
o_peekable.next();
@@ -31,10 +30,10 @@ pub(crate) fn union_ordered_iter<T: Ord + Debug>(
}
}
}
v.push(s_peekable.next().unwrap());
v.push(s_peekable.next().unwrap().to_string());
}
for o_i in o_peekable {
v.push(o_i);
v.push(o_i.to_string());
}
for w in v.windows(2) {
debug_assert!(w[1] > w[0]);
@@ -57,17 +56,17 @@ pub(crate) fn union_ordered_iter<T: Ord + Debug>(
/// impl ChildDepState for Layout {
/// type Ctx = ();
/// // The layout depends on the layout of the children.
/// type DepState = Layout;
/// type DepState = (Layout,);
/// fn reduce<'a>(
/// &mut self,
/// _node: NodeView,
/// children: impl Iterator<Item = &'a Self::DepState>,
/// children: impl Iterator<Item = (&'a Self,)>,
/// _ctx: &Self::Ctx,
/// ) -> bool
/// where
/// Self::DepState: 'a{
/// /// Children are layed out form left to right. The width of the parent is the sum of the widths and the max of the heights.
/// let new = children.copied().reduce(|c1, c2| Layout{
/// let new = children.map(|(&c,)|c).reduce(|c1, c2| Layout{
/// width: c1.width + c2.width,
/// height: c1.height.max(c2.height)
/// }).unwrap_or_default();
@@ -81,15 +80,15 @@ pub trait ChildDepState {
/// The context is passed to the [ChildDepState::reduce] when it is resolved.
type Ctx;
/// A state from each child node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
/// This must be either a [ChildDepState], or [NodeDepState].
type DepState;
/// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState] or [NodeDepState].
type DepState: ElementBorrowable;
/// The part of a node that this state cares about. This is used to determine if the state should be updated when a node is updated.
const NODE_MASK: NodeMask = NodeMask::NONE;
/// Resolve the state current node's state from the state of the children, the state of the node, and some external context.
fn reduce<'a>(
&mut self,
node: NodeView,
children: impl Iterator<Item = &'a Self::DepState>,
children: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
ctx: &Self::Ctx,
) -> bool
where
@@ -109,7 +108,7 @@ pub trait ChildDepState {
/// impl ParentDepState for FontSize {
/// type Ctx = ();
/// // The font size depends on the font size of the parent element.
/// type DepState = Self;
/// type DepState = (Self,);
/// const NODE_MASK: NodeMask =
/// NodeMask::new_with_attrs(AttributeMask::Static(&[
/// "font-size"
@@ -117,12 +116,12 @@ pub trait ChildDepState {
/// fn reduce<'a>(
/// &mut self,
/// node: NodeView,
/// parent: Option<&'a Self::DepState>,
/// parent: Option<(&'a Self,)>,
/// ctx: &Self::Ctx,
/// ) -> bool{
/// let old = *self;
/// // If the font size was set on the parent, it is passed down to the current element
/// if let Some(parent) = parent {
/// if let Some((parent,)) = parent {
/// *self = *parent;
/// }
/// // If the current node overrides the font size, use that size insead.
@@ -143,15 +142,15 @@ pub trait ParentDepState {
/// The context is passed to the [ParentDepState::reduce] when it is resolved.
type Ctx;
/// A state from from the parent node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
/// This must be either a [ParentDepState] or [NodeDepState]
type DepState;
/// Depstate must be a tuple containing any number of borrowed elements that are either [ParentDepState] or [NodeDepState].
type DepState: ElementBorrowable;
/// The part of a node that this state cares about. This is used to determine if the state should be updated when a node is updated.
const NODE_MASK: NodeMask = NodeMask::NONE;
/// Resolve the state current node's state from the state of the parent node, the state of the node, and some external context.
fn reduce<'a>(
&mut self,
node: NodeView,
parent: Option<&'a Self::DepState>,
parent: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
ctx: &Self::Ctx,
) -> bool;
}
@@ -169,6 +168,7 @@ pub trait ParentDepState {
///
/// impl NodeDepState for TabIndex {
/// type Ctx = ();
/// type DepState = ();
/// const NODE_MASK: NodeMask =
/// NodeMask::new_with_attrs(AttributeMask::Static(&[
/// "tabindex"
@@ -193,42 +193,44 @@ pub trait ParentDepState {
/// }
/// }
/// ```
/// The generic argument (Depstate) must be a tuple containing any number of borrowed elements that are either a [ChildDepState], [ParentDepState] or [NodeDepState].
// Todo: once GATs land we can model multable dependencies better
pub trait NodeDepState<DepState = ()> {
pub trait NodeDepState {
/// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState], [ParentDepState] or [NodeDepState].
type DepState: ElementBorrowable;
/// The state passed to [NodeDepState::reduce] when it is resolved.
type Ctx;
/// The part of a node that this state cares about. This is used to determine if the state should be updated when a node is updated.
const NODE_MASK: NodeMask = NodeMask::NONE;
/// Resolve the state current node's state from the state of the sibling states, the state of the node, and some external context.
fn reduce(&mut self, node: NodeView, siblings: DepState, ctx: &Self::Ctx) -> bool;
fn reduce<'a>(
&mut self,
node: NodeView,
node_state: <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>,
ctx: &Self::Ctx,
) -> bool;
}
/// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom].
pub trait State: Default + Clone {
pub trait State: Default + Clone + 'static {
#[doc(hidden)]
fn update<
'a,
T: Traversable<Node = Self, Id = RealNodeId>,
T2: Traversable<Node = NodeData, Id = RealNodeId>,
>(
dirty: &[(RealNodeId, NodeMask)],
state_tree: &'a mut T,
rdom: &'a T2,
ctx: &AnyMap,
) -> FxHashSet<RealNodeId>;
const PASSES: &'static [AnyPass<Node<Self>>];
#[doc(hidden)]
const MASKS: &'static [NodeMask];
#[doc(hidden)]
fn update<T: TreeView<Node<Self>>>(
dirty: DirtyNodeStates,
tree: &mut T,
ctx: SendAnyMap,
) -> FxDashSet<RealNodeId> {
let passes = Self::PASSES.iter().collect();
resolve_passes(tree, dirty, passes, ctx)
}
}
impl ChildDepState for () {
type Ctx = ();
type DepState = ();
fn reduce<'a>(
&mut self,
_: NodeView,
_: impl Iterator<Item = &'a Self::DepState>,
_: &Self::Ctx,
) -> bool
fn reduce<'a>(&mut self, _: NodeView, _: impl Iterator<Item = ()>, _: &Self::Ctx) -> bool
where
Self::DepState: 'a,
{
@@ -239,14 +241,55 @@ impl ChildDepState for () {
impl ParentDepState for () {
type Ctx = ();
type DepState = ();
fn reduce<'a>(&mut self, _: NodeView, _: Option<&'a Self::DepState>, _: &Self::Ctx) -> bool {
fn reduce<'a>(&mut self, _: NodeView, _: Option<()>, _: &Self::Ctx) -> bool {
false
}
}
impl NodeDepState<()> for () {
impl NodeDepState for () {
type DepState = ();
type Ctx = ();
fn reduce(&mut self, _: NodeView, _sibling: (), _: &Self::Ctx) -> bool {
false
}
}
pub trait ElementBorrowable {
type ElementBorrowed<'a>
where
Self: 'a;
fn borrow_elements(&self) -> Self::ElementBorrowed<'_>;
}
macro_rules! impl_element_borrowable {
($($t:ident),*) => {
impl< $($t),* > ElementBorrowable for ($($t,)*) {
type ElementBorrowed<'a> = ($(&'a $t,)*) where Self: 'a;
#[allow(clippy::unused_unit, non_snake_case)]
fn borrow_elements<'a>(&'a self) -> Self::ElementBorrowed<'a> {
let ($($t,)*) = self;
($(&$t,)*)
}
}
};
}
impl_element_borrowable!();
impl_element_borrowable!(A);
impl_element_borrowable!(A, B);
impl_element_borrowable!(A, B, C);
impl_element_borrowable!(A, B, C, D);
impl_element_borrowable!(A, B, C, D, E);
impl_element_borrowable!(A, B, C, D, E, F);
impl_element_borrowable!(A, B, C, D, E, F, G);
impl_element_borrowable!(A, B, C, D, E, F, G, H);
impl_element_borrowable!(A, B, C, D, E, F, G, H, I);
impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J);
impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K);
impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
-102
View File
@@ -1,102 +0,0 @@
/// This is a interface for a tree with the ability to jump to a specific node
pub trait Traversable {
type Id: Copy;
type Node;
fn height(&self, id: Self::Id) -> Option<u16>;
fn get(&self, id: Self::Id) -> Option<&Self::Node>;
fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node>;
fn children(&self, node: Self::Id) -> &[Self::Id];
fn parent(&self, node: Self::Id) -> Option<Self::Id>;
fn map<N, F: Fn(&Self::Node) -> &N, FMut: Fn(&mut Self::Node) -> &mut N>(
&mut self,
f: F,
f_mut: FMut,
) -> Map<Self, N, F, FMut>
where
Self: Sized,
{
Map {
tree: self,
f,
f_mut,
}
}
// this is safe because no node will have itself as it's parent
fn get_node_parent_mut(
&mut self,
id: Self::Id,
) -> (Option<&mut Self::Node>, Option<&mut Self::Node>) {
let node = self.get_mut(id).map(|n| n as *mut _);
let parent = self
.parent(id)
.and_then(|n| self.get_mut(n))
.map(|n| n as *mut _);
unsafe { (node.map(|n| &mut *n), parent.map(|n| &mut *n)) }
}
// this is safe because no node will have itself as a child
fn get_node_children_mut(
&mut self,
id: Self::Id,
) -> (Option<&mut Self::Node>, Vec<&mut Self::Node>) {
let node = self.get_mut(id).map(|n| n as *mut _);
let mut children = Vec::new();
let children_indexes = self.children(id).to_vec();
for id in children_indexes {
if let Some(n) = self.get_mut(id) {
children.push(unsafe { &mut *(n as *mut _) });
}
}
unsafe { (node.map(|n| &mut *n), children) }
}
}
/// Maps one type of tree to another. Similar to [std::iter::Map].
pub struct Map<
'a,
T: Traversable,
N,
F: Fn(&<T as Traversable>::Node) -> &N,
FMut: Fn(&mut <T as Traversable>::Node) -> &mut N,
> {
f: F,
f_mut: FMut,
tree: &'a mut T,
}
impl<
'a,
T: Traversable,
N,
F: Fn(&<T as Traversable>::Node) -> &N,
FMut: Fn(&mut <T as Traversable>::Node) -> &mut N,
> Traversable for Map<'a, T, N, F, FMut>
{
type Id = <T as Traversable>::Id;
type Node = N;
fn height(&self, id: Self::Id) -> Option<u16> {
self.tree.height(id)
}
fn get(&self, id: Self::Id) -> Option<&Self::Node> {
self.tree.get(id).map(&self.f)
}
fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node> {
self.tree.get_mut(id).map(&self.f_mut)
}
fn children(&self, id: Self::Id) -> &[Self::Id] {
self.tree.children(id)
}
fn parent(&self, id: Self::Id) -> Option<Self::Id> {
self.tree.parent(id)
}
}
+859
View File
@@ -0,0 +1,859 @@
use core::panic;
use parking_lot::lock_api::RawMutex as _;
use parking_lot::{RawMutex, RwLock};
use slab::Slab;
use std::cell::UnsafeCell;
use std::collections::VecDeque;
use std::marker::PhantomData;
use std::sync::Arc;
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
pub struct NodeId(pub usize);
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Node<T> {
value: T,
parent: Option<NodeId>,
children: Vec<NodeId>,
height: u16,
}
#[derive(Debug)]
pub struct Tree<T> {
nodes: Slab<Node<T>>,
root: NodeId,
}
impl<T> Tree<T> {
fn try_remove(&mut self, id: NodeId) -> Option<Node<T>> {
self.nodes.try_remove(id.0).map(|node| {
if let Some(parent) = node.parent {
self.nodes
.get_mut(parent.0)
.unwrap()
.children
.retain(|child| child != &id);
}
for child in &node.children {
self.remove_recursive(*child);
}
node
})
}
fn remove_recursive(&mut self, node: NodeId) {
let node = self.nodes.remove(node.0);
for child in node.children {
self.remove_recursive(child);
}
}
fn set_height(&mut self, node: NodeId, height: u16) {
let self_mut = self as *mut Self;
let node = self.nodes.get_mut(node.0).unwrap();
node.height = height;
unsafe {
// Safety: No node has itself as a child
for child in &node.children {
(*self_mut).set_height(*child, height + 1);
}
}
}
}
pub trait TreeView<T>: Sized {
type Iterator<'a>: Iterator<Item = &'a T>
where
T: 'a,
Self: 'a;
type IteratorMut<'a>: Iterator<Item = &'a mut T>
where
T: 'a,
Self: 'a;
fn root(&self) -> NodeId;
fn contains(&self, id: NodeId) -> bool {
self.get(id).is_some()
}
fn get(&self, id: NodeId) -> Option<&T>;
fn get_unchecked(&self, id: NodeId) -> &T {
unsafe { self.get(id).unwrap_unchecked() }
}
fn get_mut(&mut self, id: NodeId) -> Option<&mut T>;
fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T {
unsafe { self.get_mut(id).unwrap_unchecked() }
}
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>>;
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>>;
fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> {
let mut_ptr: *mut Self = self;
unsafe {
// Safety: No node has itself as a child.
(*mut_ptr).get_mut(id).and_then(|parent| {
(*mut_ptr)
.children_mut(id)
.map(|children| (parent, children))
})
}
}
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]>;
fn parent(&self, id: NodeId) -> Option<&T>;
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T>;
fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)> {
let mut_ptr: *mut Self = self;
unsafe {
// Safety: No node has itself as a parent.
(*mut_ptr)
.get_mut(id)
.map(|node| (node, (*mut_ptr).parent_mut(id)))
}
}
fn parent_id(&self, id: NodeId) -> Option<NodeId>;
fn height(&self, id: NodeId) -> Option<u16>;
fn map<T2, F: Fn(&T) -> &T2, FMut: Fn(&mut T) -> &mut T2>(
&mut self,
map: F,
map_mut: FMut,
) -> TreeMap<T, T2, Self, F, FMut> {
TreeMap::new(self, map, map_mut)
}
fn size(&self) -> usize;
fn traverse_depth_first(&self, mut f: impl FnMut(&T)) {
let mut stack = vec![self.root()];
while let Some(id) = stack.pop() {
if let Some(node) = self.get(id) {
f(node);
if let Some(children) = self.children_ids(id) {
stack.extend(children.iter().copied().rev());
}
}
}
}
fn traverse_depth_first_mut(&mut self, mut f: impl FnMut(&mut T)) {
let mut stack = vec![self.root()];
while let Some(id) = stack.pop() {
if let Some(node) = self.get_mut(id) {
f(node);
if let Some(children) = self.children_ids(id) {
stack.extend(children.iter().copied().rev());
}
}
}
}
fn traverse_breadth_first(&self, mut f: impl FnMut(&T)) {
let mut queue = VecDeque::new();
queue.push_back(self.root());
while let Some(id) = queue.pop_front() {
if let Some(node) = self.get(id) {
f(node);
if let Some(children) = self.children_ids(id) {
for id in children {
queue.push_back(*id);
}
}
}
}
}
fn traverse_breadth_first_mut(&mut self, mut f: impl FnMut(&mut T)) {
let mut queue = VecDeque::new();
queue.push_back(self.root());
while let Some(id) = queue.pop_front() {
if let Some(node) = self.get_mut(id) {
f(node);
if let Some(children) = self.children_ids(id) {
for id in children {
queue.push_back(*id);
}
}
}
}
}
}
pub trait TreeLike<T>: TreeView<T> {
fn new(root: T) -> Self;
fn create_node(&mut self, value: T) -> NodeId;
fn add_child(&mut self, parent: NodeId, child: NodeId);
fn remove(&mut self, id: NodeId) -> Option<T>;
fn remove_all_children(&mut self, id: NodeId) -> Vec<T>;
fn replace(&mut self, old: NodeId, new: NodeId);
fn insert_before(&mut self, id: NodeId, new: NodeId);
fn insert_after(&mut self, id: NodeId, new: NodeId);
}
pub struct ChildNodeIterator<'a, T, Tr: TreeView<T>> {
tree: &'a Tr,
children_ids: &'a [NodeId],
index: usize,
node_type: PhantomData<T>,
}
impl<'a, T: 'a, Tr: TreeView<T>> Iterator for ChildNodeIterator<'a, T, Tr> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.children_ids.get(self.index).map(|id| {
self.index += 1;
self.tree.get_unchecked(*id)
})
}
}
pub struct ChildNodeIteratorMut<'a, T, Tr: TreeView<T> + 'a> {
tree: *mut Tr,
children_ids: &'a [NodeId],
index: usize,
node_type: PhantomData<T>,
}
unsafe impl<'a, T, Tr: TreeView<T> + 'a> Sync for ChildNodeIteratorMut<'a, T, Tr> {}
impl<'a, T, Tr: TreeView<T>> ChildNodeIteratorMut<'a, T, Tr> {
fn tree_mut(&mut self) -> &'a mut Tr {
unsafe { &mut *self.tree }
}
}
impl<'a, T: 'a, Tr: TreeView<T>> Iterator for ChildNodeIteratorMut<'a, T, Tr> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
let owned = self.children_ids.get(self.index).copied();
match owned {
Some(id) => {
self.index += 1;
Some(self.tree_mut().get_mut_unchecked(id))
}
None => None,
}
}
}
impl<T> TreeView<T> for Tree<T> {
type Iterator<'a> = ChildNodeIterator<'a, T, Tree<T>> where T: 'a;
type IteratorMut<'a> = ChildNodeIteratorMut<'a, T, Tree<T>> where T: 'a;
fn root(&self) -> NodeId {
self.root
}
fn get(&self, id: NodeId) -> Option<&T> {
self.nodes.get(id.0).map(|node| &node.value)
}
fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
self.nodes.get_mut(id.0).map(|node| &mut node.value)
}
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
self.children_ids(id).map(|children_ids| ChildNodeIterator {
tree: self,
children_ids,
index: 0,
node_type: PhantomData,
})
}
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
let raw_ptr = self as *mut Self;
unsafe {
// Safety: No node will appear as a child twice
self.children_ids(id)
.map(|children_ids| ChildNodeIteratorMut {
tree: &mut *raw_ptr,
children_ids,
index: 0,
node_type: PhantomData,
})
}
}
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> {
self.nodes.get(id.0).map(|node| node.children.as_slice())
}
fn parent(&self, id: NodeId) -> Option<&T> {
self.nodes
.get(id.0)
.and_then(|node| node.parent.map(|id| self.nodes.get(id.0).unwrap()))
.map(|node| &node.value)
}
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T> {
let self_ptr = self as *mut Self;
unsafe {
// Safety: No node has itself as a parent.
self.nodes
.get_mut(id.0)
.and_then(move |node| {
node.parent
.map(move |id| (*self_ptr).nodes.get_mut(id.0).unwrap())
})
.map(|node| &mut node.value)
}
}
fn parent_id(&self, id: NodeId) -> Option<NodeId> {
self.nodes.get(id.0).and_then(|node| node.parent)
}
fn height(&self, id: NodeId) -> Option<u16> {
self.nodes.get(id.0).map(|n| n.height)
}
fn get_unchecked(&self, id: NodeId) -> &T {
unsafe { &self.nodes.get_unchecked(id.0).value }
}
fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T {
unsafe { &mut self.nodes.get_unchecked_mut(id.0).value }
}
fn size(&self) -> usize {
self.nodes.len()
}
}
impl<T> TreeLike<T> for Tree<T> {
fn new(root: T) -> Self {
let mut nodes = Slab::default();
let root = NodeId(nodes.insert(Node {
value: root,
parent: None,
children: Vec::new(),
height: 0,
}));
Self { nodes, root }
}
fn create_node(&mut self, value: T) -> NodeId {
NodeId(self.nodes.insert(Node {
value,
parent: None,
children: Vec::new(),
height: 0,
}))
}
fn add_child(&mut self, parent: NodeId, new: NodeId) {
self.nodes.get_mut(new.0).unwrap().parent = Some(parent);
let parent = self.nodes.get_mut(parent.0).unwrap();
parent.children.push(new);
let height = parent.height + 1;
self.set_height(new, height);
}
fn remove(&mut self, id: NodeId) -> Option<T> {
self.try_remove(id).map(|node| node.value)
}
fn remove_all_children(&mut self, id: NodeId) -> Vec<T> {
let mut children = Vec::new();
let self_mut = self as *mut Self;
for child in self.children_ids(id).unwrap() {
unsafe {
// Safety: No node has itself as a child
children.push((*self_mut).remove(*child).unwrap());
}
}
children
}
fn replace(&mut self, old_id: NodeId, new_id: NodeId) {
// remove the old node
let old = self
.try_remove(old_id)
.expect("tried to replace a node that doesn't exist");
// update the parent's link to the child
if let Some(parent_id) = old.parent {
let parent = self.nodes.get_mut(parent_id.0).unwrap();
for id in &mut parent.children {
if *id == old_id {
*id = new_id;
}
}
let height = parent.height + 1;
self.set_height(new_id, height);
}
}
fn insert_before(&mut self, id: NodeId, new: NodeId) {
let node = self.nodes.get(id.0).unwrap();
let parent_id = node.parent.expect("tried to insert before root");
self.nodes.get_mut(new.0).unwrap().parent = Some(parent_id);
let parent = self.nodes.get_mut(parent_id.0).unwrap();
let index = parent
.children
.iter()
.position(|child| child == &id)
.unwrap();
parent.children.insert(index, new);
let height = parent.height + 1;
self.set_height(new, height);
}
fn insert_after(&mut self, id: NodeId, new: NodeId) {
let node = self.nodes.get(id.0).unwrap();
let parent_id = node.parent.expect("tried to insert before root");
self.nodes.get_mut(new.0).unwrap().parent = Some(parent_id);
let parent = self.nodes.get_mut(parent_id.0).unwrap();
let index = parent
.children
.iter()
.position(|child| child == &id)
.unwrap();
parent.children.insert(index + 1, new);
let height = parent.height + 1;
self.set_height(new, height);
}
}
pub struct TreeMap<'a, T1, T2, Tr, F, FMut>
where
Tr: TreeView<T1>,
F: Fn(&T1) -> &T2,
FMut: Fn(&mut T1) -> &mut T2,
{
tree: &'a mut Tr,
map: F,
map_mut: FMut,
in_node_type: PhantomData<T1>,
out_node_type: PhantomData<T2>,
}
impl<'a, T1, T2, Tr, F, FMut> TreeMap<'a, T1, T2, Tr, F, FMut>
where
Tr: TreeView<T1>,
F: Fn(&T1) -> &T2,
FMut: Fn(&mut T1) -> &mut T2,
{
pub fn new(tree: &'a mut Tr, map: F, map_mut: FMut) -> Self {
TreeMap {
tree,
map,
map_mut,
in_node_type: PhantomData,
out_node_type: PhantomData,
}
}
}
impl<'a, T1, T2, Tr, F, FMut> TreeView<T2> for TreeMap<'a, T1, T2, Tr, F, FMut>
where
Tr: TreeView<T1>,
F: Fn(&T1) -> &T2,
FMut: Fn(&mut T1) -> &mut T2,
{
type Iterator<'b> = ChildNodeIterator<'b, T2, TreeMap<'a, T1, T2, Tr, F, FMut>>
where
T2: 'b,
Self:'b;
type IteratorMut<'b> = ChildNodeIteratorMut<'b, T2, TreeMap<'a, T1, T2, Tr, F, FMut>>
where
T2: 'b,
Self:'b;
fn root(&self) -> NodeId {
self.tree.root()
}
fn get(&self, id: NodeId) -> Option<&T2> {
self.tree.get(id).map(|node| (self.map)(node))
}
fn get_mut(&mut self, id: NodeId) -> Option<&mut T2> {
self.tree.get_mut(id).map(|node| (self.map_mut)(node))
}
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
self.children_ids(id).map(|children_ids| ChildNodeIterator {
tree: self,
children_ids,
index: 0,
node_type: PhantomData,
})
}
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
let raw_ptr = self as *mut Self;
unsafe {
// Safety: No node can be a child twice.
self.children_ids(id)
.map(|children_ids| ChildNodeIteratorMut {
tree: &mut *raw_ptr,
children_ids,
index: 0,
node_type: PhantomData,
})
}
}
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> {
self.tree.children_ids(id)
}
fn parent(&self, id: NodeId) -> Option<&T2> {
self.tree.parent(id).map(|node| (self.map)(node))
}
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T2> {
self.tree.parent_mut(id).map(|node| (self.map_mut)(node))
}
fn parent_id(&self, id: NodeId) -> Option<NodeId> {
self.tree.parent_id(id)
}
fn height(&self, id: NodeId) -> Option<u16> {
self.tree.height(id)
}
fn get_unchecked(&self, id: NodeId) -> &T2 {
(self.map)(self.tree.get_unchecked(id))
}
fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T2 {
(self.map_mut)(self.tree.get_mut_unchecked(id))
}
fn size(&self) -> usize {
self.tree.size()
}
}
/// A view into a tree that can be shared between multiple threads. Nodes are locked invividually.
pub struct SharedView<'a, T, Tr: TreeView<T>> {
tree: Arc<UnsafeCell<&'a mut Tr>>,
node_locks: Arc<RwLock<Vec<RawMutex>>>,
node_type: PhantomData<T>,
}
impl<'a, T, Tr: TreeView<T>> SharedView<'a, T, Tr> {
/// Checks if a node is currently locked. Returns None if the node does not exist.
pub fn check_lock(&self, id: NodeId) -> Option<bool> {
let locks = self.node_locks.read();
locks.get(id.0).map(|lock| lock.is_locked())
}
}
unsafe impl<'a, T, Tr: TreeView<T>> Send for SharedView<'a, T, Tr> {}
unsafe impl<'a, T, Tr: TreeView<T>> Sync for SharedView<'a, T, Tr> {}
impl<'a, T, Tr: TreeView<T>> Clone for SharedView<'a, T, Tr> {
fn clone(&self) -> Self {
Self {
tree: self.tree.clone(),
node_locks: self.node_locks.clone(),
node_type: PhantomData,
}
}
}
impl<'a, T, Tr: TreeView<T>> SharedView<'a, T, Tr> {
pub fn new(tree: &'a mut Tr) -> Self {
let tree = Arc::new(UnsafeCell::new(tree));
let mut node_locks = Vec::new();
for _ in 0..unsafe { (*tree.get()).size() } {
node_locks.push(RawMutex::INIT);
}
Self {
tree,
node_locks: Arc::new(RwLock::new(node_locks)),
node_type: PhantomData,
}
}
fn lock_node(&self, node: NodeId) {
let read = self.node_locks.read();
let lock = read.get(node.0);
match lock {
Some(lock) => lock.lock(),
None => {
drop(read);
let mut write = self.node_locks.write();
write.resize_with(node.0 + 1, || RawMutex::INIT);
unsafe { write.get_unchecked(node.0).lock() }
}
}
}
fn unlock_node(&self, node: NodeId) {
let read = self.node_locks.read();
let lock = read.get(node.0);
match lock {
Some(lock) => unsafe { lock.unlock() },
None => {
panic!("unlocking node that was not locked")
}
}
}
fn with_node<R>(&self, node_id: NodeId, f: impl FnOnce(&'a mut Tr) -> R) -> R {
self.lock_node(node_id);
let tree = unsafe { &mut *self.tree.get() };
let r = f(tree);
self.unlock_node(node_id);
r
}
}
impl<'a, T, Tr: TreeView<T>> TreeView<T> for SharedView<'a, T, Tr> {
type Iterator<'b> = Tr::Iterator<'b> where T: 'b, Self: 'b;
type IteratorMut<'b>=Tr::IteratorMut<'b>
where
T: 'b,
Self: 'b;
fn root(&self) -> NodeId {
unsafe { (*self.tree.get()).root() }
}
fn get(&self, id: NodeId) -> Option<&T> {
self.with_node(id, |t| t.get(id))
}
fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
self.with_node(id, |t| t.get_mut(id))
}
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
self.with_node(id, |t| t.children(id))
}
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
self.with_node(id, |t| t.children_mut(id))
}
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> {
self.with_node(id, |t| t.children_ids(id))
}
fn parent(&self, id: NodeId) -> Option<&T> {
self.with_node(id, |t| t.get(id))
}
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T> {
self.with_node(id, |t| t.parent_mut(id))
}
fn parent_id(&self, id: NodeId) -> Option<NodeId> {
self.with_node(id, |t| t.parent_id(id))
}
fn height(&self, id: NodeId) -> Option<u16> {
unsafe { (*self.tree.get()).height(id) }
}
fn size(&self) -> usize {
unsafe { (*self.tree.get()).size() }
}
}
#[test]
fn creation() {
let mut tree = Tree::new(1);
let parent = tree.root();
let child = tree.create_node(0);
tree.add_child(parent, child);
println!("Tree: {:#?}", tree);
assert_eq!(tree.size(), 2);
assert_eq!(tree.height(parent), Some(0));
assert_eq!(tree.height(child), Some(1));
assert_eq!(*tree.get(parent).unwrap(), 1);
assert_eq!(*tree.get(child).unwrap(), 0);
assert_eq!(tree.parent_id(parent), None);
assert_eq!(tree.parent_id(child).unwrap(), parent);
assert_eq!(tree.children_ids(parent).unwrap(), &[child]);
}
#[test]
fn insertion() {
let mut tree = Tree::new(0);
let parent = tree.root();
let child = tree.create_node(2);
tree.add_child(parent, child);
let before = tree.create_node(1);
tree.insert_before(child, before);
let after = tree.create_node(3);
tree.insert_after(child, after);
println!("Tree: {:#?}", tree);
assert_eq!(tree.size(), 4);
assert_eq!(tree.height(parent), Some(0));
assert_eq!(tree.height(child), Some(1));
assert_eq!(tree.height(before), Some(1));
assert_eq!(tree.height(after), Some(1));
assert_eq!(*tree.get(parent).unwrap(), 0);
assert_eq!(*tree.get(before).unwrap(), 1);
assert_eq!(*tree.get(child).unwrap(), 2);
assert_eq!(*tree.get(after).unwrap(), 3);
assert_eq!(tree.parent_id(before).unwrap(), parent);
assert_eq!(tree.parent_id(child).unwrap(), parent);
assert_eq!(tree.parent_id(after).unwrap(), parent);
assert_eq!(tree.children_ids(parent).unwrap(), &[before, child, after]);
}
#[test]
fn deletion() {
let mut tree = Tree::new(0);
let parent = tree.root();
let child = tree.create_node(2);
tree.add_child(parent, child);
let before = tree.create_node(1);
tree.insert_before(child, before);
let after = tree.create_node(3);
tree.insert_after(child, after);
println!("Tree: {:#?}", tree);
assert_eq!(tree.size(), 4);
assert_eq!(tree.height(parent), Some(0));
assert_eq!(tree.height(child), Some(1));
assert_eq!(tree.height(before), Some(1));
assert_eq!(tree.height(after), Some(1));
assert_eq!(*tree.get(parent).unwrap(), 0);
assert_eq!(*tree.get(before).unwrap(), 1);
assert_eq!(*tree.get(child).unwrap(), 2);
assert_eq!(*tree.get(after).unwrap(), 3);
assert_eq!(tree.parent_id(before).unwrap(), parent);
assert_eq!(tree.parent_id(child).unwrap(), parent);
assert_eq!(tree.parent_id(after).unwrap(), parent);
assert_eq!(tree.children_ids(parent).unwrap(), &[before, child, after]);
tree.remove(child);
println!("Tree: {:#?}", tree);
assert_eq!(tree.size(), 3);
assert_eq!(tree.height(parent), Some(0));
assert_eq!(tree.height(before), Some(1));
assert_eq!(tree.height(after), Some(1));
assert_eq!(*tree.get(parent).unwrap(), 0);
assert_eq!(*tree.get(before).unwrap(), 1);
assert_eq!(tree.get(child), None);
assert_eq!(*tree.get(after).unwrap(), 3);
assert_eq!(tree.parent_id(before).unwrap(), parent);
assert_eq!(tree.parent_id(after).unwrap(), parent);
assert_eq!(tree.children_ids(parent).unwrap(), &[before, after]);
tree.remove(before);
println!("Tree: {:#?}", tree);
assert_eq!(tree.size(), 2);
assert_eq!(tree.height(parent), Some(0));
assert_eq!(tree.height(after), Some(1));
assert_eq!(*tree.get(parent).unwrap(), 0);
assert_eq!(tree.get(before), None);
assert_eq!(*tree.get(after).unwrap(), 3);
assert_eq!(tree.parent_id(after).unwrap(), parent);
assert_eq!(tree.children_ids(parent).unwrap(), &[after]);
tree.remove(after);
println!("Tree: {:#?}", tree);
assert_eq!(tree.size(), 1);
assert_eq!(tree.height(parent), Some(0));
assert_eq!(*tree.get(parent).unwrap(), 0);
assert_eq!(tree.get(after), None);
assert_eq!(tree.children_ids(parent).unwrap(), &[]);
}
#[test]
fn shared_view() {
use std::thread;
let mut tree = Tree::new(1);
let parent = tree.root();
let child = tree.create_node(0);
tree.add_child(parent, child);
let shared = SharedView::new(&mut tree);
thread::scope(|s| {
let (mut shared1, mut shared2, mut shared3) =
(shared.clone(), shared.clone(), shared.clone());
s.spawn(move || {
assert_eq!(*shared1.get_mut(parent).unwrap(), 1);
assert_eq!(*shared1.get_mut(child).unwrap(), 0);
});
s.spawn(move || {
assert_eq!(*shared2.get_mut(child).unwrap(), 0);
assert_eq!(*shared2.get_mut(parent).unwrap(), 1);
});
s.spawn(move || {
assert_eq!(*shared3.get_mut(parent).unwrap(), 1);
assert_eq!(*shared3.get_mut(child).unwrap(), 0);
});
});
}
#[test]
fn map() {
#[derive(Debug, PartialEq)]
struct Value {
value: i32,
}
impl Value {
fn new(value: i32) -> Self {
Self { value }
}
}
let mut tree = Tree::new(Value::new(1));
let parent = tree.root();
let child = tree.create_node(Value::new(0));
tree.add_child(parent, child);
let mut mapped = tree.map(|x| &x.value, |x| &mut x.value);
*mapped.get_mut(child).unwrap() = 1;
*mapped.get_mut(parent).unwrap() = 2;
assert_eq!(*tree.get(parent).unwrap(), Value::new(2));
assert_eq!(*tree.get(child).unwrap(), Value::new(1));
}
#[test]
fn traverse_depth_first() {
let mut tree = Tree::new(0);
let parent = tree.root();
let child1 = tree.create_node(1);
tree.add_child(parent, child1);
let grandchild1 = tree.create_node(2);
tree.add_child(child1, grandchild1);
let child2 = tree.create_node(3);
tree.add_child(parent, child2);
let grandchild2 = tree.create_node(4);
tree.add_child(child2, grandchild2);
let mut node_count = 0;
tree.traverse_depth_first(move |node| {
assert_eq!(*node, node_count);
node_count += 1;
});
}
+334 -35
View File
@@ -1,10 +1,8 @@
use crate::{
real_dom::{NodeType, RealDom},
state::State,
RealNodeId,
};
use dioxus_core::{DomEdit, ElementId, Mutations};
use crate::{node::NodeType, real_dom::RealDom, state::State, tree::TreeView, NodeId, RealNodeId};
use dioxus_core::{Mutation, Mutations};
use std::fmt::Debug;
#[derive(Debug)]
pub enum ElementProduced {
/// The iterator produced an element by progressing to the next node in a depth first order.
Progressed(RealNodeId),
@@ -57,7 +55,7 @@ pub struct PersistantElementIter {
impl Default for PersistantElementIter {
fn default() -> Self {
PersistantElementIter {
stack: smallvec::smallvec![(RealNodeId::ElementId(ElementId(0)), NodePosition::AtNode)],
stack: smallvec::smallvec![(NodeId(0), NodePosition::AtNode)],
}
}
}
@@ -74,11 +72,10 @@ impl PersistantElementIter {
let ids_removed: Vec<_> = mutations
.edits
.iter()
.filter_map(|e| {
// nodes within templates will never be removed
if let DomEdit::Remove { root } = e {
let id = rdom.resolve_maybe_id(*root);
Some(id)
.filter_map(|m| {
// nodes within templates will never be removedns
if let Mutation::Remove { id } = m {
Some(rdom.element_to_node_id(*id))
} else {
None
}
@@ -96,27 +93,25 @@ impl PersistantElementIter {
// if a child is removed or inserted before or at the current element, update the child index
for (el_id, child_idx) in self.stack.iter_mut() {
if let NodePosition::InChild(child_idx) = child_idx {
if let NodeType::Element { children, .. } = &rdom[*el_id].node_data.node_type {
if let Some(children) = &rdom.tree.children_ids(*el_id) {
for m in &mutations.edits {
match m {
DomEdit::Remove { root } => {
let id = rdom.resolve_maybe_id(*root);
Mutation::Remove { id } => {
let id = rdom.element_to_node_id(*id);
if children.iter().take(*child_idx + 1).any(|c| *c == id) {
*child_idx -= 1;
}
}
DomEdit::InsertBefore { root, nodes } => {
let id = rdom.resolve_maybe_id(*root);
let n = nodes.len();
Mutation::InsertBefore { id, m } => {
let id = rdom.element_to_node_id(*id);
if children.iter().take(*child_idx + 1).any(|c| *c == id) {
*child_idx += n as usize;
*child_idx += *m;
}
}
DomEdit::InsertAfter { root, nodes } => {
let id = rdom.resolve_maybe_id(*root);
let n = nodes.len();
Mutation::InsertAfter { id, m } => {
let id = rdom.element_to_node_id(*id);
if children.iter().take(*child_idx).any(|c| *c == id) {
*child_idx += n as usize;
*child_idx += *m;
}
}
_ => (),
@@ -131,18 +126,19 @@ impl PersistantElementIter {
/// get the next element
pub fn next<S: State>(&mut self, rdom: &RealDom<S>) -> ElementProduced {
if self.stack.is_empty() {
let id = RealNodeId::ElementId(ElementId(0));
let id = NodeId(0);
let new = (id, NodePosition::AtNode);
self.stack.push(new);
ElementProduced::Looped(id)
} else {
let (last, o_child_idx) = self.stack.last_mut().unwrap();
let (last, old_child_idx) = self.stack.last_mut().unwrap();
let node = &rdom[*last];
match &node.node_data.node_type {
NodeType::Element { children, .. } => {
*o_child_idx = o_child_idx.map(|i| i + 1);
NodeType::Element { .. } => {
let children = rdom.tree.children_ids(*last).unwrap();
*old_child_idx = old_child_idx.map(|i| i + 1);
// if we have children, go to the next child
let child_idx = o_child_idx.get_or_insert(0);
let child_idx = old_child_idx.get_or_insert(0);
if child_idx >= children.len() {
self.pop();
self.next(rdom)
@@ -172,7 +168,8 @@ impl PersistantElementIter {
rdom: &RealDom<S>,
) -> RealNodeId {
match &rdom[new_node].node_data.node_type {
NodeType::Element { children, .. } => {
NodeType::Element { .. } => {
let children = rdom.tree.children_ids(new_node).unwrap();
if children.is_empty() {
new_node
} else {
@@ -184,19 +181,20 @@ impl PersistantElementIter {
}
}
if self.stack.is_empty() {
let new_node = RealNodeId::ElementId(ElementId(0));
let new_node = NodeId(0);
ElementProduced::Looped(push_back(&mut self.stack, new_node, rdom))
} else {
let (last, o_child_idx) = self.stack.last_mut().unwrap();
let (last, old_child_idx) = self.stack.last_mut().unwrap();
let node = &rdom[*last];
match &node.node_data.node_type {
NodeType::Element { children, .. } => {
NodeType::Element { .. } => {
let children = rdom.tree.children_ids(*last).unwrap();
// if we have children, go to the next child
if let NodePosition::InChild(0) = o_child_idx {
if let NodePosition::InChild(0) = old_child_idx {
ElementProduced::Progressed(self.pop())
} else {
*o_child_idx = o_child_idx.map(|i| i - 1);
if let NodePosition::InChild(child_idx) = o_child_idx {
*old_child_idx = old_child_idx.map(|i| i - 1);
if let NodePosition::InChild(child_idx) = old_child_idx {
if *child_idx >= children.len() || children.is_empty() {
self.pop();
self.prev(rdom)
@@ -227,3 +225,304 @@ impl PersistantElementIter {
self.stack.pop().unwrap().0
}
}
#[derive(Default, Clone, Debug)]
struct Empty {}
impl State for Empty {
const PASSES: &'static [crate::AnyPass<crate::node::Node<Self>>] = &[];
const MASKS: &'static [crate::NodeMask] = &[];
}
#[test]
#[allow(unused_variables)]
fn traverse() {
use dioxus::prelude::*;
#[allow(non_snake_case)]
fn Base(cx: Scope) -> Element {
render!(
div{
div{
"hello"
p{
"world"
}
"hello world"
}
}
)
}
let mut vdom = VirtualDom::new(Base);
let mutations = vdom.rebuild();
let mut rdom: RealDom<Empty> = RealDom::new();
let _to_update = rdom.apply_mutations(mutations);
let mut iter = PersistantElementIter::new();
let div_tag = "div".to_string();
assert!(matches!(
&rdom[iter.next(&rdom).id()].node_data.node_type,
NodeType::Element { tag: div_tag, .. }
));
assert!(matches!(
&rdom[iter.next(&rdom).id()].node_data.node_type,
NodeType::Element { tag: div_tag, .. }
));
let text1 = "hello".to_string();
assert!(matches!(
&rdom[iter.next(&rdom).id()].node_data.node_type,
NodeType::Text { text: text1, .. }
));
let p_tag = "p".to_string();
assert!(matches!(
&rdom[iter.next(&rdom).id()].node_data.node_type,
NodeType::Element { tag: p_tag, .. }
));
let text2 = "world".to_string();
assert!(matches!(
&rdom[iter.next(&rdom).id()].node_data.node_type,
NodeType::Text { text: text2, .. }
));
let text3 = "hello world".to_string();
assert!(matches!(
&rdom[iter.next(&rdom).id()].node_data.node_type,
NodeType::Text { text: text3, .. }
));
assert!(matches!(
&rdom[iter.next(&rdom).id()].node_data.node_type,
NodeType::Element { tag: div_tag, .. }
));
assert!(matches!(
&rdom[iter.prev(&rdom).id()].node_data.node_type,
NodeType::Text { text: text3, .. }
));
assert!(matches!(
&rdom[iter.prev(&rdom).id()].node_data.node_type,
NodeType::Text { text: text2, .. }
));
assert!(matches!(
&rdom[iter.prev(&rdom).id()].node_data.node_type,
NodeType::Element { tag: p_tag, .. }
));
assert!(matches!(
&rdom[iter.prev(&rdom).id()].node_data.node_type,
NodeType::Text { text: text1, .. }
));
assert!(matches!(
&rdom[iter.prev(&rdom).id()].node_data.node_type,
NodeType::Element { tag: div_tag, .. }
));
assert!(matches!(
&rdom[iter.prev(&rdom).id()].node_data.node_type,
NodeType::Element { tag: div_tag, .. }
));
assert!(matches!(
&rdom[iter.prev(&rdom).id()].node_data.node_type,
NodeType::Element { tag: div_tag, .. }
));
assert!(matches!(
&rdom[iter.prev(&rdom).id()].node_data.node_type,
NodeType::Text { text: text3, .. }
));
}
#[test]
#[allow(unused_variables)]
fn persist_removes() {
use dioxus::prelude::*;
#[allow(non_snake_case)]
fn Base(cx: Scope) -> Element {
let children = match cx.generation() % 2 {
0 => 3,
1 => 2,
_ => unreachable!(),
};
render!(
div{
(0..children).map(|i|{
rsx!{
p{
key: "{i}",
"{i}"
}
}
})
}
)
}
let mut vdom = VirtualDom::new(Base);
let mut rdom: RealDom<Empty> = RealDom::new();
let build = vdom.rebuild();
let _to_update = rdom.apply_mutations(build);
// this will end on the node that is removed
let mut iter1 = PersistantElementIter::new();
// this will end on the after node that is removed
let mut iter2 = PersistantElementIter::new();
// div
iter1.next(&rdom).id();
iter2.next(&rdom).id();
// p
iter1.next(&rdom).id();
iter2.next(&rdom).id();
// "1"
iter1.next(&rdom).id();
iter2.next(&rdom).id();
// p
iter1.next(&rdom).id();
iter2.next(&rdom).id();
// "2"
iter1.next(&rdom).id();
iter2.next(&rdom).id();
// p
iter2.next(&rdom).id();
// "3"
iter2.next(&rdom).id();
vdom.mark_dirty(ScopeId(0));
let update = vdom.render_immediate();
iter1.prune(&update, &rdom);
iter2.prune(&update, &rdom);
let _to_update = rdom.apply_mutations(update);
let p_tag = "1".to_string();
let idx = iter1.next(&rdom).id();
assert!(matches!(
&rdom[idx].node_data.node_type,
NodeType::Element { tag: p_tag, .. }
));
let text = "2".to_string();
let idx = iter1.next(&rdom).id();
assert!(matches!(
&rdom[idx].node_data.node_type,
NodeType::Text { text, .. }
));
let div_tag = "div".to_string();
let idx = iter2.next(&rdom).id();
assert!(matches!(
&rdom[idx].node_data.node_type,
NodeType::Element { tag: div_tag, .. }
));
}
#[test]
#[allow(unused_variables)]
fn persist_instertions_before() {
use dioxus::prelude::*;
#[allow(non_snake_case)]
fn Base(cx: Scope) -> Element {
let children = match cx.generation() % 2 {
0 => 3,
1 => 2,
_ => unreachable!(),
};
render!(
div{
(0..children).map(|i|{
rsx!{
p{
key: "{i}",
"{i}"
}
}
})
}
)
}
let mut vdom = VirtualDom::new(Base);
let mut rdom: RealDom<Empty> = RealDom::new();
let build = vdom.rebuild();
let _to_update = rdom.apply_mutations(build);
let mut iter = PersistantElementIter::new();
// div
iter.next(&rdom).id();
// p
iter.next(&rdom).id();
// "1"
iter.next(&rdom).id();
// p
iter.next(&rdom).id();
// "2"
iter.next(&rdom).id();
vdom.mark_dirty(ScopeId(0));
let update = vdom.render_immediate();
iter.prune(&update, &rdom);
let _to_update = rdom.apply_mutations(update);
let p_tag = "div".to_string();
let idx = iter.next(&rdom).id();
assert!(matches!(
&rdom[idx].node_data.node_type,
NodeType::Element { tag: p_tag, .. }
));
}
#[test]
#[allow(unused_variables)]
fn persist_instertions_after() {
use dioxus::prelude::*;
#[allow(non_snake_case)]
fn Base(cx: Scope) -> Element {
let children = match cx.generation() % 2 {
0 => 3,
1 => 2,
_ => unreachable!(),
};
render!(
div{
(0..children).map(|i|{
rsx!{
p{
key: "{i}",
"{i}"
}
}
})
}
)
}
let mut vdom = VirtualDom::new(Base);
let mut rdom: RealDom<Empty> = RealDom::new();
let build = vdom.rebuild();
let _to_update = rdom.apply_mutations(build);
let mut iter = PersistantElementIter::new();
// div
iter.next(&rdom).id();
// p
iter.next(&rdom).id();
// "hello"
iter.next(&rdom).id();
// p
iter.next(&rdom).id();
// "world"
iter.next(&rdom).id();
let update = vdom.rebuild();
iter.prune(&update, &rdom);
let _to_update = rdom.apply_mutations(update);
let p_tag = "p".to_string();
let idx = iter.next(&rdom).id();
assert!(matches!(
&rdom[idx].node_data.node_type,
NodeType::Element { tag: p_tag, .. }
));
let text = "hello world".to_string();
let idx = iter.next(&rdom).id();
assert!(matches!(
&rdom[idx].node_data.node_type,
NodeType::Text { text, .. }
));
}