1use std::{
27 cmp::Ordering,
28 fmt,
29 sync::{
30 Arc,
31 atomic::{AtomicUsize, Ordering as AtomicOrdering},
32 },
33};
34
35use super::{
36 CHUNK_ROWS, MIN_ROWS_PER_THREAD, Parallelism, Projection, Rebuild, Structure, ViewSpec,
37 VisibleRows,
38 aggregate::{ViewAggregates, flat_totals},
39 compare_sorted, display,
40 display::{AggregateValues, BuiltStructure, DisplayRow},
41 merge, run_on_threads,
42};
43#[cfg(any(test, feature = "test-support"))]
44use super::{ExpandDepth, Expansion};
45use crate::{
46 model::{GridRow, OwnedValue},
47 store::{RowId, StoreSnapshot},
48};
49
50#[derive(Clone, Default)]
53#[non_exhaustive]
54pub enum TreeFilter {
55 #[default]
59 KeepAncestors,
60 HideSubtree,
63 Custom(Arc<dyn TreeFilterRule>),
66}
67
68impl TreeFilter {
69 fn is_shown(&self, row: &TreeFilterContext) -> bool {
71 match self {
72 Self::KeepAncestors => row.passes || row.has_passing_descendant,
73 Self::HideSubtree => row.passes && row.ancestors_pass,
74 Self::Custom(rule) => rule.is_shown(row),
75 }
76 }
77}
78
79impl fmt::Debug for TreeFilter {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 Self::KeepAncestors => f.write_str("KeepAncestors"),
83 Self::HideSubtree => f.write_str("HideSubtree"),
84 Self::Custom(_) => f.write_str("Custom(..)"),
85 }
86 }
87}
88
89pub trait TreeFilterRule: Send + Sync + 'static {
93 fn is_shown(&self, row: &TreeFilterContext) -> bool;
96}
97
98#[derive(Clone, Copy, Debug)]
100pub struct TreeFilterContext {
101 passes: bool,
102 has_passing_descendant: bool,
103 ancestors_pass: bool,
104 has_passing_ancestor: bool,
105}
106
107impl TreeFilterContext {
108 pub(crate) fn new(
111 passes: bool,
112 has_passing_descendant: bool,
113 ancestors_pass: bool,
114 has_passing_ancestor: bool,
115 ) -> Self {
116 Self {
117 passes,
118 has_passing_descendant,
119 ancestors_pass,
120 has_passing_ancestor,
121 }
122 }
123
124 pub fn passes(&self) -> bool {
126 self.passes
127 }
128
129 pub fn has_passing_descendant(&self) -> bool {
131 self.has_passing_descendant
132 }
133
134 pub fn ancestors_pass(&self) -> bool {
136 self.ancestors_pass
137 }
138
139 pub fn has_passing_ancestor(&self) -> bool {
141 self.has_passing_ancestor
142 }
143}
144
145#[derive(Clone, Default)]
153#[non_exhaustive]
154pub enum TreeTotals {
155 #[default]
157 Leaves,
158 AllRows,
160 Custom(Arc<dyn TreeTotalsRule>),
162}
163
164impl TreeTotals {
165 fn is_counted(&self, row: &TreeTotalsContext) -> bool {
167 match self {
168 Self::Leaves => !row.is_branch,
169 Self::AllRows => true,
170 Self::Custom(rule) => rule.is_counted(row),
171 }
172 }
173}
174
175impl fmt::Debug for TreeTotals {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 match self {
178 Self::Leaves => f.write_str("Leaves"),
179 Self::AllRows => f.write_str("AllRows"),
180 Self::Custom(_) => f.write_str("Custom(..)"),
181 }
182 }
183}
184
185pub trait TreeTotalsRule: Send + Sync + 'static {
188 fn is_counted(&self, row: &TreeTotalsContext) -> bool;
190}
191
192#[derive(Clone, Copy, Debug)]
194pub struct TreeTotalsContext {
195 depth: u16,
196 is_branch: bool,
197 passes: bool,
198}
199
200impl TreeTotalsContext {
201 pub(crate) fn new(depth: u16, is_branch: bool, passes: bool) -> Self {
204 Self {
205 depth,
206 is_branch,
207 passes,
208 }
209 }
210
211 pub fn depth(&self) -> u16 {
213 self.depth
214 }
215
216 pub fn is_branch(&self) -> bool {
218 self.is_branch
219 }
220
221 pub fn passes(&self) -> bool {
223 self.passes
224 }
225}
226
227pub struct TreeSpec<R: GridRow> {
229 column: Option<R::Column>,
230 filter: TreeFilter,
231 totals: TreeTotals,
232}
233
234impl<R: GridRow> Clone for TreeSpec<R> {
235 fn clone(&self) -> Self {
236 Self {
237 column: self.column,
238 filter: self.filter.clone(),
239 totals: self.totals.clone(),
240 }
241 }
242}
243
244impl<R: GridRow> Default for TreeSpec<R> {
245 fn default() -> Self {
246 Self {
247 column: None,
248 filter: TreeFilter::default(),
249 totals: TreeTotals::default(),
250 }
251 }
252}
253
254impl<R: GridRow> fmt::Debug for TreeSpec<R> {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 f.debug_struct("TreeSpec")
257 .field("column", &self.column)
258 .field("filter", &self.filter)
259 .field("totals", &self.totals)
260 .finish()
261 }
262}
263
264impl<R: GridRow> TreeSpec<R> {
265 pub fn new() -> Self {
268 Self::default()
269 }
270
271 pub fn with_column(mut self, column: R::Column) -> Self {
273 self.column = Some(column);
274 self
275 }
276
277 pub fn column(&self) -> Option<R::Column> {
279 self.column
280 }
281
282 pub fn with_filter(mut self, filter: TreeFilter) -> Self {
285 self.filter = filter;
286 self
287 }
288
289 pub fn filter(&self) -> &TreeFilter {
291 &self.filter
292 }
293
294 pub fn with_totals(mut self, totals: TreeTotals) -> Self {
296 self.totals = totals;
297 self
298 }
299
300 pub fn totals(&self) -> &TreeTotals {
302 &self.totals
303 }
304}
305
306impl<R: GridRow> ViewSpec<R> {
307 pub fn with_tree(mut self, tree: TreeSpec<R>) -> Self {
316 self.structure = Structure::Tree(tree);
317 self
318 }
319
320 pub fn tree(&self) -> Option<&TreeSpec<R>> {
322 match self.structure() {
323 Structure::Tree(tree) => Some(tree),
324 Structure::Flat | Structure::Groups(_) => None,
325 }
326 }
327}
328
329#[cfg(any(test, feature = "test-support"))]
330impl<R: GridRow> ViewSpec<R> {
331 #[doc(hidden)]
335 pub fn with_tree_expansion(
336 self,
337 depth: ExpandDepth,
338 toggled: impl IntoIterator<Item = R::Key>,
339 ) -> Self {
340 let expansion = toggled
341 .into_iter()
342 .fold(Expansion::new(depth), |expansion, key| {
343 let expanded = expansion.is_row_expanded(&key, 0);
345 expansion.with_row(key, !expanded, 0)
346 });
347 self.with_expansion(expansion)
348 }
349}
350
351#[derive(Clone, Copy, Debug, PartialEq, Eq)]
353pub struct Branch {
354 children: u32,
355 descendants: u32,
356 expanded: bool,
357}
358
359impl Branch {
360 pub fn child_count(&self) -> usize {
362 self.children as usize
363 }
364
365 pub fn descendant_count(&self) -> usize {
368 self.descendants as usize
369 }
370
371 pub fn is_expanded(&self) -> bool {
374 self.expanded
375 }
376}
377
378const NONE: u32 = u32::MAX;
380
381#[derive(Clone, Copy, Debug)]
383struct TreeNode {
384 descendants: u32,
386 children: u32,
388 depth: u16,
390 expanded: bool,
392 passes: bool,
394}
395
396#[derive(Clone, Debug, Default)]
398pub(crate) struct TreeData {
399 nodes: Vec<TreeNode>,
401 cycles: usize,
403 keeps_ancestors: bool,
407}
408
409impl TreeData {
410 fn branch(&self, order_ix: usize) -> Option<Branch> {
413 let node = self.nodes.get(order_ix)?;
414 (node.children > 0).then_some(Branch {
415 children: node.children,
416 descendants: node.descendants,
417 expanded: node.expanded,
418 })
419 }
420
421 pub(crate) fn depth(&self, order_ix: usize) -> u16 {
424 self.nodes.get(order_ix).map_or(0, |node| node.depth)
425 }
426
427 fn counted<'a, R: GridRow>(
431 &'a self,
432 rule: &'a TreeTotals,
433 live: &'a [Option<&'a R>],
434 ) -> impl Iterator<Item = &'a R> + Clone + 'a {
435 let mut has_live_child = vec![false; self.nodes.len()];
437 let mut ancestors: Vec<usize> = Vec::new();
438 for (order_ix, node) in self.nodes.iter().enumerate() {
439 ancestors.truncate(usize::from(node.depth));
440 if live[order_ix].is_some()
441 && let Some(&parent) = ancestors.last()
442 {
443 has_live_child[parent] = true;
444 }
445 ancestors.push(order_ix);
446 }
447 self.nodes.iter().zip(live).zip(has_live_child).filter_map(
448 move |((node, row), is_branch)| {
449 let row = (*row)?;
450 rule.is_counted(&TreeTotalsContext::new(node.depth, is_branch, node.passes))
451 .then_some(row)
452 },
453 )
454 }
455
456 fn totals<'a, R: GridRow>(
459 &self,
460 aggregates: &ViewAggregates<'_, R>,
461 rule: &TreeTotals,
462 rows: &[RowId],
463 row: impl Fn(RowId) -> Option<&'a R>,
464 ) -> (Vec<OwnedValue>, usize) {
465 let live: Vec<Option<&'a R>> = rows.iter().map(|&id| row(id)).collect();
466 let count = live.iter().flatten().count();
467 if aggregates.is_empty() {
468 return (Vec::new(), count);
469 }
470 let (totals, _) = flat_totals(aggregates, self.counted(rule, &live));
471 (totals, count)
472 }
473}
474
475impl Projection {
476 pub fn branch(&self, id: RowId) -> Option<Branch> {
479 self.tree_data()?.branch(self.order_ix(id)?)
480 }
481
482 pub fn tree_cycles(&self) -> usize {
486 self.tree_data().map_or(0, |tree| tree.cycles)
487 }
488}
489
490static CYCLE_ROWS: AtomicUsize = AtomicUsize::new(0);
495
496fn is_new_cycle_count(last: &AtomicUsize, cycles: usize) -> bool {
499 last.swap(cycles, AtomicOrdering::Relaxed) != cycles && cycles > 0
500}
501
502#[derive(Clone, Copy, Debug, Default)]
504struct Link {
505 parent: u32,
508 passes: bool,
510 shown: bool,
512}
513
514#[derive(Clone, Copy, Debug)]
516struct Lineage {
517 has_passing_descendant: bool,
518 ancestors_pass: bool,
519 has_passing_ancestor: bool,
520 nearest_shown: u32,
522}
523
524struct Children {
527 starts: Vec<u32>,
528 items: Vec<u32>,
529}
530
531impl Children {
532 fn new(
535 nodes: usize,
536 order: impl DoubleEndedIterator<Item = u32> + Clone,
537 parent: impl Fn(u32) -> u32,
538 ) -> Self {
539 let mut starts = vec![0u32; nodes + 1];
540 let mut count = 0;
541 for node in order.clone() {
542 let parent = parent(node);
543 if parent != NONE {
544 starts[parent as usize] += 1;
545 count += 1;
546 }
547 }
548 let mut end = 0;
550 for start in &mut starts {
551 end += *start;
552 *start = end;
553 }
554 let mut items = vec![0u32; count];
556 for node in order.rev() {
557 let parent = parent(node);
558 if parent != NONE {
559 let start = &mut starts[parent as usize];
560 *start -= 1;
561 items[*start as usize] = node;
562 }
563 }
564 Self { starts, items }
565 }
566
567 fn range(&self, node: u32) -> (u32, u32) {
569 let node = node as usize;
570 (self.starts[node], self.starts[node + 1])
571 }
572}
573
574fn parallelism(rows: usize, budget: usize) -> Parallelism {
577 Parallelism {
578 threads: budget.min(rows / MIN_ROWS_PER_THREAD).max(1),
579 chunk_rows: CHUNK_ROWS,
580 }
581}
582
583pub(super) fn rebuild<R: GridRow>(
585 engine: &Rebuild,
586 snapshot: &StoreSnapshot<R>,
587 spec: &ViewSpec<R>,
588 tree: &TreeSpec<R>,
589) -> Projection {
590 let parallelism = parallelism(snapshot.len(), engine.max_threads());
591 let projection = build(snapshot, spec, tree, parallelism);
592 let cycles = projection.tree_cycles();
593 if is_new_cycle_count(&CYCLE_ROWS, cycles) {
594 log::warn!(
595 "gpui-kit-grid: the parents of {cycles} tree rows form cycles; those rows are shown as roots"
596 );
597 }
598 projection
599}
600
601fn build<R: GridRow>(
603 snapshot: &StoreSnapshot<R>,
604 spec: &ViewSpec<R>,
605 tree: &TreeSpec<R>,
606 parallelism: Parallelism,
607) -> Projection {
608 let mut rows = Vec::with_capacity(snapshot.len());
611 rows.extend(snapshot.iter());
612 let mut places = vec![NONE; snapshot.capacity()];
613 for (place, (id, _)) in rows.iter().enumerate() {
614 places[id.ix()] = place as u32;
615 }
616
617 let mut links = map_rows(
620 &rows,
621 parallelism,
622 || VisibleRows::new(spec),
623 |visible, place, row| Link {
624 parent: row
625 .parent_key()
626 .and_then(|key| snapshot.row_id(key))
627 .and_then(|id| places.get(id.ix()).copied())
628 .filter(|&parent| parent != place as u32)
629 .unwrap_or(NONE),
630 passes: visible.contains(row),
631 shown: true,
632 },
633 );
634 let cycles = break_cycles(&mut links);
635 let mut shown = if spec.filter.is_some() || spec.quick_filter.is_some() {
636 apply_filter(&mut links, tree.filter())
637 } else {
638 links.len()
639 };
640 let fading_seqs = add_fading(snapshot, spec, &places, &mut rows, &mut links);
641 shown += fading_seqs.len();
642 drop(places);
643
644 let sorted = sort_shown(
646 snapshot,
647 spec,
648 &rows,
649 &links,
650 &fading_seqs,
651 shown,
652 parallelism,
653 );
654 let children = Children::new(links.len(), sorted.iter().copied(), |place| {
655 links[place as usize].parent
656 });
657
658 let expansion = spec.expansion();
660 let mut order = Vec::with_capacity(shown);
661 let mut nodes = Vec::with_capacity(shown);
662 let mut stack: Vec<(u32, u32, u32)> = Vec::with_capacity(shown);
665 for &root in &sorted {
666 if links[root as usize].parent != NONE {
667 continue;
668 }
669 let mut next = Some(root);
670 loop {
671 if let Some(place) = next.take() {
672 let (id, row) = rows[place as usize];
673 let (start, end) = children.range(place);
674 let depth = stack.len().min(usize::from(u16::MAX)) as u16;
675 stack.push((order.len() as u32, start, end));
676 order.push(id);
677 nodes.push(TreeNode {
678 descendants: 0,
679 children: end - start,
680 depth,
681 expanded: end > start && expansion.is_row_expanded(row.key(), depth),
682 passes: links[place as usize].passes,
683 });
684 }
685 let Some(top) = stack.last_mut() else {
686 break;
687 };
688 if top.1 < top.2 {
689 next = Some(children.items[top.1 as usize]);
690 top.1 += 1;
691 } else {
692 let order_ix = top.0 as usize;
693 stack.pop();
694 nodes[order_ix].descendants = (order.len() - order_ix - 1) as u32;
695 }
696 }
697 }
698 debug_assert_eq!(order.len(), shown, "every shown row hangs from a root");
699
700 let data = TreeData {
701 nodes,
702 cycles,
703 keeps_ancestors: matches!(tree.filter(), TreeFilter::KeepAncestors),
704 };
705 let aggregates = ViewAggregates::new(spec);
706 let totals = if aggregates.is_empty() {
707 AggregateValues::default()
708 } else {
709 let (totals, _) = data.totals(&aggregates, tree.totals(), &order, |id| {
710 snapshot.row(id).map(|row| &**row)
711 });
712 AggregateValues::new(aggregates.columns(), totals)
713 };
714 let lines = lay_out(&order, &data.nodes);
715 Projection::with_lines(snapshot, order, lines, BuiltStructure::Tree(data))
716 .with_row_count(shown - fading_seqs.len())
717 .with_totals(totals)
718}
719
720fn add_fading<'a, R: GridRow>(
726 snapshot: &'a StoreSnapshot<R>,
727 spec: &ViewSpec<R>,
728 places: &[u32],
729 rows: &mut Vec<(RowId, &'a Arc<R>)>,
730 links: &mut Vec<Link>,
731) -> Vec<u64> {
732 let mut fading = snapshot.fading_rows().peekable();
733 if fading.peek().is_none() {
734 return Vec::new();
735 }
736 let mut visible = VisibleRows::new(spec);
737 let mut seqs = Vec::new();
738 for (id, row, _, seq) in fading {
739 if !visible.contains(row) {
740 continue;
741 }
742 let parent = match row.parent_key() {
743 Some(key) if key != row.key() => {
744 match snapshot
745 .row_id(key)
746 .and_then(|parent| places.get(parent.ix()).copied())
747 {
748 Some(parent) if parent != NONE && links[parent as usize].shown => parent,
749 _ => continue,
751 }
752 }
753 _ => NONE,
754 };
755 rows.push((id, row));
756 links.push(Link {
757 parent,
758 passes: true,
759 shown: true,
760 });
761 seqs.push(seq);
762 }
763 seqs
764}
765
766fn map_rows<'a, R: GridRow, S, T: Copy + Default + Send>(
770 rows: &[(RowId, &'a Arc<R>)],
771 parallelism: Parallelism,
772 state: impl Fn() -> S + Sync,
773 map: impl Fn(&mut S, usize, &'a Arc<R>) -> T + Sync,
774) -> Vec<T> {
775 if parallelism.threads <= 1 {
776 let mut state = state();
777 return rows
778 .iter()
779 .enumerate()
780 .map(|(place, &(_, row))| map(&mut state, place, row))
781 .collect();
782 }
783
784 let chunk_rows = parallelism.chunk_rows;
785 let next_chunk = AtomicUsize::new(0);
786 let share = || {
788 let mut state = state();
789 let mut starts = Vec::new();
790 let mut values = Vec::new();
791 loop {
792 let start = next_chunk.fetch_add(chunk_rows, AtomicOrdering::Relaxed);
793 if start >= rows.len() {
794 break;
795 }
796 let chunk = &rows[start..rows.len().min(start + chunk_rows)];
797 starts.push(start);
798 values.extend(
799 chunk
800 .iter()
801 .enumerate()
802 .map(|(offset, &(_, row))| map(&mut state, start + offset, row)),
803 );
804 }
805 (starts, values)
806 };
807 let mut mapped = vec![T::default(); rows.len()];
808 for (starts, values) in run_on_threads(&vec![&share; parallelism.threads]) {
809 let mut values = values.as_slice();
810 for start in starts {
811 let (chunk, rest) = values.split_at(chunk_rows.min(rows.len() - start));
812 mapped[start..start + chunk.len()].copy_from_slice(chunk);
813 values = rest;
814 }
815 }
816 mapped
817}
818
819fn break_cycles(links: &mut [Link]) -> usize {
824 const UNVISITED: u8 = 0;
825 const ON_PATH: u8 = 1;
826 const DONE: u8 = 2;
827
828 let mut state = vec![UNVISITED; links.len()];
829 let mut path: Vec<u32> = Vec::with_capacity(links.len());
830 let mut broken = 0;
831 for start in 0..links.len() as u32 {
832 let mut place = start;
833 while state[place as usize] == UNVISITED {
834 state[place as usize] = ON_PATH;
835 path.push(place);
836 let parent = links[place as usize].parent;
837 if parent == NONE {
838 break;
839 }
840 if state[parent as usize] == ON_PATH {
841 for &member in path.iter().rev() {
843 links[member as usize].parent = NONE;
844 broken += 1;
845 if member == parent {
846 break;
847 }
848 }
849 break;
850 }
851 place = parent;
852 }
853 for place in path.drain(..) {
854 state[place as usize] = DONE;
855 }
856 }
857 broken
858}
859
860fn apply_filter(links: &mut [Link], filter: &TreeFilter) -> usize {
863 let rows = links.len();
864 let all = 0..rows as u32;
865 let children = Children::new(rows, all.clone(), |place| links[place as usize].parent);
866
867 let mut preorder = Vec::with_capacity(rows);
869 let mut stack = Vec::with_capacity(rows);
870 for root in all.filter(|&place| links[place as usize].parent == NONE) {
871 stack.push(root);
872 while let Some(place) = stack.pop() {
873 preorder.push(place);
874 let (start, end) = children.range(place);
875 stack.extend(children.items[start as usize..end as usize].iter().rev());
876 }
877 }
878 debug_assert_eq!(preorder.len(), rows, "cycles are broken");
879
880 let mut lineage = vec![
881 Lineage {
882 has_passing_descendant: false,
883 ancestors_pass: true,
884 has_passing_ancestor: false,
885 nearest_shown: NONE,
886 };
887 rows
888 ];
889 for &place in preorder.iter().rev() {
891 let link = links[place as usize];
892 if link.parent != NONE && (link.passes || lineage[place as usize].has_passing_descendant) {
893 lineage[link.parent as usize].has_passing_descendant = true;
894 }
895 }
896 let mut shown = 0;
899 for &place in &preorder {
900 let link = links[place as usize];
901 let (ancestors_pass, has_passing_ancestor, parent_shown) = match link.parent {
902 NONE => (true, false, NONE),
903 parent => {
904 let above = lineage[parent as usize];
905 let passes = links[parent as usize].passes;
906 (
907 above.ancestors_pass && passes,
908 above.has_passing_ancestor || passes,
909 above.nearest_shown,
910 )
911 }
912 };
913 let row = &mut lineage[place as usize];
914 row.ancestors_pass = ancestors_pass;
915 row.has_passing_ancestor = has_passing_ancestor;
916 let is_shown = filter.is_shown(&TreeFilterContext::new(
917 link.passes,
918 row.has_passing_descendant,
919 ancestors_pass,
920 has_passing_ancestor,
921 ));
922 row.nearest_shown = if is_shown { place } else { parent_shown };
923 shown += usize::from(is_shown);
924 let link = &mut links[place as usize];
925 link.shown = is_shown;
926 link.parent = parent_shown;
927 }
928 shown
929}
930
931fn sort_shown<R: GridRow>(
935 snapshot: &StoreSnapshot<R>,
936 spec: &ViewSpec<R>,
937 rows: &[(RowId, &Arc<R>)],
938 links: &[Link],
939 fading_seqs: &[u64],
940 shown: usize,
941 parallelism: Parallelism,
942) -> Vec<u32> {
943 let live = rows.len() - fading_seqs.len();
944 let seq = |place: u32, id: RowId| match (place as usize).checked_sub(live) {
945 Some(fading) => fading_seqs[fading],
946 None => snapshot.seq(id),
947 };
948 match spec.sort.split_first() {
949 None => sorted_places(
951 rows,
952 links,
953 shown,
954 parallelism,
955 |place, id, _| (seq(place, id), place),
956 |a, b| a.0.cmp(&b.0),
957 |&(_, place)| place,
958 ),
959 Some((&(first, first_direction), rest)) => sorted_places(
960 rows,
961 links,
962 shown,
963 parallelism,
964 |place, id, row| ((row.value(first), seq(place, id), id, row), place),
965 move |a, b| compare_sorted(&a.0, &b.0, first_direction, rest),
966 |&(_, place)| place,
967 ),
968 }
969}
970
971fn sorted_places<'a, R: GridRow, E: Copy + Send + Sync>(
976 rows: &[(RowId, &'a Arc<R>)],
977 links: &[Link],
978 shown: usize,
979 parallelism: Parallelism,
980 entry: impl Fn(u32, RowId, &'a Arc<R>) -> E + Sync,
981 compare: impl Fn(&E, &E) -> Ordering + Sync,
982 place: impl Fn(&E) -> u32,
983) -> Vec<u32> {
984 let extend = |entries: &mut Vec<E>, start: usize, end: usize| {
985 entries.extend(
986 (start..end)
987 .filter(|&ix| links[ix].shown)
988 .map(|ix| entry(ix as u32, rows[ix].0, rows[ix].1)),
989 );
990 };
991 if parallelism.threads <= 1 {
992 let mut entries = Vec::with_capacity(shown);
993 extend(&mut entries, 0, rows.len());
994 entries.sort_unstable_by(&compare);
995 return entries.iter().map(place).collect();
996 }
997
998 let next_chunk = AtomicUsize::new(0);
999 let sorted_run = || {
1000 let mut entries = Vec::new();
1001 loop {
1002 let start = next_chunk.fetch_add(parallelism.chunk_rows, AtomicOrdering::Relaxed);
1003 if start >= rows.len() {
1004 break;
1005 }
1006 extend(
1007 &mut entries,
1008 start,
1009 rows.len().min(start + parallelism.chunk_rows),
1010 );
1011 }
1012 entries.sort_unstable_by(&compare);
1013 entries
1014 };
1015 let mut runs = run_on_threads(&vec![&sorted_run; parallelism.threads]);
1016 runs.retain(|run| !run.is_empty());
1017
1018 while runs.len() > 2 {
1021 let merges: Vec<_> = runs
1022 .chunks(2)
1023 .map(|pair| {
1024 let compare = &compare;
1025 move || match pair {
1026 [first, second] => {
1027 let mut merged = Vec::with_capacity(first.len() + second.len());
1028 merge(first, second, compare, |entry| *entry, &mut merged);
1029 merged
1030 }
1031 _ => pair.concat(),
1032 }
1033 })
1034 .collect();
1035 runs = run_on_threads(&merges);
1036 }
1037 let mut places = Vec::with_capacity(shown);
1038 match runs.as_slice() {
1039 [first, second] => merge(first, second, &compare, place, &mut places),
1040 runs => places.extend(runs.iter().flatten().map(place)),
1041 }
1042 places
1043}
1044
1045fn lay_out(rows: &[RowId], nodes: &[TreeNode]) -> Vec<DisplayRow> {
1047 let mut lines = Vec::with_capacity(rows.len());
1048 let mut order_ix = 0;
1049 while let (Some(&row), Some(node)) = (rows.get(order_ix), nodes.get(order_ix)) {
1050 lines.push(DisplayRow::Data {
1051 row,
1052 depth: node.depth,
1053 });
1054 order_ix += 1;
1055 if node.children > 0 && !node.expanded {
1056 order_ix += node.descendants as usize;
1057 }
1058 }
1059 lines
1060}
1061
1062pub(super) fn compact<R: GridRow>(
1079 previous: &Projection,
1080 snapshot: &StoreSnapshot<R>,
1081) -> Projection {
1082 let Some(tree) = previous.tree_data() else {
1083 return display::compact(previous, snapshot);
1084 };
1085 let count = previous.rows.len();
1086 let live: Vec<bool> = previous
1087 .rows
1088 .iter()
1089 .map(|&id| snapshot.row(id).is_some())
1090 .collect();
1091 let mut kept: Vec<bool> = previous
1093 .rows
1094 .iter()
1095 .zip(&live)
1096 .zip(&tree.nodes)
1097 .map(|((&id, &live), node)| live || (node.passes && snapshot.fading_row(id).is_some()))
1098 .collect();
1099
1100 let mut ancestors: Vec<u32> = Vec::with_capacity(count);
1104 let mut parents = vec![NONE; count];
1105 let mut roots = vec![NONE; count];
1106 let mut depths = vec![0u16; count];
1107 let mut fading = Vec::new();
1108 for order_ix in 0..count {
1109 ancestors.truncate(usize::from(tree.nodes[order_ix].depth));
1110 let old_parent = ancestors.last().copied().unwrap_or(NONE);
1111 ancestors.push(order_ix as u32);
1112 if !kept[order_ix] {
1113 continue;
1114 }
1115 let parent_stays =
1116 old_parent != NONE && kept[old_parent as usize] && live[old_parent as usize];
1117 if !live[order_ix] {
1118 fading.push(order_ix);
1119 if !is_fading_place(snapshot, previous, order_ix, old_parent, parent_stays) {
1120 kept[order_ix] = false;
1121 continue;
1122 }
1123 }
1124 if parent_stays {
1125 let parent = old_parent as usize;
1126 parents[order_ix] = old_parent;
1127 roots[order_ix] = roots[parent];
1128 depths[order_ix] = depths[parent].saturating_add(1);
1129 } else {
1130 roots[order_ix] = order_ix as u32;
1131 }
1132 }
1133
1134 let mut children = vec![0u32; count];
1138 let mut descendants = vec![0u32; count];
1139 let mut has_passing_descendant = vec![false; count];
1140 for order_ix in (0..count).rev() {
1141 if !kept[order_ix] {
1142 continue;
1143 }
1144 let passes = tree.nodes[order_ix].passes;
1145 if tree.keeps_ancestors && !passes && !has_passing_descendant[order_ix] {
1146 kept[order_ix] = false;
1147 continue;
1148 }
1149 let parent = parents[order_ix];
1150 if parent != NONE {
1151 let parent = parent as usize;
1152 children[parent] += 1;
1153 descendants[parent] += 1 + descendants[order_ix];
1154 has_passing_descendant[parent] |=
1155 live[order_ix] && (passes || has_passing_descendant[order_ix]);
1156 }
1157 }
1158 for order_ix in fading {
1161 let parent = parents[order_ix];
1162 if parent != NONE && !kept[parent as usize] {
1163 kept[order_ix] = false;
1164 }
1165 }
1166
1167 let mut starts = vec![0u32; count];
1170 let mut next = 0;
1171 for order_ix in 0..count {
1172 if kept[order_ix] && roots[order_ix] == order_ix as u32 {
1173 starts[order_ix] = next;
1174 next += 1 + descendants[order_ix];
1175 }
1176 }
1177 let mut placed = vec![NONE; next as usize];
1178 let mut row_count = 0;
1179 for order_ix in (0..count).filter(|&order_ix| kept[order_ix]) {
1180 let start = &mut starts[roots[order_ix] as usize];
1181 placed[*start as usize] = order_ix as u32;
1182 *start += 1;
1183 row_count += usize::from(live[order_ix]);
1184 }
1185 let rows: Vec<RowId> = placed
1186 .iter()
1187 .map(|&order_ix| previous.rows[order_ix as usize])
1188 .collect();
1189 let nodes: Vec<TreeNode> = placed
1190 .iter()
1191 .map(|&order_ix| {
1192 let order_ix = order_ix as usize;
1193 let old = tree.nodes[order_ix];
1194 TreeNode {
1195 descendants: descendants[order_ix],
1196 children: children[order_ix],
1197 depth: depths[order_ix],
1198 expanded: old.expanded,
1199 passes: old.passes,
1200 }
1201 })
1202 .collect();
1203
1204 let lines = lay_out(&rows, &nodes);
1205 Projection::with_lines(
1206 snapshot,
1207 rows,
1208 lines,
1209 BuiltStructure::Tree(TreeData {
1210 nodes,
1211 cycles: tree.cycles,
1212 keeps_ancestors: tree.keeps_ancestors,
1213 }),
1214 )
1215 .with_row_count(row_count)
1216 .with_totals(previous.totals.clone())
1217}
1218
1219fn is_fading_place<R: GridRow>(
1224 snapshot: &StoreSnapshot<R>,
1225 previous: &Projection,
1226 order_ix: usize,
1227 old_parent: u32,
1228 parent_stays: bool,
1229) -> bool {
1230 let Some((row, _)) = snapshot.fading_row(previous.rows[order_ix]) else {
1231 return false;
1232 };
1233 match row.parent_key() {
1234 Some(key) if key != row.key() => {
1235 parent_stays && snapshot.row_id(key) == previous.rows.get(old_parent as usize).copied()
1236 }
1237 _ => old_parent == NONE,
1238 }
1239}
1240
1241pub(super) fn refresh<R: GridRow>(
1244 previous: &Projection,
1245 snapshot: &StoreSnapshot<R>,
1246 spec: &ViewSpec<R>,
1247) -> Projection {
1248 let Some(tree) = previous.tree_data() else {
1249 return previous.duplicate().with_store_version(snapshot.version());
1250 };
1251 let aggregates = ViewAggregates::new(spec);
1252 let default = TreeTotals::default();
1253 let rule = spec.tree().map_or(&default, TreeSpec::totals);
1254 let (totals, live) = tree.totals(&aggregates, rule, &previous.rows, |id| {
1255 snapshot.row(id).map(|row| &**row)
1256 });
1257 previous
1258 .duplicate()
1259 .with_store_version(snapshot.version())
1260 .with_row_count(live)
1261 .with_totals(AggregateValues::new(aggregates.columns(), totals))
1262}
1263
1264pub(super) fn relayout<R: GridRow>(
1269 previous: &Projection,
1270 snapshot: &StoreSnapshot<R>,
1271 spec: &ViewSpec<R>,
1272) -> Projection {
1273 let Some(tree) = previous.tree_data() else {
1274 return previous.duplicate();
1275 };
1276 let expansion = spec.expansion();
1277 let mut nodes = tree.nodes.clone();
1278 for (node, &id) in nodes.iter_mut().zip(&previous.rows) {
1279 if node.children > 0
1280 && let Some(row) = snapshot.row(id)
1281 {
1282 node.expanded = expansion.is_row_expanded(row.key(), node.depth);
1283 }
1284 }
1285 let lines = lay_out(&previous.rows, &nodes);
1286 Projection::with_lines(
1287 snapshot,
1288 previous.rows.clone(),
1289 lines,
1290 BuiltStructure::Tree(TreeData {
1291 nodes,
1292 cycles: tree.cycles,
1293 keeps_ancestors: tree.keeps_ancestors,
1294 }),
1295 )
1296 .with_store_version(previous.store_version)
1297 .with_row_count(previous.row_count)
1298 .with_totals(previous.totals.clone())
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303 use std::{panic, time::Instant};
1304
1305 use super::{super::ProjectionEngine, super::SortDirection, *};
1306 use crate::{
1307 highlight::HighlightPolicy,
1308 model::{CellValue, ColumnIx, ColumnSet, GridColumns},
1309 quick_filter::{ContainsAllTerms, QuickFilter},
1310 store::{Ingest, Op},
1311 };
1312
1313 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1314 enum Column {
1315 Group,
1316 Price,
1317 Name,
1318 }
1319
1320 impl GridColumns for Column {
1321 fn all() -> &'static [Self] {
1322 &[Self::Group, Self::Price, Self::Name]
1323 }
1324
1325 fn id(self) -> &'static str {
1326 match self {
1327 Self::Group => "group",
1328 Self::Price => "price",
1329 Self::Name => "name",
1330 }
1331 }
1332
1333 fn ix(self) -> ColumnIx {
1334 ColumnIx::new(self as u16)
1335 }
1336 }
1337
1338 struct Node {
1340 id: u32,
1341 parent: Option<u32>,
1342 group: i64,
1343 price: f64,
1344 name: &'static str,
1345 }
1346
1347 impl Node {
1348 fn random(rng: &mut fastrand::Rng, id: u32, count: u32) -> Self {
1351 let parent = match rng.u8(0..20) {
1352 0..=2 => None,
1353 3 => Some(id),
1354 4 => Some(count + rng.u32(0..10)),
1355 5 | 6 => Some(rng.u32(0..count.max(1))),
1356 _ => Some(rng.u32(0..id.max(1))),
1357 };
1358 Self {
1359 id,
1360 parent,
1361 group: rng.i64(0..4),
1362 price: [0.5, 1.0, f64::NAN, -0.0][rng.usize(..4)],
1363 name: ["", "a", "ab", "b", "я"][rng.usize(..5)],
1364 }
1365 }
1366 }
1367
1368 impl GridRow for Node {
1369 type Key = u32;
1370 type Column = Column;
1371
1372 fn key(&self) -> &u32 {
1373 &self.id
1374 }
1375
1376 fn value(&self, column: Column) -> CellValue<'_> {
1377 match column {
1378 Column::Group => CellValue::Int(self.group),
1379 Column::Price => CellValue::Float(self.price),
1380 Column::Name => CellValue::Text(self.name),
1381 }
1382 }
1383
1384 fn parent_key(&self) -> Option<&u32> {
1385 assert_ne!(self.id, u32::MAX, "parent_key failed");
1386 self.parent.as_ref()
1387 }
1388 }
1389
1390 fn random_snapshot(
1394 rng: &mut fastrand::Rng,
1395 count: u32,
1396 fade: bool,
1397 ) -> Arc<StoreSnapshot<Node>> {
1398 let epoch = Instant::now();
1399 let policy = if fade {
1400 HighlightPolicy::default().with_removal_fade(std::time::Duration::from_secs(3_600))
1401 } else {
1402 HighlightPolicy::default()
1403 };
1404 let mut ingest = Ingest::<Node>::new(policy, epoch);
1405 let mut ids: Vec<u32> = (0..count).collect();
1406 rng.shuffle(&mut ids);
1407 ingest.apply(
1408 ids.iter()
1409 .map(|&id| Op::UpsertSilent(Arc::new(Node::random(rng, id, count)))),
1410 epoch,
1411 );
1412 ingest.apply(
1413 (0..count / 4).map(|_| Op::Remove(rng.u32(0..count.max(1)))),
1414 epoch,
1415 );
1416 ingest.apply(
1417 (0..count / 4).map(|_| {
1418 let id = rng.u32(0..count.max(1));
1419 Op::UpsertSilent(Arc::new(Node::random(rng, id, count)))
1420 }),
1421 epoch,
1422 );
1423 ingest.snapshot()
1424 }
1425
1426 struct ShowsSubtree;
1428
1429 impl TreeFilterRule for ShowsSubtree {
1430 fn is_shown(&self, row: &TreeFilterContext) -> bool {
1431 row.passes() || row.has_passing_descendant() || row.has_passing_ancestor()
1432 }
1433 }
1434
1435 fn random_spec(rng: &mut fastrand::Rng) -> ViewSpec<Node> {
1436 let columns = Column::all();
1437 let direction = |rng: &mut fastrand::Rng| {
1438 [SortDirection::Ascending, SortDirection::Descending][rng.usize(..2)]
1439 };
1440 let sort: Vec<_> = (0..rng.usize(0..=2))
1441 .map(|_| (columns[rng.usize(..columns.len())], direction(rng)))
1442 .collect();
1443 let filter = [
1444 TreeFilter::KeepAncestors,
1445 TreeFilter::HideSubtree,
1446 TreeFilter::Custom(Arc::new(ShowsSubtree)),
1447 ][rng.usize(..3)]
1448 .clone();
1449 let mut name = ColumnSet::EMPTY;
1450 name.insert(Column::Name.ix());
1451 let text = ["", "a", "b", "я"][rng.usize(..4)];
1452 let depth = [
1453 ExpandDepth::All,
1454 ExpandDepth::Levels(0),
1455 ExpandDepth::Levels(1),
1456 ExpandDepth::Levels(2),
1457 ][rng.usize(..4)];
1458 let toggled: Vec<u32> = (0..rng.usize(0..8)).map(|_| rng.u32(0..300)).collect();
1459 let spec = ViewSpec::new()
1460 .with_sort(sort)
1461 .with_quick_filter(QuickFilter::new(text, name, &ContainsAllTerms))
1462 .with_tree(TreeSpec::new().with_filter(filter))
1463 .with_tree_expansion(depth, toggled);
1464 if rng.bool() {
1465 spec.with_filter(ColumnSet::first(columns.len()), |node: &Node| {
1466 node.group != 1
1467 })
1468 } else {
1469 spec
1470 }
1471 }
1472
1473 type Shape = (
1475 Vec<RowId>,
1476 Vec<DisplayRow>,
1477 Vec<Option<Branch>>,
1478 usize,
1479 usize,
1480 );
1481
1482 fn shape(projection: &Projection) -> Shape {
1483 (
1484 projection.rows().to_vec(),
1485 (0..projection.len())
1486 .filter_map(|ix| projection.line(ix))
1487 .collect(),
1488 projection
1489 .rows()
1490 .iter()
1491 .map(|&id| projection.branch(id))
1492 .collect(),
1493 projection.tree_cycles(),
1494 projection.row_count(),
1495 )
1496 }
1497
1498 #[test]
1499 fn test_parallel_tree_matches_sequential() {
1500 let mut fading = 0;
1504 for seed in 0..40 {
1505 let mut rng = fastrand::Rng::with_seed(seed);
1506 let count = [0, 1, 2, 7, 100, 1_000, 3_000][rng.usize(..7)];
1507 let snapshot = random_snapshot(&mut rng, count, seed % 2 == 1);
1508 for _ in 0..6 {
1509 let spec = random_spec(&mut rng);
1510 let Structure::Tree(tree) = spec.structure() else {
1511 unreachable!("a tree view")
1512 };
1513 let sequential = build(&snapshot, &spec, tree, parallelism(0, 1));
1514 let parallelism = Parallelism {
1515 threads: rng.usize(2..=7),
1516 chunk_rows: rng.usize(1..=97),
1517 };
1518 let parallel = build(&snapshot, &spec, tree, parallelism);
1519 assert!(parallel.is_tree());
1520 assert_eq!(
1521 shape(¶llel),
1522 shape(&sequential),
1523 "seed {seed}, {count} rows, {parallelism:?}, sort {:?}, {:?}",
1524 spec.sort(),
1525 tree.filter(),
1526 );
1527 fading += parallel.rows().len() - parallel.row_count();
1528 }
1529 }
1530 assert!(fading > 100, "{fading} fading rows");
1531 }
1532
1533 struct Failing;
1535
1536 impl TreeFilterRule for Failing {
1537 fn is_shown(&self, row: &TreeFilterContext) -> bool {
1538 assert!(row.ancestors_pass(), "tree filter failed");
1539 row.passes()
1540 }
1541 }
1542
1543 #[test]
1544 fn test_tree_panic_resumes() {
1545 let epoch = Instant::now();
1548 let mut rng = fastrand::Rng::with_seed(5);
1549 let mut ingest = Ingest::<Node>::new(HighlightPolicy::default(), epoch);
1550 ingest.apply(
1551 (0..2_000).map(|id| Op::UpsertSilent(Arc::new(Node::random(&mut rng, id, 2_000)))),
1552 epoch,
1553 );
1554 let mut failing = Node::random(&mut rng, 0, 1);
1555 failing.id = u32::MAX;
1556 ingest.apply([Op::UpsertSilent(Arc::new(failing))], epoch);
1557 let snapshot = ingest.snapshot();
1558 let parallelism = Parallelism {
1559 threads: 4,
1560 chunk_rows: 16,
1561 };
1562 let message = |panic: Box<dyn std::any::Any + Send>| {
1563 panic
1564 .downcast_ref::<String>()
1565 .cloned()
1566 .or_else(|| panic.downcast_ref::<&str>().map(|text| (*text).to_owned()))
1567 .unwrap_or_default()
1568 };
1569 let spec = ViewSpec::new().with_tree(TreeSpec::new());
1570 let panic = panic::catch_unwind(panic::AssertUnwindSafe(|| {
1571 build(&snapshot, &spec, &TreeSpec::new(), parallelism)
1572 }))
1573 .expect_err("parent_key panics");
1574 assert!(message(panic).contains("parent_key failed"));
1575
1576 let mut ingest = Ingest::<Node>::new(HighlightPolicy::default(), epoch);
1577 ingest.apply(
1578 (0..2_000).map(|id| {
1579 Op::UpsertSilent(Arc::new(Node {
1580 id,
1581 parent: id.checked_sub(1),
1582 group: 0,
1583 price: 1.0,
1584 name: "a",
1585 }))
1586 }),
1587 epoch,
1588 );
1589 let snapshot = ingest.snapshot();
1590 let tree = TreeSpec::new().with_filter(TreeFilter::Custom(Arc::new(Failing)));
1591 let spec = ViewSpec::new()
1592 .with_filter(ColumnSet::first(3), |node: &Node| node.id % 2 == 0)
1593 .with_tree(tree.clone());
1594 let panic = panic::catch_unwind(panic::AssertUnwindSafe(|| {
1595 build(&snapshot, &spec, &tree, parallelism)
1596 }))
1597 .expect_err("the rule panics");
1598 assert!(message(panic).contains("tree filter failed"));
1599 }
1600
1601 #[test]
1602 fn test_cycle_warning_once() {
1603 let last = AtomicUsize::new(0);
1606 let warnings: Vec<bool> = [0, 2, 2, 2, 3, 0, 0, 3, 3]
1607 .into_iter()
1608 .map(|cycles| is_new_cycle_count(&last, cycles))
1609 .collect();
1610 assert_eq!(
1611 warnings,
1612 [false, true, false, false, true, false, false, true, false]
1613 );
1614 }
1615
1616 struct Roots;
1618
1619 impl TreeTotalsRule for Roots {
1620 fn is_counted(&self, row: &TreeTotalsContext) -> bool {
1621 row.depth() == 0
1622 }
1623 }
1624
1625 #[test]
1626 fn test_tree_totals_rows() {
1627 let epoch = Instant::now();
1632 let mut ingest = Ingest::<Node>::new(HighlightPolicy::default(), epoch);
1633 let node = |id, parent, name| {
1634 Op::UpsertSilent(Arc::new(Node {
1635 id,
1636 parent,
1637 group: 0,
1638 price: 1.0,
1639 name,
1640 }))
1641 };
1642 ingest.apply(
1643 [
1644 node(0, None, ""),
1645 node(1, Some(0), ""),
1646 node(2, Some(0), "b"),
1647 node(3, Some(1), "b"),
1648 node(4, None, "b"),
1649 node(5, None, ""),
1650 node(6, Some(5), ""),
1651 ],
1652 epoch,
1653 );
1654 let snapshot = ingest.snapshot();
1655 let keys = |projection: &Projection, totals: &TreeTotals| -> Vec<u32> {
1656 let tree = projection.tree_data().expect("a tree");
1657 let live: Vec<Option<&Node>> = projection
1658 .rows()
1659 .iter()
1660 .map(|&id| snapshot.row(id).map(|row| &**row))
1661 .collect();
1662 tree.counted(totals, &live).map(|row| row.id).collect()
1663 };
1664 let engine = Rebuild::default();
1665 let all = engine.rebuild(&snapshot, &ViewSpec::new().with_tree(TreeSpec::new()));
1666 assert_eq!(keys(&all, &TreeTotals::Leaves), [3, 2, 4, 6]);
1667 assert_eq!(keys(&all, &TreeTotals::AllRows), [0, 1, 3, 2, 4, 5, 6]);
1668 assert_eq!(keys(&all, &TreeTotals::Custom(Arc::new(Roots))), [0, 4, 5]);
1669
1670 let mut name = ColumnSet::EMPTY;
1673 name.insert(Column::Name.ix());
1674 let filtered = engine.rebuild(
1675 &snapshot,
1676 &ViewSpec::new()
1677 .with_quick_filter(QuickFilter::new("b", name, &ContainsAllTerms))
1678 .with_tree(TreeSpec::new()),
1679 );
1680 assert_eq!(keys(&filtered, &TreeTotals::Leaves), [3, 2, 4]);
1681 struct Passing;
1682 impl TreeTotalsRule for Passing {
1683 fn is_counted(&self, row: &TreeTotalsContext) -> bool {
1684 row.passes() && !row.is_branch()
1685 }
1686 }
1687 assert_eq!(
1688 keys(&filtered, &TreeTotals::Custom(Arc::new(Passing))),
1689 [3, 2, 4]
1690 );
1691 assert_eq!(keys(&filtered, &TreeTotals::AllRows), [0, 1, 3, 2, 4]);
1692 let tree = filtered.tree_data().unwrap();
1694 let depths: Vec<u16> = (0..filtered.rows().len())
1695 .map(|ix| tree.depth(ix))
1696 .collect();
1697 assert_eq!(depths, [0, 1, 2, 1, 0]);
1698 }
1699}