wasi_common/
string_array.rs

1use crate::{Error, ErrorExt};
2use wiggle::{GuestMemory, GuestPtr};
3
4pub struct StringArray {
5    elems: Vec<String>,
6}
7
8#[derive(Debug, thiserror::Error)]
9pub enum StringArrayError {
10    #[error("Number of elements exceeds 2^32")]
11    NumberElements,
12    #[error("Element size exceeds 2^32")]
13    ElementSize,
14    #[error("Cumulative size exceeds 2^32")]
15    CumulativeSize,
16}
17
18impl StringArray {
19    pub fn new() -> Self {
20        StringArray { elems: Vec::new() }
21    }
22
23    pub fn push(&mut self, elem: String) -> Result<(), StringArrayError> {
24        if self.elems.len() + 1 > std::u32::MAX as usize {
25            return Err(StringArrayError::NumberElements);
26        }
27        if elem.as_bytes().len() + 1 > std::u32::MAX as usize {
28            return Err(StringArrayError::ElementSize);
29        }
30        if self.cumulative_size() as usize + elem.as_bytes().len() + 1 > std::u32::MAX as usize {
31            return Err(StringArrayError::CumulativeSize);
32        }
33        self.elems.push(elem);
34        Ok(())
35    }
36
37    pub fn number_elements(&self) -> u32 {
38        self.elems.len() as u32
39    }
40
41    pub fn cumulative_size(&self) -> u32 {
42        self.elems
43            .iter()
44            .map(|e| e.as_bytes().len() + 1)
45            .sum::<usize>() as u32
46    }
47
48    pub fn write_to_guest(
49        &self,
50        memory: &mut GuestMemory<'_>,
51        buffer: GuestPtr<u8>,
52        element_heads: GuestPtr<GuestPtr<u8>>,
53    ) -> Result<(), Error> {
54        let element_heads = element_heads.as_array(self.number_elements());
55        let buffer = buffer.as_array(self.cumulative_size());
56        let mut cursor = 0;
57        for (elem, head) in self.elems.iter().zip(element_heads.iter()) {
58            let bytes = elem.as_bytes();
59            let len = bytes.len() as u32;
60            {
61                let elem_buffer = buffer
62                    .get_range(cursor..(cursor + len))
63                    .ok_or(Error::invalid_argument())?; // Elements don't fit in buffer provided
64                memory.copy_from_slice(bytes, elem_buffer)?;
65            }
66            memory.write(
67                buffer.get(cursor + len).ok_or(Error::invalid_argument())?,
68                0,
69            )?; // 0 terminate
70            memory.write(head?, buffer.get(cursor).expect("already validated"))?;
71            cursor += len + 1;
72        }
73        Ok(())
74    }
75}