Skip to main content

fidl_next_codec/wire/
table.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
5use core::mem::MaybeUninit;
6
7use munge::munge;
8
9use crate::{
10    Constrained, DecodeError, Decoder, DecoderExt as _, Slot, ValidationError, Wire, wire,
11};
12
13/// A FIDL table
14#[repr(C)]
15pub struct Table<'de> {
16    len: wire::Uint64,
17    ptr: wire::Pointer<'de, wire::Envelope>,
18}
19
20impl Constrained for Table<'_> {
21    type Constraint = ();
22
23    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
24        Ok(())
25    }
26}
27
28// SAFETY: `Table` has stable layout and no padding.
29unsafe impl Wire for Table<'static> {
30    type Narrowed<'de> = Table<'de>;
31
32    #[inline]
33    fn zero_padding(_: &mut MaybeUninit<Self>) {
34        // Wire tables have no padding
35    }
36}
37
38impl<'de> Table<'de> {
39    /// Encodes that a table contains `len` values in a slot.
40    #[inline]
41    pub fn encode_len(out: &mut MaybeUninit<Self>, len: usize) {
42        munge!(let Self { len: table_len, ptr } = out);
43        table_len.write(wire::Uint64(len.try_into().unwrap()));
44        wire::Pointer::encode_present(ptr);
45    }
46
47    /// Decodes the fields of the table with a decoding function.
48    ///
49    /// The decoding function receives the ordinal of the field, its slot, and the decoder.
50    #[inline]
51    pub fn decode_with<D: Decoder<'de> + ?Sized>(
52        slot: Slot<'_, Self>,
53        decoder: &mut D,
54        f: impl Fn(i64, Slot<'_, wire::Envelope>, &mut D) -> Result<(), DecodeError>,
55    ) -> Result<(), DecodeError> {
56        munge!(let Self { len, mut ptr } = slot);
57
58        if wire::Pointer::is_encoded_present(ptr.as_mut())? {
59            let mut envelopes = decoder.take_slice_slot::<wire::Envelope>(**len as usize)?;
60
61            for i in 0..**len as usize {
62                let mut envelope = envelopes.index(i);
63                if !wire::Envelope::is_encoded_zero(envelope.as_mut()) {
64                    f((i + 1) as i64, envelope, decoder)?;
65                }
66            }
67
68            wire::Pointer::set_decoded_slice(ptr, envelopes);
69        } else if **len != 0 {
70            return Err(DecodeError::InvalidOptionalSize(**len));
71        }
72
73        Ok(())
74    }
75}
76
77impl Table<'_> {
78    #[inline]
79    fn get_raw(&self, ordinal: usize) -> Option<*mut wire::Envelope> {
80        if ordinal == 0 || ordinal > *self.len as usize {
81            return None;
82        }
83
84        // SAFETY: `self.ptr` points to an array of `Envelope` of length `self.len`.
85        // We checked that `ordinal` is within `1..=self.len`, so `ordinal - 1` is within bounds.
86        unsafe {
87            let ptr = self.ptr.as_ptr().add(ordinal - 1);
88            (!(*ptr).is_zero()).then_some(ptr)
89        }
90    }
91
92    /// Returns a reference to the envelope for the given ordinal, if any.
93    #[inline]
94    pub fn get(&self, ordinal: usize) -> Option<&wire::Envelope> {
95        // SAFETY: `get_raw` always returns an envelope that is safe to
96        // dereference. We have a reference to `self`, so we can also alias
97        // the envelope.
98        unsafe { Some(&*self.get_raw(ordinal)?) }
99    }
100
101    /// Returns a mutable reference to the envelope for the given ordinal, if any.
102    #[inline]
103    pub fn get_mut(&mut self, ordinal: usize) -> Option<&mut wire::Envelope> {
104        // SAFETY: `get_raw` always returns an envelope that is safe to
105        // dereference. We have a mutable reference to `self`, so we can also
106        // mutably alias the envelope.
107        unsafe { Some(&mut *self.get_raw(ordinal)?) }
108    }
109}