sim_codec_classfile/
modified_utf8.rs1use sim_text::CodeUnitString;
4
5use crate::{ByteError, ByteErrorKind, ByteReader, ByteWriter};
6
7pub fn decode_modified_utf8(
12 bytes: &[u8],
13 code_unit_budget: usize,
14) -> Result<CodeUnitString, ByteError> {
15 let mut reader = ByteReader::new(bytes, code_unit_budget);
16 let mut units = Vec::new();
17 let mut pending_high: Option<usize> = None;
18 while reader.remaining() != 0 {
19 let at = reader.offset();
20 let first = reader.read_u1()?;
21 let unit = match first {
22 0 => {
23 return Err(ByteError::new(
24 ByteErrorKind::IllegalZero,
25 at,
26 "literal zero is illegal in modified UTF-8",
27 ));
28 }
29 1..=0x7f => u16::from(first),
30 0xc0..=0xdf => {
31 let second = continuation(&mut reader)?;
32 let value = (u16::from(first & 0x1f) << 6) | u16::from(second & 0x3f);
33 if value == 0 {
34 if first != 0xc0 || second != 0x80 {
35 return Err(ByteError::new(
36 ByteErrorKind::OverlongModifiedUtf8,
37 at,
38 "non-canonical modified UTF-8 NUL",
39 ));
40 }
41 } else if value < 0x80 {
42 return Err(ByteError::new(
43 ByteErrorKind::OverlongModifiedUtf8,
44 at,
45 "overlong two-byte modified UTF-8",
46 ));
47 }
48 value
49 }
50 0xe0..=0xef => {
51 let second = continuation(&mut reader)?;
52 let third = continuation(&mut reader)?;
53 let value = (u16::from(first & 0x0f) << 12)
54 | (u16::from(second & 0x3f) << 6)
55 | u16::from(third & 0x3f);
56 if value < 0x800 {
57 return Err(ByteError::new(
58 ByteErrorKind::OverlongModifiedUtf8,
59 at,
60 "overlong three-byte modified UTF-8",
61 ));
62 }
63 value
64 }
65 _ => {
66 return Err(ByteError::new(
67 ByteErrorKind::InvalidModifiedUtf8,
68 at,
69 "invalid modified UTF-8 lead byte",
70 ));
71 }
72 };
73
74 if let Some(high_at) = pending_high.take() {
75 if !(0xdc00..=0xdfff).contains(&unit) {
76 return Err(ByteError::new(
77 ByteErrorKind::MalformedSurrogate,
78 high_at,
79 "high surrogate is not followed by a low surrogate",
80 ));
81 }
82 } else if (0xd800..=0xdbff).contains(&unit) {
83 pending_high = Some(at);
84 } else if (0xdc00..=0xdfff).contains(&unit) {
85 return Err(ByteError::new(
86 ByteErrorKind::MalformedSurrogate,
87 at,
88 "low surrogate has no preceding high surrogate",
89 ));
90 }
91 let next_len = units.len() + 1;
92 if next_len > code_unit_budget {
93 return Err(ByteError::new(
94 ByteErrorKind::BudgetExceeded,
95 at,
96 format!("code-unit length {next_len} exceeds budget {code_unit_budget}"),
97 ));
98 }
99 units.try_reserve_exact(1).map_err(|error| {
100 ByteError::new(
101 ByteErrorKind::BudgetExceeded,
102 at,
103 format!("code-unit allocation failed: {error}"),
104 )
105 })?;
106 units.push(unit);
107 }
108 if let Some(at) = pending_high {
109 return Err(ByteError::new(
110 ByteErrorKind::MalformedSurrogate,
111 at,
112 "high surrogate is truncated",
113 ));
114 }
115 Ok(CodeUnitString::try_from_code_units(units)
116 .expect("allocation preflight enforces the tighter caller budget"))
117}
118
119fn continuation(reader: &mut ByteReader<'_>) -> Result<u8, ByteError> {
120 let at = reader.offset();
121 let byte = reader.read_u1()?;
122 if byte & 0xc0 != 0x80 {
123 return Err(ByteError::new(
124 ByteErrorKind::InvalidModifiedUtf8,
125 at,
126 "invalid modified UTF-8 continuation byte",
127 ));
128 }
129 Ok(byte)
130}
131
132pub fn encode_modified_utf8(
134 text: &CodeUnitString,
135 byte_budget: usize,
136) -> Result<Vec<u8>, ByteError> {
137 let units = text.as_code_units();
138 validate_surrogates(units)?;
139 let required = units.iter().try_fold(0usize, |length, unit| {
140 let width = if *unit == 0 {
141 2
142 } else if *unit <= 0x7f {
143 1
144 } else if *unit <= 0x7ff {
145 2
146 } else {
147 3
148 };
149 length.checked_add(width).ok_or_else(|| {
150 ByteError::new(
151 ByteErrorKind::LengthOverflow,
152 length,
153 "modified UTF-8 output length overflow",
154 )
155 })
156 })?;
157 let mut writer = ByteWriter::new(byte_budget);
158 if required > byte_budget {
160 return Err(ByteError::new(
161 ByteErrorKind::BudgetExceeded,
162 0,
163 format!("modified UTF-8 output length {required} exceeds budget {byte_budget}"),
164 ));
165 }
166 for unit in units {
167 match *unit {
168 0 => writer.write_bytes(&[0xc0, 0x80])?,
169 1..=0x7f => writer.write_u1(*unit as u8)?,
170 0x80..=0x7ff => {
171 writer.write_bytes(&[0xc0 | (*unit >> 6) as u8, 0x80 | (*unit & 0x3f) as u8])?
172 }
173 _ => writer.write_bytes(&[
174 0xe0 | (*unit >> 12) as u8,
175 0x80 | ((*unit >> 6) & 0x3f) as u8,
176 0x80 | (*unit & 0x3f) as u8,
177 ])?,
178 }
179 }
180 Ok(writer.into_bytes())
181}
182
183fn validate_surrogates(units: &[u16]) -> Result<(), ByteError> {
184 let mut index = 0;
185 while index < units.len() {
186 match units[index] {
187 0xd800..=0xdbff
188 if units
189 .get(index + 1)
190 .is_some_and(|next| (0xdc00..=0xdfff).contains(next)) =>
191 {
192 index += 2
193 }
194 0xd800..=0xdfff => {
195 return Err(ByteError::new(
196 ByteErrorKind::MalformedSurrogate,
197 index,
198 "unpaired surrogate code unit",
199 ));
200 }
201 _ => index += 1,
202 }
203 }
204 Ok(())
205}