Skip to content

Commit

Permalink
Don't allocate a vector twice in wcs2zstring()
Browse files Browse the repository at this point in the history
We were passing a slice (and not a vec) to `CString::new()`, meaning it would
allocate a new Vec internally to hold the bytes.

Also document that the resulting CString will be silently truncated at the first
interior NUL.
  • Loading branch information
mqudsi committed May 9, 2024
1 parent 883bcba commit 5dcc595
Showing 1 changed file with 16 additions and 7 deletions.
23 changes: 16 additions & 7 deletions src/common.rs
Expand Up @@ -1232,21 +1232,30 @@ pub fn wcs2osstring(input: &wstr) -> OsString {
}

/// Same as [`wcs2string`]. Meant to be used when we need a zero-terminated string to feed legacy APIs.
/// Note: if `input` contains any interior NUL bytes, the result will be truncated at the first!
pub fn wcs2zstring(input: &wstr) -> CString {
if input.is_empty() {
return CString::default();
}

let mut result = vec![];
let mut vec = Vec::with_capacity(input.len() + 1);
wcs2string_callback(input, |buff| {
result.extend_from_slice(buff);
vec.extend_from_slice(buff);
true
});
let until_nul = match result.iter().position(|c| *c == b'\0') {
Some(pos) => &result[..pos],
None => &result[..],
};
CString::new(until_nul).unwrap()
vec.push(b'\0');

match CString::from_vec_with_nul(vec) {
Ok(cstr) => cstr,
Err(err) => {
// `input` contained a NUL in the middle; we can retrieve `vec`, though
let mut vec = err.into_bytes();
let pos = vec.iter().position(|c| *c == b'\0').unwrap();
vec.truncate(pos + 1);
// Safety: We truncated after the first NUL
unsafe { CString::from_vec_with_nul_unchecked(vec) }
}
}
}

/// Like wcs2string, but appends to `receiver` instead of returning a new string.
Expand Down

0 comments on commit 5dcc595

Please sign in to comment.