Skip to main content

iris_abi/
range.rs

1//! What the host answers when a decoder asks for bytes it has not been given.
2//!
3//! The request itself is [`crate::RangeRequest`] when it travels as a record. Across the WebAssembly
4//! boundary it is a call rather than a record, because the decoder is stopped inside it waiting for
5//! the answer and a record would mean framing a request the caller is going to consume immediately.
6//! What crosses is three numbers in and one number out, and the number that comes out is this.
7//!
8//! The reason it is an integer rather than a boolean is that the four ways a range can fail are not
9//! the same failure. Two of them are the decoder asking for the wrong thing and are worth reporting
10//! back so it can ask for the right thing instead, and two of them are the host being unable to
11//! serve a request that was perfectly reasonable. A decoder that cannot tell those apart has no
12//! choice but to give up on all four.
13
14/// The answer to `iris.require_range`.
15///
16/// Only [`RangeStatus::SERVED`] means the bytes are in the buffer the decoder named. For every other
17/// value the buffer has not been written to and holds whatever it held before.
18#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
19pub struct RangeStatus(pub u32);
20
21impl RangeStatus {
22    /// The bytes are in the buffer, all of them, exactly as many as were asked for.
23    pub const SERVED: Self = Self(0);
24
25    /// The range runs past the end of the source.
26    ///
27    /// The decoder asked for the wrong thing. This is never a short read, which is the whole reason
28    /// it is a status rather than a returned length: a decoder that gets fewer bytes than it asked
29    /// for and does not notice produces an answer that is wrong and looks right.
30    pub const OUT_OF_BOUNDS: Self = Self(1);
31
32    /// No single request to this host can cover a range that long.
33    ///
34    /// The host is reading through a window and the range does not fit in one view of it. Asking
35    /// again in pieces works, which is what makes this different from the two below.
36    pub const TOO_LARGE: Self = Self(2);
37
38    /// The host tried and could not get the bytes.
39    ///
40    /// A read that failed, a connection that dropped, a source that contradicted itself. Nothing the
41    /// decoder does differently will help, and the host already knows the details.
42    pub const UNAVAILABLE: Self = Self(3);
43
44    /// This host has nothing to serve ranges from.
45    ///
46    /// A decoder that pulls ranges was run by a host that handed it the whole source and did not
47    /// expect to be asked, or by one that has not attached a source yet. It is a host bug rather
48    /// than a decoder bug, and it is separate from [`RangeStatus::UNAVAILABLE`] because the fix is
49    /// in a different place.
50    pub const NO_SOURCE: Self = Self(4);
51
52    /// Whether the bytes arrived.
53    #[must_use]
54    pub const fn is_served(self) -> bool {
55        self.0 == Self::SERVED.0
56    }
57
58    /// The name of this status, if it is one we assigned.
59    #[must_use]
60    pub const fn name(self) -> Option<&'static str> {
61        match self {
62            Self::SERVED => Some("served"),
63            Self::OUT_OF_BOUNDS => Some("out of bounds"),
64            Self::TOO_LARGE => Some("larger than one request can cover"),
65            Self::UNAVAILABLE => Some("unavailable"),
66            Self::NO_SOURCE => Some("no source attached"),
67            _ => None,
68        }
69    }
70}
71
72impl core::fmt::Display for RangeStatus {
73    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74        match self.name() {
75            Some(name) => f.write_str(name),
76            None => write!(f, "range status {}", self.0),
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    /// Somewhere to print into, because this crate has no allocator and so has no `to_string`.
86    struct Buf {
87        bytes: [u8; 64],
88        len: usize,
89    }
90
91    impl Buf {
92        const fn new() -> Self {
93            Self {
94                bytes: [0; 64],
95                len: 0,
96            }
97        }
98
99        fn text(&self) -> &str {
100            core::str::from_utf8(&self.bytes[..self.len]).expect("everything written here is utf8")
101        }
102    }
103
104    impl core::fmt::Write for Buf {
105        fn write_str(&mut self, text: &str) -> core::fmt::Result {
106            let end = self.len + text.len();
107            let room = self.bytes.get_mut(self.len..end).ok_or(core::fmt::Error)?;
108            room.copy_from_slice(text.as_bytes());
109            self.len = end;
110            Ok(())
111        }
112    }
113
114    fn printed(status: RangeStatus) -> Buf {
115        use core::fmt::Write as _;
116        let mut buf = Buf::new();
117        write!(&mut buf, "{status}").expect("a status is shorter than the buffer");
118        buf
119    }
120
121    #[test]
122    fn only_zero_means_the_bytes_are_there() {
123        assert!(RangeStatus::SERVED.is_served());
124        for status in [
125            RangeStatus::OUT_OF_BOUNDS,
126            RangeStatus::TOO_LARGE,
127            RangeStatus::UNAVAILABLE,
128            RangeStatus::NO_SOURCE,
129        ] {
130            assert!(!status.is_served(), "{status} is not the bytes arriving");
131        }
132    }
133
134    #[test]
135    fn a_status_from_a_later_host_still_prints() {
136        let future = RangeStatus(9001);
137        assert_eq!(future.name(), None);
138        assert_eq!(printed(future).text(), "range status 9001");
139    }
140}