147fec44cSPat Hickey use crate::{Error, ErrorExt};
2*f1411653SAlex Crichton use wiggle::{GuestMemory, GuestPtr};
347fec44cSPat Hickey 
447fec44cSPat Hickey pub struct StringArray {
547fec44cSPat Hickey     elems: Vec<String>,
647fec44cSPat Hickey }
747fec44cSPat Hickey 
847fec44cSPat Hickey #[derive(Debug, thiserror::Error)]
947fec44cSPat Hickey pub enum StringArrayError {
1047fec44cSPat Hickey     #[error("Number of elements exceeds 2^32")]
1147fec44cSPat Hickey     NumberElements,
1247fec44cSPat Hickey     #[error("Element size exceeds 2^32")]
1347fec44cSPat Hickey     ElementSize,
1447fec44cSPat Hickey     #[error("Cumulative size exceeds 2^32")]
1547fec44cSPat Hickey     CumulativeSize,
1647fec44cSPat Hickey }
1747fec44cSPat Hickey 
1847fec44cSPat Hickey impl StringArray {
new() -> Self1947fec44cSPat Hickey     pub fn new() -> Self {
2047fec44cSPat Hickey         StringArray { elems: Vec::new() }
2147fec44cSPat Hickey     }
2247fec44cSPat Hickey 
push(&mut self, elem: String) -> Result<(), StringArrayError>2347fec44cSPat Hickey     pub fn push(&mut self, elem: String) -> Result<(), StringArrayError> {
2447fec44cSPat Hickey         if self.elems.len() + 1 > std::u32::MAX as usize {
2547fec44cSPat Hickey             return Err(StringArrayError::NumberElements);
2647fec44cSPat Hickey         }
2747fec44cSPat Hickey         if elem.as_bytes().len() + 1 > std::u32::MAX as usize {
2847fec44cSPat Hickey             return Err(StringArrayError::ElementSize);
2947fec44cSPat Hickey         }
3047fec44cSPat Hickey         if self.cumulative_size() as usize + elem.as_bytes().len() + 1 > std::u32::MAX as usize {
3147fec44cSPat Hickey             return Err(StringArrayError::CumulativeSize);
3247fec44cSPat Hickey         }
3347fec44cSPat Hickey         self.elems.push(elem);
3447fec44cSPat Hickey         Ok(())
3547fec44cSPat Hickey     }
3647fec44cSPat Hickey 
number_elements(&self) -> u323747fec44cSPat Hickey     pub fn number_elements(&self) -> u32 {
3847fec44cSPat Hickey         self.elems.len() as u32
3947fec44cSPat Hickey     }
4047fec44cSPat Hickey 
cumulative_size(&self) -> u324147fec44cSPat Hickey     pub fn cumulative_size(&self) -> u32 {
4247fec44cSPat Hickey         self.elems
4347fec44cSPat Hickey             .iter()
4447fec44cSPat Hickey             .map(|e| e.as_bytes().len() + 1)
4547fec44cSPat Hickey             .sum::<usize>() as u32
4647fec44cSPat Hickey     }
4747fec44cSPat Hickey 
write_to_guest( &self, memory: &mut GuestMemory<'_>, buffer: GuestPtr<u8>, element_heads: GuestPtr<GuestPtr<u8>>, ) -> Result<(), Error>48*f1411653SAlex Crichton     pub fn write_to_guest(
4947fec44cSPat Hickey         &self,
50*f1411653SAlex Crichton         memory: &mut GuestMemory<'_>,
51*f1411653SAlex Crichton         buffer: GuestPtr<u8>,
52*f1411653SAlex Crichton         element_heads: GuestPtr<GuestPtr<u8>>,
5347fec44cSPat Hickey     ) -> Result<(), Error> {
5447fec44cSPat Hickey         let element_heads = element_heads.as_array(self.number_elements());
5547fec44cSPat Hickey         let buffer = buffer.as_array(self.cumulative_size());
5647fec44cSPat Hickey         let mut cursor = 0;
5747fec44cSPat Hickey         for (elem, head) in self.elems.iter().zip(element_heads.iter()) {
5847fec44cSPat Hickey             let bytes = elem.as_bytes();
5947fec44cSPat Hickey             let len = bytes.len() as u32;
6047fec44cSPat Hickey             {
6147fec44cSPat Hickey                 let elem_buffer = buffer
6247fec44cSPat Hickey                     .get_range(cursor..(cursor + len))
6347fec44cSPat Hickey                     .ok_or(Error::invalid_argument())?; // Elements don't fit in buffer provided
64*f1411653SAlex Crichton                 memory.copy_from_slice(bytes, elem_buffer)?;
6547fec44cSPat Hickey             }
66*f1411653SAlex Crichton             memory.write(
67*f1411653SAlex Crichton                 buffer.get(cursor + len).ok_or(Error::invalid_argument())?,
68*f1411653SAlex Crichton                 0,
69*f1411653SAlex Crichton             )?; // 0 terminate
70*f1411653SAlex Crichton             memory.write(head?, buffer.get(cursor).expect("already validated"))?;
7147fec44cSPat Hickey             cursor += len + 1;
7247fec44cSPat Hickey         }
7347fec44cSPat Hickey         Ok(())
7447fec44cSPat Hickey     }
7547fec44cSPat Hickey }
76