pub fn push(out: &mut Vec<u8>, c: u32) {
if c < 0x80 {
out.push(c as u8);
} else if c < 0x800 {
out.push(0xC0 | (c >> 6) as u8);
out.push(0x80 | (c & 0x3F) as u8);
} else if c < 0x10000 {
out.push(0xE0 | (c >> 12) as u8);
out.push(0x80 | ((c >> 6) & 0x3F) as u8);
out.push(0x80 | (c & 0x3F) as u8);
} else {
out.push(0xF0 | (c >> 18) as u8);
out.push(0x80 | ((c >> 12) & 0x3F) as u8);
out.push(0x80 | ((c >> 6) & 0x3F) as u8);
out.push(0x80 | (c & 0x3F) as u8);
}
}
pub fn encode(c: u32) -> Vec<u8> {
let mut v = Vec::with_capacity(4);
push(&mut v, c);
v
}
pub fn len(c: u32) -> usize {
match c {
0..0x80 => 1,
0x80..0x800 => 2,
0x800..0x10000 => 3,
_ => 4,
}
}
pub fn decode(bytes: &[u8], i: usize) -> (u32, usize) {
let b0 = bytes[i];
let need = match b0 {
0x00..=0x7F => return (u32::from(b0), 1),
0xC0..=0xDF => 2,
0xE0..=0xEF => 3,
0xF0..=0xF7 => 4,
_ => return (u32::from(b0), 1),
};
if i + need > bytes.len() {
return (u32::from(b0), 1);
}
let mut c = u32::from(b0) & (0x7F >> need);
for k in 1..need {
let b = bytes[i + k];
if b & 0xC0 != 0x80 {
return (u32::from(b0), 1);
}
c = (c << 6) | u32::from(b & 0x3F);
}
(c, need)
}
pub fn chars(bytes: &[u8]) -> Vec<u32> {
let mut out = Vec::with_capacity(bytes.len());
let mut i = 0;
while i < bytes.len() {
let (c, n) = decode(bytes, i);
out.push(c);
i += n;
}
out
}
pub fn single_char(bytes: &[u8]) -> Option<u32> {
if bytes.is_empty() {
return None;
}
let (c, n) = decode(bytes, 0);
(n == bytes.len()).then_some(c)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip() {
for c in [
0, 0x41, 0x7F, 0x80, 0xE9, 0x7FF, 0x800, 0xD800, 0xFFFF, 0x10000, 0x10FFFF,
] {
let e = encode(c);
assert_eq!(e.len(), len(c));
assert_eq!(decode(&e, 0), (c, e.len()));
assert_eq!(single_char(&e), Some(c));
}
assert_eq!(chars("aĆ©š".as_bytes()), vec![0x61, 0xE9, 0x1F600]);
}
}