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:
1 parent
b9aad5e8f4
commit
67a6fa9eb8
38 files changed
+3405
-2417
No files matched your search
@@ -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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
@@ -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
@@ -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);
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
@@ -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, .. }
|
||||
));
|
||||
}
|
||||
Reference in new issue
Block a user