1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
use crate::windows;
use windows::core::HSTRING;
pub trait HStringExt {
/// Similar to `HSTRING::as_wide()`, but truncates the slice to the specified length, avoiding to cut a UTF-16 surrogate pair in half by reducing the length by one additional wide char, if needed.
///
/// With a target length larger than the string length, the behavior is identical to that of `as_wide()`.
fn as_safely_truncated_wide(&self, target_len: usize) -> &[u16];
/// Writes the `HSTRING` into the buffer, followed by a terminating null character.
fn write_truncated(&self, buf: &mut [u16]);
}
impl HStringExt for HSTRING {
fn as_safely_truncated_wide(&self, target_len: usize) -> &[u16] {
let slice = self.as_wide();
let len = slice.len();
if target_len >= len {
// Nothing to do.
return slice;
}
if len
.checked_sub(1)
.map(|last_index| is_leading_surrogate(slice[last_index]))
.unwrap_or(false /*empty string doesn't end in leading surrogate*/)
{
// Don't cut surrogate pair in half. Remove whole pair instead.
&slice[..len - 1]
} else {
// Ordinary truncation.
&slice[..len]
}
}
fn write_truncated(&self, buf: &mut [u16]) {
let truncated_slice =
self.as_safely_truncated_wide(self.len().min(buf.len() - 1 /*null-termination*/));
buf[..truncated_slice.len()].copy_from_slice(truncated_slice);
buf[truncated_slice.len()] = 0;
}
}
const fn is_leading_surrogate(wide_char: u16) -> bool {
wide_char >= 0xd800 && wide_char <= 0xdbff
}