1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#![allow(clippy::len_without_is_empty)]

use super::{AsContext, AsContextMut, Func, Index, Stored};
use alloc::vec::Vec;
use core::{fmt, fmt::Display};

/// A raw index to a table entity.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct TableIdx(u32);

impl Index for TableIdx {
    fn into_usize(self) -> usize {
        self.0 as usize
    }

    fn from_usize(value: usize) -> Self {
        let value = value.try_into().unwrap_or_else(|error| {
            panic!("index {value} is out of bounds as table index: {error}")
        });
        Self(value)
    }
}

/// Errors that may occur upon operating with table entities.
#[derive(Debug)]
#[non_exhaustive]
pub enum TableError {
    /// Occurs when growing a table out of its set bounds.
    GrowOutOfBounds {
        /// The maximum allowed table size.
        maximum: usize,
        /// The current table size before the growth operation.
        current: usize,
        /// The amount of requested invalid growth.
        grow_by: usize,
    },
    /// Occurs when accessing the table out of bounds.
    AccessOutOfBounds {
        /// The current size of the table.
        current: usize,
        /// The accessed index that is out of bounds.
        offset: usize,
    },
    /// Occurs when a table type does not satisfy the constraints of another.
    UnsatisfyingTableType {
        /// The unsatisfying [`TableType`].
        unsatisfying: TableType,
        /// The required [`TableType`].
        required: TableType,
    },
}

impl Display for TableError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::GrowOutOfBounds {
                maximum,
                current,
                grow_by,
            } => {
                write!(
                    f,
                    "tried to grow table with size of {} and maximum of {} by {} out of bounds",
                    current, maximum, grow_by
                )
            }
            Self::AccessOutOfBounds { current, offset } => {
                write!(
                    f,
                    "out of bounds access of table element {} of table with size {}",
                    offset, current,
                )
            }
            Self::UnsatisfyingTableType {
                unsatisfying,
                required,
            } => {
                write!(
                    f,
                    "table type {:?} does not satisfy requirements of {:?}",
                    unsatisfying, required,
                )
            }
        }
    }
}

/// A descriptor for a [`Table`] instance.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TableType {
    /// The initial size of the [`Table`].
    initial: usize,
    /// The optional maximum size fo the [`Table`].
    maximum: Option<usize>,
}

impl TableType {
    /// Creates a new [`TableType`].
    ///
    /// # Panics
    ///
    /// - If the `initial` limit is greater than the `maximum` limit if any.
    pub fn new(initial: usize, maximum: Option<usize>) -> Self {
        if let Some(maximum) = maximum {
            assert!(initial <= maximum);
        }
        Self { initial, maximum }
    }

    /// Returns the initial size.
    pub fn initial(self) -> usize {
        self.initial
    }

    /// Returns the maximum size if any.
    pub fn maximum(self) -> Option<usize> {
        self.maximum
    }

    /// Checks if `self` satisfies the given `TableType`.
    ///
    /// # Errors
    ///
    /// - If the initial limits of the `required` [`TableType`] are greater than `self`.
    /// - If the maximum limits of the `required` [`TableType`] are greater than `self`.
    pub(crate) fn satisfies(&self, required: &TableType) -> Result<(), TableError> {
        if required.initial() > self.initial() {
            return Err(TableError::UnsatisfyingTableType {
                unsatisfying: *self,
                required: *required,
            });
        }
        match (required.maximum(), self.maximum()) {
            (None, _) => (),
            (Some(max_required), Some(max)) if max_required >= max => (),
            _ => {
                return Err(TableError::UnsatisfyingTableType {
                    unsatisfying: *self,
                    required: *required,
                });
            }
        }
        Ok(())
    }
}

/// A Wasm table entity.
#[derive(Debug)]
pub struct TableEntity {
    table_type: TableType,
    elements: Vec<Option<Func>>,
}

impl TableEntity {
    /// Creates a new table entity with the given resizable limits.
    pub fn new(table_type: TableType) -> Self {
        Self {
            elements: vec![None; table_type.initial()],
            table_type,
        }
    }

    /// Returns the resizable limits of the table.
    pub fn table_type(&self) -> TableType {
        self.table_type
    }

    /// Returns the current length of the table.
    ///
    /// # Note
    ///
    /// The returned length must be valid within the
    /// resizable limits of the table entity.
    pub fn len(&self) -> usize {
        self.elements.len()
    }

