use crate::Error; use std::collections::HashMap; use std::convert::TryInto; use std::ffi::{CString, OsString}; use wiggle::GuestPtr; #[derive(Debug, Eq, Hash, PartialEq)] pub enum PendingString { Bytes(Vec), OsString(OsString), } impl From> for PendingString { fn from(bytes: Vec) -> Self { Self::Bytes(bytes) } } impl From for PendingString { fn from(s: OsString) -> Self { Self::OsString(s) } } impl PendingString { pub fn into_string(self) -> Result { let res = match self { Self::Bytes(v) => String::from_utf8(v)?, #[cfg(unix)] Self::OsString(s) => { use std::os::unix::ffi::OsStringExt; String::from_utf8(s.into_vec())? } #[cfg(windows)] Self::OsString(s) => { use std::os::windows::ffi::OsStrExt; let bytes: Vec = s.encode_wide().collect(); String::from_utf16(&bytes)? } }; Ok(res) } } #[derive(Debug, thiserror::Error)] pub enum StringArrayError { /// Provided sequence of bytes contained an unexpected NUL byte. #[error("provided sequence contained an unexpected NUL byte")] Nul(#[from] std::ffi::NulError), /// Too many elements: must fit into u32 #[error("too many elements")] NumElements, /// Element size: must fit into u32 #[error("element too big")] ElemSize, /// Cumulative element size: must fit into u32 #[error("cumulative element size too big")] CumElemSize, /// Provided sequence of bytes was not a valid UTF-8. #[error("provided sequence is not valid UTF-8: {0}")] InvalidUtf8(#[from] std::string::FromUtf8Error), /// Provided sequence of bytes was not a valid UTF-16. /// /// This error is expected to only occur on Windows hosts. #[error("provided sequence is not valid UTF-16: {0}")] InvalidUtf16(#[from] std::string::FromUtf16Error), } pub struct StringArray { elems: Vec, pub number_elements: u32, pub cumulative_size: u32, } impl StringArray { pub fn from_pending_vec(elems: Vec) -> Result { let elems = elems .into_iter() .map(|arg| arg.into_string()) .collect::, StringArrayError>>()?; Self::from_strings(elems) } pub fn from_pending_map( elems: HashMap, ) -> Result { let mut pairs = Vec::new(); for (k, v) in elems.into_iter() { let mut pair = k.into_string()?; pair.push('='); pair.push_str(&v.into_string()?); pairs.push(pair); } Self::from_strings(pairs) } pub fn from_strings(elems: Vec) -> Result { let elems = elems .into_iter() .map(|s| CString::new(s)) .collect::, _>>()?; let number_elements = elems .len() .try_into() .map_err(|_| StringArrayError::NumElements)?; let mut cumulative_size: u32 = 0; for elem in elems.iter() { let elem_bytes = elem .as_bytes_with_nul() .len() .try_into() .map_err(|_| StringArrayError::ElemSize)?; cumulative_size = cumulative_size .checked_add(elem_bytes) .ok_or(StringArrayError::CumElemSize)?; } Ok(Self { elems, number_elements, cumulative_size, }) } pub fn write_to_guest<'a>( &self, buffer: &GuestPtr<'a, u8>, element_heads: &GuestPtr<'a, GuestPtr<'a, u8>>, ) -> Result<(), Error> { let element_heads = element_heads.as_array(self.number_elements); let buffer = buffer.as_array(self.cumulative_size); let mut cursor = 0; for (elem, head) in self.elems.iter().zip(element_heads.iter()) { let bytes = elem.as_bytes_with_nul(); let len: u32 = bytes.len().try_into()?; let elem_buffer = buffer .get_range(cursor..(cursor + len)) .ok_or(Error::Inval)?; // Elements don't fit in buffer provided elem_buffer.copy_from_slice(bytes)?; head?.write( elem_buffer .get(0) .expect("all elem buffers at least length 1"), )?; cursor += len; } Ok(()) } }