1 use crate::{Error, ErrorExt};
2 use wiggle::{GuestMemory, GuestPtr};
3 
4 pub struct StringArray {
5     elems: Vec<String>,
6 }
7 
8 #[derive(Debug, thiserror::Error)]
9 pub 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 
18 impl 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 }
76