ridl_rt/payload.rs
1//! Encoding, verifying and decoding a payload.
2//!
3//! A generated payload type implements [`Payload<E>`] once for each encoding.
4//! Its `verify` checks the structure of the bytes and the typl constraints of
5//! the value before decoding. Map uniqueness can require repeated key reads.
6//! Its `decode` takes a [`Ref`], and only
7//! [`Ref::verify`] and [`Ref::encode`] build a `Ref`. So a value is decoded
8//! only from bytes that were checked, or that its own encoder wrote.
9
10use core::marker::PhantomData;
11
12use crate::encoding::Encoding;
13
14/// A type that can be carried as a payload in the encoding `E`.
15///
16/// Generated code implements this trait and never calls its methods directly:
17/// it calls [`Ref::verify`], [`Ref::encode`] and [`Ref::decode`].
18pub trait Payload<E: Encoding>: Sized {
19 /// The largest encoded size of any legal value, in bytes.
20 const MAX_SIZE: usize;
21
22 /// What a successful check or encode leaves behind: the bytes, an
23 /// accessor over the bytes, or a parsed value.
24 type View<'a>;
25
26 /// Writes `self` into `out` and returns the bytes written.
27 ///
28 /// The bytes are a subslice of `out`, not necessarily a prefix of it: an
29 /// encoding whose builder works backwards — FlatBuffers does — fills `out`
30 /// from its end. A caller reads [`Encoded::bytes`] and passes it on rather
31 /// than assuming where in `out` it sits.
32 fn encode<'o>(&self, out: &'o mut [u8]) -> Result<Encoded<'o, Self::View<'o>>, EncodeError>;
33
34 /// Checks the structure of `buf` and the typl constraints of the value it
35 /// holds. Verifying map uniqueness can require repeated key reads.
36 fn verify(buf: &[u8]) -> Result<Self::View<'_>, VerifyError>;
37
38 /// Builds the value from a proof. It cannot fail, because the proof shows
39 /// that the bytes were checked or encoded.
40 fn decode(r: Ref<'_, Self, E>) -> Self;
41}
42
43/// A view that holds the verified bytes it reads.
44///
45/// A generated view — the `<T>FbView<'a>` a FlatBuffers `Payload::verify`
46/// hands back — implements this trait rather than carrying `bytes` as an
47/// inherent method, so that a field whose accessor is named `bytes` does not
48/// meet it: a trait method has its own namespace. The accessor is inherent
49/// and wins the dot call; the buffer is reached through the trait's path.
50///
51/// ```
52/// use ridl_rt::payload::View;
53///
54/// struct Raw<'a> {
55/// buf: &'a [u8],
56/// }
57///
58/// impl<'a> Raw<'a> {
59/// /// A field accessor that happens to share the trait method's name.
60/// fn bytes(&self) -> usize {
61/// self.buf.len()
62/// }
63/// }
64///
65/// impl<'a> View<'a> for Raw<'a> {
66/// fn bytes(&self) -> &'a [u8] {
67/// self.buf
68/// }
69/// }
70///
71/// let raw = Raw { buf: &[1, 2, 3] };
72/// let count: usize = raw.bytes();
73/// let buffer: &[u8] = View::bytes(&raw);
74/// assert_eq!(count, 3);
75/// assert_eq!(buffer, &[1, 2, 3]);
76/// ```
77pub trait View<'a> {
78 /// The verified bytes this view reads.
79 fn bytes(&self) -> &'a [u8];
80}
81
82/// The result of [`Payload::encode`]: the bytes written and their view. It is
83/// data, not a proof.
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub struct Encoded<'a, V> {
86 /// The encoded bytes, a subslice of the output buffer.
87 pub bytes: &'a [u8],
88 /// The view of those bytes.
89 pub view: V,
90}
91
92/// A proof that some bytes passed `T::verify` or were written by `T::encode`.
93///
94/// The fields are private and [`Ref::verify`] and [`Ref::encode`] are the only
95/// constructors. A crate outside `ridl-rt` builds a `Ref` by checking:
96///
97/// ```
98/// use ridl_rt::encoding::ReprC;
99/// use ridl_rt::payload::{Payload, Ref, VerifyError};
100///
101/// fn check<'a, T>(bytes: &'a [u8]) -> Result<Ref<'a, T, ReprC>, VerifyError>
102/// where
103/// T: Payload<ReprC, View<'a> = &'a [u8]>,
104/// {
105/// Ref::verify(bytes)
106/// }
107/// ```
108///
109/// and cannot build one from its fields:
110///
111/// ```compile_fail,E0451
112/// use core::marker::PhantomData;
113/// use ridl_rt::encoding::ReprC;
114/// use ridl_rt::payload::{Payload, Ref};
115///
116/// fn forge<'a, T>(bytes: &'a [u8]) -> Ref<'a, T, ReprC>
117/// where
118/// T: Payload<ReprC, View<'a> = &'a [u8]>,
119/// {
120/// Ref { bytes, view: bytes, _e: PhantomData }
121/// }
122/// ```
123pub struct Ref<'a, T: Payload<E>, E: Encoding> {
124 bytes: &'a [u8],
125 view: T::View<'a>,
126 _e: PhantomData<E>,
127}
128
129impl<'a, T: Payload<E>, E: Encoding> Ref<'a, T, E> {
130 /// Checks `buf` with `T::verify` and returns the proof.
131 pub fn verify(buf: &'a [u8]) -> Result<Self, VerifyError> {
132 let view = T::verify(buf)?;
133 Ok(Ref {
134 bytes: buf,
135 view,
136 _e: PhantomData,
137 })
138 }
139
140 /// Encodes `value` into `out` with `T::encode` and returns the proof.
141 ///
142 /// It does not check the value's typl constraints: a value is trusted to
143 /// be valid when constructed, and the receiver's `verify` reports a value
144 /// that is not.
145 pub fn encode(value: &T, out: &'a mut [u8]) -> Result<Self, EncodeError> {
146 let Encoded { bytes, view } = value.encode(out)?;
147 Ok(Ref {
148 bytes,
149 view,
150 _e: PhantomData,
151 })
152 }
153
154 /// The checked or encoded bytes.
155 pub fn bytes(&self) -> &'a [u8] {
156 self.bytes
157 }
158
159 /// Lends the view.
160 pub fn view(&self) -> &T::View<'a> {
161 &self.view
162 }
163
164 /// Moves the view out.
165 pub fn into_view(self) -> T::View<'a> {
166 self.view
167 }
168
169 /// Builds the value with `T::decode`.
170 pub fn decode(self) -> T {
171 T::decode(self)
172 }
173}
174
175/// An encode that failed.
176#[non_exhaustive]
177#[derive(Clone, Copy, Debug, PartialEq, Eq)]
178pub enum EncodeError {
179 /// The output buffer is shorter than the encoding.
180 Capacity {
181 /// The bytes the encoding needs: a lower bound, not always the whole
182 /// requirement.
183 ///
184 /// An encoder that knows its size before it writes reports the whole
185 /// encoding. One that builds incrementally — the FlatBuffers encoder
186 /// does, because a child's size is known only once it is written —
187 /// reports what it needed at the point it gave up, which is at least
188 /// `available + 1` and at most the whole encoding. Either way a
189 /// caller that grows the buffer to `needed` has made progress, and
190 /// one that wants a buffer that always suffices uses
191 /// [`Payload::MAX_SIZE`].
192 needed: usize,
193 /// The bytes the output buffer has.
194 available: usize,
195 },
196}
197
198/// A check that failed.
199#[non_exhaustive]
200#[derive(Clone, Copy, Debug, PartialEq, Eq)]
201pub enum VerifyError {
202 /// The bytes are not a well-formed encoding: a serialization failure, ridl
203 /// §10.3.
204 Structure(Malformed),
205 /// The bytes are well formed and hold a value that breaks a typl
206 /// constraint: `INVALID_VALUE`, ridl §10.2.
207 Contract(Violation),
208}
209
210/// How the bytes of an encoding are malformed.
211#[non_exhaustive]
212#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213pub enum Malformed {
214 /// An offset or a length points outside the buffer.
215 OutOfBounds,
216 /// A value is not at its required alignment.
217 Unaligned,
218 /// A required field is absent.
219 MissingRequired,
220 /// A string is not valid UTF-8.
221 Utf8,
222 /// A union's type does not match its value.
223 Union,
224 /// The nesting is deeper than the verifier's limit.
225 TooDeep,
226 /// The buffer holds more tables than the verifier's limit.
227 TooManyTables,
228 /// The buffer is larger than the verifier's limit.
229 TooLarge,
230}
231
232/// A value that breaks a typl constraint.
233#[derive(Clone, Copy, Debug, PartialEq, Eq)]
234pub struct Violation {
235 /// The name of the typl type whose constraint failed.
236 pub type_name: &'static str,
237 /// The kind of constraint that failed.
238 pub rule: Rule,
239}
240
241/// The kind of typl constraint a [`Violation`] breaks.
242#[non_exhaustive]
243#[derive(Clone, Copy, Debug, PartialEq, Eq)]
244pub enum Rule {
245 /// A number is outside its declared range.
246 Range,
247 /// A number is not its range's lower bound plus a whole multiple of its
248 /// declared step.
249 Step,
250 /// A string, a byte sequence or a collection is outside its declared
251 /// length bounds.
252 Length,
253 /// A string does not match its declared pattern.
254 Pattern,
255 /// A discriminant names no declared variant.
256 Variant,
257 /// A map contains more than one entry with the same key.
258 Unique,
259}
260
261#[cfg(test)]
262mod tests {
263 use core::marker::PhantomData;
264
265 use super::{EncodeError, Encoded, Payload, Ref, Rule, VerifyError};
266 use crate::encoding::ReprC;
267
268 /// A payload for this test only: its view is the bytes.
269 struct Raw;
270
271 impl Payload<ReprC> for Raw {
272 const MAX_SIZE: usize = 0;
273 type View<'a> = &'a [u8];
274
275 fn encode<'o>(&self, out: &'o mut [u8]) -> Result<Encoded<'o, &'o [u8]>, EncodeError> {
276 let bytes: &'o [u8] = &out[..0];
277 Ok(Encoded { bytes, view: bytes })
278 }
279
280 fn verify(buf: &[u8]) -> Result<&[u8], VerifyError> {
281 Ok(buf)
282 }
283
284 fn decode(_: Ref<'_, Self, ReprC>) -> Self {
285 Raw
286 }
287 }
288
289 /// The struct literal names every field, so a field added to `Ref` fails
290 /// to compile this test. The `compile_fail` doctest on `Ref` cannot pin
291 /// the field set, because it fails for any compile error.
292 #[test]
293 fn a_ref_is_built_from_exactly_its_three_fields() {
294 let bytes: &[u8] = &[1, 2];
295 let proof: Ref<'_, Raw, ReprC> = Ref {
296 bytes,
297 view: bytes,
298 _e: PhantomData,
299 };
300 assert_eq!(proof.bytes(), bytes);
301 }
302
303 /// The match names every variant with no `_` arm, so a `Rule` variant
304 /// added or removed fails this test to compile.
305 #[test]
306 fn rule_is_exactly_these_six_variants() {
307 fn all(r: Rule) {
308 match r {
309 Rule::Range => {}
310 Rule::Step => {}
311 Rule::Length => {}
312 Rule::Pattern => {}
313 Rule::Variant => {}
314 Rule::Unique => {}
315 }
316 }
317 all(Rule::Range);
318 all(Rule::Unique);
319 }
320}