Skip to main content

iris_abi/
caps.rs

1//! Capabilities, and the set of them each side carries.
2//!
3//! A capability is one bit meaning "this side can do this thing". The host says what it offers, the
4//! decoder says what it requires, and if the decoder requires something the host does not offer
5//! then the two of them stop, on purpose, with a message that says which bit was the problem.
6//!
7//! The point of naming capabilities rather than bumping a version number for each one is that
8//! capabilities compose. A decoder that needs sliding windows and a decoder that needs filter
9//! pushdown are not ordered relative to each other, and pretending they are by giving them version
10//! numbers means every host has to implement every feature in order to claim the number.
11
12use crate::wire::Reader;
13
14/// One capability, identified by its bit position.
15#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
16pub struct Capability(pub u16);
17
18impl Capability {
19    /// The decoder pulls bytes of the source by asking for ranges, rather than being handed the
20    /// whole thing up front. This is the normal mode and the reason the project exists.
21    pub const REQUIRE_RANGE: Self = Self(0);
22    /// The host can move a window over a source that is larger than the guest can address, so a
23    /// 32-bit guest is not limited to four gigabytes of input.
24    pub const SLIDING_WINDOW: Self = Self(1);
25    /// The decoder honours a column projection instead of producing every column and letting the
26    /// host throw most of them away.
27    pub const PROJECTION: Self = Self(2);
28    /// The decoder honours a filter pushed down to it.
29    pub const FILTER_PUSHDOWN: Self = Self(3);
30    /// The decoder can start at an arbitrary row rather than only at the beginning.
31    pub const RANDOM_ACCESS: Self = Self(4);
32    /// The decoder keeps no state between calls, so the host is free to reuse one instance for
33    /// unrelated scans or to run several scans against the same instance.
34    pub const STATELESS: Self = Self(5);
35    /// The decoder is prepared to be interrupted partway through and resumed, which is what lets a
36    /// host put a time limit on a scan without killing it.
37    pub const RESUMABLE: Self = Self(6);
38
39    /// The name of this capability, if it is one we assigned.
40    #[must_use]
41    pub const fn name(self) -> Option<&'static str> {
42        match self {
43            Self::REQUIRE_RANGE => Some("require-range"),
44            Self::SLIDING_WINDOW => Some("sliding-window"),
45            Self::PROJECTION => Some("projection"),
46            Self::FILTER_PUSHDOWN => Some("filter-pushdown"),
47            Self::RANDOM_ACCESS => Some("random-access"),
48            Self::STATELESS => Some("stateless"),
49            Self::RESUMABLE => Some("resumable"),
50            _ => None,
51        }
52    }
53}
54
55impl core::fmt::Display for Capability {
56    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57        match self.name() {
58            Some(name) => f.write_str(name),
59            None => write!(f, "capability bit {}", self.0),
60        }
61    }
62}
63
64/// A set of capabilities.
65///
66/// The set is a fixed 32 bytes, so it holds 256 capabilities and needs no allocation. On the wire
67/// it is a variable-length byte string, so a future version can make it wider without breaking
68/// anything, and [`CapabilitySet::has_bits_beyond_this_build`] is how this version notices that
69/// happened instead of silently ignoring the extra bits.
70#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
71pub struct CapabilitySet {
72    bits: [u8; Self::BYTES],
73    beyond: bool,
74}
75
76impl CapabilitySet {
77    /// How many bytes of bitset this build carries.
78    pub const BYTES: usize = 32;
79
80    /// The highest capability bit this build can represent.
81    pub const MAX_BIT: u16 = 255;
82
83    /// An empty set.
84    #[must_use]
85    pub const fn new() -> Self {
86        Self {
87            bits: [0; Self::BYTES],
88            beyond: false,
89        }
90    }
91
92    /// Adds a capability and returns the set, so sets can be built in one expression.
93    ///
94    /// A capability above [`CapabilitySet::MAX_BIT`] cannot be represented by this build and is
95    /// ignored. That cannot happen by accident, because every capability this build knows about is
96    /// a constant in this file.
97    #[must_use]
98    pub const fn with(mut self, cap: Capability) -> Self {
99        if cap.0 <= Self::MAX_BIT {
100            let byte = (cap.0 / 8) as usize;
101            self.bits[byte] |= 1 << (cap.0 % 8);
102        }
103        self
104    }
105
106    /// Whether the set contains a capability.
107    #[must_use]
108    pub const fn contains(self, cap: Capability) -> bool {
109        if cap.0 > Self::MAX_BIT {
110            return false;
111        }
112        let byte = (cap.0 / 8) as usize;
113        self.bits[byte] & (1 << (cap.0 % 8)) != 0
114    }
115
116    /// Whether the set is empty as far as this build can tell.
117    #[must_use]
118    pub fn is_empty(self) -> bool {
119        !self.beyond && self.bits.iter().all(|b| *b == 0)
120    }
121
122    /// Whether the bytes this set was decoded from had bits set past what this build can hold.
123    ///
124    /// This matters on the required side. A decoder built against a later version of the ABI may
125    /// require a capability that did not exist when this host was compiled, and a host that just
126    /// truncated the bitset would conclude the decoder required nothing and run it anyway. That is
127    /// the failure this flag exists to prevent.
128    #[must_use]
129    pub const fn has_bits_beyond_this_build(self) -> bool {
130        self.beyond
131    }
132
133    /// Reads a set from its wire form.
134    ///
135    /// Bytes past what this build can hold are not stored, but if any of them are non-zero the set
136    /// remembers that.
137    #[must_use]
138    pub fn from_bytes(bytes: &[u8]) -> Self {
139        let mut out = Self::new();
140        let take = bytes.len().min(Self::BYTES);
141        out.bits[..take].copy_from_slice(&bytes[..take]);
142        out.beyond = bytes[take..].iter().any(|b| *b != 0);
143        out
144    }
145
146    /// The wire form, with trailing zero bytes trimmed off so an empty set costs nothing.
147    ///
148    /// Trimming is safe because a reader treats a missing byte as zero, which is what
149    /// [`CapabilitySet::from_bytes`] does.
150    #[must_use]
151    pub fn as_bytes(&self) -> &[u8] {
152        let end = self
153            .bits
154            .iter()
155            .rposition(|b| *b != 0)
156            .map_or(0, |i| i.saturating_add(1));
157        &self.bits[..end]
158    }
159
160    /// The capabilities in this set that are not in `other`.
161    ///
162    /// The `beyond` flag rides along, because a bit this build cannot name is by definition a bit
163    /// `other` does not offer.
164    #[must_use]
165    pub fn difference(self, other: Self) -> Self {
166        let mut out = Self::new();
167        for i in 0..Self::BYTES {
168            out.bits[i] = self.bits[i] & !other.bits[i];
169        }
170        out.beyond = self.beyond;
171        out
172    }
173
174    /// The capabilities in both sets.
175    #[must_use]
176    pub fn intersection(self, other: Self) -> Self {
177        let mut out = Self::new();
178        for i in 0..Self::BYTES {
179            out.bits[i] = self.bits[i] & other.bits[i];
180        }
181        out
182    }
183
184    /// The capabilities in either set.
185    #[must_use]
186    pub fn union(self, other: Self) -> Self {
187        let mut out = Self::new();
188        for i in 0..Self::BYTES {
189            out.bits[i] = self.bits[i] | other.bits[i];
190        }
191        out.beyond = self.beyond || other.beyond;
192        out
193    }
194
195    /// Every capability in the set, lowest bit first.
196    ///
197    /// Bits past [`CapabilitySet::MAX_BIT`] cannot be listed, which is what
198    /// [`CapabilitySet::has_bits_beyond_this_build`] is for.
199    pub fn iter(&self) -> impl Iterator<Item = Capability> + '_ {
200        (0..=Self::MAX_BIT)
201            .map(Capability)
202            .filter(move |c| self.contains(*c))
203    }
204}
205
206// The two constants have to agree or the bounds checks in this file are wrong.
207const _: () = assert!(CapabilitySet::BYTES * 8 == CapabilitySet::MAX_BIT as usize + 1);
208
209impl Reader<'_> {
210    /// Reads a length-prefixed capability set.
211    ///
212    /// # Errors
213    ///
214    /// Returns the same errors as [`Reader::var_bytes`].
215    pub fn capability_set(&mut self) -> crate::error::Result<CapabilitySet> {
216        Ok(CapabilitySet::from_bytes(self.var_bytes()?))
217    }
218}