Skip to main content

jvm_types/
modified_utf8.rs

1//! Java modified UTF-8 over UTF-16 code units, without a length prefix.
2
3use alloc::vec::Vec;
4
5/// Encodes UTF-16 code units, preserving unpaired surrogates.
6pub fn encode(units: &[u16]) -> Vec<u8> {
7    let mut bytes = Vec::with_capacity(units.len());
8    for &value in units {
9        if (0x0001..=0x007f).contains(&value) {
10            bytes.push(value as u8);
11        } else if value <= 0x07ff {
12            bytes.push((0xc0 | (value >> 6)) as u8);
13            bytes.push((0x80 | (value & 0x3f)) as u8);
14        } else {
15            bytes.push((0xe0 | (value >> 12)) as u8);
16            bytes.push((0x80 | ((value >> 6) & 0x3f)) as u8);
17            bytes.push((0x80 | (value & 0x3f)) as u8);
18        }
19    }
20    bytes
21}
22
23/// Decodes using Java DataInput's one-, two-, and three-byte rules.
24/// Returns `None` for malformed input; unpaired surrogates are preserved.
25pub fn decode(bytes: &[u8]) -> Option<Vec<u16>> {
26    let continuation = |i: usize| match bytes.get(i) {
27        Some(&b) if b & 0xc0 == 0x80 => Some((b & 0x3f) as u16),
28        _ => None,
29    };
30
31    let mut units = Vec::with_capacity(bytes.len());
32    let mut i = 0;
33    while i < bytes.len() {
34        let b = bytes[i] as u16;
35        let (unit, length) = match bytes[i] {
36            0x00..=0x7f => (b, 1),
37            0xc0..=0xdf => (((b & 0x1f) << 6) | continuation(i + 1)?, 2),
38            0xe0..=0xef => (((b & 0x0f) << 12) | (continuation(i + 1)? << 6) | continuation(i + 2)?, 3),
39            _ => return None,
40        };
41        units.push(unit);
42        i += length;
43    }
44    Some(units)
45}