Skip to main content

dag/set/
id_static.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8use std::any::Any;
9use std::borrow::Cow;
10use std::cmp;
11use std::fmt;
12use std::sync::atomic::Ordering;
13use std::sync::Arc;
14
15use nonblocking::non_blocking_result;
16
17use super::hints::Flags;
18use super::AsyncSetQuery;
19use super::BoxVertexStream;
20use super::Hints;
21use crate::idset::IdList;
22use crate::idset::OrderedSpan;
23use crate::ops::DagAlgorithm;
24use crate::ops::IdConvert;
25use crate::protocol::disable_remote_protocol;
26use crate::Group;
27use crate::Id;
28use crate::IdSet;
29use crate::Result;
30use crate::Set;
31use crate::Vertex;
32
33/// A set backed by [`IdSet`] + [`IdMap`].
34/// Efficient for DAG calculation.
35#[derive(Clone)]
36pub struct IdStaticSet {
37    spans: IdSet,
38    pub(crate) map: Arc<dyn IdConvert + Send + Sync>,
39    pub(crate) dag: Arc<dyn DagAlgorithm + Send + Sync>,
40    hints: Hints,
41    // If true, iterate in ASC order instead of DESC.
42    iteration_order: IterationOrder,
43}
44
45/// Iteration order of the `IdStaticSet`.
46#[derive(Clone, Debug)]
47enum IterationOrder {
48    /// From smaller ids to larger ids.
49    Asc,
50    /// From larger ids to smaller ids.
51    Desc,
52    /// Custom iteration order. Must match `IdStaticSet.spans`.
53    Custom(IdList),
54    /// Custom iteration order, reversed.
55    CustomReversed(IdList),
56}
57
58/// Basic iteration order. A subset of `IterationOrder`.
59#[derive(Copy, Clone, Debug)]
60pub enum BasicIterationOrder {
61    /// From smaller ids to larger ids.
62    Asc,
63    /// From larger ids to smaller ids.
64    Desc,
65}
66
67struct Iter {
68    iter: Box<dyn DoubleEndedIterator<Item = Id> + Send + Sync + 'static>,
69    map: Arc<dyn IdConvert + Send + Sync>,
70    reversed: bool,
71    buf: Vec<Result<Vertex>>,
72}
73
74impl Iter {
75    fn into_box_stream(self) -> BoxVertexStream {
76        Box::pin(futures::stream::unfold(self, |this| this.next()))
77    }
78
79    async fn next(mut self) -> Option<(Result<Vertex>, Self)> {
80        if let Some(name) = self.buf.pop() {
81            return Some((name, self));
82        }
83        let map = &self.map;
84        let opt_id = if self.reversed {
85            self.iter.next_back()
86        } else {
87            self.iter.next()
88        };
89        match opt_id {
90            None => None,
91            Some(id) => {
92                let contains = map
93                    .contains_vertex_id_locally(&[id])
94                    .await
95                    .unwrap_or_default();
96                if contains == &[true] {
97                    Some((map.vertex_name(id).await, self))
98                } else {
99                    // On demand prefetch in batch.
100                    let batch_size = crate::config::BATCH_SIZE.load(Ordering::Acquire);
101                    let mut ids = Vec::with_capacity(batch_size);
102                    ids.push(id);
103                    for _ in ids.len()..batch_size {
104                        if let Some(id) = if self.reversed {
105                            self.iter.next_back()
106                        } else {
107                            self.iter.next()
108                        } {
109                            ids.push(id);
110                        } else {
111                            break;
112                        }
113                    }
114                    ids.reverse();
115                    self.buf = match self.map.vertex_name_batch(&ids).await {
116                        Err(e) => return Some((Err(e), self)),
117                        Ok(names) => names,
118                    };
119                    if self.buf.len() != ids.len() {
120                        let result =
121                            crate::errors::bug("vertex_name_batch does not return enough items");
122                        return Some((result, self));
123                    }
124                    let name = self.buf.pop().expect("buf is not empty");
125                    Some((name, self))
126                }
127            }
128        }
129    }
130}
131
132struct DebugSpan {
133    // start, end are for debug fmt, not iteration order.
134    span: OrderedSpan,
135    end_name: Option<Vertex>,
136    start_name: Option<Vertex>,
137}
138
139impl fmt::Debug for DebugSpan {
140    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141        match (
142            self.span.start == self.span.end,
143            &self.start_name,
144            &self.end_name,
145        ) {
146            (true, Some(name), _) => {
147                fmt::Debug::fmt(&name, f)?;
148                write!(f, "+{:?}", self.span.start)?;
149            }
150            (true, None, _) => {
151                write!(f, "{:?}", self.span.start)?;
152            }
153            (false, Some(start), Some(end)) => {
154                fmt::Debug::fmt(&start, f)?;
155                write!(f, ":")?;
156                fmt::Debug::fmt(&end, f)?;
157                write!(f, "+{:?}:{:?}", self.span.start, self.span.end)?;
158            }
159            (false, _, _) => {
160                write!(f, "{:?}:{:?}", self.span.start, self.span.end)?;
161            }
162        }
163        Ok(())
164    }
165}
166
167impl fmt::Debug for IdStaticSet {
168    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169        write!(f, "<spans ")?;
170        let spans_iter: Box<dyn Iterator<Item = OrderedSpan>> = match self.iteration_order {
171            IterationOrder::Custom(ref list) | IterationOrder::CustomReversed(ref list) => {
172                Box::new(list.as_spans().iter().copied())
173            }
174            _ => Box::new(self.spans.as_spans().iter().map(|s| OrderedSpan {
175                start: s.low,
176                end: s.high,
177            })),
178        };
179
180        let len = spans_iter.size_hint().0;
181        let limit = f.width().unwrap_or(3);
182        f.debug_list()
183            .entries(spans_iter.take(limit).map(|span| DebugSpan {
184                span,
185                end_name: disable_remote_protocol(|| {
186                    non_blocking_result(self.map.vertex_name(span.end)).ok()
187                }),
188                start_name: disable_remote_protocol(|| {
189                    non_blocking_result(self.map.vertex_name(span.start)).ok()
190                }),
191            }))
192            .finish()?;
193        match len.saturating_sub(limit) {
194            0 => {}
195            1 => write!(f, " + 1 span")?,
196            n => write!(f, " + {} spans", n)?,
197        }
198        match &self.iteration_order {
199            // + means ASC order.
200            IterationOrder::Asc => write!(f, " +")?,
201            // For compatibility with existing tests, do not show a sign for DESC (default) order.
202            // Otherwise this should show "-".
203            IterationOrder::Desc => {}
204            IterationOrder::Custom(_) => write!(f, " ?")?,
205            IterationOrder::CustomReversed(_) => write!(f, " ¿")?,
206        }
207        write!(f, ">")?;
208        Ok(())
209    }
210}
211
212impl IdStaticSet {
213    pub(crate) fn from_id_set_idmap_dag(
214        spans: IdSet,
215        map: Arc<dyn IdConvert + Send + Sync>,
216        dag: Arc<dyn DagAlgorithm + Send + Sync>,
217    ) -> Self {
218        let hints = Hints::new_with_idmap_dag(map.clone(), dag.clone());
219        hints.add_flags(Flags::ID_DESC | Flags::TOPO_DESC);
220        if spans.is_empty() {
221            hints.add_flags(Flags::EMPTY);
222        } else {
223            hints.set_min_id(spans.min().unwrap());
224            hints.set_max_id(spans.max().unwrap());
225        }
226        Self {
227            spans,
228            map,
229            hints,
230            dag,
231            iteration_order: IterationOrder::Desc,
232        }
233    }
234
235    /// Construct from `list`, `map`, `dag`. The ids in the `list` must match the map and dag.
236    pub(crate) fn from_id_list_idmap_dag(
237        list: IdList,
238        map: Arc<dyn IdConvert + Send + Sync>,
239        dag: Arc<dyn DagAlgorithm + Send + Sync>,
240    ) -> Self {
241        let hints = Hints::new_with_idmap_dag(map.clone(), dag.clone());
242
243        // Calculate hints (flags, min_id, max_id).
244        let mut flags = Flags::ID_DESC | Flags::TOPO_DESC | Flags::ID_ASC | Flags::EMPTY;
245        let mut min_id = None;
246        let mut max_id = None;
247        let mut last_min_id = None;
248        let mut last_max_id = None;
249        for span in list.as_spans() {
250            let (this_min_id, this_max_id) = (span.min(), span.max());
251            flags -= Flags::EMPTY;
252            if span.start < span.end || last_min_id.unwrap_or(Id::MAX) < this_max_id {
253                // Not DESC or TOPO.
254                flags -= Flags::ID_DESC | Flags::TOPO_DESC;
255            }
256            if span.start > span.end || last_max_id.unwrap_or(Id::MIN) > this_min_id {
257                // Not ASC.
258                flags -= Flags::ID_ASC;
259            }
260            (last_min_id, last_max_id) = (Some(this_min_id), Some(this_max_id));
261            min_id = Some(this_min_id.min(min_id.unwrap_or(Id::MAX)));
262            max_id = Some(this_max_id.max(max_id.unwrap_or(Id::MIN)));
263        }
264
265        hints.add_flags(flags);
266        if let Some(min_id) = min_id {
267            hints.set_min_id(min_id);
268        }
269        if let Some(max_id) = max_id {
270            hints.set_max_id(max_id);
271        }
272
273        let spans = list.to_set();
274
275        // If `list` is already sorted, then just use BasicIterationOrder.
276        let iteration_order = if flags.contains(Flags::ID_DESC) {
277            IterationOrder::Desc
278        } else if flags.contains(Flags::ID_ASC) {
279            IterationOrder::Asc
280        } else {
281            IterationOrder::Custom(list)
282        };
283
284        Self {
285            spans,
286            map,
287            hints,
288            dag,
289            iteration_order,
290        }
291    }
292
293    /// Get the low-level `IdSet`, which no longer preserves iteration order.
294    pub(crate) fn id_set_losing_order(&self) -> &IdSet {
295        &self.spans
296    }
297
298    /// Get the low-level `IdSet`, or `None` if iteration order cannot be preserved.
299    /// Note: `reserved` is not preserved and needs to be considered separately.
300    pub(crate) fn id_set_try_preserving_order(&self) -> Option<&IdSet> {
301        if self
302            .hints()
303            .flags()
304            .intersects(Flags::ID_DESC | Flags::ID_ASC)
305        {
306            Some(&self.spans)
307        } else {
308            None
309        }
310    }
311
312    /// If `lhs` and `rhs` are compatible, return a new IdStaticSet with:
313    /// - `map` and `dag` set to the newer version of `lhs` and `rhs`.
314    /// - `spans` set to `edit_spans(&lhs.spans, &rhs.spans)`.
315    /// Otherwise return `None`.
316    ///
317    /// Iteration order will not be preserved.
318    pub(crate) fn from_edit_spans(
319        lhs: &Self,
320        rhs: &Self,
321        edit_spans: fn(&IdSet, &IdSet) -> IdSet,
322    ) -> Option<Self> {
323        let order = lhs.map.map_version().partial_cmp(rhs.map.map_version())?;
324        let spans = edit_spans(&lhs.spans, &rhs.spans);
325        let picked = match order {
326            cmp::Ordering::Less => rhs,
327            cmp::Ordering::Greater | cmp::Ordering::Equal => lhs,
328        };
329        let (map, dag) = (picked.map.clone(), picked.dag.clone());
330        let mut result = Self::from_id_set_idmap_dag(spans, map, dag);
331        if let Some(order) = lhs.iteration_order() {
332            result.set_iteration_order(order);
333        }
334        Some(result)
335    }
336
337    /// Change the iteration order between (DESC default) and ASC.
338    pub fn reversed(mut self) -> Self {
339        match self.iteration_order {
340            IterationOrder::Desc => {
341                self.hints.remove_flags(Flags::ID_DESC | Flags::TOPO_DESC);
342                self.hints.add_flags(Flags::ID_ASC);
343                self.iteration_order = IterationOrder::Asc
344            }
345            IterationOrder::Asc => {
346                self.hints.remove_flags(Flags::ID_ASC);
347                self.hints.add_flags(Flags::ID_DESC | Flags::TOPO_DESC);
348                self.iteration_order = IterationOrder::Desc
349            }
350            IterationOrder::Custom(list) => {
351                // Conservatively drop order-related flags.
352                self.hints
353                    .remove_flags(Flags::ID_ASC | Flags::ID_DESC | Flags::TOPO_DESC);
354                self.iteration_order = IterationOrder::CustomReversed(list);
355            }
356            IterationOrder::CustomReversed(list) => {
357                self.hints
358                    .remove_flags(Flags::ID_ASC | Flags::ID_DESC | Flags::TOPO_DESC);
359                self.iteration_order = IterationOrder::Custom(list);
360            }
361        }
362        self
363    }
364
365    /// Update iteration order. Only `Asc` and `Desc` is accepted.
366    pub(crate) fn set_iteration_order(&mut self, order: BasicIterationOrder) {
367        // Only reuse Asc or Desc order. Cannot handle custom order.
368        match order {
369            BasicIterationOrder::Asc => {
370                self.hints.remove_flags(Flags::ID_DESC | Flags::TOPO_DESC);
371                self.hints.add_flags(Flags::ID_ASC);
372                self.iteration_order = IterationOrder::Asc;
373            }
374            BasicIterationOrder::Desc => {
375                self.hints.remove_flags(Flags::ID_ASC);
376                self.hints.add_flags(Flags::ID_DESC | Flags::TOPO_DESC);
377                self.iteration_order = IterationOrder::Desc
378            }
379        }
380    }
381
382    /// Obtain the iteration order. Only `Asc` and `Desc` is returned. Otherwise report as `None`.
383    pub(crate) fn iteration_order(&self) -> Option<BasicIterationOrder> {
384        match self.iteration_order {
385            IterationOrder::Asc => Some(BasicIterationOrder::Asc),
386            IterationOrder::Desc => Some(BasicIterationOrder::Desc),
387            #[allow(unreachable_patterns)]
388            _ => None,
389        }
390    }
391
392    async fn max(&self) -> Result<Option<Vertex>> {
393        debug_assert_eq!(self.spans.max(), self.spans.iter_desc().nth(0));
394        self.resolve_optional_id(self.spans.max()).await
395    }
396
397    async fn min(&self) -> Result<Option<Vertex>> {
398        debug_assert_eq!(self.spans.min(), self.spans.iter_desc().rev().nth(0));
399        self.resolve_optional_id(self.spans.min()).await
400    }
401
402    async fn resolve_optional_id(&self, id: Option<Id>) -> Result<Option<Vertex>> {
403        match id {
404            Some(id) => {
405                let map = &self.map;
406                let name = map.vertex_name(id).await?;
407                Ok(Some(name))
408            }
409            None => Ok(None),
410        }
411    }
412
413    pub(crate) fn slice_spans(mut self, skip: u64, take: u64) -> Self {
414        let (skip, take) = match self.iteration_order {
415            IterationOrder::Asc | IterationOrder::CustomReversed(_) => {
416                let len = self.spans.count();
417                // [---take1----][skip]
418                // [skip2][take2][skip]
419                // [--------len-------]
420                let take1 = len.saturating_sub(skip);
421                let take2 = take1.min(take);
422                let skip2 = take1 - take2;
423                (skip2, take2)
424            }
425            IterationOrder::Desc | IterationOrder::Custom(_) => {
426                // [skip][take][---]
427                // [------len------]
428                (skip, take)
429            }
430        };
431        match self.iteration_order {
432            IterationOrder::Custom(ref mut list) | IterationOrder::CustomReversed(ref mut list) => {
433                match (skip, take) {
434                    (0, u64::MAX) => {}
435                    (0, _) => *list = list.take(take),
436                    (_, u64::MAX) => *list = list.skip(skip),
437                    _ => *list = list.skip(skip).take(take),
438                };
439                self.spans = list.to_set();
440            }
441            _ => match (skip, take) {
442                (0, u64::MAX) => {}
443                (0, _) => self.spans = self.spans.take(take),
444                (_, u64::MAX) => self.spans = self.spans.skip(skip),
445                _ => self.spans = self.spans.skip(skip).take(take),
446            },
447        }
448        self
449    }
450
451    // used by iter and iter_rev.
452    fn get_iter_and_reversed(
453        &self,
454    ) -> (
455        Box<dyn DoubleEndedIterator<Item = Id> + Send + Sync + 'static>,
456        bool,
457    ) {
458        let iter: Box<dyn DoubleEndedIterator<Item = Id> + Send + Sync + 'static> =
459            match self.iteration_order {
460                IterationOrder::Custom(ref list) | IterationOrder::CustomReversed(ref list) => {
461                    Box::new(list.into_iter())
462                }
463                _ => Box::new(self.spans.clone().into_iter()),
464            };
465        let reversed = matches!(
466            self.iteration_order,
467            IterationOrder::Asc | IterationOrder::CustomReversed(_)
468        );
469        (iter, reversed)
470    }
471}
472
473#[async_trait::async_trait]
474impl AsyncSetQuery for IdStaticSet {
475    async fn iter(&self) -> Result<BoxVertexStream> {
476        let (iter, reversed) = self.get_iter_and_reversed();
477        let iter = Iter {
478            iter,
479            map: self.map.clone(),
480            reversed,
481            buf: Default::default(),
482        };
483        Ok(iter.into_box_stream())
484    }
485
486    async fn iter_rev(&self) -> Result<BoxVertexStream> {
487        let (iter, reversed) = self.get_iter_and_reversed();
488        let iter = Iter {
489            iter,
490            map: self.map.clone(),
491            reversed: !reversed,
492            buf: Default::default(),
493        };
494        Ok(iter.into_box_stream())
495    }
496
497    // Usually, the "count" should not be manually implemented so the universal fast path can
498    // apply. However, the IdStaticSet does not need a separate "universal fast path".
499    // So let's just override the "count".
500    async fn count(&self) -> Result<u64> {
501        Ok(self.spans.count())
502    }
503
504    async fn count_slow(&self) -> Result<u64> {
505        Ok(self.spans.count())
506    }
507
508    async fn size_hint(&self) -> (u64, Option<u64>) {
509        let size = self.spans.count();
510        (size, Some(size))
511    }
512
513    async fn first(&self) -> Result<Option<Vertex>> {
514        match self.iteration_order {
515            IterationOrder::Asc => self.min().await,
516            IterationOrder::Desc => self.max().await,
517            IterationOrder::Custom(ref list) => {
518                self.resolve_optional_id(list.into_iter().next()).await
519            }
520            IterationOrder::CustomReversed(ref list) => {
521                self.resolve_optional_id(list.into_iter().next_back()).await
522            }
523        }
524    }
525
526    async fn last(&self) -> Result<Option<Vertex>> {
527        match self.iteration_order {
528            IterationOrder::Asc => self.max().await,
529            IterationOrder::Desc => self.min().await,
530            IterationOrder::Custom(ref list) => {
531                self.resolve_optional_id(list.into_iter().next_back()).await
532            }
533            IterationOrder::CustomReversed(ref list) => {
534                self.resolve_optional_id(list.into_iter().next()).await
535            }
536        }
537    }
538
539    async fn is_empty(&self) -> Result<bool> {
540        Ok(self.spans.is_empty())
541    }
542
543    async fn contains(&self, name: &Vertex) -> Result<bool> {
544        let result = match self.map.vertex_id_with_max_group(name, Group::MAX).await? {
545            Some(id) => self.spans.contains(id),
546            None => false,
547        };
548        Ok(result)
549    }
550
551    async fn contains_fast(&self, name: &Vertex) -> Result<Option<bool>> {
552        self.contains(name).await.map(Some)
553    }
554
555    fn as_any(&self) -> &dyn Any {
556        self
557    }
558
559    fn hints(&self) -> &Hints {
560        &self.hints
561    }
562
563    fn id_convert(&self) -> Option<&dyn IdConvert> {
564        Some(self.map.as_ref() as &dyn IdConvert)
565    }
566
567    fn specialized_reverse(&self) -> Option<Set> {
568        Some(Set::from_query(self.clone().reversed()))
569    }
570
571    fn specialized_take(&self, take: u64) -> Option<Set> {
572        Some(Set::from_query(self.clone().slice_spans(0, take)))
573    }
574
575    fn specialized_skip(&self, skip: u64) -> Option<Set> {
576        Some(Set::from_query(self.clone().slice_spans(skip, u64::MAX)))
577    }
578
579    /// Specialized "flatten_id" implementation.
580    fn specialized_flatten_id(&self) -> Option<Cow<IdStaticSet>> {
581        Some(Cow::Borrowed(self))
582    }
583}
584
585#[cfg(test)]
586#[allow(clippy::redundant_clone)]
587pub(crate) mod tests {
588    use std::ops::Deref;
589    use std::sync::atomic::Ordering::Acquire;
590
591    use futures::TryStreamExt;
592    use nonblocking::non_blocking_result as r;
593
594    use super::super::tests::*;
595    use super::super::Set;
596    use super::*;
597    use crate::ops::IdMapSnapshot;
598    use crate::set::difference::DifferenceSet;
599    use crate::set::intersection::IntersectionSet;
600    use crate::set::slice::SliceSet;
601    use crate::set::union::UnionSet;
602    use crate::tests::build_segments;
603    use crate::Dag;
604    use crate::DagAlgorithm;
605
606    /// Test with a predefined DAG.
607    pub(crate) fn with_dag<R, F: Fn(&Dag) -> R>(func: F) -> R {
608        let built = build_segments(
609            r#"
610            A--B--C--D
611                \--E--F--G"#,
612            "D G",
613            2,
614        );
615        //  0--1--2--3
616        //      \--4--5--6
617        func(&built.name_dag)
618    }
619
620    #[test]
621    fn test_dag_invariants() -> Result<()> {
622        with_dag(|dag| {
623            let bef = r(dag.range("B".into(), "F".into()))?;
624            check_invariants(bef.deref())?;
625            assert_eq!(nb(bef.size_hint()), (3, Some(3)));
626
627            Ok(())
628        })
629    }
630
631    #[test]
632    fn test_dag_fast_paths() -> Result<()> {
633        with_dag(|dag| {
634            let abcd = r(dag.ancestors("D".into()))?;
635            let abefg = r(dag.ancestors("G".into()))?;
636
637            let ab = abcd.intersection(&abefg);
638            check_invariants(ab.deref())?;
639
640            assert!(nb(abcd.contains(&vec![b'A'].into()))?);
641            assert!(!nb(abcd.contains(&vec![b'E'].into()))?);
642
643            // should not be "<and <...> <...>>"
644            assert_eq!(dbg(&ab), "<spans [A:B+0:1]>");
645
646            let abcdefg = abcd.union(&abefg);
647            check_invariants(abcd.deref())?;
648            // should not be "<or <...> <...>>"
649            assert_eq!(dbg(&abcdefg), "<spans [A:G+0:6]>");
650
651            let cd = abcd.difference(&abefg);
652            check_invariants(cd.deref())?;
653            // should not be "<difference <...> <...>>"
654            assert_eq!(dbg(&cd), "<spans [C:D+2:3]>");
655
656            Ok(())
657        })
658    }
659
660    #[test]
661    fn test_dag_fast_path_set_ops() -> Result<()> {
662        with_dag(|dag| {
663            let abcd = r(dag.ancestors("D".into()))?.reverse();
664            let unordered = abcd.take(2).union_zip(&abcd.skip(3));
665
666            // Intersection and difference can flatten the "unordered" set because rhs order does
667            // not matter.
668            assert_eq!(
669                dbg(abcd.intersection(&unordered)),
670                "<spans [D+3, A:B+0:1] +>"
671            );
672            assert_eq!(dbg(abcd.difference(&unordered)), "<spans [C+2] +>");
673
674            // but lhs order matters (no fast path if lhs order is to be preserved).
675            assert_eq!(
676                dbg(unordered.intersection(&abcd)),
677                "<and <or <spans [A:B+0:1] +> <spans [D+3] +> (order=Zip)> <spans [A:D+0:3] +>>"
678            );
679            assert_eq!(
680                dbg(unordered.difference(&abcd)),
681                "<diff <or <spans [A:B+0:1] +> <spans [D+3] +> (order=Zip)> <spans [A:D+0:3] +>>"
682            );
683
684            // Union drops order (by flattening) aggresively on both sides.
685            assert_eq!(dbg(abcd.union(&unordered)), "<spans [A:D+0:3] +>");
686
687            // Union (preserving order) cannot flatten sets for fast paths.
688            assert_eq!(
689                dbg(abcd.union_preserving_order(&unordered)),
690                "<or <spans [A:D+0:3] +> <or <spans [A:B+0:1] +> <spans [D+3] +> (order=Zip)>>"
691            );
692
693            Ok(())
694        })
695    }
696
697    /// Show set iteration and flatten set iteration for debugging purpose.
698    fn dbg_flat(set: &Set) -> String {
699        let flat = set.specialized_flatten_id();
700        let flat_str = match flat {
701            Some(flat) => format!(" flat:{}", fmt_iter(&Set::from_query(flat.into_owned()))),
702            None => String::new(),
703        };
704        format!("{}{}", fmt_iter(set), flat_str)
705    }
706
707    // Construct diff, intersection, union sets directly to bypass fast paths.
708    fn set_ops(a: &Set, b: &Set) -> (Set, Set, Set) {
709        let difference = DifferenceSet::new(a.clone(), b.clone());
710        let intersection = IntersectionSet::new(a.clone(), b.clone());
711        let union = UnionSet::new(a.clone(), b.clone());
712        (
713            Set::from_query(difference),
714            Set::from_query(intersection),
715            Set::from_query(union),
716        )
717    }
718
719    #[test]
720    fn test_dag_specialized_flatten_id_fast_path_with_set_ops() -> Result<()> {
721        with_dag(|dag| {
722            let mut abcd = "A B C D"
723                .split_whitespace()
724                .map(|s: &'static str| r(dag.sort(&s.into())).unwrap())
725                .collect::<Vec<_>>();
726            let d = abcd.pop().unwrap();
727            let c = abcd.pop().unwrap();
728            let b = abcd.pop().unwrap();
729            let a = abcd.pop().unwrap();
730
731            let acb = a.union_preserving_order(&b.union_preserving_order(&c).reverse());
732            let bcd = b.union_preserving_order(&c).union_preserving_order(&d);
733
734            // All set operations can use fast paths.
735            let diff = acb.difference(&bcd);
736            let intersect = acb.intersection(&bcd);
737            let union1 = diff.union_preserving_order(&intersect);
738            let reversed1 = union1.reverse();
739            let union2 = reversed1.union_zip(&diff);
740            let reversed2 = union2.reverse();
741
742            // Show the values of the sets.
743            assert_eq!(dbg_flat(&diff), "[A] flat:[A]");
744            assert_eq!(dbg_flat(&intersect), "[C, B] flat:[C, B]");
745            assert_eq!(dbg_flat(&union1), "[A, C, B] flat:[C, B, A]");
746            assert_eq!(dbg_flat(&reversed1), "[B, C, A] flat:[A, B, C]");
747            assert_eq!(dbg_flat(&union2), "[B, C, A] flat:[A, B, C]");
748            assert_eq!(dbg_flat(&reversed2), "[A, C, B] flat:[C, B, A]");
749
750            // The union2 should use a fast path to "count".
751            let count1 = union2
752                .as_any()
753                .downcast_ref::<UnionSet>()
754                .unwrap()
755                .test_slow_count
756                .load(Acquire);
757            let _ = r(union2.count())?;
758            let count2 = union2
759                .as_any()
760                .downcast_ref::<UnionSet>()
761                .unwrap()
762                .test_slow_count
763                .load(Acquire);
764            assert_eq!(count1, count2, "union.count() should not use slow path");
765
766            // dag.sort(reversed2) should have a fast path.
767            let count1 = dag.internal_stats.sort_slow_path_count.load(Acquire);
768            let _ = r(dag.sort(&reversed2))?;
769            let count2 = dag.internal_stats.sort_slow_path_count.load(Acquire);
770            assert_eq!(count1, count2, "dag.sort() should not use slow path");
771
772            // Show the debug format. This shows whether internal structure is flattened or not.
773            assert_eq!(
774                wrap_dbg_lines(&reversed2),
775                r#"
776                <reverse
777                  <or
778                    <reverse
779                      <or
780                        <diff
781                          <or <spans [A+0]> <reverse <or <spans [B+1]> <spans [C+2]>>>>
782                          <or <or <spans [B+1]> <spans [C+2]>> <spans [D+3]>>>
783                        <and
784                          <or <spans [A+0]> <reverse <or <spans [B+1]> <spans [C+2]>>>>
785                          <or <or <spans [B+1]> <spans [C+2]>> <spans [D+3]>>>>>
786                    <diff
787                      <or <spans [A+0]> <reverse <or <spans [B+1]> <spans [C+2]>>>>
788                      <or <or <spans [B+1]> <spans [C+2]>> <spans [D+3]>>> (order=Zip)>>"#
789            );
790
791            // Flattened turns the tree into a single set.
792            let flattened = reversed2.specialized_flatten_id().unwrap();
793            assert_eq!(dbg(&flattened), "<spans [A:C+0:2]>");
794
795            Ok(())
796        })
797    }
798
799    #[test]
800    fn test_dag_specialized_flatten_id_fast_path_with_slices() -> Result<()> {
801        // SliceSet cannot use fast paths easily. It must check the order.
802        with_dag(|dag| {
803            let abcd = r(dag.ancestors("D".into()))?.reverse();
804            let abefg = r(dag.ancestors("G".into()))?.reverse();
805
806            let slice12 =
807                |a: &Set| -> Set { Set::from_query(SliceSet::new(a.clone(), 1, Some(2))) };
808
809            let (d, i, u) = set_ops(&abcd, &abefg);
810            assert_eq!(dbg_flat(&d), "[C, D] flat:[C, D]");
811            assert_eq!(dbg_flat(&i), "[A, B] flat:[A, B]");
812            assert_eq!(
813                dbg_flat(&u),
814                "[A, B, C, D, E, F, G] flat:[A, B, C, D, E, F, G]"
815            );
816            assert_eq!(dbg_flat(&slice12(&d)), "[D] flat:[D]");
817            assert_eq!(dbg_flat(&slice12(&i)), "[B] flat:[B]");
818            assert_eq!(dbg_flat(&slice12(&u)), "[B, C]"); // no fast path for union_preserving_order
819
820            // Make abcd and abefg use different order.
821            let (d, i, u) = set_ops(&abcd.reverse(), &abefg);
822            assert_eq!(dbg_flat(&d), "[D, C] flat:[D, C]");
823            assert_eq!(dbg_flat(&i), "[B, A] flat:[B, A]");
824            assert_eq!(
825                dbg_flat(&u),
826                "[D, C, B, A, E, F, G] flat:[G, F, E, D, C, B, A]"
827            );
828            assert_eq!(dbg_flat(&slice12(&d)), "[C] flat:[C]");
829            assert_eq!(dbg_flat(&slice12(&i)), "[A] flat:[A]");
830            assert_eq!(dbg_flat(&slice12(&u)), "[C, B]"); // no fast path for union_preserving_order
831
832            // Set without either order.
833            let unordered = abcd.skip(1).take(2).union_zip(&abefg.take(2));
834            assert!(
835                !unordered
836                    .hints()
837                    .flags()
838                    .intersects(Flags::ID_ASC | Flags::ID_DESC)
839            );
840            assert_eq!(dbg_flat(&unordered), "[B, A, C] flat:[A, B, C]");
841
842            // S & unordered; or S - unordered can preserve order and maintain fast path.
843            assert_eq!(
844                dbg_flat(&slice12(&abcd.intersection(&unordered))),
845                "[B, C] flat:[B, C]"
846            );
847            assert_eq!(
848                dbg_flat(&slice12(&abefg.difference(&unordered))),
849                "[F, G] flat:[F, G]"
850            );
851
852            // S + unordered (any order) usually does not have a fast path.
853            assert_eq!(
854                dbg_flat(&slice12(&abcd.union_preserving_order(&unordered))),
855                "[B, C]"
856            );
857            assert_eq!(dbg_flat(&slice12(&abcd.union_zip(&unordered))), "[B, C]");
858
859            // "union" does not promise order and might have a fast path.
860            assert_eq!(
861                dbg_flat(&slice12(&abcd.union(&unordered))),
862                "[B, C] flat:[B, C]"
863            );
864
865            Ok(())
866        })
867    }
868
869    #[test]
870    fn test_dag_no_fast_paths() -> Result<()> {
871        let f = |s: Set| -> String { dbg(s) };
872        with_dag(|dag1| -> Result<()> {
873            with_dag(|dag2| -> Result<()> {
874                let abcd = r(dag1.ancestors("D".into()))?;
875                let abefg = r(dag2.ancestors("G".into()))?;
876
877                // Since abcd and abefg are from 2 "separate" Dags, fast paths should not
878                // be used for intersection, union, and difference.
879
880                let ab = abcd.intersection(&abefg);
881                check_invariants(ab.deref())?;
882                // should not be "<spans ...>"
883                assert_eq!(
884                    dbg(&ab),
885                    "<and <spans [A:D+0:3]> <spans [E:G+4:6, A:B+0:1]>>"
886                );
887
888                let abcdefg = abcd.union(&abefg);
889                check_invariants(abcd.deref())?;
890                // should not be "<spans ...>"
891                assert_eq!(
892                    dbg(&abcdefg),
893                    "<or <spans [A:D+0:3]> <spans [E:G+4:6, A:B+0:1]>>"
894                );
895
896                let cd = abcd.difference(&abefg);
897                check_invariants(cd.deref())?;
898                // should not be "<spans ...>"
899                assert_eq!(
900                    dbg(&cd),
901                    "<diff <spans [A:D+0:3]> <spans [E:G+4:6, A:B+0:1]>>"
902                );
903
904                // Should not use FULL hint fast paths for "&, |, -" operations, because
905                // dag1 and dag2 are not considered compatible.
906                let a1 = || r(dag1.all()).unwrap();
907                let a2 = || r(dag2.all()).unwrap();
908                assert_eq!(f(a1() & a2()), "<and <spans [A:G+0:6]> <spans [A:G+0:6]>>");
909                assert_eq!(f(a1() | a2()), "<or <spans [A:G+0:6]> <spans [A:G+0:6]>>");
910                assert_eq!(f(a1() - a2()), "<diff <spans [A:G+0:6]> <spans [A:G+0:6]>>");
911
912                // No fast path for manually constructed StaticSet either, because
913                // the StaticSets do not have DAG associated to test compatibility.
914                // However, "all & z" is changed to "z & all" for performance.
915                let z = || Set::from("Z");
916                assert_eq!(f(z() & a2()), "<and <static [Z]> <spans [A:G+0:6]>>");
917                assert_eq!(f(z() | a2()), "<or <static [Z]> <spans [A:G+0:6]>>");
918                assert_eq!(f(z() - a2()), "<diff <static [Z]> <spans [A:G+0:6]>>");
919                assert_eq!(f(a1() & z()), "<and <static [Z]> <spans [A:G+0:6]>>");
920                assert_eq!(f(a1() | z()), "<or <spans [A:G+0:6]> <static [Z]>>");
921                assert_eq!(f(a1() - z()), "<diff <spans [A:G+0:6]> <static [Z]>>");
922
923                // EMPTY fast paths can still be used.
924                let e = Set::empty;
925                assert_eq!(f(e() & a1()), "<empty>");
926                assert_eq!(f(e() | a1()), "<spans [A:G+0:6]>");
927                assert_eq!(f(e() - a1()), "<empty>");
928                assert_eq!(f(a1() & e()), "<empty>");
929                assert_eq!(f(a1() | e()), "<spans [A:G+0:6]>");
930                assert_eq!(f(a1() - e()), "<spans [A:G+0:6]>");
931
932                // dag.sort() has to use slow path for an incompatible set.
933                let count1 = dag1.internal_stats.sort_slow_path_count.load(Acquire);
934                let _ = r(dag1.sort(&abefg))?;
935                let count2 = dag1.internal_stats.sort_slow_path_count.load(Acquire);
936                assert_eq!(
937                    count1 + 1,
938                    count2,
939                    "dag.sort() should use slow path for incompatible set"
940                );
941
942                Ok(())
943            })
944        })
945    }
946
947    #[test]
948    fn test_dag_all() -> Result<()> {
949        with_dag(|dag| {
950            let all = r(dag.all())?;
951            assert_eq!(dbg(&all), "<spans [A:G+0:6]>");
952
953            let ac: Set = "A C".into();
954            let ac = r(dag.sort(&ac))?;
955
956            let intersection = all.intersection(&ac);
957            // should not be "<and ...>"
958            assert_eq!(dbg(&intersection), "<spans [C+2, A+0]>");
959            Ok(())
960        })
961    }
962
963    #[test]
964    fn test_sort() -> Result<()> {
965        with_dag(|dag| -> Result<()> {
966            let set = "G C A E".into();
967            let sorted = r(dag.sort(&set))?;
968            assert_eq!(dbg(&sorted), "<spans [G+6, E+4, C+2] + 1 span>");
969            Ok(())
970        })
971    }
972
973    #[test]
974    fn test_reversed() -> Result<()> {
975        with_dag(|dag| {
976            let desc = r(dag.all())?;
977            let asc = desc
978                .as_any()
979                .downcast_ref::<IdStaticSet>()
980                .unwrap()
981                .clone()
982                .reversed();
983            check_invariants(&asc)?;
984            assert_eq!(dbg(&asc), "<spans [A:G+0:6] +>");
985            assert_eq!(
986                dbg(r(r(asc.iter())?.try_collect::<Vec<_>>())?),
987                "[A, B, C, D, E, F, G]"
988            );
989
990            Ok(())
991        })
992    }
993
994    #[test]
995    fn test_intersect_difference_preserve_reverse_order() -> Result<()> {
996        with_dag(|dag| -> Result<()> {
997            let abc = "A B C".into();
998            let cd = "C D".into();
999            let cba = r(dag.sort(&abc))?; // DESC
1000            let dc = r(dag.sort(&cd))?;
1001            let abc = cba.reverse();
1002
1003            let ab = abc.clone() - dc.clone();
1004            check_invariants(&*ab)?;
1005            assert_eq!(fmt_iter(&ab), "[A, B]");
1006
1007            let abc2 = abc.clone() & cba.clone();
1008            check_invariants(&*abc2)?;
1009            assert_eq!(fmt_iter(&abc2), "[A, B, C]");
1010
1011            let cba2 = cba & abc;
1012            check_invariants(&*cba2)?;
1013            assert_eq!(fmt_iter(&cba2), "[C, B, A]");
1014            Ok(())
1015        })
1016    }
1017
1018    #[test]
1019    fn test_skip_take_reverse() -> Result<()> {
1020        with_dag(|dag| {
1021            let set = r(dag.sort(&Set::from("A B C")))?;
1022            check_skip_take_reverse(set)
1023        })
1024    }
1025
1026    #[test]
1027    fn test_dag_hints_ancestors() -> Result<()> {
1028        with_dag(|dag| -> Result<()> {
1029            let abc = r(dag.ancestors("B C".into()))?;
1030            let abe = r(dag.common_ancestors("E".into()))?;
1031            let f: Set = "F".into();
1032            let all = r(dag.all())?;
1033
1034            assert!(has_ancestors_flag(abc.clone()));
1035            assert!(has_ancestors_flag(abe.clone()));
1036            assert!(has_ancestors_flag(all.clone()));
1037            assert!(has_ancestors_flag(r(dag.roots(abc.clone()))?));
1038            assert!(has_ancestors_flag(r(dag.parents(all.clone()))?));
1039
1040            assert!(!has_ancestors_flag(f.clone()));
1041            assert!(!has_ancestors_flag(r(dag.roots(f.clone()))?));
1042            assert!(!has_ancestors_flag(r(dag.parents(f.clone()))?));
1043
1044            Ok(())
1045        })
1046    }
1047
1048    #[test]
1049    fn test_dag_hints_ancestors_inheritance() -> Result<()> {
1050        with_dag(|dag1| -> Result<()> {
1051            with_dag(|dag2| -> Result<()> {
1052                let abc = r(dag1.ancestors("B C".into()))?;
1053
1054                // The ANCESTORS flag is kept by 'sort', 'parents', 'roots' on
1055                // the same dag.
1056                assert!(has_ancestors_flag(r(dag1.sort(&abc))?));
1057                assert!(has_ancestors_flag(r(dag1.parents(abc.clone()))?));
1058                assert!(has_ancestors_flag(r(dag1.roots(abc.clone()))?));
1059
1060                // The ANCESTORS flag is removed on a different dag, since the
1061                // different dag does not assume same graph / ancestry
1062                // relationship.
1063                assert!(!has_ancestors_flag(r(dag2.sort(&abc))?));
1064                assert!(!has_ancestors_flag(r(dag2.parents(abc.clone()))?));
1065                assert!(!has_ancestors_flag(r(dag2.roots(abc.clone()))?));
1066
1067                Ok(())
1068            })
1069        })
1070    }
1071
1072    #[test]
1073    fn test_dag_hints_ancestors_fast_paths() -> Result<()> {
1074        with_dag(|dag| -> Result<()> {
1075            let bfg: Set = "B F G".into();
1076
1077            // Set the ANCESTORS flag. It's incorrect but make it easier to test fast paths.
1078            bfg.hints().add_flags(Flags::ANCESTORS);
1079
1080            // Fast paths are not used if the set is not "bound" to the dag.
1081            assert_eq!(dbg(r(dag.ancestors(bfg.clone()))?), "<static [B, F, G]>");
1082            assert_eq!(dbg(r(dag.heads(bfg.clone()))?), "<spans [G+6]>");
1083
1084            // Binding to the Dag enables fast paths.
1085            let bfg = r(dag.sort(&bfg))?;
1086            bfg.hints().add_flags(Flags::ANCESTORS);
1087            assert_eq!(
1088                dbg(r(dag.ancestors(bfg.clone()))?),
1089                "<spans [F:G+5:6, B+1]>"
1090            );
1091
1092            // 'heads' has a fast path that uses 'heads_ancestors' to do the calculation.
1093            // (in this case the result is incorrect because the hints are wrong).
1094            assert_eq!(dbg(r(dag.heads(bfg.clone()))?), "<spans [G+6]>");
1095
1096            // 'ancestors' has a fast path that returns set as-is.
1097            // (in this case the result is incorrect because the hints are wrong).
1098            assert_eq!(
1099                dbg(r(dag.ancestors(bfg.clone()))?),
1100                "<spans [F:G+5:6, B+1]>"
1101            );
1102
1103            Ok(())
1104        })
1105    }
1106
1107    #[test]
1108    fn test_custom_order_hints() -> Result<()> {
1109        with_dag(|dag| -> Result<()> {
1110            // Valid Ids are 0 to 6.
1111            let map = dag.id_map_snapshot()?;
1112            let dag = dag.dag_snapshot()?;
1113            let show_hints = move |ids: &[u64]| {
1114                let list = IdList::from_ids(ids.iter().map(|&id| Id(id)));
1115                let set = IdStaticSet::from_id_list_idmap_dag(list, map.clone(), dag.clone());
1116                check_invariants(&set).unwrap();
1117                let set_reversed = set.clone().reversed();
1118                check_invariants(&set_reversed).unwrap();
1119                let hints = set.hints();
1120                format!(
1121                    "Order: {:?} Min: {:?} Max: {:?} {:?}",
1122                    set.iteration_order(),
1123                    hints.min_id(),
1124                    hints.max_id(),
1125                    hints.flags(),
1126                )
1127            };
1128
1129            assert_eq!(
1130                show_hints(&[]),
1131                "Order: Some(Desc) Min: None Max: None Flags(EMPTY | ID_DESC | ID_ASC | TOPO_DESC | ANCESTORS)"
1132            );
1133            assert_eq!(
1134                show_hints(&[2]),
1135                "Order: Some(Desc) Min: Some(2) Max: Some(2) Flags(ID_DESC | ID_ASC | TOPO_DESC | HAS_MIN_ID | HAS_MAX_ID)"
1136            );
1137            assert_eq!(
1138                show_hints(&[1, 2]),
1139                "Order: Some(Asc) Min: Some(1) Max: Some(2) Flags(ID_ASC | HAS_MIN_ID | HAS_MAX_ID)"
1140            );
1141            assert_eq!(
1142                show_hints(&[2, 1]),
1143                "Order: Some(Desc) Min: Some(1) Max: Some(2) Flags(ID_DESC | TOPO_DESC | HAS_MIN_ID | HAS_MAX_ID)"
1144            );
1145            assert_eq!(
1146                show_hints(&[1, 2, 4, 5]),
1147                "Order: Some(Asc) Min: Some(1) Max: Some(5) Flags(ID_ASC | HAS_MIN_ID | HAS_MAX_ID)"
1148            );
1149            assert_eq!(
1150                show_hints(&[5, 4, 2, 1]),
1151                "Order: Some(Desc) Min: Some(1) Max: Some(5) Flags(ID_DESC | TOPO_DESC | HAS_MIN_ID | HAS_MAX_ID)"
1152            );
1153            assert_eq!(
1154                show_hints(&[4, 5, 1, 2]),
1155                "Order: None Min: Some(1) Max: Some(5) Flags(HAS_MIN_ID | HAS_MAX_ID)"
1156            );
1157            assert_eq!(
1158                show_hints(&[2, 1, 5, 4]),
1159                "Order: None Min: Some(1) Max: Some(5) Flags(HAS_MIN_ID | HAS_MAX_ID)"
1160            );
1161            assert_eq!(
1162                show_hints(&[4, 5, 1]),
1163                "Order: None Min: Some(1) Max: Some(5) Flags(HAS_MIN_ID | HAS_MAX_ID)"
1164            );
1165            assert_eq!(
1166                show_hints(&[2, 1, 5]),
1167                "Order: None Min: Some(1) Max: Some(5) Flags(HAS_MIN_ID | HAS_MAX_ID)"
1168            );
1169
1170            Ok(())
1171        })
1172    }
1173
1174    #[test]
1175    fn test_custom_order_debug_fmt() -> Result<()> {
1176        with_dag(|dag| -> Result<()> {
1177            let map = dag.id_map_snapshot()?;
1178            let dag = dag.dag_snapshot()?;
1179            let list = move |ids: &[u64]| {
1180                let list = IdList::from_ids(ids.iter().map(|&id| Id(id)));
1181                IdStaticSet::from_id_list_idmap_dag(list, map.clone(), dag.clone())
1182            };
1183
1184            assert_eq!(dbg(list(&[1, 3, 2])), "<spans [B+1, D:C+3:2] ?>");
1185            assert_eq!(dbg(list(&[1, 3, 2]).reversed()), "<spans [B+1, D:C+3:2] ¿>");
1186
1187            Ok(())
1188        })
1189    }
1190
1191    fn has_ancestors_flag(set: Set) -> bool {
1192        set.hints().contains(Flags::ANCESTORS)
1193    }
1194
1195    /// Break <nested <nested <nested ... >>> into multi-lines.
1196    fn wrap_dbg_lines(value: &dyn fmt::Debug) -> String {
1197        #[derive(Default, Debug)]
1198        struct Fmt<'a> {
1199            head: &'a str,
1200            tail: &'a str,
1201            body: Vec<Fmt<'a>>,
1202            len: usize,
1203        }
1204
1205        fn indent(s: &str, prefix: &str) -> String {
1206            format!(
1207                "\n{}{}",
1208                prefix,
1209                s.trim().replace('\n', &format!("\n{}", prefix))
1210            )
1211        }
1212
1213        impl<'a> Fmt<'a> {
1214            // to_parse -> (Fmt, rest)
1215            fn parse(mut s: &'a str) -> (Self, &'a str) {
1216                let mut out = Self::default();
1217                let mut seen_left = false;
1218                let mut i = 0;
1219                while i < s.len() {
1220                    let ch = s.as_bytes()[i];
1221                    match ch {
1222                        b'<' => {
1223                            if seen_left {
1224                                if out.head.is_empty() {
1225                                    out.head = &s[..i].trim();
1226                                    out.len += out.head.len();
1227                                }
1228                                let (nested, rest) = Self::parse(&s[i..]);
1229                                out.len += nested.len;
1230                                out.body.push(nested);
1231                                s = rest;
1232                                i = 0;
1233                                continue;
1234                            } else {
1235                                seen_left = true;
1236                                i += 1;
1237                            }
1238                        }
1239                        b'>' => {
1240                            out.tail = &s[..i + 1].trim();
1241                            out.len += out.tail.len();
1242                            if out.head.is_empty() {
1243                                (out.head, out.tail) = out.tail.split_once(' ').unwrap();
1244                            }
1245                            let rest = &s[i + 1..];
1246                            return (out, rest);
1247                        }
1248                        _ => i += 1,
1249                    }
1250                }
1251                panic!("unbalanced <> in fmt string");
1252            }
1253
1254            fn pretty(&self) -> String {
1255                let mut out = String::new();
1256                let need_wrap = !self.body.is_empty() && self.len > 80;
1257                out.push_str(self.head);
1258                for f in &self.body {
1259                    let mut s = f.pretty();
1260                    if need_wrap {
1261                        s = indent(&s, "  ");
1262                    } else {
1263                        s = format!(" {}", s);
1264                    }
1265                    out.push_str(&s);
1266                }
1267                if self.tail != ">" {
1268                    out.push(' ');
1269                }
1270                out.push_str(self.tail);
1271                out
1272            }
1273        }
1274
1275        let s = dbg(value);
1276        let f = Fmt::parse(&s).0;
1277        indent(&f.pretty(), "                ")
1278    }
1279}