makepad_stitch/
table.rs

1use {
2    crate::{
3        decode::{Decode, DecodeError, Decoder},
4        downcast::{DowncastMut, DowncastRef},
5        elem::{Elem, ElemEntity, ElemEntityT},
6        extern_ref::UnguardedExternRef,
7        func_ref::UnguardedFuncRef,
8        limits::Limits,
9        ref_::{Ref, RefType, UnguardedRef},
10        store::{Handle, HandlePair, Store, StoreId, UnguardedHandle},
11        trap::Trap,
12    },
13    std::{error::Error, fmt},
14};
15
16/// A Wasm table.
17#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
18#[repr(transparent)]
19pub struct Table(pub(crate) Handle<TableEntity>);
20
21impl Table {
22    /// Creates a new [`Table`] with the given [`TableType`] and initialization [`Ref`] in the
23    /// given [`Store`].
24    ///
25    /// # Errors
26    ///
27    /// - If the [`RefType`] of the initialization [`Ref`] does not match the [`RefType`] of the
28    ///   elements in the [`Table`] to be created.
29    ///
30    /// # Panics
31    ///
32    /// - If the [`TableType`] is invalid.
33    /// - If the initialization [`Ref`] is not owned by the given [`Store`].
34    pub fn new(store: &mut Store, type_: TableType, val: Ref) -> Result<Self, TableError> {
35        assert!(type_.is_valid(), "invalid table type");
36        unsafe { Self::new_unguarded(store, type_, val.to_unguarded(store.id())) }
37    }
38
39    /// An unguarded version of [`Table::new`].
40    unsafe fn new_unguarded(
41        store: &mut Store,
42        type_: TableType,
43        val: UnguardedRef,
44    ) -> Result<Self, TableError> {
45        match (type_.elem, val) {
46            (RefType::FuncRef, UnguardedRef::FuncRef(val)) => Ok(Self(
47                store.insert_table(TableEntity::FuncRef(TableEntityT::new(type_.limits, val))),
48            )),
49            (RefType::ExternRef, UnguardedRef::ExternRef(val)) => Ok(Self(
50                store.insert_table(TableEntity::ExternRef(TableEntityT::new(type_.limits, val))),
51            )),
52            _ => Err(TableError::ElemTypeMismatch),
53        }
54    }
55
56    /// Returns the [`TableType`] of this [`Table`].
57    pub fn type_(self, store: &Store) -> TableType {
58        match self.0.as_ref(store) {
59            TableEntity::FuncRef(table) => TableType {
60                limits: table.limits(),
61                elem: RefType::FuncRef,
62            },
63            TableEntity::ExternRef(table) => TableType {
64                limits: table.limits(),
65                elem: RefType::ExternRef,
66            },
67        }
68    }
69
70    /// Returns the element at the given index in this [`Table`].
71    ///
72    /// # Errors
73    ///
74    /// - If the access is out of bounds.
75    pub fn get(self, store: &Store, idx: u32) -> Option<Ref> {
76        self.get_unguarded(store, idx)
77            .map(|val| unsafe { Ref::from_unguarded(val, store.id()) })
78    }
79
80    /// An unguarded version of [`Table::get`].
81    fn get_unguarded(self, store: &Store, idx: u32) -> Option<UnguardedRef> {
82        match self.0.as_ref(store) {
83            TableEntity::FuncRef(table) => table.get(idx).map(UnguardedRef::FuncRef),
84            TableEntity::ExternRef(table) => table.get(idx).map(UnguardedRef::ExternRef),
85        }
86    }
87
88    /// Sets the element at the given index in this [`Table`] to the given [`Ref`].
89    ///
90    /// # Errors
91    ///
92    /// - If the access is out of bounds.
93    /// - If the [`RefType`] of the given [`Ref`] does not match the [`RefType`] of the elements in
94    ///   this [`Table`].
95    ///
96    /// # Panics
97    ///
98    /// - If the given [`Ref`] is not owned by the given [`Store`].
99    pub fn set(self, store: &mut Store, idx: u32, val: Ref) -> Result<(), TableError> {
100        unsafe { self.set_unguarded(store, idx, val.to_unguarded(store.id())) }
101    }
102
103    /// An unguarded version of [`Table::set`].
104    unsafe fn set_unguarded(
105        self,
106        store: &mut Store,
107        idx: u32,
108        val: UnguardedRef,
109    ) -> Result<(), TableError> {
110        match (self.0.as_mut(store), val) {
111            (TableEntity::FuncRef(table), UnguardedRef::FuncRef(val)) => table.set(idx, val),
112            (TableEntity::ExternRef(table), UnguardedRef::ExternRef(val)) => table.set(idx, val),
113            _ => Err(TableError::ElemTypeMismatch),
114        }
115    }
116
117    /// Returns the size of this [`Table`] in number of elements.
118    pub fn size(&self, store: &Store) -> u32 {
119        match self.0.as_ref(store) {
120            TableEntity::FuncRef(table) => table.size(),
121            TableEntity::ExternRef(table) => table.size(),
122        }
123    }
124
125    /// Grows this [`Table`] by the given number of elements with the given initialization [`Ref`].
126    ///
127    /// Returns the previous size of this [`Table`] in number of elements.
128    ///
129    /// # Errors
130    ///
131    /// - If the [`RefType`] of the given initialization [`Ref`] does not match the [`RefType`] of
132    ///   the elements in this [`Table`].
133    /// - If this [`Table`] failed to grow.
134    ///
135    /// # Panics
136    ///
137    /// - If the given initialization [`Ref`] is not owned by the given [`Store`].
138    pub fn grow(self, store: &mut Store, val: Ref, count: u32) -> Result<(), TableError> {
139        unsafe { self.grow_unguarded(store, val.to_unguarded(store.id()), count) }
140    }
141
142    /// An unguarded version of [`Table::grow`].
143    unsafe fn grow_unguarded(
144        self,
145        store: &mut Store,
146        val: UnguardedRef,
147        count: u32,
148    ) -> Result<(), TableError> {
149        match (self.0.as_mut(store), val) {
150            (TableEntity::FuncRef(table), UnguardedRef::FuncRef(val)) => table.grow(val, count),
151            (TableEntity::ExternRef(table), UnguardedRef::ExternRef(val)) => table.grow(val, count),
152            _ => Err(TableError::ElemTypeMismatch),
153        }
154        .map(|_| ())
155    }
156
157    pub(crate) fn init(
158        self,
159        store: &mut Store,
160        dst_idx: u32,
161        src_elem: Elem,
162        src_idx: u32,
163        count: u32,
164    ) -> Result<(), Trap> {
165        let (dst_table, src_elem) = HandlePair(self.0, src_elem.0).as_mut_pair(store);
166        match (dst_table, src_elem) {
167            (TableEntity::FuncRef(table), ElemEntity::FuncRef(src_elem)) => {
168                table.init(dst_idx, src_elem, src_idx, count)
169            }
170            (TableEntity::ExternRef(table), ElemEntity::ExternRef(src_elem)) => {
171                table.init(dst_idx, src_elem, src_idx, count)
172            }
173            _ => panic!(),
174        }
175    }
176
177    /// Converts the given [`UnguardedTable`] to a [`Table`].
178    ///
179    /// # Safety
180    ///
181    /// The given [`UnguardedTable`] must be owned by the [`Store`] with the given [`StoreId`].
182    pub(crate) unsafe fn from_unguarded(table: UnguardedTable, store_id: StoreId) -> Self {
183        Self(Handle::from_unguarded(table, store_id))
184    }
185
186    /// Converts this [`Table`] to an [`UnguardedTable`].
187    ///
188    /// # Panics
189    ///
190    /// This [`Table`] is not owned by the [`Store`] with the given [`StoreId`].
191    pub(crate) fn to_unguarded(self, store_id: StoreId) -> UnguardedTable {
192        self.0.to_unguarded(store_id)
193    }
194}
195
196/// An unguarded version of [`Table`].
197pub(crate) type UnguardedTable = UnguardedHandle<TableEntity>;
198
199/// The type of a [`Table`].
200#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
201pub struct TableType {
202    /// The [`Limits`] of the [`Table`].
203    pub limits: Limits,
204    /// The [`RefType`] of the elements in the [`Table`].
205    pub elem: RefType,
206}
207
208impl TableType {
209    /// Returns `true` if this [`TableType`] is valid.
210    ///
211    /// A [`TableType`] is valid if its [`Limits`] are valid within range `u32::MAX`.
212    pub fn is_valid(self) -> bool {
213        if !self.limits.is_valid(u32::MAX) {
214            return false;
215        }
216        true
217    }
218
219    /// Returns `true` if this [`TableType`] is a subtype of the given [`TableType`].
220    ///
221    /// A [`TableType`] is a subtype of another [`TableType`] if its [`Limits`] are a sublimit of
222    /// the other's and the [`RefType`] of its elements is the same as the other's.
223    pub fn is_subtype_of(self, other: Self) -> bool {
224        if !self.limits.is_sublimit_of(other.limits) {
225            return false;
226        }
227        if self.elem != other.elem {
228            return false;
229        }
230        true
231    }
232}
233
234impl Decode for TableType {
235    fn decode(decoder: &mut Decoder<'_>) -> Result<Self, DecodeError> {
236        let elem = decoder.decode()?;
237        let limits = decoder.decode()?;
238        Ok(Self { limits, elem })
239    }
240}
241
242/// An error that can occur when operating on a [`Table`].
243#[derive(Clone, Copy, Debug)]
244#[non_exhaustive]
245pub enum TableError {
246    AccessOutOfBounds,
247    ElemTypeMismatch,
248    FailedToGrow,
249}
250
251impl fmt::Display for TableError {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        match self {
254            Self::AccessOutOfBounds => write!(f, "table access out of bounds"),
255            Self::ElemTypeMismatch => write!(f, "table element type mismatch"),
256            Self::FailedToGrow => write!(f, "table failed to grow"),
257        }
258    }
259}
260
261impl Error for TableError {}
262
263/// The representation of a [`Table`] in a [`Store`].
264#[derive(Debug)]
265pub(crate) enum TableEntity {
266    FuncRef(TableEntityT<UnguardedFuncRef>),
267    ExternRef(TableEntityT<UnguardedExternRef>),
268}
269
270impl TableEntity {
271    /// Returns a reference to the inner value of this [`TableEntity`] if it is a
272    /// [`TableEntityT<T>`].
273    pub(crate) fn downcast_ref<T>(&self) -> Option<&TableEntityT<T>>
274    where
275        TableEntityT<T>: DowncastRef<Self>,
276    {
277        TableEntityT::downcast_ref(self)
278    }
279
280    /// Returns a mutable reference to the inner value of this [`TableEntity`] if it is a
281    /// [`TableEntityT<T>`].
282    pub(crate) fn downcast_mut<T>(&mut self) -> Option<&mut TableEntityT<T>>
283    where
284        TableEntityT<T>: DowncastMut<Self>,
285    {
286        TableEntityT::downcast_mut(self)
287    }
288}
289
290/// A typed [`TableEntity`].
291#[derive(Debug)]
292pub(crate) struct TableEntityT<T> {
293    max: Option<u32>,
294    elems: Vec<T>,
295}
296
297impl<T> TableEntityT<T>
298where
299    T: Copy,
300{
301    /// Creates a new [`TableEntityT`] with the given [`Limits`] and initialization value.
302    fn new(limits: Limits, val: T) -> Self {
303        let min = limits.min as usize;
304        Self {
305            max: limits.max,
306            elems: vec![val; min],
307        }
308    }
309
310    /// Returns the [`Limits`] of this [`TableEntity`].
311    fn limits(&self) -> Limits {
312        Limits {
313            min: u32::try_from(self.elems.len()).unwrap(),
314            max: self.max,
315        }
316    }
317
318    /// Returns the element at the given index in this [`TableEntity`].
319    ///
320    /// # Errors
321    ///
322    /// If the access is out of bounds.
323    pub(crate) fn get(&self, idx: u32) -> Option<T> {
324        let idx = idx as usize;
325        let elem = self.elems.get(idx)?;
326        Some(*elem)
327    }
328
329    /// Sets the element at the given index in this [`TableEntity`] to the given value.
330    ///
331    /// # Errors
332    ///
333    /// If the access is out of bounds.
334    pub(crate) fn set(&mut self, idx: u32, val: T) -> Result<(), TableError> {
335        let idx = idx as usize;
336        let elem = self
337            .elems
338            .get_mut(idx)
339            .ok_or(TableError::AccessOutOfBounds)?;
340        *elem = val;
341        Ok(())
342    }
343
344    /// Returns the size of this [`TableEntity`] in number of elements.
345    pub(crate) fn size(&self) -> u32 {
346        self.elems.len() as u32
347    }
348
349    /// Grows this [`TableEntity`] by the given number of elements with the given initialization
350    /// value.
351    ///
352    /// Returns the previous size of this [`TableEntity`] in number of elements.
353    ///
354    /// # Errors
355    ///
356    /// If this [`TableEntity`] failed to grow.
357    pub(crate) fn grow(&mut self, val: T, count: u32) -> Result<u32, TableError> {
358        if count > self.max.unwrap_or(u32::MAX) - self.size() {
359            return Err(TableError::FailedToGrow)?;
360        }
361        let count = count as usize;
362        let size = self.size();
363        self.elems.resize(self.elems.len() + count, val);
364        Ok(size)
365    }
366
367    pub(crate) fn fill(&mut self, idx: u32, val: T, count: u32) -> Result<(), Trap> {
368        let idx = idx as usize;
369        let count = count as usize;
370        let elems = self
371            .elems
372            .get_mut(idx..)
373            .and_then(|elems| elems.get_mut(..count))
374            .ok_or(Trap::TableAccessOutOfBounds)?;
375        elems.fill(val);
376        Ok(())
377    }
378
379    pub(crate) fn copy(
380        &mut self,
381        dst_idx: u32,
382        src_table: &TableEntityT<T>,
383        src_idx: u32,
384        count: u32,
385    ) -> Result<(), Trap> {
386        let dst_idx = dst_idx as usize;
387        let src_idx = src_idx as usize;
388        let count = count as usize;
389        let dst_elems = self
390            .elems
391            .get_mut(dst_idx..)
392            .and_then(|elems| elems.get_mut(..count))
393            .ok_or(Trap::TableAccessOutOfBounds)?;
394        let src_elems = src_table
395            .elems
396            .get(src_idx..)
397            .and_then(|elems| elems.get(..count))
398            .ok_or(Trap::TableAccessOutOfBounds)?;
399        dst_elems.copy_from_slice(src_elems);
400        Ok(())
401    }
402
403    pub(crate) fn copy_within(
404        &mut self,
405        dst_idx: u32,
406        src_idx: u32,
407        count: u32,
408    ) -> Result<(), Trap> {
409        let dst_idx = dst_idx as usize;
410        let src_idx = src_idx as usize;
411        let count = count as usize;
412        if count > self.elems.len()
413            || dst_idx > self.elems.len() - count
414            || src_idx > self.elems.len() - count
415        {
416            return Err(Trap::TableAccessOutOfBounds)?;
417        }
418        self.elems.copy_within(src_idx..src_idx + count, dst_idx);
419        Ok(())
420    }
421
422    pub(crate) fn init(
423        &mut self,
424        dst_idx: u32,
425        src_elem: &ElemEntityT<T>,
426        src_idx: u32,
427        count: u32,
428    ) -> Result<(), Trap> {
429        let dst_idx = dst_idx as usize;
430        let src_idx = src_idx as usize;
431        let count = count as usize;
432        let dst_elems = self
433            .elems
434            .get_mut(dst_idx..)
435            .and_then(|elems| elems.get_mut(..count))
436            .ok_or(Trap::TableAccessOutOfBounds)?;
437        let src_elems = src_elem
438            .elems()
439            .get(src_idx..)
440            .and_then(|elems| elems.get(..count))
441            .ok_or(Trap::TableAccessOutOfBounds)?;
442        dst_elems.copy_from_slice(src_elems);
443        Ok(())
444    }
445}
446
447impl DowncastRef<TableEntity> for TableEntityT<UnguardedFuncRef> {
448    fn downcast_ref(table: &TableEntity) -> Option<&Self> {
449        match table {
450            TableEntity::FuncRef(table) => Some(table),
451            _ => None,
452        }
453    }
454}
455
456impl DowncastMut<TableEntity> for TableEntityT<UnguardedFuncRef> {
457    fn downcast_mut(table: &mut TableEntity) -> Option<&mut Self> {
458        match table {
459            TableEntity::FuncRef(table) => Some(table),
460            _ => None,
461        }
462    }
463}
464
465impl DowncastRef<TableEntity> for TableEntityT<UnguardedExternRef> {
466    fn downcast_ref(table: &TableEntity) -> Option<&Self> {
467        match table {
468            TableEntity::ExternRef(table) => Some(table),
469            _ => None,
470        }
471    }
472}
473
474impl DowncastMut<TableEntity> for TableEntityT<UnguardedExternRef> {
475    fn downcast_mut(table: &mut TableEntity) -> Option<&mut Self> {
476        match table {
477            TableEntity::ExternRef(table) => Some(table),
478            _ => None,
479        }
480    }
481}