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}