1 use crate::Error;
2 use std::convert::TryInto;
3 use std::ffi::CString;
4 use wiggle::GuestPtr;
5 
6 pub struct StringArray {
7     elems: Vec<CString>,
8     pub number_elements: u32,
9     pub cumulative_size: u32,
10 }
11 
12 #[derive(Debug, thiserror::Error)]
13 pub enum StringArrayError {
14     /// Provided sequence of bytes contained an unexpected NUL byte.
15     #[error("provided sequence contained an unexpected NUL byte")]
16     Nul(#[from] std::ffi::NulError),
17     /// Too many elements: must fit into u32
18     #[error("too many elements")]
19     NumElements,
20     /// Element size: must fit into u32
21     #[error("element too big")]
22     ElemSize,
23     /// Cumulative element size: must fit into u32
24     #[error("cumulative element size too big")]
25     CumElemSize,
26 }
27 
28 impl StringArray {
29     pub fn new(elems: Vec<String>) -> Result<Self, StringArrayError> {
30         let elems = elems
31             .into_iter()
32             .map(|s| CString::new(s))
33             .collect::<Result<Vec<CString>, _>>()?;
34         let number_elements = elems
35             .len()
36             .try_into()
37             .map_err(|_| StringArrayError::NumElements)?;
38         let mut cumulative_size: u32 = 0;
39         for elem in elems.iter() {
40             let elem_bytes = elem
41                 .as_bytes_with_nul()
42                 .len()
43                 .try_into()
44                 .map_err(|_| StringArrayError::ElemSize)?;
45             cumulative_size = cumulative_size
46                 .checked_add(elem_bytes)
47                 .ok_or(StringArrayError::CumElemSize)?;
48         }
49         Ok(Self {
50             elems,
51             number_elements,
52             cumulative_size,
53         })
54     }
55 
56     pub fn write_to_guest<'a>(
57         &self,
58         buffer: &GuestPtr<'a, u8>,
59         element_heads: &GuestPtr<'a, GuestPtr<'a, u8>>,
60     ) -> Result<(), Error> {
61         let element_heads = element_heads.as_array(self.number_elements);
62         let buffer = buffer.as_array(self.cumulative_size);
63         let mut cursor = 0;
64         for (elem, head) in self.elems.iter().zip(element_heads.iter()) {
65             let bytes = elem.as_bytes_with_nul();
66             let len: u32 = bytes.len().try_into()?;
67             let elem_buffer = buffer
68                 .get_range(cursor..(cursor + len))
69                 .ok_or(Error::Inval)?; // Elements don't fit in buffer provided
70             elem_buffer.copy_from_slice(bytes)?;
71             head?.write(
72                 elem_buffer
73                     .get(0)
74                     .expect("all elem buffers at least length 1"),
75             )?;
76             cursor += len;
77         }
78         Ok(())
79     }
80 }
81