1use alloc::{string::String, vec::Vec};
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6#[non_exhaustive]
7pub enum Encoding {
8 Utf8,
9 Utf8Bom,
10 Utf16Le,
11 Utf16Be,
12}
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16#[non_exhaustive]
17pub enum Error {
18 Invalid,
19 Unsupported,
20 Limit,
21}
22
23pub fn decode(bytes: &[u8], limit: usize) -> Result<(String, Encoding), Error> {
25 if bytes.len() > limit {
26 return Err(Error::Limit);
27 }
28 if bytes.starts_with(&[0xff, 0xfe, 0, 0]) || bytes.starts_with(&[0, 0, 0xfe, 0xff]) {
29 return Err(Error::Unsupported);
30 }
31 let (b, enc) = if bytes.starts_with(&[0xef, 0xbb, 0xbf]) {
32 (&bytes[3..], Encoding::Utf8Bom)
33 } else if bytes.starts_with(&[0xff, 0xfe]) {
34 (&bytes[2..], Encoding::Utf16Le)
35 } else if bytes.starts_with(&[0xfe, 0xff]) {
36 (&bytes[2..], Encoding::Utf16Be)
37 } else {
38 (bytes, Encoding::Utf8)
39 };
40 let text = match enc {
41 Encoding::Utf8 | Encoding::Utf8Bom => {
42 String::from(core::str::from_utf8(b).map_err(|_| Error::Invalid)?)
43 }
44 _ => {
45 if b.len() % 2 != 0 {
46 return Err(Error::Invalid);
47 }
48 let units = b.chunks_exact(2).map(|p| {
49 if enc == Encoding::Utf16Le {
50 u16::from_le_bytes([p[0], p[1]])
51 } else {
52 u16::from_be_bytes([p[0], p[1]])
53 }
54 });
55 let mut s = String::new();
56 for c in core::char::decode_utf16(units) {
57 s.push(c.map_err(|_| Error::Invalid)?);
58 }
59 s
60 }
61 };
62 if text.len() > limit {
63 return Err(Error::Limit);
64 }
65 Ok((text, enc))
66}
67
68pub fn encode(text: &str, enc: Encoding) -> Vec<u8> {
70 let mut out = Vec::new();
71 match enc {
72 Encoding::Utf8 => out.extend_from_slice(text.as_bytes()),
73 Encoding::Utf8Bom => {
74 out.extend_from_slice(&[0xef, 0xbb, 0xbf]);
75 out.extend_from_slice(text.as_bytes());
76 }
77 Encoding::Utf16Le | Encoding::Utf16Be => {
78 out.extend_from_slice(if enc == Encoding::Utf16Le {
79 &[0xff, 0xfe]
80 } else {
81 &[0xfe, 0xff]
82 });
83 for u in text.encode_utf16() {
84 out.extend_from_slice(&if enc == Encoding::Utf16Le {
85 u.to_le_bytes()
86 } else {
87 u.to_be_bytes()
88 });
89 }
90 }
91 }
92 out
93}