willow_data_model/
relative_encodings.rs

1use syncify::syncify;
2use syncify::syncify_replace;
3
4#[syncify(encoding_sync)]
5pub(super) mod encoding {
6    use super::*;
7
8    #[syncify_replace(use ufotofu::sync::{BulkConsumer, BulkProducer};)]
9    use ufotofu::local_nb::{BulkConsumer, BulkProducer};
10
11    use willow_encoding::{is_bitflagged, CompactWidth, DecodeError};
12    #[syncify_replace(use willow_encoding::sync::{Encodable, Decodable, RelativeDecodable, RelativeEncodable};)]
13    use willow_encoding::{Decodable, Encodable, RelativeDecodable, RelativeEncodable};
14
15    #[syncify_replace(use willow_encoding::sync::{decode_max_power, encode_max_power};)]
16    use willow_encoding::{decode_max_power, encode_max_power};
17
18    #[syncify_replace(use willow_encoding::sync::{ decode_compact_width_be, encode_compact_width_be};)]
19    use willow_encoding::{decode_compact_width_be, encode_compact_width_be};
20
21    #[syncify_replace(use willow_encoding::sync::produce_byte;)]
22    use willow_encoding::produce_byte;
23
24    use core::mem::size_of;
25
26    use crate::{
27        entry::Entry,
28        grouping::{Area, AreaSubspace, Range, Range3d, RangeEnd},
29        parameters::{NamespaceId, PayloadDigest, SubspaceId},
30        path::Path,
31        ScratchSpacePathDecoding,
32    };
33
34    // Path <> Path
35    impl<const MCL: usize, const MCC: usize, const MPL: usize>
36        RelativeEncodable<Path<MCL, MCC, MPL>> for Path<MCL, MCC, MPL>
37    {
38        /// Encode a path relative to another path.
39        ///
40        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_path_relative)
41        async fn relative_encode<Consumer>(
42            &self,
43            reference: &Path<MCL, MCC, MPL>,
44            consumer: &mut Consumer,
45        ) -> Result<(), Consumer::Error>
46        where
47            Consumer: BulkConsumer<Item = u8>,
48        {
49            let lcp = self.longest_common_prefix(reference);
50            let lcp_component_count = lcp.get_component_count();
51            encode_max_power(lcp_component_count, MCC, consumer).await?;
52
53            let suffix_component_count = self.get_component_count() - lcp_component_count;
54            encode_max_power(suffix_component_count, MCC, consumer).await?;
55
56            for component in self.suffix_components(lcp_component_count) {
57                encode_max_power(component.len(), MCL, consumer).await?;
58
59                consumer
60                    .bulk_consume_full_slice(component.as_ref())
61                    .await
62                    .map_err(|f| f.reason)?;
63            }
64
65            Ok(())
66        }
67    }
68
69    impl<const MCL: usize, const MCC: usize, const MPL: usize>
70        RelativeDecodable<Path<MCL, MCC, MPL>> for Path<MCL, MCC, MPL>
71    {
72        /// Decode a path relative to this path.
73        ///
74        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_path_relative)
75        async fn relative_decode<Producer>(
76            reference: &Path<MCL, MCC, MPL>,
77            producer: &mut Producer,
78        ) -> Result<Self, DecodeError<Producer::Error>>
79        where
80            Producer: BulkProducer<Item = u8>,
81            Self: Sized,
82        {
83            let lcp_component_count: usize = decode_max_power(MCC, producer).await?.try_into()?;
84
85            if lcp_component_count == 0 {
86                let decoded = Path::<MCL, MCC, MPL>::decode(producer).await?;
87
88                // === Necessary to produce canonic encodings. ===
89                if lcp_component_count
90                    != decoded
91                        .longest_common_prefix(reference)
92                        .get_component_count()
93                {
94                    return Err(DecodeError::InvalidInput);
95                }
96                // ===============================================
97
98                return Ok(decoded);
99            }
100
101            let prefix = reference
102                .create_prefix(lcp_component_count as usize)
103                .ok_or(DecodeError::InvalidInput)?;
104
105            let mut buf = ScratchSpacePathDecoding::<MCC, MPL>::new();
106
107            // Copy the accumulated component lengths of the prefix into the scratch buffer.
108            let raw_prefix_acc_component_lengths = &prefix.raw_buf()
109                [size_of::<usize>()..size_of::<usize>() * (lcp_component_count + 1)];
110            unsafe {
111                // Safe because len is less than size_of::<usize>() times the MCC, because `prefix` respects the MCC.
112                buf.set_many_component_accumulated_lengths_from_ne(
113                    raw_prefix_acc_component_lengths,
114                );
115            }
116
117            // Copy the raw path data of the prefix into the scratch buffer.
118            unsafe {
119                // safe because we just copied the accumulated component lengths for the first `lcp_component_count` components.
120                buf.path_data_until_as_mut(lcp_component_count)
121                    .copy_from_slice(
122                        &reference.raw_buf()[size_of::<usize>()
123                            * (reference.get_component_count() + 1)
124                            ..size_of::<usize>() * (reference.get_component_count() + 1)
125                                + prefix.get_path_length()],
126                    );
127            }
128
129            let remaining_component_count: usize =
130                decode_max_power(MCC, producer).await?.try_into()?;
131            let total_component_count = lcp_component_count + remaining_component_count;
132            if total_component_count > MCC {
133                return Err(DecodeError::InvalidInput);
134            }
135
136            let mut accumulated_component_length: usize = prefix.get_path_length(); // Always holds the acc length of all components we copied so far.
137            for i in lcp_component_count..total_component_count {
138                let component_len: usize = decode_max_power(MCL, producer).await?.try_into()?;
139                if component_len > MCL {
140                    return Err(DecodeError::InvalidInput);
141                }
142
143                accumulated_component_length += component_len;
144                if accumulated_component_length > MPL {
145                    return Err(DecodeError::InvalidInput);
146                }
147
148                buf.set_component_accumulated_length(accumulated_component_length, i);
149
150                // Decode the component itself into the scratch buffer.
151                producer
152                    .bulk_overwrite_full_slice(unsafe {
153                        // Safe because we called set_component_Accumulated_length for all j <= i
154                        buf.path_data_as_mut(i)
155                    })
156                    .await?;
157            }
158
159            let decoded = unsafe { buf.to_path(total_component_count) };
160
161            // === Necessary to produce canonic encodings. ===
162            if lcp_component_count
163                != decoded
164                    .longest_common_prefix(reference)
165                    .get_component_count()
166            {
167                return Err(DecodeError::InvalidInput);
168            }
169            // ===============================================
170
171            Ok(decoded)
172        }
173    }
174
175    // Entry <> Entry
176
177    impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
178        RelativeEncodable<Entry<MCL, MCC, MPL, N, S, PD>> for Entry<MCL, MCC, MPL, N, S, PD>
179    where
180        N: NamespaceId + Encodable,
181        S: SubspaceId + Encodable,
182        PD: PayloadDigest + Encodable,
183    {
184        /// Encode an [`Entry`] relative to a reference [`Entry`].
185        ///
186        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_etry_relative_entry).
187        async fn relative_encode<Consumer>(
188            &self,
189            reference: &Entry<MCL, MCC, MPL, N, S, PD>,
190            consumer: &mut Consumer,
191        ) -> Result<(), Consumer::Error>
192        where
193            Consumer: BulkConsumer<Item = u8>,
194        {
195            let time_diff = self.timestamp().abs_diff(reference.timestamp());
196
197            let mut header: u8 = 0b0000_0000;
198
199            if self.namespace_id() != reference.namespace_id() {
200                header |= 0b1000_0000;
201            }
202
203            if self.subspace_id() != reference.subspace_id() {
204                header |= 0b0100_0000;
205            }
206
207            if self.timestamp() > reference.timestamp() {
208                header |= 0b0010_0000;
209            }
210
211            header |= CompactWidth::from_u64(time_diff).bitmask(4);
212
213            header |= CompactWidth::from_u64(self.payload_length()).bitmask(6);
214
215            consumer.consume(header).await?;
216
217            if self.namespace_id() != reference.namespace_id() {
218                self.namespace_id().encode(consumer).await?;
219            }
220
221            if self.subspace_id() != reference.subspace_id() {
222                self.subspace_id().encode(consumer).await?;
223            }
224
225            self.path()
226                .relative_encode(reference.path(), consumer)
227                .await?;
228
229            encode_compact_width_be(time_diff, consumer).await?;
230
231            encode_compact_width_be(self.payload_length(), consumer).await?;
232
233            self.payload_digest().encode(consumer).await?;
234
235            Ok(())
236        }
237    }
238
239    impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
240        RelativeDecodable<Entry<MCL, MCC, MPL, N, S, PD>> for Entry<MCL, MCC, MPL, N, S, PD>
241    where
242        N: NamespaceId + Decodable + std::fmt::Debug,
243        S: SubspaceId + Decodable + std::fmt::Debug,
244        PD: PayloadDigest + Decodable,
245    {
246        /// Decode an [`Entry`] relative from this [`Entry`].
247        ///
248        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_etry_relative_entry).
249        async fn relative_decode<Producer>(
250            reference: &Entry<MCL, MCC, MPL, N, S, PD>,
251            producer: &mut Producer,
252        ) -> Result<Entry<MCL, MCC, MPL, N, S, PD>, DecodeError<Producer::Error>>
253        where
254            Producer: BulkProducer<Item = u8>,
255            Self: Sized,
256        {
257            let header = produce_byte(producer).await?;
258
259            // Verify that bit 3 is 0 as specified.
260            if is_bitflagged(header, 3) {
261                return Err(DecodeError::InvalidInput);
262            }
263
264            let is_namespace_encoded = is_bitflagged(header, 0);
265            let is_subspace_encoded = is_bitflagged(header, 1);
266            let add_or_subtract_time_diff = is_bitflagged(header, 2);
267            let compact_width_time_diff = CompactWidth::decode_fixed_width_bitmask(header, 4);
268            let compact_width_payload_length = CompactWidth::decode_fixed_width_bitmask(header, 6);
269
270            let namespace_id = if is_namespace_encoded {
271                N::decode(producer).await?
272            } else {
273                reference.namespace_id().clone()
274            };
275
276            /*
277            // Verify that the encoded namespace wasn't the same as ours
278            // Which would indicate invalid input
279            if is_namespace_encoded && namespace_id == reference.get_namespace_id() {
280                return Err(DecodeError::InvalidInput);
281            }
282            */
283
284            let subspace_id = if is_subspace_encoded {
285                S::decode(producer).await?
286            } else {
287                reference.subspace_id().clone()
288            };
289
290            /*
291            // Verify that the encoded subspace wasn't the same as ours
292            // Which would indicate invalid input
293            if is_subspace_encoded && subspace_id == reference.get_subspace_id() {
294                return Err(DecodeError::InvalidInput);
295            }
296            */
297
298            let path = Path::<MCL, MCC, MPL>::relative_decode(reference.path(), producer).await?;
299
300            let time_diff = decode_compact_width_be(compact_width_time_diff, producer).await?;
301
302            // Add or subtract safely here to avoid overflows caused by malicious or faulty encodings.
303            let timestamp = if add_or_subtract_time_diff {
304                reference
305                    .timestamp()
306                    .checked_add(time_diff)
307                    .ok_or(DecodeError::InvalidInput)?
308            } else {
309                reference
310                    .timestamp()
311                    .checked_sub(time_diff)
312                    .ok_or(DecodeError::InvalidInput)?
313            };
314
315            /*
316            // Verify that the correct add_or_subtract_time_diff flag was set.
317            let should_have_subtracted = timestamp <= reference.get_timestamp();
318            if add_or_subtract_time_diff && should_have_subtracted {
319                return Err(DecodeError::InvalidInput);
320            }
321            */
322
323            let payload_length =
324                decode_compact_width_be(compact_width_payload_length, producer).await?;
325
326            let payload_digest = PD::decode(producer).await?;
327
328            Ok(Entry::new(
329                namespace_id,
330                subspace_id,
331                path,
332                timestamp,
333                payload_length,
334                payload_digest,
335            ))
336        }
337    }
338
339    impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
340        RelativeEncodable<(N, Area<MCL, MCC, MPL, S>)> for Entry<MCL, MCC, MPL, N, S, PD>
341    where
342        N: NamespaceId + Encodable,
343        S: SubspaceId + Encodable,
344        PD: PayloadDigest + Encodable,
345    {
346        /// Encode an [`Entry`] relative to a reference [`NamespaceId`] and [`Area`].
347        ///
348        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_entry_in_namespace_area).
349        async fn relative_encode<Consumer>(
350            &self,
351            reference: &(N, Area<MCL, MCC, MPL, S>),
352            consumer: &mut Consumer,
353        ) -> Result<(), Consumer::Error>
354        where
355            Consumer: BulkConsumer<Item = u8>,
356        {
357            let (namespace, out) = reference;
358
359            if self.namespace_id() != namespace {
360                panic!("Tried to encode an entry relative to a namespace it does not belong to")
361            }
362
363            if !out.includes_entry(self) {
364                panic!("Tried to encode an entry relative to an area it is not included by")
365            }
366
367            let time_diff = core::cmp::min(
368                self.timestamp() - out.times().start,
369                u64::from(&out.times().end) - self.timestamp(),
370            );
371
372            let mut header = 0b0000_0000;
373
374            if out.subspace().is_any() {
375                header |= 0b1000_0000;
376            }
377
378            if self.timestamp() - out.times().start
379                <= u64::from(&out.times().end) - self.timestamp()
380            {
381                header |= 0b0100_0000;
382            }
383
384            header |= CompactWidth::from_u64(time_diff).bitmask(2);
385            header |= CompactWidth::from_u64(self.payload_length()).bitmask(4);
386
387            consumer.consume(header).await?;
388
389            if out.subspace().is_any() {
390                self.subspace_id().encode(consumer).await?;
391            }
392
393            self.path().relative_encode(out.path(), consumer).await?;
394            encode_compact_width_be(time_diff, consumer).await?;
395            encode_compact_width_be(self.payload_length(), consumer).await?;
396            self.payload_digest().encode(consumer).await?;
397
398            Ok(())
399        }
400    }
401
402    impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
403        RelativeDecodable<(N, Area<MCL, MCC, MPL, S>)> for Entry<MCL, MCC, MPL, N, S, PD>
404    where
405        N: NamespaceId + Decodable,
406        S: SubspaceId + Decodable + std::fmt::Debug,
407        PD: PayloadDigest + Decodable,
408    {
409        /// Decode an [`Entry`] relative to a reference [`NamespaceId`] and [`Area`].
410        ///
411        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_entry_in_namespace_area).
412        async fn relative_decode<Producer>(
413            reference: &(N, Area<MCL, MCC, MPL, S>),
414            producer: &mut Producer,
415        ) -> Result<Self, DecodeError<Producer::Error>>
416        where
417            Producer: BulkProducer<Item = u8>,
418            Self: Sized,
419        {
420            let (namespace, out) = reference;
421
422            let header = produce_byte(producer).await?;
423
424            let is_subspace_encoded = is_bitflagged(header, 0);
425            let add_time_diff_to_start = is_bitflagged(header, 1);
426            let time_diff_compact_width = CompactWidth::decode_fixed_width_bitmask(header, 2);
427            let payload_length_compact_width = CompactWidth::decode_fixed_width_bitmask(header, 4);
428
429            if is_bitflagged(header, 6) || is_bitflagged(header, 7) {
430                return Err(DecodeError::InvalidInput);
431            }
432
433            let subspace_id = if is_subspace_encoded {
434                match &out.subspace() {
435                    AreaSubspace::Any => S::decode(producer).await?,
436                    AreaSubspace::Id(_) => return Err(DecodeError::InvalidInput),
437                }
438            } else {
439                match &out.subspace() {
440                    AreaSubspace::Any => return Err(DecodeError::InvalidInput),
441                    AreaSubspace::Id(id) => id.clone(),
442                }
443            };
444
445            let path = Path::relative_decode(out.path(), producer).await?;
446
447            if !path.is_prefixed_by(out.path()) {
448                return Err(DecodeError::InvalidInput);
449            }
450
451            let time_diff = decode_compact_width_be(time_diff_compact_width, producer).await?;
452            let payload_length =
453                decode_compact_width_be(payload_length_compact_width, producer).await?;
454
455            let payload_digest = PD::decode(producer).await?;
456
457            let timestamp = if add_time_diff_to_start {
458                out.times().start.checked_add(time_diff)
459            } else {
460                u64::from(&out.times().end).checked_sub(time_diff)
461            }
462            .ok_or(DecodeError::InvalidInput)?;
463
464            // === Necessary to produce canonic encodings. ===
465            // Verify that the correct add_or_subtract_time_diff flag was set.
466            let should_have_added = timestamp.checked_sub(out.times().start)
467                <= u64::from(&out.times().end).checked_sub(timestamp);
468
469            if add_time_diff_to_start != should_have_added {
470                return Err(DecodeError::InvalidInput);
471            }
472            // ===============================================
473
474            if !out.times().includes(&timestamp) {
475                return Err(DecodeError::InvalidInput);
476            }
477
478            Ok(Self::new(
479                namespace.clone(),
480                subspace_id,
481                path,
482                timestamp,
483                payload_length,
484                payload_digest,
485            ))
486        }
487    }
488
489    impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
490        RelativeEncodable<(N, Range3d<MCL, MCC, MPL, S>)> for Entry<MCL, MCC, MPL, N, S, PD>
491    where
492        N: NamespaceId + Encodable,
493        S: SubspaceId + Encodable,
494        PD: PayloadDigest + Encodable,
495    {
496        /// Encode an [`Entry`] relative to a reference [`NamespaceId`] and [`Range3d`].
497        ///
498        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_entry_in_namespace_3drange).
499        async fn relative_encode<Consumer>(
500            &self,
501            reference: &(N, Range3d<MCL, MCC, MPL, S>),
502            consumer: &mut Consumer,
503        ) -> Result<(), Consumer::Error>
504        where
505            Consumer: BulkConsumer<Item = u8>,
506        {
507            let (namespace, out) = reference;
508
509            if self.namespace_id() != namespace {
510                panic!("Tried to encode an entry relative to a namespace it does not belong to")
511            }
512
513            if !out.includes_entry(self) {
514                panic!("Tried to encode an entry relative to a 3d range it is not included by")
515            }
516
517            let time_diff = core::cmp::min(
518                self.timestamp().abs_diff(out.times().start),
519                self.timestamp().abs_diff(u64::from(&out.times().end)),
520            );
521
522            let mut header = 0b0000_0000;
523
524            // Encode e.get_subspace_id()?
525            if self.subspace_id() != &out.subspaces().start {
526                header |= 0b1000_0000;
527            }
528
529            // Encode e.get_path() relative to out.get_paths().start or to out.get_paths().end?
530            let encode_path_relative_to_start = match &out.paths().end {
531                RangeEnd::Closed(end_path) => {
532                    let start_lcp = self.path().longest_common_prefix(&out.paths().start);
533                    let end_lcp = self.path().longest_common_prefix(end_path);
534
535                    start_lcp.get_component_count() >= end_lcp.get_component_count()
536                }
537                RangeEnd::Open => true,
538            };
539
540            if encode_path_relative_to_start {
541                header |= 0b0100_0000;
542            }
543
544            // Add time_diff to out.get_times().start, or subtract from out.get_times().end?
545            let add_time_diff_with_start =
546                time_diff == self.timestamp().abs_diff(out.times().start);
547
548            if add_time_diff_with_start {
549                header |= 0b0010_0000;
550            }
551
552            // 2-bit integer n such that 2^n gives compact_width(time_diff)
553            header |= CompactWidth::from_u64(time_diff).bitmask(4);
554
555            // 2-bit integer n such that 2^n gives compact_width(e.get_payload_length())
556            header |= CompactWidth::from_u64(self.payload_length()).bitmask(6);
557
558            consumer.consume(header).await?;
559
560            if self.subspace_id() != &out.subspaces().start {
561                self.subspace_id().encode(consumer).await?;
562            }
563
564            // Encode e.get_path() relative to out.get_paths().start or to out.get_paths().end?
565            match &out.paths().end {
566                RangeEnd::Closed(end_path) => {
567                    if encode_path_relative_to_start {
568                        self.path()
569                            .relative_encode(&out.paths().start, consumer)
570                            .await?;
571                    } else {
572                        self.path().relative_encode(end_path, consumer).await?;
573                    }
574                }
575                RangeEnd::Open => {
576                    self.path()
577                        .relative_encode(&out.paths().start, consumer)
578                        .await?;
579                }
580            }
581
582            encode_compact_width_be(time_diff, consumer).await?;
583            encode_compact_width_be(self.payload_length(), consumer).await?;
584            self.payload_digest().encode(consumer).await?;
585
586            Ok(())
587        }
588    }
589
590    impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
591        RelativeDecodable<(N, Range3d<MCL, MCC, MPL, S>)> for Entry<MCL, MCC, MPL, N, S, PD>
592    where
593        N: NamespaceId + Decodable,
594        S: SubspaceId + Decodable + std::fmt::Debug,
595        PD: PayloadDigest + Decodable,
596    {
597        /// Decode an [`Entry`] relative to a reference [`NamespaceId`] and [`Range3d`].
598        ///
599        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_entry_in_namespace_3drange).
600        async fn relative_decode<Producer>(
601            reference: &(N, Range3d<MCL, MCC, MPL, S>),
602            producer: &mut Producer,
603        ) -> Result<Self, DecodeError<Producer::Error>>
604        where
605            Producer: BulkProducer<Item = u8>,
606            Self: Sized,
607        {
608            let (namespace, out) = reference;
609
610            let header = produce_byte(producer).await?;
611
612            // Decode e.get_subspace_id()?
613            let is_subspace_encoded = is_bitflagged(header, 0);
614
615            // Decode e.get_path() relative to out.get_paths().start or to out.get_paths().end?
616            let decode_path_relative_to_start = is_bitflagged(header, 1);
617
618            // Add time_diff to out.get_times().start, or subtract from out.get_times().end?
619            let add_time_diff_with_start = is_bitflagged(header, 2);
620
621            if is_bitflagged(header, 3) {
622                return Err(DecodeError::InvalidInput);
623            }
624
625            let time_diff_compact_width = CompactWidth::decode_fixed_width_bitmask(header, 4);
626            let payload_length_compact_width = CompactWidth::decode_fixed_width_bitmask(header, 6);
627
628            let subspace_id = if is_subspace_encoded {
629                S::decode(producer).await?
630            } else {
631                out.subspaces().start.clone()
632            };
633
634            // === Necessary to produce canonic encodings. ===
635            // Verify that encoding the subspace was necessary.
636            if subspace_id == out.subspaces().start && is_subspace_encoded {
637                return Err(DecodeError::InvalidInput);
638            }
639            // ===============================================
640
641            // Verify that subspace is included by range
642            if !out.subspaces().includes(&subspace_id) {
643                return Err(DecodeError::InvalidInput);
644            }
645
646            let path = if decode_path_relative_to_start {
647                Path::relative_decode(&out.paths().start, producer).await?
648            } else {
649                match &out.paths().end {
650                    RangeEnd::Closed(end_path) => Path::relative_decode(end_path, producer).await?,
651                    RangeEnd::Open => return Err(DecodeError::InvalidInput),
652                }
653            };
654
655            // Verify that path is included by range
656            if !out.paths().includes(&path) {
657                return Err(DecodeError::InvalidInput);
658            }
659
660            // === Necessary to produce canonic encodings. ===
661            // Verify that the path was encoded relative to the correct bound of the referenc path range.
662            let should_have_encoded_path_relative_to_start = match &out.paths().end {
663                RangeEnd::Closed(end_path) => {
664                    let start_lcp = path.longest_common_prefix(&out.paths().start);
665                    let end_lcp = path.longest_common_prefix(end_path);
666
667                    start_lcp.get_component_count() >= end_lcp.get_component_count()
668                }
669                RangeEnd::Open => true,
670            };
671
672            if decode_path_relative_to_start != should_have_encoded_path_relative_to_start {
673                return Err(DecodeError::InvalidInput);
674            }
675            // =================================================
676
677            let time_diff = decode_compact_width_be(time_diff_compact_width, producer).await?;
678
679            let payload_length =
680                decode_compact_width_be(payload_length_compact_width, producer).await?;
681
682            let payload_digest = PD::decode(producer).await?;
683
684            let timestamp = if add_time_diff_with_start {
685                out.times().start.checked_add(time_diff)
686            } else {
687                match &out.times().end {
688                    RangeEnd::Closed(end_time) => end_time.checked_sub(time_diff),
689                    RangeEnd::Open => u64::from(&out.times().end).checked_sub(time_diff),
690                }
691            }
692            .ok_or(DecodeError::InvalidInput)?;
693
694            // Verify that timestamp is included by range
695            if !out.times().includes(&timestamp) {
696                return Err(DecodeError::InvalidInput);
697            }
698
699            // === Necessary to produce canonic encodings. ===
700            // Verify that time_diff is what it should have been
701            let correct_time_diff = core::cmp::min(
702                timestamp.abs_diff(out.times().start),
703                timestamp.abs_diff(u64::from(&out.times().end)),
704            );
705
706            if time_diff != correct_time_diff {
707                return Err(DecodeError::InvalidInput);
708            }
709
710            // Verify that the combine with start bitflag in the header was correct
711            let should_have_added_to_start = time_diff == timestamp.abs_diff(out.times().start);
712
713            if should_have_added_to_start != add_time_diff_with_start {
714                return Err(DecodeError::InvalidInput);
715            }
716            // ==============================================
717
718            Ok(Self::new(
719                namespace.clone(),
720                subspace_id,
721                path,
722                timestamp,
723                payload_length,
724                payload_digest,
725            ))
726        }
727    }
728
729    impl<const MCL: usize, const MCC: usize, const MPL: usize, S>
730        RelativeEncodable<Area<MCL, MCC, MPL, S>> for Area<MCL, MCC, MPL, S>
731    where
732        S: SubspaceId + Encodable,
733    {
734        /// Encode an [`Area`] relative to another [`Area`] which [includes](https://willowprotocol.org/specs/grouping-entries/index.html#area_include_area) it.
735        ///
736        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_area_in_area).
737        async fn relative_encode<Consumer>(
738            &self,
739            out: &Area<MCL, MCC, MPL, S>,
740            consumer: &mut Consumer,
741        ) -> Result<(), Consumer::Error>
742        where
743            Consumer: BulkConsumer<Item = u8>,
744        {
745            if !out.includes_area(self) {
746                panic!("Tried to encode an area relative to a area it is not included by")
747            }
748
749            let start_diff = core::cmp::min(
750                self.times().start - out.times().start,
751                u64::from(&out.times().end) - self.times().start,
752            );
753
754            let end_diff = core::cmp::min(
755                u64::from(&self.times().end) - out.times().start,
756                u64::from(&out.times().end) - u64::from(&self.times().end),
757            );
758
759            let mut header = 0;
760
761            if self.subspace() != out.subspace() {
762                header |= 0b1000_0000;
763            }
764
765            if self.times().end == RangeEnd::Open {
766                header |= 0b0100_0000;
767            }
768
769            if start_diff == self.times().start - out.times().start {
770                header |= 0b0010_0000;
771            }
772
773            if self.times().end != RangeEnd::Open
774                && end_diff == u64::from(&self.times().end) - out.times().start
775            {
776                header |= 0b0001_0000;
777            }
778
779            header |= CompactWidth::from_u64(start_diff).bitmask(4);
780            header |= CompactWidth::from_u64(end_diff).bitmask(6);
781
782            consumer.consume(header).await?;
783
784            match (&self.subspace(), &out.subspace()) {
785                (AreaSubspace::Any, AreaSubspace::Any) => {} // Same subspace
786                (AreaSubspace::Id(_), AreaSubspace::Id(_)) => {} // Same subspace
787                (AreaSubspace::Id(subspace), AreaSubspace::Any) => {
788                    subspace.encode(consumer).await?;
789                }
790                (AreaSubspace::Any, AreaSubspace::Id(_)) => {
791                    unreachable!(
792                        "We should have already rejected an area not included by another area!"
793                    )
794                }
795            }
796
797            self.path().relative_encode(out.path(), consumer).await?;
798
799            encode_compact_width_be(start_diff, consumer).await?;
800
801            if self.times().end != RangeEnd::Open {
802                encode_compact_width_be(end_diff, consumer).await?;
803            }
804
805            Ok(())
806        }
807    }
808
809    impl<const MCL: usize, const MCC: usize, const MPL: usize, S>
810        RelativeDecodable<Area<MCL, MCC, MPL, S>> for Area<MCL, MCC, MPL, S>
811    where
812        S: SubspaceId + Decodable,
813    {
814        /// Decode an [`Area`] relative to another [`Area`] which [includes](https://willowprotocol.org/specs/grouping-entries/index.html#area_include_area) it.
815        ///
816        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_area_in_area).
817        async fn relative_decode<Producer>(
818            out: &Area<MCL, MCC, MPL, S>,
819            producer: &mut Producer,
820        ) -> Result<Self, DecodeError<Producer::Error>>
821        where
822            Producer: BulkProducer<Item = u8>,
823            Self: Sized,
824        {
825            let header = produce_byte(producer).await?;
826
827            // Decode subspace?
828            let is_subspace_encoded = is_bitflagged(header, 0);
829
830            // Decode end value of times?
831            let is_times_end_open = is_bitflagged(header, 1);
832
833            // Add start_diff to out.get_times().start, or subtract from out.get_times().end?
834            let add_start_diff = is_bitflagged(header, 2);
835
836            // Add end_diff to out.get_times().start, or subtract from out.get_times().end?
837            let add_end_diff = is_bitflagged(header, 3);
838
839            // === Necessary to produce canonic encodings. ===
840            // Verify that we don't add_end_diff when open...
841            if add_end_diff && is_times_end_open {
842                return Err(DecodeError::InvalidInput);
843            }
844            // ===============================================
845
846            let start_diff_compact_width = CompactWidth::decode_fixed_width_bitmask(header, 4);
847            let end_diff_compact_width = CompactWidth::decode_fixed_width_bitmask(header, 6);
848
849            // === Necessary to produce canonic encodings. ===
850            // Verify the last two bits are zero if is_times_end_open
851            if is_times_end_open && (end_diff_compact_width != CompactWidth::One) {
852                return Err(DecodeError::InvalidInput);
853            }
854            // ===============================================
855
856            let subspace = if is_subspace_encoded {
857                let id = S::decode(producer).await?;
858                let sub = AreaSubspace::Id(id);
859
860                // === Necessary to produce canonic encodings. ===
861                // Verify that subspace wasn't needlessly encoded
862                if &sub == out.subspace() {
863                    return Err(DecodeError::InvalidInput);
864                }
865                // ===============================================
866
867                sub
868            } else {
869                out.subspace().clone()
870            };
871
872            // Verify that the decoded subspace is included by the reference subspace
873            match (&out.subspace(), &subspace) {
874                (AreaSubspace::Any, AreaSubspace::Any) => {}
875                (AreaSubspace::Any, AreaSubspace::Id(_)) => {}
876                (AreaSubspace::Id(_), AreaSubspace::Any) => {
877                    return Err(DecodeError::InvalidInput);
878                }
879                (AreaSubspace::Id(a), AreaSubspace::Id(b)) => {
880                    if a != b {
881                        return Err(DecodeError::InvalidInput);
882                    }
883                }
884            }
885
886            let path = Path::relative_decode(out.path(), producer).await?;
887
888            // Verify the decoded path is prefixed by the reference path
889            if !path.is_prefixed_by(out.path()) {
890                return Err(DecodeError::InvalidInput);
891            }
892
893            let start_diff = decode_compact_width_be(start_diff_compact_width, producer).await?;
894
895            let start = if add_start_diff {
896                out.times().start.checked_add(start_diff)
897            } else {
898                u64::from(&out.times().end).checked_sub(start_diff)
899            }
900            .ok_or(DecodeError::InvalidInput)?;
901
902            // Verify they sent correct start diff
903            let expected_start_diff = core::cmp::min(
904                start.checked_sub(out.times().start),
905                u64::from(&out.times().end).checked_sub(start),
906            )
907            .ok_or(DecodeError::InvalidInput)?;
908
909            if expected_start_diff != start_diff {
910                return Err(DecodeError::InvalidInput);
911            }
912
913            // === Necessary to produce canonic encodings. ===
914            // Verify that bit 2 of the header was set correctly
915            let should_add_start_diff = start_diff
916                == start
917                    .checked_sub(out.times().start)
918                    .ok_or(DecodeError::InvalidInput)?;
919
920            if add_start_diff != should_add_start_diff {
921                return Err(DecodeError::InvalidInput);
922            }
923            // ===============================================
924
925            let end = if is_times_end_open {
926                if add_end_diff {
927                    return Err(DecodeError::InvalidInput);
928                }
929
930                RangeEnd::Open
931            } else {
932                let end_diff = decode_compact_width_be(end_diff_compact_width, producer).await?;
933
934                let end = if add_end_diff {
935                    out.times().start.checked_add(end_diff)
936                } else {
937                    u64::from(&out.times().end).checked_sub(end_diff)
938                }
939                .ok_or(DecodeError::InvalidInput)?;
940
941                // Verify they sent correct end diff
942                let expected_end_diff = core::cmp::min(
943                    end.checked_sub(out.times().start),
944                    u64::from(&out.times().end).checked_sub(end),
945                )
946                .ok_or(DecodeError::InvalidInput)?;
947
948                if end_diff != expected_end_diff {
949                    return Err(DecodeError::InvalidInput);
950                }
951
952                // === Necessary to produce canonic encodings. ===
953                let should_add_end_diff = end_diff
954                    == end
955                        .checked_sub(out.times().start)
956                        .ok_or(DecodeError::InvalidInput)?;
957
958                if add_end_diff != should_add_end_diff {
959                    return Err(DecodeError::InvalidInput);
960                }
961                // ============================================
962
963                RangeEnd::Closed(end)
964            };
965
966            let times = Range { start, end };
967
968            // Verify the decoded time range is included by the reference time range
969            if !out.times().includes_range(&times) {
970                return Err(DecodeError::InvalidInput);
971            }
972
973            Ok(Self::new(subspace, path, times))
974        }
975    }
976
977    impl<const MCL: usize, const MCC: usize, const MPL: usize, S>
978        RelativeEncodable<Range3d<MCL, MCC, MPL, S>> for Range3d<MCL, MCC, MPL, S>
979    where
980        S: SubspaceId + Encodable + std::fmt::Debug,
981    {
982        /// Encode an [`Range3d`] relative to another [`Range3d`] which [includes](https://willowprotocol.org/specs/grouping-entries/index.html#area_include_area) it.
983        ///
984        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_area_in_area).
985        async fn relative_encode<Consumer>(
986            &self,
987            reference: &Range3d<MCL, MCC, MPL, S>,
988            consumer: &mut Consumer,
989        ) -> Result<(), Consumer::Error>
990        where
991            Consumer: BulkConsumer<Item = u8>,
992        {
993            let start_to_start = self.times().start.abs_diff(reference.times().start);
994            let start_to_end = match reference.times().end {
995                RangeEnd::Closed(end) => self.times().start.abs_diff(end),
996                RangeEnd::Open => u64::MAX,
997            };
998            let end_to_start = match self.times().end {
999                RangeEnd::Closed(end) => end.abs_diff(reference.times().start),
1000                RangeEnd::Open => u64::MAX,
1001            };
1002            let end_to_end = match (&self.times().end, &reference.times().end) {
1003                (RangeEnd::Closed(self_end), RangeEnd::Closed(ref_end)) => {
1004                    self_end.abs_diff(*ref_end)
1005                }
1006                (RangeEnd::Closed(_), RangeEnd::Open) => u64::MAX,
1007                (RangeEnd::Open, RangeEnd::Closed(_)) => u64::MAX,
1008                (RangeEnd::Open, RangeEnd::Open) => 0, // shouldn't matter right???
1009            };
1010
1011            let start_time_diff = core::cmp::min(start_to_start, start_to_end);
1012
1013            let end_time_diff = core::cmp::min(end_to_start, end_to_end);
1014
1015            let mut header_1 = 0b0000_0000;
1016
1017            // Bits 0, 1 - Encode r.get_subspaces().start?
1018            if self.subspaces().start == reference.subspaces().start {
1019                header_1 |= 0b0100_0000;
1020            } else if reference.subspaces().end == self.subspaces().start {
1021                header_1 |= 0b1000_0000;
1022            } else {
1023                header_1 |= 0b1100_0000;
1024            }
1025
1026            // Bits 2, 3 - Encode r.get_subspaces().end?
1027            if self.subspaces().end == RangeEnd::Open {
1028                // Do nothing
1029            } else if self.subspaces().end == reference.subspaces().start {
1030                header_1 |= 0b0001_0000;
1031            } else if self.subspaces().end == reference.subspaces().end {
1032                header_1 |= 0b0010_0000;
1033            } else if self.subspaces().end != RangeEnd::Open {
1034                header_1 |= 0b0011_0000;
1035            }
1036
1037            // Bit 4 - Encode r.get_paths().start relative to ref.get_paths().start or to ref.get_paths().end?
1038            if let RangeEnd::Closed(ref_path_end) = &reference.paths().end {
1039                let lcp_start_start = self
1040                    .paths()
1041                    .start
1042                    .longest_common_prefix(&reference.paths().start);
1043                let lcp_start_end = self.paths().start.longest_common_prefix(ref_path_end);
1044
1045                if lcp_start_start.get_component_count() >= lcp_start_end.get_component_count() {
1046                    header_1 |= 0b0000_1000;
1047                }
1048            } else {
1049                header_1 |= 0b0000_1000;
1050            }
1051
1052            // Bit 5 - Self path end open?
1053            if self.paths().end == RangeEnd::Open {
1054                header_1 |= 0b0000_0100;
1055            }
1056
1057            // Bit 6 - Encode r.get_paths().end relative to ref.get_paths().start or to ref.get_paths().end (if at all)?
1058            match (&self.paths().end, &reference.paths().end) {
1059                (RangeEnd::Closed(self_path_end), RangeEnd::Closed(ref_path_end)) => {
1060                    let lcp_end_start =
1061                        self_path_end.longest_common_prefix(&reference.paths().start);
1062                    let lcp_end_end = self_path_end.longest_common_prefix(ref_path_end);
1063
1064                    if lcp_end_start.get_component_count() > lcp_end_end.get_component_count() {
1065                        header_1 |= 0b0000_0010;
1066                    }
1067                }
1068                (RangeEnd::Closed(_), RangeEnd::Open) => {
1069                    header_1 |= 0b0000_0010;
1070                }
1071                (RangeEnd::Open, RangeEnd::Closed(_)) => {}
1072                (RangeEnd::Open, RangeEnd::Open) => {}
1073            }
1074
1075            // Bit 7 - Self time end open?
1076            if self.times().end == RangeEnd::Open {
1077                header_1 |= 0b0000_0001;
1078            }
1079
1080            consumer.consume(header_1).await?;
1081
1082            let mut header_2 = 0b0000_0000;
1083
1084            // Bit 8 - Encode r.get_times().start relative to ref.get_times().start or ref.get_times().end?
1085            if start_to_start <= start_to_end {
1086                header_2 |= 0b1000_0000;
1087            }
1088
1089            // Bit 9 -Add or subtract start_time_diff?
1090            if is_bitflagged(header_2, 0) && self.times().start >= reference.times().start
1091                || !is_bitflagged(header_2, 0) && self.times().start >= reference.times().end
1092            {
1093                header_2 |= 0b0100_0000;
1094            }
1095
1096            // Bit 10, 11 - 2-bit integer n such that 2^n gives compact_width(start_time_diff)
1097            header_2 |= CompactWidth::from_u64(start_time_diff).bitmask(2);
1098
1099            // Bit 12 - Encode r.get_times().end relative to ref.get_times().start or ref.get_times().end (if at all)?
1100            if self.times().end != RangeEnd::Open && end_to_start <= end_to_end {
1101                header_2 |= 0b0000_1000;
1102            }
1103
1104            // Bit 13 - Add or subtract end_time_diff (if encoding it at all)?
1105            if self.times().end == RangeEnd::Open {
1106                // do nothing
1107            } else if (is_bitflagged(header_2, 4) && self.times().end >= reference.times().start)
1108                || (!is_bitflagged(header_2, 4) && self.times().end >= reference.times().end)
1109            {
1110                header_2 |= 0b0000_0100;
1111            }
1112
1113            // Bits 14, 15 - ignored, or 2-bit integer n such that 2^n gives compact_width(end_time_diff)
1114            if self.times().end == RangeEnd::Open {
1115                // do nothing
1116            } else {
1117                header_2 |= CompactWidth::from_u64(end_time_diff).bitmask(6);
1118            }
1119
1120            consumer.consume(header_2).await?;
1121
1122            if (self.subspaces().start == reference.subspaces().start)
1123                || (reference.subspaces().end == self.subspaces().start)
1124            {
1125                // Don't encode
1126            } else {
1127                self.subspaces().start.encode(consumer).await?;
1128            }
1129
1130            if self.subspaces().end == RangeEnd::Open
1131                || (self.subspaces().end == reference.subspaces().start)
1132                || (self.subspaces().end == reference.subspaces().end)
1133            {
1134                // Don't encode end subspace
1135            } else if let RangeEnd::Closed(end_subspace) = &self.subspaces().end {
1136                end_subspace.encode(consumer).await?;
1137            }
1138
1139            if is_bitflagged(header_1, 4) {
1140                self.paths()
1141                    .start
1142                    .relative_encode(&reference.paths().start, consumer)
1143                    .await?;
1144            } else if let RangeEnd::Closed(end_path) = &reference.paths().end {
1145                self.paths()
1146                    .start
1147                    .relative_encode(end_path, consumer)
1148                    .await?;
1149            }
1150
1151            if let RangeEnd::Closed(end_path) = &self.paths().end {
1152                if is_bitflagged(header_1, 6) {
1153                    end_path
1154                        .relative_encode(&reference.paths().start, consumer)
1155                        .await?
1156                } else if let RangeEnd::Closed(ref_end_path) = &reference.paths().end {
1157                    end_path.relative_encode(ref_end_path, consumer).await?;
1158                }
1159            }
1160
1161            encode_compact_width_be(start_time_diff, consumer).await?;
1162            encode_compact_width_be(end_time_diff, consumer).await?;
1163
1164            Ok(())
1165        }
1166    }
1167
1168    impl<const MCL: usize, const MCC: usize, const MPL: usize, S>
1169        RelativeDecodable<Range3d<MCL, MCC, MPL, S>> for Range3d<MCL, MCC, MPL, S>
1170    where
1171        S: SubspaceId + Decodable + std::fmt::Debug,
1172    {
1173        /// Encode an [`Range3d`] relative to another [`Range3d`] which [includes](https://willowprotocol.org/specs/grouping-entries/index.html#area_include_area) it.
1174        ///
1175        /// [Definition](https://willowprotocol.org/specs/encodings/index.html#enc_area_in_area).
1176        async fn relative_decode<Producer>(
1177            reference: &Range3d<MCL, MCC, MPL, S>,
1178            producer: &mut Producer,
1179        ) -> Result<Self, DecodeError<Producer::Error>>
1180        where
1181            Producer: BulkProducer<Item = u8>,
1182            Self: Sized,
1183        {
1184            let header_1 = produce_byte(producer).await?;
1185
1186            let subspace_start_flags = header_1 & 0b1100_0000;
1187            let subspace_end_flags = header_1 & 0b0011_0000;
1188            let is_path_start_rel_to_start = is_bitflagged(header_1, 4);
1189            let is_path_end_open = is_bitflagged(header_1, 5);
1190            let is_path_end_rel_to_start = is_bitflagged(header_1, 6);
1191            let is_times_end_open = is_bitflagged(header_1, 7);
1192
1193            let header_2 = produce_byte(producer).await?;
1194
1195            let is_time_start_rel_to_start = is_bitflagged(header_2, 0);
1196            let add_or_subtract_start_time_diff = is_bitflagged(header_2, 1);
1197            let start_time_diff_compact_width =
1198                CompactWidth::decode_fixed_width_bitmask(header_2, 2);
1199            let is_times_end_rel_to_start = is_bitflagged(header_2, 4);
1200            let add_or_subtract_end_time_diff = is_bitflagged(header_2, 5);
1201            let end_time_diff_compact_width = CompactWidth::decode_fixed_width_bitmask(header_2, 6);
1202
1203            // Decode subspace start
1204            let subspace_start = match subspace_start_flags {
1205                0b0100_0000 => reference.subspaces().start.clone(),
1206                0b1000_0000 => match &reference.subspaces().end {
1207                    RangeEnd::Closed(end) => end.clone(),
1208                    RangeEnd::Open => Err(DecodeError::InvalidInput)?,
1209                },
1210                // This can only be 0b1100_0000
1211                _ => S::decode(producer).await?,
1212            };
1213
1214            let subspace_end = match subspace_end_flags {
1215                0b0000_0000 => RangeEnd::Open,
1216                0b0001_0000 => RangeEnd::Closed(reference.subspaces().start.clone()),
1217                0b0010_0000 => match &reference.subspaces().end {
1218                    RangeEnd::Closed(end) => RangeEnd::Closed(end.clone()),
1219                    RangeEnd::Open => Err(DecodeError::InvalidInput)?,
1220                },
1221                // This can only be 0b0011_0000
1222                _ => RangeEnd::Closed(S::decode(producer).await?),
1223            };
1224
1225            let path_start = match (is_path_start_rel_to_start, &reference.paths().end) {
1226                (true, RangeEnd::Closed(_)) => {
1227                    Path::relative_decode(&reference.paths().start, producer).await?
1228                }
1229                (true, RangeEnd::Open) => {
1230                    Path::relative_decode(&reference.paths().start, producer).await?
1231                }
1232                (false, RangeEnd::Closed(path_end)) => {
1233                    Path::relative_decode(path_end, producer).await?
1234                }
1235                (false, RangeEnd::Open) => Err(DecodeError::InvalidInput)?,
1236            };
1237
1238            let path_end = if is_path_end_open {
1239                RangeEnd::Open
1240            } else if is_path_end_rel_to_start {
1241                RangeEnd::Closed(Path::relative_decode(&reference.paths().start, producer).await?)
1242            } else {
1243                match &reference.paths().end {
1244                    RangeEnd::Closed(end) => {
1245                        RangeEnd::Closed(Path::relative_decode(end, producer).await?)
1246                    }
1247                    RangeEnd::Open => Err(DecodeError::InvalidInput)?,
1248                }
1249            };
1250
1251            let start_time_diff =
1252                decode_compact_width_be(start_time_diff_compact_width, producer).await?;
1253
1254            let time_start = match (is_time_start_rel_to_start, add_or_subtract_start_time_diff) {
1255                (true, true) => reference.times().start.checked_add(start_time_diff),
1256                (true, false) => reference.times().start.checked_sub(start_time_diff),
1257                (false, true) => match reference.times().end {
1258                    RangeEnd::Closed(ref_end) => ref_end.checked_add(start_time_diff),
1259                    RangeEnd::Open => Err(DecodeError::InvalidInput)?,
1260                },
1261                (false, false) => match reference.times().end {
1262                    RangeEnd::Closed(ref_end) => ref_end.checked_sub(start_time_diff),
1263                    RangeEnd::Open => Err(DecodeError::InvalidInput)?,
1264                },
1265            }
1266            .ok_or(DecodeError::InvalidInput)?;
1267
1268            let end_time_diff =
1269                decode_compact_width_be(end_time_diff_compact_width, producer).await?;
1270
1271            let time_end = if is_times_end_open {
1272                RangeEnd::Open
1273            } else {
1274                match (is_times_end_rel_to_start, add_or_subtract_end_time_diff) {
1275                    (true, true) => RangeEnd::Closed(
1276                        reference
1277                            .times()
1278                            .start
1279                            .checked_add(end_time_diff)
1280                            .ok_or(DecodeError::InvalidInput)?,
1281                    ),
1282                    (true, false) => RangeEnd::Closed(
1283                        reference
1284                            .times()
1285                            .start
1286                            .checked_sub(end_time_diff)
1287                            .ok_or(DecodeError::InvalidInput)?,
1288                    ),
1289                    (false, true) => match reference.times().end {
1290                        RangeEnd::Closed(ref_end) => RangeEnd::Closed(
1291                            ref_end
1292                                .checked_add(end_time_diff)
1293                                .ok_or(DecodeError::InvalidInput)?,
1294                        ),
1295                        RangeEnd::Open => Err(DecodeError::InvalidInput)?,
1296                    },
1297                    (false, false) => match reference.times().end {
1298                        RangeEnd::Closed(ref_end) => RangeEnd::Closed(
1299                            ref_end
1300                                .checked_sub(end_time_diff)
1301                                .ok_or(DecodeError::InvalidInput)?,
1302                        ),
1303                        RangeEnd::Open => Err(DecodeError::InvalidInput)?,
1304                    },
1305                }
1306            };
1307
1308            Ok(Self::new(
1309                Range {
1310                    start: subspace_start,
1311                    end: subspace_end,
1312                },
1313                Range {
1314                    start: path_start,
1315                    end: path_end,
1316                },
1317                Range {
1318                    start: time_start,
1319                    end: time_end,
1320                },
1321            ))
1322        }
1323    }
1324}