radix_wasmi/
table.rs

1#![allow(clippy::len_without_is_empty)]
2extern crate radix_wasmi_arena as wasmi_arena;
3
4use super::{AsContext, AsContextMut, Func, Stored};
5use alloc::vec::Vec;
6use core::{fmt, fmt::Display};
7use wasmi_arena::ArenaIndex;
8
9/// A raw index to a table entity.
10#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
11pub struct TableIdx(u32);
12
13impl ArenaIndex for TableIdx {
14    fn into_usize(self) -> usize {
15        self.0 as usize
16    }
17
18    fn from_usize(value: usize) -> Self {
19        let value = value.try_into().unwrap_or_else(|error| {
20            panic!("index {value} is out of bounds as table index: {error}")
21        });
22        Self(value)
23    }
24}
25
26/// Errors that may occur upon operating with table entities.
27#[derive(Debug)]
28#[non_exhaustive]
29pub enum TableError {
30    /// Occurs when growing a table out of its set bounds.
31    GrowOutOfBounds {
32        /// The maximum allowed table size.
33        maximum: u32,
34        /// The current table size before the growth operation.
35        current: u32,
36        /// The amount of requested invalid growth.
37        delta: u32,
38    },
39    /// Occurs when accessing the table out of bounds.
40    AccessOutOfBounds {
41        /// The current size of the table.
42        current: u32,
43        /// The accessed index that is out of bounds.
44        offset: u32,
45    },
46    /// Occurs when a table type does not satisfy the constraints of another.
47    UnsatisfyingTableType {
48        /// The unsatisfying [`TableType`].
49        unsatisfying: TableType,
50        /// The required [`TableType`].
51        required: TableType,
52    },
53}
54
55impl Display for TableError {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        match self {
58            Self::GrowOutOfBounds {
59                maximum,
60                current,
61                delta,
62            } => {
63                write!(
64                    f,
65                    "tried to grow table with size of {current} and maximum of \
66                    {maximum} by {delta} out of bounds",
67                )
68            }
69            Self::AccessOutOfBounds { current, offset } => {
70                write!(
71                    f,
72                    "out of bounds access of table element {offset} \
73                    of table with size {current}",
74                )
75            }
76            Self::UnsatisfyingTableType {
77                unsatisfying,
78                required,
79            } => {
80                write!(
81                    f,
82                    "table type {unsatisfying:?} does not satisfy requirements \
83                    of {required:?}",
84                )
85            }
86        }
87    }
88}
89
90/// A descriptor for a [`Table`] instance.
91#[derive(Debug, Copy, Clone, PartialEq, Eq)]
92pub struct TableType {
93    /// The minimum number of elements the [`Table`] must have.
94    min: u32,
95    /// The optional maximum number of elements the [`Table`] can have.
96    ///
97    /// If this is `None` then the [`Table`] is not limited in size.
98    max: Option<u32>,
99}
100
101impl TableType {
102    /// Creates a new [`TableType`].
103    ///
104    /// # Panics
105    ///
106    /// If `min` is greater than `max`.
107    pub fn new(min: u32, max: Option<u32>) -> Self {
108        if let Some(max) = max {
109            assert!(min <= max);
110        }
111        Self { min, max }
112    }
113
114    /// Returns minimum number of elements the [`Table`] must have.
115    pub fn minimum(self) -> u32 {
116        self.min
117    }
118
119    /// The optional maximum number of elements the [`Table`] can have.
120    ///
121    /// If this returns `None` then the [`Table`] is not limited in size.
122    pub fn maximum(self) -> Option<u32> {
123        self.max
124    }
125
126    /// Checks if `self` satisfies the given `TableType`.
127    ///
128    /// # Errors
129    ///
130    /// - If the initial limits of the `required` [`TableType`] are greater than `self`.
131    /// - If the maximum limits of the `required` [`TableType`] are greater than `self`.
132    pub(crate) fn satisfies(&self, required: &TableType) -> Result<(), TableError> {
133        if required.minimum() > self.minimum() {
134            return Err(TableError::UnsatisfyingTableType {
135                unsatisfying: *self,
136                required: *required,
137            });
138        }
139        match (required.maximum(), self.maximum()) {
140            (None, _) => (),
141            (Some(max_required), Some(max)) if max_required >= max => (),
142            _ => {
143                return Err(TableError::UnsatisfyingTableType {
144                    unsatisfying: *self,
145                    required: *required,
146                });
147            }
148        }
149        Ok(())
150    }
151}
152
153/// A Wasm table entity.
154#[derive(Debug, Clone)]
155pub struct TableEntity {
156    ty: TableType,
157    elements: Vec<Option<Func>>,
158}
159
160impl TableEntity {
161    /// Creates a new table entity with the given resizable limits.
162    pub fn new(ty: TableType) -> Self {
163        Self {
164            elements: vec![None; ty.minimum() as usize],
165            ty,
166        }
167    }
168
169    /// Returns the resizable limits of the table.
170    pub fn ty(&self) -> TableType {
171        self.ty
172    }
173
174    /// Returns the current size of the [`Table`].
175    pub fn size(&self) -> u32 {
176        self.elements.len() as u32
177    }
178
179    /// Grows the table by the given amount of elements.
180    ///
181    /// # Note
182    ///
183    /// The newly added elements are initialized to `None`.
184    ///
185    /// # Errors
186    ///
187    /// If the table is grown beyond its maximum limits.
188    pub fn grow(&mut self, delta: u32) -> Result<(), TableError> {
189        let maximum = self.ty.maximum().unwrap_or(u32::MAX);
190        let current = self.size();
191        let new_len = current
192            .checked_add(delta)
193            .filter(|&new_len| new_len <= maximum)
194            .ok_or(TableError::GrowOutOfBounds {
195                maximum,
196                current,
197                delta,
198            })? as usize;
199        self.elements.resize(new_len, None);
200        Ok(())
201    }
202
203    /// Returns the [`Table`] element value at `index`.
204    ///
205    /// # Errors
206    ///
207    /// If `index` is out of bounds.
208    pub fn get(&self, index: u32) -> Result<Option<Func>, TableError> {
209        let element = self.elements.get(index as usize).copied().ok_or_else(|| {
210            TableError::AccessOutOfBounds {
211                current: self.size(),
212                offset: index,
213            }
214        })?;
215        Ok(element)
216    }
217
218    /// Writes the `value` provided into `index` within this [`Table`].
219    ///
220    /// # Errors
221    ///
222    /// If `index` is out of bounds.
223    pub fn set(&mut self, index: u32, value: Option<Func>) -> Result<(), TableError> {
224        let current = self.size();
225        let element =
226            self.elements
227                .get_mut(index as usize)
228                .ok_or(TableError::AccessOutOfBounds {
229                    current,
230                    offset: index,
231                })?;
232        *element = value;
233        Ok(())
234    }
235}
236
237/// A Wasm table reference.
238#[derive(Debug, Copy, Clone)]
239#[repr(transparent)]
240pub struct Table(Stored<TableIdx>);
241
242impl Table {
243    /// Creates a new table reference.
244    pub(super) fn from_inner(stored: Stored<TableIdx>) -> Self {
245        Self(stored)
246    }
247
248    /// Returns the underlying stored representation.
249    pub(super) fn into_inner(self) -> Stored<TableIdx> {
250        self.0
251    }
252
253    /// Creates a new table to the store.
254    pub fn new(mut ctx: impl AsContextMut, ty: TableType) -> Self {
255        ctx.as_context_mut().store.alloc_table(TableEntity::new(ty))
256    }
257
258    /// Returns the type and limits of the table.
259    ///
260    /// # Panics
261    ///
262    /// Panics if `ctx` does not own this [`Table`].
263    pub fn ty(&self, ctx: impl AsContext) -> TableType {
264        ctx.as_context().store.resolve_table(*self).ty()
265    }
266
267    /// Returns the current size of the [`Table`].
268    ///
269    /// # Panics
270    ///
271    /// If `ctx` does not own this [`Table`].
272    pub fn size(&self, ctx: impl AsContext) -> u32 {
273        ctx.as_context().store.resolve_table(*self).size()
274    }
275
276    /// Grows the table by the given amount of elements.
277    ///
278    /// # Note
279    ///
280    /// The newly added elements are initialized to `None`.
281    ///
282    /// # Errors
283    ///
284    /// If the table is grown beyond its maximum limits.
285    ///
286    /// # Panics
287    ///
288    /// Panics if `ctx` does not own this [`Table`].
289    pub fn grow(&self, mut ctx: impl AsContextMut, delta: u32) -> Result<(), TableError> {
290        ctx.as_context_mut()
291            .store
292            .resolve_table_mut(*self)
293            .grow(delta)
294    }
295
296    /// Returns the [`Table`] element value at `index`.
297    ///
298    /// # Errors
299    ///
300    /// If `index` is out of bounds.
301    ///
302    /// # Panics
303    ///
304    /// Panics if `ctx` does not own this [`Table`].
305    pub fn get(&self, ctx: impl AsContext, index: u32) -> Result<Option<Func>, TableError> {
306        ctx.as_context().store.resolve_table(*self).get(index)
307    }
308
309    /// Writes the `value` provided into `index` within this [`Table`].
310    ///
311    /// # Errors
312    ///
313    /// If `index` is out of bounds.
314    ///
315    /// # Panics
316    ///
317    /// Panics if `ctx` does not own this [`Table`].
318    pub fn set(
319        &self,
320        mut ctx: impl AsContextMut,
321        index: u32,
322        value: Option<Func>,
323    ) -> Result<(), TableError> {
324        ctx.as_context_mut()
325            .store
326            .resolve_table_mut(*self)
327            .set(index, value)
328    }
329}