ab_io_type/metadata.rs
1mod compact;
2mod stack;
3#[cfg(test)]
4mod tests;
5mod type_details;
6mod type_name;
7
8use crate::alignment::Alignment;
9use crate::metadata::compact::compact_metadata;
10use crate::metadata::type_details::decode_type_details;
11use crate::metadata::type_name::type_name;
12
13/// Max capacity for metadata bytes used in fixed size buffers
14pub const MAX_METADATA_CAPACITY: usize = 8192;
15
16/// Concatenates metadata sources.
17///
18/// Returns both a scratch memory and number of bytes in it that correspond to metadata
19pub const fn concat_metadata_sources(sources: &[&[u8]]) -> ([u8; MAX_METADATA_CAPACITY], usize) {
20 let mut metadata_scratch = [0u8; MAX_METADATA_CAPACITY];
21 let mut remainder = metadata_scratch.as_mut_slice();
22
23 // For loops are not yet usable in const environment
24 let mut i = 0;
25 while i < sources.len() {
26 let source = sources[i];
27 let target;
28 (target, remainder) = remainder.split_at_mut(source.len());
29 target.copy_from_slice(source);
30 i += 1;
31 }
32
33 let remainder_len = remainder.len();
34 let size = metadata_scratch.len() - remainder_len;
35 (metadata_scratch, size)
36}
37
38#[derive(Debug, Copy, Clone)]
39pub struct IoTypeDetails {
40 /// Recommended capacity that must be allocated by the host.
41 ///
42 /// If actual data is larger, it will be passed down to the guest as it is, if smaller than the
43 /// host must allocate the recommended capacity for guest anyway.
44 pub recommended_capacity: u32,
45 /// Alignment of the type
46 pub alignment: Alignment,
47}
48
49impl IoTypeDetails {
50 /// Create an instance for regular bytes (alignment 1)
51 #[inline(always)]
52 pub const fn bytes(recommended_capacity: u32) -> Self {
53 Self {
54 recommended_capacity,
55 alignment: Alignment::of::<u8>(),
56 }
57 }
58}
59
60/// Metadata types contained in [`TrivialType::METADATA`] and [`IoType::METADATA`].
61///
62/// Metadata encoding consists of this enum variant treated as `u8` followed by optional metadata
63/// encoding rules specific to metadata type variant (see variant's description).
64///
65/// This metadata is enough to fully reconstruct the hierarchy of the type to generate language
66/// bindings, auto-generate UI forms, etc.
67///
68/// [`TrivialType::METADATA`]: crate::trivial_type::TrivialType::METADATA
69/// [`IoType::METADATA`]: crate::IoType::METADATA
70#[derive(Debug, Copy, Clone, Eq, PartialEq)]
71#[repr(u8)]
72pub enum IoTypeMetadataKind {
73 /// `()`
74 Unit,
75 /// `bool`
76 Bool,
77 /// `u8`
78 U8,
79 /// `u16`
80 U16,
81 /// `u32`
82 U32,
83 /// `u64`
84 U64,
85 /// `u128`
86 U128,
87 /// `i8`
88 I8,
89 /// `i16`
90 I16,
91 /// `i32`
92 I32,
93 /// `i64`
94 I64,
95 /// `i128`
96 I128,
97 /// `struct S {..}`, `struct S(..);` or `struct S;`
98 ///
99 /// Structs are encoded as follows:
100 /// * Length of struct name in bytes (u8)
101 /// * Struct name as UTF-8 bytes
102 /// * Number of fields (u8)
103 ///
104 /// Each field is encoded as follows:
105 /// * Length of the field name in bytes (u8), zero for fields of tuple structs
106 /// * Field name as UTF-8 bytes
107 /// * Recursive metadata of the field's type
108 ///
109 /// Fields are laid out like in a `#[repr(C)]` struct. Decoders reject structs that have both
110 /// fields with names and fields with zero-length names, and layouts with padding.
111 Struct,
112 /// `enum E { A, B {..}, C(..) }`
113 ///
114 /// Enums are encoded as follows:
115 /// * Length of enum name in bytes (u8)
116 /// * Enum name as UTF-8 bytes
117 /// * Number of variants (u8)
118 ///
119 /// Each variant is encoded as follows:
120 /// * Length of the variant name in bytes (u8)
121 /// * Variant name as UTF-8 bytes
122 /// * Number of fields (u8), zero for variants without fields
123 /// * Fields, each encoded like a field of [`Self::Struct`], with zero-length names for fields
124 /// of tuple variants
125 ///
126 /// Each variant is a 1-byte discriminant followed by its fields laid out like in a
127 /// `#[repr(u8)]` enum, all variants must have the same size. Decoders reject enums without
128 /// variants, variants that have both fields with names and fields with zero-length names, and
129 /// layouts with padding.
130 Enum,
131 /// Array `[T; N]`.
132 ///
133 /// Encoded as follows:
134 /// * 4 bytes number of elements (little-endian)
135 /// * Recursive metadata of a contained type
136 Array,
137 /// [`MaybeData`](crate::maybe_data::MaybeData).
138 ///
139 /// Encoded as follows:
140 /// * Recursive metadata of a contained type
141 ///
142 /// Decoders only accept it as the outermost type.
143 MaybeData,
144 /// [`VariableElements`](crate::variable_elements::VariableElements).
145 ///
146 /// Encoded as follows:
147 /// * 4 bytes recommended allocation in elements (little-endian)
148 /// * Recursive metadata of a contained type
149 ///
150 /// Decoders only accept it as the outermost type.
151 VariableElements,
152 /// [`FixedCapacityElements`](crate::fixed_capacity_elements::FixedCapacityElements).
153 ///
154 /// Encoded as follows:
155 /// * 4 bytes capacity in elements (little-endian)
156 /// * Recursive metadata of a contained type
157 ///
158 /// Elements follow a little-endian `u32` length, padded with zeroes to the alignment of
159 /// elements if it is larger.
160 FixedCapacityElements,
161 /// [`FixedCapacityString`](crate::fixed_capacity_string::FixedCapacityString).
162 ///
163 /// This is a string only by convention, there is no runtime verification done, contents is
164 /// treated as regular bytes.
165 ///
166 /// Encoded as follows:
167 /// * 4 bytes capacity in bytes (little-endian)
168 ///
169 /// Bytes follow a little-endian `u32` length.
170 FixedCapacityString,
171 /// Unaligned wrapper over another [`TrivialType`].
172 ///
173 /// [`TrivialType`]: crate::trivial_type::TrivialType
174 ///
175 /// Encoded as follows:
176 /// * Recursive metadata of a contained type
177 Unaligned,
178}
179
180const impl TryFrom<u8> for IoTypeMetadataKind {
181 type Error = ();
182
183 #[inline]
184 fn try_from(byte: u8) -> Result<Self, Self::Error> {
185 Ok(match byte {
186 0 => Self::Unit,
187 1 => Self::Bool,
188 2 => Self::U8,
189 3 => Self::U16,
190 4 => Self::U32,
191 5 => Self::U64,
192 6 => Self::U128,
193 7 => Self::I8,
194 8 => Self::I16,
195 9 => Self::I32,
196 10 => Self::I64,
197 11 => Self::I128,
198 12 => Self::Struct,
199 13 => Self::Enum,
200 14 => Self::Array,
201 15 => Self::MaybeData,
202 16 => Self::VariableElements,
203 17 => Self::FixedCapacityElements,
204 18 => Self::FixedCapacityString,
205 19 => Self::Unaligned,
206 _ => {
207 return Err(());
208 }
209 })
210 }
211}
212
213impl IoTypeMetadataKind {
214 // TODO: Create wrapper type for metadata bytes and move this method there
215 /// Produce compact metadata.
216 ///
217 /// Compact metadata retains the layout, but throws some details. Specifically:
218 /// * Names of structs, fields, enums and enum variants are replaced with zero-length names, so
219 /// a struct with named fields has the same compact metadata as a tuple struct with fields of
220 /// the same types
221 /// * A struct with a single field is replaced with compact metadata of the type of the field
222 /// and a struct without fields with `()`, so a newtype like `struct Wrapper(u64);` has the
223 /// same compact metadata as `u64`, also when contained in other types
224 ///
225 /// Enums are retained, even with a single variant or a single field, since the discriminant is
226 /// a part of their layout.
227 ///
228 /// Compact metadata of valid metadata is valid metadata too, it decodes into the same capacity
229 /// and alignment (see [`Self::type_details()`]), and compacting it again doesn't change it.
230 ///
231 /// This is typically called by higher-level functions and doesn't need to be used directly.
232 ///
233 /// This function takes an `input` that starts with metadata defined in [`IoTypeMetadataKind`]
234 /// and `output` where compact metadata must be written. Since input might have other data past
235 /// the data structure to be processed, the remainder of input and output are returned to the
236 /// caller.
237 ///
238 /// `None` is returned for an unexpected metadata kind, metadata nested deeper than `MAX_DEPTH`
239 /// (see [`Self::type_details()`]), truncated metadata and `output` that is too small for
240 /// compact metadata. Metadata is not validated otherwise, [`Self::type_details()`] does
241 /// that.
242 #[inline]
243 pub const fn compact<'i, 'o, const MAX_DEPTH: usize>(
244 input: &'i [u8],
245 output: &'o mut [u8],
246 ) -> Option<(&'i [u8], &'o mut [u8])> {
247 compact_metadata::<MAX_DEPTH>(input, output)
248 }
249
250 // TODO: Create wrapper type for metadata bytes and move this method there
251 /// Decode type name.
252 ///
253 /// Expected to be UTF-8, but must be parsed before printed as text, which is somewhat costly.
254 #[inline]
255 pub const fn type_name(metadata: &[u8]) -> Option<&[u8]> {
256 type_name(metadata)
257 }
258
259 // TODO: Create wrapper type for metadata bytes and move this method there
260 /// Decode type, return its recommended capacity that should be allocated by the host.
261 ///
262 /// If actual data is larger, it will be passed down to the guest as it is, if smaller than host
263 /// should allocate recommended capacity for guest anyway.
264 ///
265 /// Returns type details and whatever slice of bytes from `metadata` that is left after
266 /// type decoding. Invalid metadata and metadata nested deeper than `MAX_DEPTH` result in `None`
267 /// being returned.
268 ///
269 /// `MAX_DEPTH` is the max nesting depth of compound types (structs, enums, arrays, `MaybeData`,
270 /// `VariableElements`, `FixedCapacityElements` and `Unaligned`), like 2 for
271 /// `Unaligned<[u8; 4]>`. Variants of an enum don't count, only types of their fields do. There
272 /// is no recursion, so stack usage grows with `MAX_DEPTH`, but doesn't depend on metadata.
273 #[inline]
274 pub const fn type_details<const MAX_DEPTH: usize>(
275 metadata: &[u8],
276 ) -> Option<(IoTypeDetails, &[u8])> {
277 decode_type_details::<MAX_DEPTH>(metadata)
278 }
279}