1pub fn encode(text: &str) -> Vec<u8> {
6 text.encode_utf16().flat_map(u16::to_be_bytes).collect()
7}
8
9pub fn units(c: char) -> usize {
11 c.len_utf16()
12}
13
14pub fn decode(bytes: &[u8]) -> String {
16 let mut out: String = char::decode_utf16(code_units(bytes))
17 .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
18 .collect();
19 if bytes.len() % 2 == 1 {
20 out.push(char::REPLACEMENT_CHARACTER);
21 }
22 out
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum Ucs2Error {
27 OddLength,
29 UnpairedSurrogate(usize),
31}
32
33impl std::fmt::Display for Ucs2Error {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match self {
36 Self::OddLength => f.write_str("UCS2 data has an odd number of octets"),
37 Self::UnpairedSurrogate(i) => write!(f, "unpaired UTF-16 surrogate at unit {i}"),
38 }
39 }
40}
41
42impl std::error::Error for Ucs2Error {}
43
44pub fn decode_strict(bytes: &[u8]) -> Result<String, Ucs2Error> {
46 if bytes.len() % 2 == 1 {
47 return Err(Ucs2Error::OddLength);
48 }
49 let mut out = String::with_capacity(bytes.len() / 2);
50 let mut idx = 0;
51 for r in char::decode_utf16(code_units(bytes)) {
52 let c = r.map_err(|_| Ucs2Error::UnpairedSurrogate(idx))?;
53 idx += c.len_utf16();
54 out.push(c);
55 }
56 Ok(out)
57}
58
59fn code_units(bytes: &[u8]) -> impl Iterator<Item = u16> + '_ {
60 bytes
61 .chunks_exact(2)
62 .map(|p| u16::from_be_bytes([p[0], p[1]]))
63}