    /// Grows the table by the given amount of elements.
    ///
    /// # Note
    ///
    /// The newly added elements are initialized to `None`.
    ///
    /// # Errors
    ///
    /// If the table is grown beyond its maximum limits.
    pub fn grow(&mut self, grow_by: usize) -> Result<(), TableError> {
        let maximum = self.table_type.maximum().unwrap_or(u32::MAX as usize);
        let current = self.len();
        let new_len = current
            .checked_add(grow_by)
            .filter(|&new_len| new_len <= maximum)
            .ok_or(TableError::GrowOutOfBounds {
                maximum,
                current,
                grow_by,
            })?;
        self.elements.resize(new_len, None);
        Ok(())
    }

    /// Returns the element at the given offset if any.
    ///
    /// # Errors
    ///
    /// If the accesses element is out of bounds of the table.
    pub fn get(&self, offset: usize) -> Result<Option<Func>, TableError> {
        let element =
            self.elements
                .get(offset)
                .copied()
                .ok_or_else(|| TableError::AccessOutOfBounds {
                    current: self.len(),
                    offset,
                })?;
        Ok(element)
    }

    /// Sets a new value to the table element at the given offset.
    ///
    /// # Errors
    ///
    /// If the accesses element is out of bounds of the table.
    pub fn set(&mut self, offset: usize, new_value: Option<Func>) -> Result<(), TableError> {
        let current = self.len();
        let element = self
            .elements
            .get_mut(offset)
            .ok_or(TableError::AccessOutOfBounds { current, offset })?;
        *element = new_value;
        Ok(())
    }
}

/// A Wasm table reference.
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct Table(Stored<TableIdx>);

impl Table {
    /// Creates a new table reference.
    pub(super) fn from_inner(stored: Stored<TableIdx>) -> Self {
        Self(stored)
    }

    /// Returns the underlying stored representation.
    pub(super) fn into_inner(self) -> Stored<TableIdx> {
        self.0
    }

    /// Creates a new table to the store.
    pub fn new(mut ctx: impl AsContextMut, table_type: TableType) -> Self {
        ctx.as_context_mut()
            .store
            .alloc_table(TableEntity::new(table_type))
    }

    /// Returns the type and limits of the table.
    ///
    /// # Panics
    ///
    /// Panics if `ctx` does not own this [`Table`].
    pub fn table_type(&self, ctx: impl AsContext) -> TableType {
        ctx.as_context().store.resolve_table(*self).table_type()
    }

    /// Returns the current length of the table.
    ///
    /// # Note
    ///
    /// The returned length must be valid within the
    /// resizable limits of the table entity.
    ///
    /// # Panics
    ///
    /// Panics if `ctx` does not own this [`Table`].
    pub fn len(&self, ctx: impl AsContext) -> usize {
        ctx.as_context().store.resolve_table(*self).len()
    }

    /// Grows the table by the given amount of elements.
    ///
    /// # Note
    ///
    /// The newly added elements are initialized to `None`.
    ///
    /// # Errors
    ///
    /// If the table is grown beyond its maximum limits.
    ///
    /// # Panics
    ///
    /// Panics if `ctx` does not own this [`Table`].
    pub fn grow(&self, mut ctx: impl AsContextMut, grow_by: usize) -> Result<(), TableError> {
        ctx.as_context_mut()
            .store
            .resolve_table_mut(*self)
            .grow(grow_by)
    }

    /// Returns the element at the given offset if any.
    ///
    /// # Errors
    ///
    /// If the accesses element is out of bounds of the table.
    ///
    /// # Panics
    ///
    /// Panics if `ctx` does not own this [`Table`].
    pub fn get(&self, ctx: impl AsContext, offset: usize) -> Result<Option<Func>, TableError> {
        ctx.as_context().store.resolve_table(*self).get(offset)
    }

    /// Sets a new value to the table element at the given offset.
    ///
    /// # Errors
    ///
    /// If the accesses element is out of bounds of the table.
    ///
    /// # Panics
    ///
    /// Panics if `ctx` does not own this [`Table`].
    pub fn set(
        &self,
        mut ctx: impl AsContextMut,
        offset: usize,
        new_value: Option<Func>,
    ) -> Result<(), TableError> {
        ctx.as_context_mut()
            .store
            .resolve_table_mut(*self)
            .set(offset, new_value)
    }
}