1use std::cmp::{Ordering, max};
2use std::ops::{Not, Range};
3
4use zcash_protocol::consensus::BlockHeight;
5
6use super::{ScanPriority, ScanRange};
7use RangeOrdering::*;
8
9#[derive(Debug, Clone, Copy)]
10enum InsertOn {
11 Left,
12 Right,
13}
14
15struct Insert {
16 on: InsertOn,
17 force_rescan: bool,
18}
19
20impl Insert {
21 fn left(force_rescan: bool) -> Self {
22 Insert {
23 on: InsertOn::Left,
24 force_rescan,
25 }
26 }
27
28 fn right(force_rescan: bool) -> Self {
29 Insert {
30 on: InsertOn::Right,
31 force_rescan,
32 }
33 }
34}
35
36impl Not for Insert {
37 type Output = Self;
38
39 fn not(self) -> Self::Output {
40 Insert {
41 on: match self.on {
42 InsertOn::Left => InsertOn::Right,
43 InsertOn::Right => InsertOn::Left,
44 },
45 force_rescan: self.force_rescan,
46 }
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51enum Dominance {
52 Left,
53 Right,
54 Equal,
55}
56
57impl From<Insert> for Dominance {
58 fn from(value: Insert) -> Self {
59 match value.on {
60 InsertOn::Left => Dominance::Left,
61 InsertOn::Right => Dominance::Right,
62 }
63 }
64}
65
66fn dominance(current: &ScanPriority, inserted: &ScanPriority, insert: Insert) -> Dominance {
71 match (current.cmp(inserted), (current, inserted)) {
72 (Ordering::Equal, _) => Dominance::Equal,
73 (_, (_, ScanPriority::Verify | ScanPriority::Scanned)) => Dominance::from(insert),
74 (_, (ScanPriority::Scanned, _)) if !insert.force_rescan => Dominance::from(!insert),
75 (Ordering::Less, _) => Dominance::from(insert),
76 (Ordering::Greater, _) => Dominance::from(!insert),
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82enum RangeOrdering {
83 LeftFirstDisjoint,
85 LeftFirstOverlap,
87 LeftContained,
89 Equal,
94 RightContained,
96 RightFirstOverlap,
98 RightFirstDisjoint,
100}
101
102impl RangeOrdering {
103 fn cmp<A: Ord>(a: &Range<A>, b: &Range<A>) -> Self {
104 assert!(a.start <= a.end && b.start <= b.end);
105 match (a.start.cmp(&b.start), a.end.cmp(&b.end)) {
106 _ if a.end <= b.start => RangeOrdering::LeftFirstDisjoint,
107 _ if b.end <= a.start => RangeOrdering::RightFirstDisjoint,
108 (Ordering::Less, Ordering::Less) => RangeOrdering::LeftFirstOverlap,
109 (Ordering::Equal, Ordering::Less)
110 | (Ordering::Greater, Ordering::Less)
111 | (Ordering::Greater, Ordering::Equal) => RangeOrdering::LeftContained,
112 (Ordering::Equal, Ordering::Equal) => RangeOrdering::Equal,
113 (Ordering::Equal, Ordering::Greater)
114 | (Ordering::Less, Ordering::Greater)
115 | (Ordering::Less, Ordering::Equal) => RangeOrdering::RightContained,
116 (Ordering::Greater, Ordering::Greater) => RangeOrdering::RightFirstOverlap,
117 }
118 }
119}
120
121#[derive(Debug, PartialEq, Eq)]
122enum Joined {
123 One(ScanRange),
124 Two(ScanRange, ScanRange),
125 Three(ScanRange, ScanRange, ScanRange),
126}
127
128fn join_nonoverlapping(left: ScanRange, right: ScanRange) -> Joined {
129 assert!(left.block_range().end <= right.block_range().start);
130
131 if left.block_range().end == right.block_range().start {
132 if left.priority() == right.priority() {
133 Joined::One(ScanRange::from_parts(
134 left.block_range().start..right.block_range().end,
135 left.priority(),
136 ))
137 } else {
138 Joined::Two(left, right)
139 }
140 } else {
141 let gap = ScanRange::from_parts(
143 left.block_range().end..right.block_range().start,
144 ScanPriority::Historic,
145 );
146
147 match join_nonoverlapping(left, gap) {
148 Joined::One(merged) => join_nonoverlapping(merged, right),
149 Joined::Two(left, gap) => match join_nonoverlapping(gap, right) {
150 Joined::One(merged) => Joined::Two(left, merged),
151 Joined::Two(gap, right) => Joined::Three(left, gap, right),
152 _ => unreachable!(),
153 },
154 _ => unreachable!(),
155 }
156 }
157}
158
159fn insert(current: ScanRange, to_insert: ScanRange, force_rescans: bool) -> Joined {
160 fn join_overlapping(left: ScanRange, right: ScanRange, insert: Insert) -> Joined {
161 assert!(
162 left.block_range().start <= right.block_range().start
163 && left.block_range().end > right.block_range().start
164 );
165
166 let dominance = match insert.on {
168 InsertOn::Left => dominance(&right.priority(), &left.priority(), insert),
169 InsertOn::Right => dominance(&left.priority(), &right.priority(), insert),
170 };
171
172 match dominance {
173 Dominance::Left => {
174 if let Some(right) = right.truncate_start(left.block_range().end) {
175 Joined::Two(left, right)
176 } else {
177 Joined::One(left)
178 }
179 }
180 Dominance::Equal => Joined::One(ScanRange::from_parts(
181 left.block_range().start..max(left.block_range().end, right.block_range().end),
182 left.priority(),
183 )),
184 Dominance::Right => match (
185 left.truncate_end(right.block_range().start),
186 left.truncate_start(right.block_range().end),
187 ) {
188 (Some(before), Some(after)) => Joined::Three(before, right, after),
189 (Some(before), None) => Joined::Two(before, right),
190 (None, Some(after)) => Joined::Two(right, after),
191 (None, None) => Joined::One(right),
192 },
193 }
194 }
195
196 match RangeOrdering::cmp(to_insert.block_range(), current.block_range()) {
197 LeftFirstDisjoint => join_nonoverlapping(to_insert, current),
198 LeftFirstOverlap | RightContained => {
199 join_overlapping(to_insert, current, Insert::left(force_rescans))
200 }
201 Equal => Joined::One(ScanRange::from_parts(
202 to_insert.block_range().clone(),
203 match dominance(
204 ¤t.priority(),
205 &to_insert.priority(),
206 Insert::right(force_rescans),
207 ) {
208 Dominance::Left | Dominance::Equal => current.priority(),
209 Dominance::Right => to_insert.priority(),
210 },
211 )),
212 RightFirstOverlap | LeftContained => {
213 join_overlapping(current, to_insert, Insert::right(force_rescans))
214 }
215 RightFirstDisjoint => join_nonoverlapping(current, to_insert),
216 }
217}
218
219#[derive(Debug, Clone)]
220#[cfg(feature = "unstable-spanning-tree")]
221pub enum SpanningTree {
222 Leaf(ScanRange),
223 Parent {
224 span: Range<BlockHeight>,
225 left: Box<SpanningTree>,
226 right: Box<SpanningTree>,
227 },
228}
229
230#[cfg(feature = "unstable-spanning-tree")]
231impl SpanningTree {
232 fn span(&self) -> Range<BlockHeight> {
233 match self {
234 SpanningTree::Leaf(entry) => entry.block_range().clone(),
235 SpanningTree::Parent { span, .. } => span.clone(),
236 }
237 }
238
239 fn from_joined(joined: Joined) -> Self {
240 match joined {
241 Joined::One(entry) => SpanningTree::Leaf(entry),
242 Joined::Two(left, right) => SpanningTree::Parent {
243 span: left.block_range().start..right.block_range().end,
244 left: Box::new(SpanningTree::Leaf(left)),
245 right: Box::new(SpanningTree::Leaf(right)),
246 },
247 Joined::Three(left, mid, right) => SpanningTree::Parent {
248 span: left.block_range().start..right.block_range().end,
249 left: Box::new(SpanningTree::Leaf(left)),
250 right: Box::new(SpanningTree::Parent {
251 span: mid.block_range().start..right.block_range().end,
252 left: Box::new(SpanningTree::Leaf(mid)),
253 right: Box::new(SpanningTree::Leaf(right)),
254 }),
255 },
256 }
257 }
258
259 fn from_insert(
260 left: Box<Self>,
261 right: Box<Self>,
262 to_insert: ScanRange,
263 insert: Insert,
264 ) -> Self {
265 let (left, right) = match insert.on {
266 InsertOn::Left => (Box::new(left.insert(to_insert, insert.force_rescan)), right),
267 InsertOn::Right => (left, Box::new(right.insert(to_insert, insert.force_rescan))),
268 };
269 SpanningTree::Parent {
270 span: left.span().start..right.span().end,
271 left,
272 right,
273 }
274 }
275
276 fn from_split(
277 left: Self,
278 right: Self,
279 to_insert: ScanRange,
280 split_point: BlockHeight,
281 force_rescans: bool,
282 ) -> Self {
283 let (l_insert, r_insert) = to_insert
284 .split_at(split_point)
285 .expect("Split point is within the range of to_insert");
286 let left = Box::new(left.insert(l_insert, force_rescans));
287 let right = Box::new(right.insert(r_insert, force_rescans));
288 SpanningTree::Parent {
289 span: left.span().start..right.span().end,
290 left,
291 right,
292 }
293 }
294
295 pub fn insert(self, to_insert: ScanRange, force_rescans: bool) -> Self {
296 match self {
297 SpanningTree::Leaf(cur) => Self::from_joined(insert(cur, to_insert, force_rescans)),
298 SpanningTree::Parent { span, left, right } => {
299 match RangeOrdering::cmp(&span, to_insert.block_range()) {
305 LeftFirstDisjoint => {
306 Self::from_insert(left, right, to_insert, Insert::right(force_rescans))
308 }
309 LeftFirstOverlap => {
310 let split_point = left.span().end;
311 if split_point > to_insert.block_range().start {
312 Self::from_split(*left, *right, to_insert, split_point, force_rescans)
313 } else {
314 Self::from_insert(left, right, to_insert, Insert::right(force_rescans))
316 }
317 }
318 RightContained => {
319 let split_point = left.span().end;
322 if to_insert.block_range().start >= split_point {
323 Self::from_insert(left, right, to_insert, Insert::right(force_rescans))
325 } else if to_insert.block_range().end <= split_point {
326 Self::from_insert(left, right, to_insert, Insert::left(force_rescans))
328 } else {
329 Self::from_split(*left, *right, to_insert, split_point, force_rescans)
331 }
332 }
333 Equal => {
334 let split_point = left.span().end;
335 if split_point > to_insert.block_range().start {
336 Self::from_split(*left, *right, to_insert, split_point, force_rescans)
337 } else {
338 right.insert(to_insert, force_rescans)
340 }
341 }
342 LeftContained => {
343 let split_point = left.span().end;
346 Self::from_split(*left, *right, to_insert, split_point, force_rescans)
347 }
348 RightFirstOverlap => {
349 let split_point = left.span().end;
350 if split_point < to_insert.block_range().end {
351 Self::from_split(*left, *right, to_insert, split_point, force_rescans)
352 } else {
353 Self::from_insert(left, right, to_insert, Insert::left(force_rescans))
355 }
356 }
357 RightFirstDisjoint => {
358 Self::from_insert(left, right, to_insert, Insert::left(force_rescans))
360 }
361 }
362 }
363 }
364 }
365
366 pub fn into_vec(self) -> Vec<ScanRange> {
367 fn go(acc: &mut Vec<ScanRange>, tree: SpanningTree) {
368 match tree {
369 SpanningTree::Leaf(entry) => {
370 if !entry.is_empty() {
371 if let Some(top) = acc.pop() {
372 match join_nonoverlapping(top, entry) {
373 Joined::One(merged) => acc.push(merged),
374 Joined::Two(l, r) => {
375 acc.push(l);
376 acc.push(r);
377 }
378 _ => unreachable!(),
379 }
380 } else {
381 acc.push(entry);
382 }
383 }
384 }
385 SpanningTree::Parent { left, right, .. } => {
386 go(acc, *left);
387 go(acc, *right);
388 }
389 }
390 }
391
392 let mut acc = vec![];
393 go(&mut acc, self);
394 acc
395 }
396}
397
398#[cfg(any(test, feature = "test-dependencies"))]
399pub mod testing {
400 use std::ops::Range;
401
402 use zcash_protocol::consensus::BlockHeight;
403
404 use crate::data_api::scanning::{ScanPriority, ScanRange};
405
406 pub fn scan_range(range: Range<u32>, priority: ScanPriority) -> ScanRange {
407 ScanRange::from_parts(
408 BlockHeight::from(range.start)..BlockHeight::from(range.end),
409 priority,
410 )
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use std::ops::Range;
417
418 use zcash_protocol::consensus::BlockHeight;
419
420 use super::RangeOrdering::*;
421 use super::{Joined, RangeOrdering, SpanningTree, join_nonoverlapping, testing::scan_range};
422 use crate::data_api::scanning::ScanPriority::*;
423 use crate::data_api::scanning::{ScanPriority, ScanRange};
424
425 #[test]
426 fn test_join_nonoverlapping() {
427 fn test_range(left: ScanRange, right: ScanRange, expected_joined: Joined) {
428 let joined = join_nonoverlapping(left, right);
429
430 assert_eq!(joined, expected_joined);
431 }
432
433 macro_rules! range {
434 ( $start:expr, $end:expr; $priority:ident ) => {
435 ScanRange::from_parts(
436 BlockHeight::from($start)..BlockHeight::from($end),
437 ScanPriority::$priority,
438 )
439 };
440 }
441
442 macro_rules! joined {
443 (
444 ($a_start:expr, $a_end:expr; $a_priority:ident)
445 ) => {
446 Joined::One(
447 range!($a_start, $a_end; $a_priority)
448 )
449 };
450 (
451 ($a_start:expr, $a_end:expr; $a_priority:ident),
452 ($b_start:expr, $b_end:expr; $b_priority:ident)
453 ) => {
454 Joined::Two(
455 range!($a_start, $a_end; $a_priority),
456 range!($b_start, $b_end; $b_priority)
457 )
458 };
459 (
460 ($a_start:expr, $a_end:expr; $a_priority:ident),
461 ($b_start:expr, $b_end:expr; $b_priority:ident),
462 ($c_start:expr, $c_end:expr; $c_priority:ident)
463
464 ) => {
465 Joined::Three(
466 range!($a_start, $a_end; $a_priority),
467 range!($b_start, $b_end; $b_priority),
468 range!($c_start, $c_end; $c_priority)
469 )
470 };
471 }
472
473 test_range(
476 range!(1, 9; OpenAdjacent),
477 range!(9, 15; OpenAdjacent),
478 joined!(
479 (1, 15; OpenAdjacent)
480 ),
481 );
482
483 test_range(
487 range!(1, 9; OpenAdjacent),
488 range!(9, 15; ChainTip),
489 joined!(
490 (1, 9; OpenAdjacent),
491 (9, 15; ChainTip)
492 ),
493 );
494
495 test_range(
498 range!(1, 9; OpenAdjacent),
499 range!(13, 15; OpenAdjacent),
500 joined!(
501 (1, 9; OpenAdjacent),
502 (9, 13; Historic),
503 (13, 15; OpenAdjacent)
504 ),
505 );
506
507 test_range(
508 range!(1, 9; Historic),
509 range!(13, 15; OpenAdjacent),
510 joined!(
511 (1, 13; Historic),
512 (13, 15; OpenAdjacent)
513 ),
514 );
515
516 test_range(
517 range!(1, 9; OpenAdjacent),
518 range!(13, 15; Historic),
519 joined!(
520 (1, 9; OpenAdjacent),
521 (9, 15; Historic)
522 ),
523 );
524 }
525
526 #[test]
527 fn range_ordering() {
528 assert_eq!(RangeOrdering::cmp(&(0..1), &(0..1)), Equal);
530
531 assert_eq!(RangeOrdering::cmp(&(0..1), &(1..2)), LeftFirstDisjoint);
533 assert_eq!(RangeOrdering::cmp(&(1..2), &(0..1)), RightFirstDisjoint);
534 assert_eq!(RangeOrdering::cmp(&(0..1), &(2..3)), LeftFirstDisjoint);
535 assert_eq!(RangeOrdering::cmp(&(2..3), &(0..1)), RightFirstDisjoint);
536 assert_eq!(RangeOrdering::cmp(&(1..2), &(2..2)), LeftFirstDisjoint);
537 assert_eq!(RangeOrdering::cmp(&(2..2), &(1..2)), RightFirstDisjoint);
538 assert_eq!(RangeOrdering::cmp(&(1..1), &(1..2)), LeftFirstDisjoint);
539 assert_eq!(RangeOrdering::cmp(&(1..2), &(1..1)), RightFirstDisjoint);
540
541 assert_eq!(RangeOrdering::cmp(&(1..2), &(0..3)), LeftContained);
543 assert_eq!(RangeOrdering::cmp(&(0..3), &(1..2)), RightContained);
544 assert_eq!(RangeOrdering::cmp(&(0..1), &(0..3)), LeftContained);
545 assert_eq!(RangeOrdering::cmp(&(0..3), &(0..1)), RightContained);
546 assert_eq!(RangeOrdering::cmp(&(2..3), &(0..3)), LeftContained);
547 assert_eq!(RangeOrdering::cmp(&(0..3), &(2..3)), RightContained);
548
549 assert_eq!(RangeOrdering::cmp(&(0..2), &(1..3)), LeftFirstOverlap);
551 assert_eq!(RangeOrdering::cmp(&(1..3), &(0..2)), RightFirstOverlap);
552 }
553
554 fn spanning_tree(to_insert: &[(Range<u32>, ScanPriority)]) -> Option<SpanningTree> {
555 to_insert.iter().fold(None, |acc, (range, priority)| {
556 let scan_range = scan_range(range.clone(), *priority);
557 match acc {
558 None => Some(SpanningTree::Leaf(scan_range)),
559 Some(t) => Some(t.insert(scan_range, false)),
560 }
561 })
562 }
563
564 #[test]
565 fn spanning_tree_insert_adjacent() {
566 let t = spanning_tree(&[
567 (0..3, Historic),
568 (3..6, Scanned),
569 (6..8, ChainTip),
570 (8..10, ChainTip),
571 ])
572 .unwrap();
573
574 assert_eq!(
575 t.into_vec(),
576 vec![
577 scan_range(0..3, Historic),
578 scan_range(3..6, Scanned),
579 scan_range(6..10, ChainTip),
580 ]
581 );
582 }
583
584 #[test]
585 fn spanning_tree_insert_overlaps() {
586 let t = spanning_tree(&[
587 (0..3, Historic),
588 (2..5, Scanned),
589 (6..8, ChainTip),
590 (7..10, Scanned),
591 ])
592 .unwrap();
593
594 assert_eq!(
595 t.into_vec(),
596 vec![
597 scan_range(0..2, Historic),
598 scan_range(2..5, Scanned),
599 scan_range(5..6, Historic),
600 scan_range(6..7, ChainTip),
601 scan_range(7..10, Scanned),
602 ]
603 );
604 }
605
606 #[test]
607 fn spanning_tree_insert_empty() {
608 let t = spanning_tree(&[
609 (0..3, Historic),
610 (3..6, Scanned),
611 (6..6, FoundNote),
612 (6..8, Scanned),
613 (8..10, ChainTip),
614 ])
615 .unwrap();
616
617 assert_eq!(
618 t.into_vec(),
619 vec![
620 scan_range(0..3, Historic),
621 scan_range(3..8, Scanned),
622 scan_range(8..10, ChainTip),
623 ]
624 );
625 }
626
627 #[test]
628 fn spanning_tree_insert_gaps() {
629 let t = spanning_tree(&[(0..3, Historic), (6..8, ChainTip)]).unwrap();
630
631 assert_eq!(
632 t.into_vec(),
633 vec![scan_range(0..6, Historic), scan_range(6..8, ChainTip),]
634 );
635
636 let t = spanning_tree(&[(0..3, Historic), (3..4, Verify), (6..8, ChainTip)]).unwrap();
637
638 assert_eq!(
639 t.into_vec(),
640 vec![
641 scan_range(0..3, Historic),
642 scan_range(3..4, Verify),
643 scan_range(4..6, Historic),
644 scan_range(6..8, ChainTip),
645 ]
646 );
647 }
648
649 #[test]
650 fn spanning_tree_insert_rfd_span() {
651 let t = spanning_tree(&[
656 (6..8, Scanned),
658 (10..12, ChainTip),
662 (3..6, Historic),
666 ])
667 .unwrap();
668
669 assert_eq!(t.span(), (3.into())..(12.into()));
670 assert_eq!(
671 t.into_vec(),
672 vec![
673 scan_range(3..6, Historic),
674 scan_range(6..8, Scanned),
675 scan_range(8..10, Historic),
676 scan_range(10..12, ChainTip),
677 ]
678 );
679 }
680
681 #[test]
682 fn spanning_tree_dominance() {
683 let t = spanning_tree(&[(0..3, Verify), (2..8, Scanned), (6..10, Verify)]).unwrap();
684 assert_eq!(
685 t.into_vec(),
686 vec![
687 scan_range(0..2, Verify),
688 scan_range(2..6, Scanned),
689 scan_range(6..10, Verify),
690 ]
691 );
692
693 let t = spanning_tree(&[(0..3, Verify), (2..8, Historic), (6..10, Verify)]).unwrap();
694 assert_eq!(
695 t.into_vec(),
696 vec![
697 scan_range(0..3, Verify),
698 scan_range(3..6, Historic),
699 scan_range(6..10, Verify),
700 ]
701 );
702
703 let t = spanning_tree(&[(0..3, Scanned), (2..8, Verify), (6..10, Scanned)]).unwrap();
704 assert_eq!(
705 t.into_vec(),
706 vec![
707 scan_range(0..2, Scanned),
708 scan_range(2..6, Verify),
709 scan_range(6..10, Scanned),
710 ]
711 );
712
713 let t = spanning_tree(&[(0..3, Scanned), (2..8, Historic), (6..10, Scanned)]).unwrap();
714 assert_eq!(
715 t.into_vec(),
716 vec![
717 scan_range(0..3, Scanned),
718 scan_range(3..6, Historic),
719 scan_range(6..10, Scanned),
720 ]
721 );
722
723 let mut t = spanning_tree(&[(0..3, ChainTip), (3..5, Scanned), (5..7, ChainTip)]).unwrap();
725 t = t.insert(scan_range(0..7, ChainTip), false);
726 assert_eq!(
727 t.into_vec(),
728 vec![
729 scan_range(0..3, ChainTip),
730 scan_range(3..5, Scanned),
731 scan_range(5..7, ChainTip),
732 ]
733 );
734
735 let mut t =
736 spanning_tree(&[(280300..280310, FoundNote), (280310..280320, Scanned)]).unwrap();
737 assert_eq!(
738 t.clone().into_vec(),
739 vec![
740 scan_range(280300..280310, FoundNote),
741 scan_range(280310..280320, Scanned)
742 ]
743 );
744 t = t.insert(scan_range(280300..280340, ChainTip), false);
745 assert_eq!(
746 t.into_vec(),
747 vec![
748 scan_range(280300..280310, ChainTip),
749 scan_range(280310..280320, Scanned),
750 scan_range(280320..280340, ChainTip)
751 ]
752 );
753 }
754
755 #[test]
756 fn spanning_tree_insert_coalesce_scanned() {
757 let mut t = spanning_tree(&[
758 (0..3, Historic),
759 (2..5, Scanned),
760 (6..8, ChainTip),
761 (7..10, Scanned),
762 ])
763 .unwrap();
764
765 t = t.insert(scan_range(0..3, Scanned), false);
766 t = t.insert(scan_range(5..8, Scanned), false);
767
768 assert_eq!(t.into_vec(), vec![scan_range(0..10, Scanned)]);
769 }
770
771 #[test]
772 fn spanning_tree_force_rescans() {
773 let mut t = spanning_tree(&[
774 (0..3, Historic),
775 (3..5, Scanned),
776 (5..7, ChainTip),
777 (7..10, Scanned),
778 ])
779 .unwrap();
780
781 t = t.insert(scan_range(4..9, OpenAdjacent), true);
782
783 let expected = vec![
784 scan_range(0..3, Historic),
785 scan_range(3..4, Scanned),
786 scan_range(4..5, OpenAdjacent),
787 scan_range(5..7, ChainTip),
788 scan_range(7..9, OpenAdjacent),
789 scan_range(9..10, Scanned),
790 ];
791 assert_eq!(t.clone().into_vec(), expected);
792
793 t = t.insert(scan_range(2..5, Ignored), true);
796 assert_eq!(t.into_vec(), expected);
797 }
798}