1use std::io::{Cursor, Error, ErrorKind, Read, Result};
2
3use crate::{
4 codec::VarInt,
5 serial::{PrefixedRead, ReadFrom},
6};
7
8pub fn read_utf(data: &mut Cursor<&[u8]>, max_utf16_units: usize) -> Result<String> {
20 let encoded_len = VarInt::read(data)?.0;
21 let Ok(encoded_len) = usize::try_from(encoded_len) else {
22 return Err(Error::new(
23 ErrorKind::InvalidData,
24 "encoded string length is negative",
25 ));
26 };
27 let Some(max_encoded_len) = max_utf16_units.checked_mul(3) else {
28 return Err(Error::new(
29 ErrorKind::InvalidInput,
30 "maximum UTF-16 length is too large",
31 ));
32 };
33 if encoded_len > max_encoded_len {
34 return Err(Error::new(
35 ErrorKind::InvalidData,
36 "encoded string length exceeds maximum",
37 ));
38 }
39
40 let start = usize::try_from(data.position()).map_err(|_| {
41 Error::new(
42 ErrorKind::UnexpectedEof,
43 "encoded string starts beyond the addressable input",
44 )
45 })?;
46 let end = start.checked_add(encoded_len).ok_or_else(|| {
47 Error::new(
48 ErrorKind::UnexpectedEof,
49 "encoded string length exceeds the addressable input",
50 )
51 })?;
52 let Some(bytes) = data.get_ref().get(start..end) else {
53 return Err(Error::new(
54 ErrorKind::UnexpectedEof,
55 "encoded string is truncated",
56 ));
57 };
58
59 let decoded = decode_java_utf8_lossy(bytes);
60 let end = u64::try_from(end).map_err(|_| {
61 Error::new(
62 ErrorKind::InvalidData,
63 "encoded string end exceeds the cursor position range",
64 )
65 })?;
66 data.set_position(end);
67 if decoded.encode_utf16().count() > max_utf16_units {
68 return Err(Error::new(
69 ErrorKind::InvalidData,
70 "decoded string length exceeds maximum",
71 ));
72 }
73 Ok(decoded)
74}
75
76fn decode_java_utf8_lossy(bytes: &[u8]) -> String {
77 let mut decoded = String::with_capacity(bytes.len());
78 let mut offset = 0;
79
80 while offset < bytes.len() {
81 let first = bytes[offset];
82 if first.is_ascii() {
83 decoded.push(char::from(first));
84 offset += 1;
85 continue;
86 }
87
88 let remaining = bytes.len() - offset;
89 match first {
90 0xC2..=0xDF => {
91 if remaining >= 2 && is_utf8_continuation(bytes[offset + 1]) {
92 let code_point =
93 (u32::from(first & 0x1F) << 6) | u32::from(bytes[offset + 1] & 0x3F);
94 decoded.push(char::from_u32(code_point).unwrap_or(char::REPLACEMENT_CHARACTER));
95 offset += 2;
96 } else {
97 decoded.push(char::REPLACEMENT_CHARACTER);
98 offset += 1;
99 }
100 }
101 0xE0..=0xEF => {
102 if remaining < 3 {
103 let malformed_len = if remaining == 1
104 || malformed_three_byte_prefix(first, bytes[offset + 1])
105 {
106 1
107 } else {
108 remaining
109 };
110 decoded.push(char::REPLACEMENT_CHARACTER);
111 offset += malformed_len;
112 continue;
113 }
114
115 let second = bytes[offset + 1];
116 let third = bytes[offset + 2];
117 if malformed_three_byte_sequence(first, second, third) {
118 decoded.push(char::REPLACEMENT_CHARACTER);
119 offset += malformed_three_byte_length(first, second);
120 continue;
121 }
122
123 let code_point = (u32::from(first & 0x0F) << 12)
124 | (u32::from(second & 0x3F) << 6)
125 | u32::from(third & 0x3F);
126 if let Some(character) = char::from_u32(code_point) {
127 decoded.push(character);
128 } else {
129 decoded.push(char::REPLACEMENT_CHARACTER);
130 }
131 offset += 3;
132 }
133 0xF0..=0xF7 => {
134 if remaining < 4 {
135 let malformed_len = malformed_four_byte_prefix_length(
136 first,
137 bytes.get(offset + 1).copied(),
138 bytes.get(offset + 2).copied(),
139 remaining,
140 );
141 decoded.push(char::REPLACEMENT_CHARACTER);
142 offset += malformed_len;
143 continue;
144 }
145
146 let second = bytes[offset + 1];
147 let third = bytes[offset + 2];
148 let fourth = bytes[offset + 3];
149 if malformed_four_byte_sequence(first, second, third, fourth) {
150 decoded.push(char::REPLACEMENT_CHARACTER);
151 offset += malformed_four_byte_length(first, second, third);
152 continue;
153 }
154
155 let code_point = (u32::from(first & 0x07) << 18)
156 | (u32::from(second & 0x3F) << 12)
157 | (u32::from(third & 0x3F) << 6)
158 | u32::from(fourth & 0x3F);
159 decoded.push(char::from_u32(code_point).unwrap_or(char::REPLACEMENT_CHARACTER));
160 offset += 4;
161 }
162 _ => {
163 decoded.push(char::REPLACEMENT_CHARACTER);
164 offset += 1;
165 }
166 }
167 }
168
169 decoded
170}
171
172const fn is_utf8_continuation(byte: u8) -> bool {
173 byte & 0xC0 == 0x80
174}
175
176const fn malformed_three_byte_prefix(first: u8, second: u8) -> bool {
177 (first == 0xE0 && second & 0xE0 != 0xA0) || !is_utf8_continuation(second)
178}
179
180const fn malformed_three_byte_sequence(first: u8, second: u8, third: u8) -> bool {
181 malformed_three_byte_prefix(first, second) || !is_utf8_continuation(third)
182}
183
184const fn malformed_three_byte_length(first: u8, second: u8) -> usize {
185 if malformed_three_byte_prefix(first, second) {
186 1
187 } else {
188 2
189 }
190}
191
192const fn malformed_four_byte_second(first: u8, second: u8) -> bool {
193 (first == 0xF0 && (second < 0x90 || second > 0xBF))
194 || (first == 0xF4 && (second < 0x80 || second > 0x8F))
195 || first > 0xF4
196 || !is_utf8_continuation(second)
197}
198
199const fn malformed_four_byte_prefix_length(
200 first: u8,
201 second: Option<u8>,
202 third: Option<u8>,
203 remaining: usize,
204) -> usize {
205 let Some(second) = second else {
206 return 1;
207 };
208 if malformed_four_byte_second(first, second) {
209 return 1;
210 }
211 if let Some(third) = third
212 && !is_utf8_continuation(third)
213 {
214 return 2;
215 }
216 remaining
217}
218
219const fn malformed_four_byte_sequence(first: u8, second: u8, third: u8, fourth: u8) -> bool {
220 malformed_four_byte_second(first, second)
221 || !is_utf8_continuation(third)
222 || !is_utf8_continuation(fourth)
223}
224
225const fn malformed_four_byte_length(first: u8, second: u8, third: u8) -> usize {
226 if malformed_four_byte_second(first, second) {
227 1
228 } else if !is_utf8_continuation(third) {
229 2
230 } else {
231 3
232 }
233}
234
235impl PrefixedRead for String {
236 fn read_prefixed_bound<P: TryInto<usize> + ReadFrom>(
237 data: &mut Cursor<&[u8]>,
238 bound: usize,
239 ) -> Result<Self> {
240 let len: usize = P::read(data)?
241 .try_into()
242 .map_err(|_| Error::other("Invalid Prefix"))?;
243
244 if len > bound {
245 Err(Error::other("To long"))?;
246 }
247
248 let mut buf = vec![0; len];
249 data.read_exact(&mut buf)?;
250 String::from_utf8(buf).map_err(Error::other)
251 }
252}
253
254impl<T: ReadFrom> PrefixedRead for Vec<T> {
255 fn read_prefixed_bound<P: TryInto<usize> + ReadFrom>(
256 data: &mut Cursor<&[u8]>,
257 bound: usize,
258 ) -> Result<Self> {
259 let len: usize = P::read(data)?
260 .try_into()
261 .map_err(|_| Error::other("Invalid Prefix"))?;
262
263 if len > bound {
264 Err(Error::other("To long"))?;
265 }
266 let mut items = Vec::with_capacity(len);
267 for _ in 0..len {
268 items.push(T::read(data)?);
269 }
270 Ok(items)
271 }
272}
273
274impl<T: PrefixedRead> PrefixedRead for Option<T> {
275 fn read_prefixed_bound<P: TryInto<usize> + ReadFrom>(
276 data: &mut Cursor<&[u8]>,
277 bound: usize,
278 ) -> Result<Self> {
279 if bool::read(data)? {
280 Ok(Some(T::read_prefixed_bound::<P>(data, bound)?))
281 } else {
282 Ok(None)
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use std::io::{Cursor, ErrorKind};
290
291 use crate::{
292 codec::VarInt,
293 serial::{WriteTo as _, prefixed_read::read_utf},
294 };
295
296 fn encoded_string(bytes: &[u8]) -> Vec<u8> {
297 let mut encoded = Vec::with_capacity(VarInt::MAX_SIZE + bytes.len());
298 VarInt(i32::try_from(bytes.len()).expect("test string length should fit in a VarInt"))
299 .write(&mut encoded)
300 .expect("test string length should encode");
301 encoded.extend_from_slice(bytes);
302 encoded
303 }
304
305 #[test]
306 fn truncated_string_leaves_cursor_after_prefix() {
307 let bytes = [3, b'a'];
308 let mut cursor = Cursor::new(bytes.as_slice());
309
310 let error = read_utf(&mut cursor, 3).expect_err("the declared body is incomplete");
311
312 assert_eq!(error.kind(), ErrorKind::UnexpectedEof);
313 assert_eq!(cursor.position(), 1);
314 }
315
316 #[test]
317 fn length_failures_match_vanilla_cursor_consumption() {
318 let encoded_too_long = encoded_string(b"abcd");
319 let mut cursor = Cursor::new(encoded_too_long.as_slice());
320 let error = read_utf(&mut cursor, 1).expect_err("four encoded bytes exceed the bound");
321 assert_eq!(error.kind(), ErrorKind::InvalidData);
322 assert_eq!(cursor.position(), 1);
323
324 let decoded_too_long = encoded_string(b"ab");
325 let mut cursor = Cursor::new(decoded_too_long.as_slice());
326 let error = read_utf(&mut cursor, 1).expect_err("two UTF-16 units exceed the bound");
327 assert_eq!(error.kind(), ErrorKind::InvalidData);
328 assert_eq!(cursor.position(), decoded_too_long.len() as u64);
329 }
330
331 #[test]
332 fn accepts_maximum_encoded_length() {
333 const MAX_UTF16_UNITS: usize = 32_767;
334 let bytes = [0xE0, 0xA0, 0x80].repeat(MAX_UTF16_UNITS);
335 let encoded = encoded_string(&bytes);
336 let mut cursor = Cursor::new(encoded.as_slice());
337
338 let decoded =
339 read_utf(&mut cursor, MAX_UTF16_UNITS).expect("the exact encoded bound is valid");
340
341 assert_eq!(decoded.encode_utf16().count(), MAX_UTF16_UNITS);
342 assert_eq!(cursor.position(), encoded.len() as u64);
343 }
344
345 #[test]
346 fn malformed_sequences_match_java_replacement_grouping() {
347 let cases: &[(&[u8], &str)] = &[
348 (&[0xC0, 0x80], "\u{FFFD}\u{FFFD}"),
349 (&[0xE0, 0x80, 0x80], "\u{FFFD}\u{FFFD}\u{FFFD}"),
350 (&[0xED, 0xA0, 0x80], "\u{FFFD}"),
351 (&[0xE1, 0x80], "\u{FFFD}"),
352 (&[0xE1, 0x80, b'A'], "\u{FFFD}A"),
353 (&[0xF0, 0x90, 0x80], "\u{FFFD}"),
354 (
355 &[0xF4, 0x90, 0x80, 0x80],
356 "\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}",
357 ),
358 ];
359
360 for &(bytes, expected) in cases {
361 let encoded = encoded_string(bytes);
362 let decoded = read_utf(&mut Cursor::new(encoded.as_slice()), 16)
363 .expect("malformed UTF-8 is decoded lossily");
364 assert_eq!(decoded, expected, "unexpected decoding for {bytes:X?}");
365 }
366 }
367
368 #[test]
369 fn rejects_impossible_maximum_without_reading_the_body() {
370 let encoded = encoded_string(b"");
371 let mut cursor = Cursor::new(encoded.as_slice());
372
373 let error =
374 read_utf(&mut cursor, usize::MAX).expect_err("the encoded bound should overflow");
375
376 assert_eq!(error.kind(), ErrorKind::InvalidInput);
377 assert_eq!(cursor.position(), 1);
378 }
379}