pub fn decode_wide_string(data: &[u8]) -> Option<(String, usize)> {
if data.len() < 4 {
return None;
}
let len = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
let byte_len = len.checked_mul(2)?;
let total = 4usize.checked_add(byte_len)?;
if data.len() < total {
return None;
}
let chars: Vec<u16> = data[4..total]
.chunks_exact(2)
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect();
let s = String::from_utf16_lossy(&chars);
Some((s, total))
}
pub fn decode_short_string(data: &[u8]) -> Option<(String, usize)> {
if data.is_empty() {
return None;
}
decode_utf16_with_len(data, 1, usize::from(data[0]))
}
pub fn decode_short_string_u16(data: &[u8]) -> Option<(String, usize)> {
if data.len() < 2 {
return None;
}
let len = usize::from(u16::from_le_bytes([data[0], data[1]]));
decode_utf16_with_len(data, 2, len)
}
fn decode_utf16_with_len(data: &[u8], prefix_len: usize, char_len: usize) -> Option<(String, usize)> {
let byte_len = char_len.checked_mul(2)?;
let total = prefix_len.checked_add(byte_len)?;
if data.len() < total {
return None;
}
let chars: Vec<u16> = data[prefix_len..total]
.chunks_exact(2)
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect();
let s = String::from_utf16_lossy(&chars);
Some((s, total))
}
#[cfg(test)]
mod tests {
use super::*;
fn utf16le(s: &str) -> Vec<u8> {
s.encode_utf16()
.flat_map(u16::to_le_bytes)
.collect::<Vec<_>>()
}
#[test]
fn decode_wide_string_valid_and_truncated() {
let mut bytes = Vec::new();
bytes.extend_from_slice(&(2u32).to_le_bytes());
bytes.extend_from_slice(&utf16le("Hi"));
let decoded = decode_wide_string(&bytes).unwrap();
assert_eq!(decoded.0, "Hi");
assert_eq!(decoded.1, 8);
assert!(decode_wide_string(&bytes[..7]).is_none());
}
#[test]
fn decode_short_string_valid_and_truncated() {
let mut bytes = Vec::new();
bytes.push(2);
bytes.extend_from_slice(&utf16le("Yo"));
let decoded = decode_short_string(&bytes).unwrap();
assert_eq!(decoded.0, "Yo");
assert_eq!(decoded.1, 5);
assert!(decode_short_string(&bytes[..4]).is_none());
}
#[test]
fn decode_short_string_u16_valid_and_truncated() {
let mut bytes = Vec::new();
bytes.extend_from_slice(&(2u16).to_le_bytes());
bytes.extend_from_slice(&utf16le("Ok"));
let decoded = decode_short_string_u16(&bytes).unwrap();
assert_eq!(decoded.0, "Ok");
assert_eq!(decoded.1, 6);
assert!(decode_short_string_u16(&bytes[..5]).is_none());
}
}