casper_wasmi/
table.rs

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