fidl_next_codec/lib.rs
1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Encoding and decoding support for FIDL.
6//!
7//! This crate provides a number of types and traits related to encoding and decoding FIDL types:
8//!
9//! ## Encoding
10//!
11//! Here, "encoding" refers to the process of converting a Rust value to an encoded FIDL message.
12//! This process is captured in the [`Encode`] trait, which is parameterized over an _encoder_.
13//!
14//! ### Encodable types
15//!
16//! Encoding a type relies on two primary traits:
17//!
18//! - [`Encodable`] is the most fundamental trait for encodable types. It specifies the encoded form
19//! of the type with the associated [`Encoded`](Encodable::Encoded) type. That encoded form is its
20//! wire type which must implement [`Wire`].
21//! - Whereas `Encodable` specifies the wire type something encodes into, [`Encode`] does the actual
22//! encoding. [`Encode`] is parameterized over the encoder used to encode the type.
23//!
24//! These traits are all you need to encode basic FIDL values. For more specialized encoding, some
25//! types may implement these specialized encoding traits as well:
26//!
27//! #### Encoding optional types
28//!
29//! [`EncodableOption`] is a variant of [`Encodable`] which specifies how optional values of a type
30//! should be encoded. Most FIDL types are [boxed](WireBox) when optional, which places the encoded
31//! value out-of-line. However, optional strings, vectors, unions, and handles are not boxed when
32//! optional. Instead, they have special optional wire types like [`WireOptionalString`] and
33//! [`WireOptionalVector`] which are optimized to save space on the wire. Implementing
34//! [`EncodableOption`] allows `Option`s of a type to be encoded.
35//!
36//! Implementing [`EncodableOption`] is optional, and only required if you encode an `Option` of
37//! your type. The generated bindings will always generate an implementation of [`EncodableOption`]
38//! for its types.
39//!
40//! [`EncodeOption`] is the variant of [`Encode`] for [`EncodableOption`].
41//!
42//! #### Encoding by reference
43//!
44//! [`EncodeRef`] is a variant of [`Encode`] for types which can be encoded by reference. [`Encode`]
45//! encodes by value - it consumes the value being encoded. This allows [`Encode`] to encode
46//! resource types, which cannot be duplicated because they contain resources like handles. By
47//! contrast, [`EncodeRef`] does not consume the value being encoded, and can only be implemented
48//! for non-resource types. [`EncodeRef`] allows for more flexible and efficient encoding for many
49//! types.
50//!
51//! Optional types can also support encoding by reference with the [`EncodeOptionRef`] trait.
52//!
53//! ### Encoders
54//!
55//! Encodable types may only support encoding with specific kinds of encoders. They express these
56//! constraints by bounding the `E` type when implementing [`Encode<E>`] to require that the encoder
57//! implements some important traits. This crate provides the most fundamental encoder traits:
58//!
59//! - Most FIDL types require encoders to implement [`Encoder`] so that they can write out-of-line
60//! data. Strings, vectors, and tables are all examples of types which write out-of-line data.
61//! The [`EncoderExt`] extension trait provides useful methods for encoders when it is brought
62//! into scope (`use fidl_next::EncoderExt as _`).
63//! - Types containing Fuchsia handles can only be encoded by encoders which implement
64//! [`HandleEncoder`](fuchsia::HandleEncoder).
65//! - The [`InternalHandleEncoder`](encoder::InternalHandleEncoder) trait is an implementation
66//! detail. FIDL envelopes, which are used by tables and unions, may contain encoded types that
67//! the decoder doesn't recognize. If a decoder encounters an envelope containing a type it
68//! doesn't recognize, it needs to ignore the data and skip any handles it contained. To skip the
69//! correct number of handles, envelopes need to track the number of handles their value encoded.
70//! This is the case even if the envelope doesn't contain any types which contain handles.
71//! [`InternalHandleEncoder`](encoder::InternalHandleEncoder) provides this functionality to
72//! envelopes without requiring the encoder to actually support encoding handles.
73//!
74//! An implementation of [`Encoder`] is provided for `Vec<Chunk>`.
75//!
76//! ## Decoding
77//!
78//! Here, "decoding" has a very specific meaning. It refers to the process of validating and
79//! rewriting a buffer of [`Chunk`]s to ensure it contains a valid [`Wire`] type. This definition is
80//! narrow, and does not include converting a wire type to a natural type.
81//!
82//! The process of decoding is captured in the [`Decode`] trait. Like encoding, [`Decode`] is
83//! parameterized over a _decoder_.
84//!
85//! ### Decodable types
86//!
87//! Types which implement [`Decode`] must first implement [`Wire`] to guarantee that their
88//! representation conforms to the requirements of the FIDL wire format specification. Then, they
89//! can specify how to validate and decode their wire form with the [`decode`](Decode::decode)
90//! method.
91//!
92//! [`decode`](Decode::decode) needs to do three things to be correct:
93//!
94//! 1. Verify that the encoded bytes are valid for the type. For `bool`s, this means verifying that
95//! the encoded byte is either exactly 0 or exactly 1. If the encoded bytes are not valid for the
96//! type, `decode` **must** return an error.
97//! 2. Reify pointers by decoding any out-of-line data and replacing presence indicators with the
98//! value of a pointer to that decoded data.
99//! 3. Move resources from the decoder into the buffer. On the wire, handles are replaced with a
100//! presence indicator. They are transported separately by the transport because they require
101//! special handling.
102//!
103//! Note that [`decode`](Decode::decode) only manipulates data in-place, and only returns whether it
104//! succeeded or failed.
105//!
106//! ### Decoders
107//!
108//! Like encoding, some types may only support encoding with specific types of decoders. We express
109//! these constraints by bounding the `D` type when implementing [`Decode<D>`] to require that the
110//! decoder implements some important traits:
111//!
112//! - Most FIDL types require decoders to implement [`Decoder`] so that they can decode out-of-line
113//! data. The [`DecoderExt`] extension trait provides useful methods for decoders when it is
114//! brought into scope (`use fidl_next::DecoderExt as _`).
115//! - Types containing Fuchsia handles can only be decoded by decoders which implement
116//! [`HandleDecoder`](fuchsia::HandleDecoder).
117//! - Like encoding, the [`InternalHandleDecoder`](decoder::InternalHandleDecoder) trait is an
118//! implementation detail for FIDL envelopes.
119//!
120//! An implementation of [`Decoder`] is provided for `&mut [Chunk]`.
121//!
122//! #### Committing
123//!
124//! Decoding a wire type can fail at any point, even after resources have been taken out of the
125//! decoder. This presents a problem: partially-decoded values cannot be dropped, but may contain
126//! resources that must be dropped.
127//!
128//! To solve this problem, taking a resource from a decoder happens in two phases:
129//!
130//! 1. While decoding the resource is copied from the decoder into the buffer. The resource is left
131//! in the decoder.
132//! 2. After decoding finishes successfully, the decoder is [committed](Decoder::commit). Calling
133//! `commit` semantically completes moving the resources from the decoder into the buffer.
134//!
135//! If decoding fails before `commit` is called, the decoder remains responsible for dropping the
136//! taken resources. After `commit` is called, the wire value is responsible for dropping the taken
137//! resources.
138//!
139//! ## Wire types
140//!
141//! FIDL types which are used in-place without copying them out of the buffer implement [`Wire`] and
142//! are called "wire types". [`Wire`] is an unsafe trait which bundles together the necessary
143//! guarantees and functional machinery for wire types. The most important thing it does is promise
144//! that the implementing type follows any layout requirements for FIDL's wire types.
145//!
146//! ### Primitives
147//!
148//! The FIDL wire specification requires "natural alignment" for wire primitives, which means that
149//! wire primitives must have alignment equal to their size. A four-byte `int32` must be
150//! four-aligned, and so may differ from Rust's native `i32` type. To accommodate these differences,
151//! multibyte primitive types have special wire forms. Single-byte primitive types have the same
152//! natural and wire types.
153//!
154//! | FIDL type | Natural type | Wire type |
155//! | ------------- | ------------- | ------------- |
156//! | `bool` | `bool` | `bool` |
157//! | `int8` | `i8` | `i8` |
158//! | `int16` | `i16` | [`WireI16`] |
159//! | `int32` | `i32` | [`WireI32`] |
160//! | `int64` | `i64` | [`WireI64`] |
161//! | `uint8` | `u8` | `u8` |
162//! | `uint16` | `u16` | [`WireU16`] |
163//! | `uint32` | `u32` | [`WireU32`] |
164//! | `uint64` | `u64` | [`WireU64`] |
165//! | `float32` | `f32` | [`WireF32`] |
166//! | `float64` | `f64` | [`WireF64`] |
167//!
168//! All wire primitives implement `Deref` and dereference to their native primitive types.
169//!
170//! ### Containers
171//!
172//! This crate provides wire types for containers supported by FIDL:
173//!
174//! | FIDL type | Natural type | Wire type |
175//! | ------------- | ------------- | ----------------- |
176//! | `box<T>` | `Option<T>` | [`WireBox<T>`] |
177//! | `array<T, N>` | `[T; N]` | `[T; N]` |
178//! | `vector<T>` | `Vec<T>` | [`WireVector<T>`] |
179//! | `string` | `String` | [`WireString`] |
180//!
181//! ### Lifetimes
182//!
183//! Wire types with out-of-line data may contain pointers to other parts of a decoded buffer, and so
184//! cannot be allowed to outlive that decoded buffer. As a result, wire types are parameterized over
185//! the lifetime of the decoder they are contained in (typically named `'de`). This lifetime isn't
186//! important when reading data with wire types, but can impose important constraints when _moving_
187//! wire types and converting them to natural types.
188//!
189//! After decoding, shared references to wire types can be obtained and used without any
190//! restrictions. These shared references allow reading the data from wire types, and provide all of
191//! the functionality required for handle-less FIDL.
192//!
193//! However, in order to move resources like handles out of wire types, wire values must be taken
194//! from the decoder. Taking a wire type out of a decoder is an all-or-nothing operation - you must
195//! take the entire decoded wire value from a decoder, and it must be moved or dropped before the
196//! decoder can be moved or dropped. You can think of taking the wire value as sticking the decoder
197//! in place until the wire value is converted to a natural type or dropped. This means that for
198//! FIDL protocols, you must take the entire received FIDL message.
199//!
200//! When wire values are taken out of a decoder, they are parameterized over the lifetime of the
201//! decoder (usually `'de`) to prevent the decoder from being dropped. These values can be treated
202//! like ordinary Rust values.
203//!
204//! ### Conversion to natural types
205//!
206//! Natural types can support conversion from a wire type by implementing [`FromWire`]. This trait
207//! has a [`from_wire`](FromWire::from_wire) method which parallels `From::from` and implements
208//! conversion from some wire type. Like `Encode` and `Decode`, the [`FromWireOption`] variant
209//! allows types to be converted from wire optional types.
210//!
211//! Natural types that can be converted from a reference to a wire type (i.e. without moving the
212//! wire type) may implement [`FromWireRef`]. Similarly for options, the [`FromWireOptionRef`] trait
213//! allows options to be converted from a reference to a wire type.
214
215#![deny(
216 future_incompatible,
217 missing_docs,
218 nonstandard_style,
219 unused,
220 warnings,
221 clippy::all,
222 clippy::alloc_instead_of_core,
223 clippy::missing_safety_doc,
224 clippy::std_instead_of_core,
225 // TODO: re-enable this lint after justifying unsafe blocks
226 // clippy::undocumented_unsafe_blocks,
227 rustdoc::broken_intra_doc_links,
228 rustdoc::missing_crate_level_docs
229)]
230#![forbid(unsafe_op_in_unsafe_fn)]
231
232#[cfg(test)]
233#[macro_use]
234mod testing;
235
236mod chunk;
237#[cfg(feature = "compat")]
238mod compat;
239mod copy_optimization;
240mod decode;
241mod decoded;
242pub mod decoder;
243mod encode;
244pub mod encoder;
245mod from_wire;
246#[cfg(feature = "fuchsia")]
247pub mod fuchsia;
248mod primitives;
249mod slot;
250mod wire;
251
252pub use bitflags::bitflags;
253pub use munge::munge;
254
255pub use self::chunk::*;
256pub use self::copy_optimization::*;
257pub use self::decode::*;
258pub use self::decoded::*;
259pub use self::decoder::{Decoder, DecoderExt};
260pub use self::encode::*;
261pub use self::encoder::{Encoder, EncoderExt};
262pub use self::from_wire::*;
263pub use self::primitives::*;
264pub use self::slot::*;
265pub use self::wire::*;