ab_io_type/fixed_capacity_elements.rs
1#[cfg(test)]
2mod tests;
3
4use crate::alignment::Alignment;
5use crate::metadata::{IoTypeMetadataKind, MAX_METADATA_CAPACITY, concat_metadata_sources};
6use crate::trivial_type::{TrivialType, try_skip_valid};
7use crate::unaligned::Unaligned;
8use core::fmt;
9
10/// Size of the prefix of [`FixedCapacityElements`] that stores the number of elements.
11///
12/// The number of elements is stored as `u32`, the prefix is extended to
13/// [`TrivialType::ALIGNMENT`] of elements if it is larger, such that elements are aligned without
14/// padding before them.
15const LEN_SIZE<Element: TrivialType>: usize =
16 usize::from(Alignment::of::<Element>().get()).max(size_of::<u32>());
17/// Ensures the capacity (encoded in metadata) and the size of [`FixedCapacityElements`] and
18/// [`FixedCapacityString`] fit into `u32`
19///
20/// [`FixedCapacityString`]: crate::fixed_capacity_string::FixedCapacityString
21pub(crate) const SUPPORTED_CAPACITY<Element: TrivialType, const CAPACITY: usize>: usize = {
22 assert!(
23 u32::MAX as usize >= CAPACITY,
24 "Capacity must not exceed `u32::MAX`"
25 );
26 assert!(
27 u32::MAX as usize >= size_of::<[Element; CAPACITY]>()
28 && u32::MAX as usize - size_of::<[Element; CAPACITY]>() >= LEN_SIZE::<Element>,
29 "Type size must be smaller than 2^32"
30 );
31 0
32};
33
34// TODO: Require `AnyBitPattern` for elements, such that parsing doesn't need to check unused
35// elements, which could then be filled with zeroes instead of requiring `Default`
36/// Container for storing a number of elements limited by the specified fixed capacity.
37///
38/// See also [`FixedCapacityBytes`] and [`FixedCapacityString`].
39///
40/// In contrast to [`VariableElements`], which can store arbitrary number of elements and can
41/// change the capacity, this container has fixed predefined capacity and occupies it regardless of
42/// how many elements are actually stored inside. This might seem limiting but allows implementing
43/// [`TrivialType`] trait, enabling its use for fields in data structures that derive
44/// [`TrivialType`] themselves, which isn't the case with [`VariableElements`].
45///
46/// The length (the number of stored elements) is a little-endian `u32` in the first 4 bytes,
47/// followed by zero bytes up to [`TrivialType::ALIGNMENT`] of elements if it is larger than 4
48/// bytes, and `CAPACITY` elements after that. As a result, the container has the same alignment as
49/// elements and no padding, regardless of the alignment of elements.
50///
51/// `CAPACITY` must not exceed `u32::MAX`, and the size of the container must be smaller than
52/// 2^32.
53///
54/// Parsing rejects a length above `CAPACITY`, but doesn't check the rest of the length prefix,
55/// which is set to zero bytes whenever the length is set. The unsafe methods of [`TrivialType`]
56/// that skip parsing can create an instance with a larger length: methods don't panic then, but
57/// such an instance has no accessible elements and nothing can be appended to it until a valid
58/// length is set with [`Self::truncate()`] or [`Self::copy_from()`].
59///
60/// Elements beyond the length are not part of the value. [`Default`] and [`Self::try_from_slice()`]
61/// fill them with default elements, while [`Self::truncate()`] and [`Self::copy_from()`] leave
62/// them as they are, so bytes of instances with the same elements can differ. Parsing checks all
63/// `CAPACITY` elements, they are values of `Element` too.
64///
65/// [`FixedCapacityBytes`]: crate::fixed_capacity_bytes::FixedCapacityBytes
66/// [`FixedCapacityString`]: crate::fixed_capacity_string::FixedCapacityString
67/// [`VariableElements`]: crate::variable_elements::VariableElements
68#[derive(Copy, Clone)]
69#[repr(C)]
70pub struct FixedCapacityElements<Element, const CAPACITY: usize>
71where
72 Element: TrivialType,
73{
74 len: [u8; LEN_SIZE::<Element>],
75 elements: [Element; CAPACITY],
76 // TODO: Technically redundant with the where-clause, which is not enforced in generic code
77 // due to https://github.com/rust-lang/rust/issues/164019, remove once fixed. Every instance
78 // evaluates the check through this field. It is initialized in `const` blocks, which are
79 // evaluated even if optimizations remove the field.
80 _supported: [(); SUPPORTED_CAPACITY::<Element, CAPACITY>],
81}
82
83impl<Element, const CAPACITY: usize> fmt::Debug for FixedCapacityElements<Element, CAPACITY>
84where
85 Element: TrivialType + fmt::Debug,
86 [(); SUPPORTED_CAPACITY::<Element, CAPACITY>]:,
87{
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 f.debug_struct("FixedCapacityElements")
90 .field("len", &self.len())
91 .field("elements", &self.get_elements())
92 .finish()
93 }
94}
95
96impl<Element, const CAPACITY: usize> Default for FixedCapacityElements<Element, CAPACITY>
97where
98 Element: TrivialType + Default,
99 [(); SUPPORTED_CAPACITY::<Element, CAPACITY>]:,
100{
101 #[inline(always)]
102 fn default() -> Self {
103 Self {
104 len: [0; LEN_SIZE::<Element>],
105 elements: [Element::default(); CAPACITY],
106 _supported: const { [(); SUPPORTED_CAPACITY::<Element, CAPACITY>] },
107 }
108 }
109}
110
111// SAFETY: The length prefix consists of bytes and elements are `TrivialType`. There is no padding
112// before elements: the size of the length prefix is a multiple of `Element::ALIGNMENT`, and so of
113// the alignment of elements, which is a smaller or equal power of two (`Alignment::of()` checks
114// it). There is no padding after elements either: their size is a multiple of their alignment,
115// which is the alignment of the container. `ALIGNMENT` is that of elements, like in metadata. The
116// parsing step checks the length and every element.
117unsafe impl<Element, const CAPACITY: usize> TrivialType for FixedCapacityElements<Element, CAPACITY>
118where
119 Element: TrivialType,
120 [(); SUPPORTED_CAPACITY::<Element, CAPACITY>]:,
121{
122 const ALIGNMENT: Alignment = Element::ALIGNMENT;
123 // Casting `CAPACITY` to `u32` is lossless, which is checked by the `SUPPORTED_CAPACITY` bound
124 const METADATA: &[u8] = {
125 #[inline(always)]
126 const fn metadata(
127 capacity: u32,
128 inner_metadata: &[u8],
129 ) -> ([u8; MAX_METADATA_CAPACITY], usize) {
130 concat_metadata_sources(&[
131 &[IoTypeMetadataKind::FixedCapacityElements as u8],
132 &capacity.to_le_bytes(),
133 inner_metadata,
134 ])
135 }
136
137 // The reference extends the lifetime of metadata scratch to that of the constant
138 let (metadata_scratch, metadata_size) = &metadata(CAPACITY as u32, Element::METADATA);
139 metadata_scratch.split_at(*metadata_size).0
140 };
141
142 #[inline(always)]
143 fn try_from_bytes_unaligned(bytes: &[u8]) -> Option<(&Unaligned<Self>, &[u8])> {
144 let (bytes, remainder) = bytes.split_at_checked(size_of::<Self>())?;
145 let len = u32::from_le_bytes(*bytes.first_chunk()?);
146 // Casting `CAPACITY` to `u32` is lossless, which is checked by the `SUPPORTED_CAPACITY`
147 // bound
148 if len > CAPACITY as u32 {
149 return None;
150 }
151 try_skip_valid::<Element>(bytes.get(LEN_SIZE::<Element>..)?, CAPACITY)?;
152 // SAFETY: The length prefix consists of bytes, the length doesn't exceed `CAPACITY` and
153 // every element is a valid value as checked above, they follow each other without padding
154 // (see above), `Unaligned<Self>` has alignment 1 and the size of `Self`
155 let value = unsafe { bytes.as_ptr().cast::<Unaligned<Self>>().as_ref_unchecked() };
156 Some((value, remainder))
157 }
158}
159
160impl<Element, const CAPACITY: usize> FixedCapacityElements<Element, CAPACITY>
161where
162 Element: TrivialType,
163 [(); SUPPORTED_CAPACITY::<Element, CAPACITY>]:,
164{
165 /// Try to create an instance from provided elements.
166 ///
167 /// Unused capacity is filled with default elements.
168 ///
169 /// Returns `None` if provided elements do not fit into the capacity.
170 #[inline(always)]
171 pub fn try_from_slice(elements: &[Element]) -> Option<Self>
172 where
173 Element: Default,
174 {
175 let mut instance = Self::default();
176 if !instance.copy_from(elements) {
177 return None;
178 }
179
180 Some(instance)
181 }
182
183 /// Access to stored elements.
184 ///
185 /// Returns no elements if the number of elements exceeds `CAPACITY`, which is only possible for
186 /// an instance created from invalid bytes.
187 #[inline(always)]
188 pub fn get_elements(&self) -> &[Element] {
189 self.elements.get(..self.len() as usize).unwrap_or_default()
190 }
191
192 /// Exclusive access to stored elements.
193 ///
194 /// Returns no elements if the number of elements exceeds `CAPACITY`, which is only possible for
195 /// an instance created from invalid bytes.
196 #[inline(always)]
197 pub fn get_elements_mut(&mut self) -> &mut [Element] {
198 let len = self.len() as usize;
199 self.elements.get_mut(..len).unwrap_or_default()
200 }
201
202 /// Number of stored elements.
203 ///
204 /// May exceed `CAPACITY` for an instance created from invalid bytes.
205 #[inline(always)]
206 pub const fn len(&self) -> u32 {
207 u32::from_le_bytes(
208 *self
209 .len
210 .first_chunk()
211 .expect("Length prefix is at least 4 bytes; qed"),
212 )
213 }
214
215 /// Returns `true` if there are no stored elements
216 #[inline(always)]
217 pub const fn is_empty(&self) -> bool {
218 self.len() == 0
219 }
220
221 /// Append some elements.
222 ///
223 /// `true` is returned on success, but if there isn't enough capacity left, `false` is.
224 #[inline(always)]
225 #[must_use = "Operation may fail"]
226 pub fn append(&mut self, elements: &[Element]) -> bool {
227 let len = self.len();
228 let Some(new_len) = u32::try_from(elements.len())
229 .ok()
230 .and_then(|count| len.checked_add(count))
231 else {
232 return false;
233 };
234 // `None` if the new length exceeds the capacity
235 let Some(target) = self.elements.get_mut(len as usize..new_len as usize) else {
236 return false;
237 };
238
239 target.copy_from_slice(elements);
240 self.set_len(new_len);
241
242 true
243 }
244
245 /// Truncate stored elements to this length.
246 ///
247 /// Returns `true` on success or `false` if `new_len` is larger than [`Self::len()`].
248 #[inline(always)]
249 #[must_use = "Operation may fail"]
250 pub const fn truncate(&mut self, new_len: u32) -> bool {
251 if new_len > self.len() {
252 return false;
253 }
254
255 self.set_len(new_len);
256
257 true
258 }
259
260 /// Copy from specified elements, replacing stored elements.
261 ///
262 /// Returns `false` if capacity is not enough to copy contents of `src`
263 #[inline(always)]
264 #[must_use = "Operation may fail"]
265 pub fn copy_from(&mut self, src: &[Element]) -> bool {
266 let Ok(new_len) = u32::try_from(src.len()) else {
267 return false;
268 };
269 let Some(target) = self.elements.get_mut(..src.len()) else {
270 return false;
271 };
272
273 target.copy_from_slice(src);
274 self.set_len(new_len);
275
276 true
277 }
278
279 /// Store the length in the first 4 bytes of the length prefix, the rest of it is zeroed
280 #[inline(always)]
281 const fn set_len(&mut self, len: u32) {
282 self.len = [0; LEN_SIZE::<Element>];
283 *self
284 .len
285 .first_chunk_mut()
286 .expect("Length prefix is at least 4 bytes; qed") = len.to_le_bytes();
287 }
288}