patract_wasmi/
table.rs

1use crate::func::FuncRef;
2use crate::module::check_limits;
3use crate::Error;
4use alloc::{rc::Rc, vec::Vec};
5use core::cell::RefCell;
6use core::fmt;
7use core::u32;
8use parity_wasm::elements::ResizableLimits;
9
10/// Reference to a table (See [`TableInstance`] for details).
11///
12/// This reference has a reference-counting semantics.
13///
14/// [`TableInstance`]: struct.TableInstance.html
15///
16#[derive(Clone, Debug)]
17pub struct TableRef(Rc<TableInstance>);
18
19impl ::core::ops::Deref for TableRef {
20    type Target = TableInstance;
21    fn deref(&self) -> &TableInstance {
22        &self.0
23    }
24}
25
26/// Runtime representation of a table.
27///
28/// A table is a array of untyped functions. It allows wasm code to call functions
29/// indirectly through a dynamic index into a table. For example, this allows emulating function
30/// pointers by way of table indices.
31///
32/// Table is created with an initial size but can be grown dynamically via [`grow`] method.
33/// Growth can be limited by an optional maximum size.
34///
35/// In future, a table might be extended to be able to hold not only functions but different types.
36///
37/// [`grow`]: #method.grow
38///
39pub struct TableInstance {
40    /// Table limits.
41    limits: ResizableLimits,
42    /// Table memory buffer.
43    buffer: RefCell<Vec<Option<FuncRef>>>,
44}
45
46impl fmt::Debug for TableInstance {
47    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48        f.debug_struct("TableInstance")
49            .field("limits", &self.limits)
50            .field("buffer.len", &self.buffer.borrow().len())
51            .finish()
52    }
53}
54
55impl TableInstance {
56    /// Allocate a table instance.
57    ///
58    /// The table allocated with initial size, specified by `initial_size`.
59    /// Maximum size can be specified by `maximum_size`.
60    ///
61    /// All table elements are allocated uninitialized.
62    ///
63    /// # Errors
64    ///
65    /// Returns `Err` if `initial_size` is greater than `maximum_size`.
66    pub fn alloc(initial_size: u32, maximum_size: Option<u32>) -> Result<TableRef, Error> {
67        let table = TableInstance::new(ResizableLimits::new(initial_size, maximum_size))?;
68        Ok(TableRef(Rc::new(table)))
69    }
70
71    fn new(limits: ResizableLimits) -> Result<TableInstance, Error> {
72        check_limits(&limits)?;
73        Ok(TableInstance {
74            buffer: RefCell::new(vec![None; limits.initial() as usize]),
75            limits,
76        })
77    }
78
79    /// Return table limits.
80    pub(crate) fn limits(&self) -> &ResizableLimits {
81        &self.limits
82    }
83
84    /// Returns size this table was created with.
85    pub fn initial_size(&self) -> u32 {
86        self.limits.initial()
87    }
88
89    /// Returns maximum size `TableInstance` can grow to.
90    pub fn maximum_size(&self) -> Option<u32> {
91        self.limits.maximum()
92    }
93
94    /// Returns current size of the table.
95    pub fn current_size(&self) -> u32 {
96        self.buffer.borrow().len() as u32
97    }
98
99    /// Increases the size of the table by given number of elements.
100    ///
101    /// # Errors
102    ///
103    /// Returns `Err` if tried to allocate more elements than permited by limit.
104    pub fn grow(&self, by: u32) -> Result<(), Error> {
105        let mut buffer = self.buffer.borrow_mut();
106        let maximum_size = self.maximum_size().unwrap_or(u32::MAX);
107        let new_size = self
108            .current_size()
109            .checked_add(by)
110            .and_then(|new_size| {
111                if maximum_size < new_size {
112                    None
113                } else {
114                    Some(new_size)
115                }
116            })
117            .ok_or_else(|| {
118                Error::Table(format!(
119                    "Trying to grow table by {} items when there are already {} items",
120                    by,
121                    self.current_size(),
122                ))
123            })?;
124        buffer.resize(new_size as usize, None);
125        Ok(())
126    }
127
128    /// Get the specific value in the table
129    pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> {
130        let buffer = self.buffer.borrow();
131        let buffer_len = buffer.len();
132        let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| {
133            Error::Table(format!(
134                "trying to read table item with index {} when there are only {} items",
135                offset, buffer_len
136            ))
137        })?;
138        Ok(table_elem)
139    }
140
141    /// Set the table element to the specified function.
142    pub fn set(&self, offset: u32, value: Option<FuncRef>) -> Result<(), Error> {
143        let mut buffer = self.buffer.borrow_mut();
144        let buffer_len = buffer.len();
145        let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| {
146            Error::Table(format!(
147                "trying to update table item with index {} when there are only {} items",
148                offset, buffer_len
149            ))
150        })?;
151        *table_elem = value;
152        Ok(())
153    }
154}