1use crate::{
9 Charset,
10 CharsetCodec,
11 CharsetDecodeError,
12 CharsetDecodeErrorKind,
13 CharsetDecodeResult,
14 CharsetEncodeError,
15 CharsetEncodeErrorKind,
16 CharsetEncodeProbe,
17 CharsetEncodeResult,
18 Unicode,
19 Utf8,
20};
21use qubit_codec::Codec;
22
23#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
52pub struct Utf8Codec;
53
54impl Utf8Codec {
55 #[must_use]
61 #[inline(always)]
62 pub const fn charset(self) -> Charset {
63 Charset::UTF_8
64 }
65}
66
67impl CharsetCodec for Utf8Codec {
68 #[inline(always)]
74 fn charset(&self) -> Charset {
75 Charset::UTF_8
76 }
77}
78
79impl CharsetEncodeProbe for Utf8Codec {
80 #[inline(always)]
91 fn encode_len(
92 &self,
93 ch: char,
94 _index: usize,
95 ) -> CharsetEncodeResult<usize> {
96 Ok(Utf8::byte_len(ch))
97 }
98}
99
100unsafe impl Codec for Utf8Codec {
101 type Value = char;
102 type Unit = u8;
103 type DecodeError = CharsetDecodeError;
104 type EncodeError = CharsetEncodeError;
105
106 #[inline(always)]
107 fn min_units_per_value(&self) -> core::num::NonZeroUsize {
108 core::num::NonZeroUsize::MIN
109 }
110
111 #[inline(always)]
112 fn max_units_per_value(&self) -> core::num::NonZeroUsize {
113 unsafe {
115 core::num::NonZeroUsize::new_unchecked(Utf8::MAX_UNITS_PER_CHAR)
116 }
117 }
118
119 #[inline(always)]
120 unsafe fn decode_unchecked(
121 &self,
122 input: &[u8],
123 index: usize,
124 ) -> CharsetDecodeResult<(char, core::num::NonZeroUsize)> {
125 let (ch, consumed) = decode_prefix(input, index)?;
126 debug_assert!(consumed.get() <= input.len() - index);
127 Ok((ch, consumed))
128 }
129
130 #[inline(always)]
131 unsafe fn encode_unchecked(
132 &self,
133 ch: &char,
134 output: &mut [u8],
135 index: usize,
136 ) -> CharsetEncodeResult<usize> {
137 let written = encode_char(*ch, output, index)?;
138 debug_assert_eq!(written, Utf8::byte_len(*ch));
139 debug_assert!(written <= output.len() - index);
140 Ok(written)
141 }
142}
143
144#[inline]
170fn decode_prefix(
171 input: &[u8],
172 index: usize,
173) -> CharsetDecodeResult<(char, core::num::NonZeroUsize)> {
174 if index > input.len() {
175 let kind = CharsetDecodeErrorKind::InvalidInputIndex {
176 input_len: input.len(),
177 };
178 return Err(CharsetDecodeError::new(Charset::UTF_8, kind, index));
179 }
180 if index == input.len() {
181 let kind = CharsetDecodeErrorKind::IncompleteSequence {
182 required: 1,
183 available: 0,
184 };
185 return Err(CharsetDecodeError::new(Charset::UTF_8, kind, index));
186 }
187 let first = input[index];
188 let length = match Utf8::byte_len_from_leading_byte(first) {
189 Some(length) => length,
190 None => {
191 let kind = CharsetDecodeErrorKind::MalformedSequence {
192 value: Some(first as u32),
193 };
194 return Err(CharsetDecodeError::new(Charset::UTF_8, kind, index));
195 }
196 };
197 if !has_units(input.len(), index, length) {
198 validate_partial(input, index)?;
199 let kind = CharsetDecodeErrorKind::IncompleteSequence {
200 required: length,
201 available: input.len() - index,
202 };
203 return Err(CharsetDecodeError::new(Charset::UTF_8, kind, index));
204 }
205 let code_point = match length {
206 1 => first as u32,
207 2 => decode_two(input, index)?,
208 3 => decode_three(input, index)?,
209 4 => decode_four(input, index)?,
210 _ => unreachable!("UTF-8 sequence length is limited to four bytes"),
211 };
212 let ch = Unicode::to_char(code_point)
213 .expect("well-formed UTF-8 decodes to a Unicode scalar");
214 Ok((
215 ch,
216 core::num::NonZeroUsize::new(length)
217 .expect("well-formed UTF-8 sequence has non-zero length"),
218 ))
219}
220
221#[inline]
241fn encode_char(
242 ch: char,
243 output: &mut [u8],
244 index: usize,
245) -> CharsetEncodeResult<usize> {
246 if index > output.len() {
247 let kind = CharsetEncodeErrorKind::BufferTooSmall {
248 required: required_index(index, 1),
249 available: 0,
250 };
251 return Err(CharsetEncodeError::new(Charset::UTF_8, kind, index));
252 }
253 let length = Utf8::byte_len(ch);
254 let available = output.len() - index;
255 if available < length {
256 let kind = CharsetEncodeErrorKind::BufferTooSmall {
257 required: required_index(index, length),
258 available,
259 };
260 return Err(CharsetEncodeError::new(Charset::UTF_8, kind, index));
261 }
262 let mut scratch = [0_u8; Utf8::MAX_BYTES_PER_CHAR];
263 let encoded = ch.encode_utf8(&mut scratch);
264 output[index..index + length].copy_from_slice(encoded.as_bytes());
265 Ok(length)
266}
267
268#[inline(always)]
269const fn has_units(len: usize, index: usize, required_units: usize) -> bool {
270 match index.checked_add(required_units) {
271 Some(end) => len >= end,
272 None => false,
273 }
274}
275
276#[inline(always)]
277const fn required_index(index: usize, required_units: usize) -> usize {
278 match index.checked_add(required_units) {
279 Some(required) => required,
280 None => usize::MAX,
281 }
282}
283
284#[inline]
300fn decode_two(input: &[u8], index: usize) -> CharsetDecodeResult<u32> {
301 let second = input[index + 1];
302 if !Utf8::is_continuation_byte(second) {
303 let kind = CharsetDecodeErrorKind::MalformedSequence {
304 value: Some(second as u32),
305 };
306 return Err(CharsetDecodeError::new(
307 Charset::UTF_8,
308 kind,
309 required_index(index, 1),
310 )
311 .with_consumed(2));
312 }
313 Ok((((input[index] & 0x1f) as u32) << 6) | ((second & 0x3f) as u32))
314}
315
316#[inline]
331fn validate_partial(input: &[u8], index: usize) -> CharsetDecodeResult<()> {
332 if has_units(input.len(), index, 2)
333 && !is_valid_second_byte(input[index], input[index + 1])
334 {
335 let kind = CharsetDecodeErrorKind::MalformedSequence {
336 value: Some(input[index + 1] as u32),
337 };
338 return Err(CharsetDecodeError::new(
339 Charset::UTF_8,
340 kind,
341 required_index(index, 1),
342 )
343 .with_consumed(2));
344 }
345 if has_units(input.len(), index, 3)
346 && !Utf8::is_continuation_byte(input[index + 2])
347 {
348 let kind = CharsetDecodeErrorKind::MalformedSequence {
349 value: Some(input[index + 2] as u32),
350 };
351 return Err(CharsetDecodeError::new(
352 Charset::UTF_8,
353 kind,
354 required_index(index, 2),
355 )
356 .with_consumed(3));
357 }
358 Ok(())
359}
360
361#[inline(always)]
373fn is_valid_second_byte(first: u8, second: u8) -> bool {
374 match first {
375 0xc2..=0xdf => Utf8::is_continuation_byte(second),
376 0xe0 => (0xa0..=0xbf).contains(&second),
377 0xed => (0x80..=0x9f).contains(&second),
378 0xe1..=0xec | 0xee..=0xef => Utf8::is_continuation_byte(second),
379 0xf0 => (0x90..=0xbf).contains(&second),
380 0xf1..=0xf3 => Utf8::is_continuation_byte(second),
381 0xf4 => (0x80..=0x8f).contains(&second),
382 _ => false,
383 }
384}
385
386#[inline]
402fn decode_three(input: &[u8], index: usize) -> CharsetDecodeResult<u32> {
403 let first = input[index];
404 let second = input[index + 1];
405 let third = input[index + 2];
406 if !is_valid_second_byte(first, second) {
407 let kind = CharsetDecodeErrorKind::MalformedSequence {
408 value: Some(second as u32),
409 };
410 return Err(CharsetDecodeError::new(
411 Charset::UTF_8,
412 kind,
413 required_index(index, 1),
414 )
415 .with_consumed(2));
416 }
417 if !Utf8::is_continuation_byte(third) {
418 let kind = CharsetDecodeErrorKind::MalformedSequence {
419 value: Some(third as u32),
420 };
421 return Err(CharsetDecodeError::new(
422 Charset::UTF_8,
423 kind,
424 required_index(index, 2),
425 )
426 .with_consumed(3));
427 }
428 Ok((((first & 0x0f) as u32) << 12)
429 | (((second & 0x3f) as u32) << 6)
430 | ((third & 0x3f) as u32))
431}
432
433#[inline]
449fn decode_four(input: &[u8], index: usize) -> CharsetDecodeResult<u32> {
450 let first = input[index];
451 let second = input[index + 1];
452 let third = input[index + 2];
453 let fourth = input[index + 3];
454 if !is_valid_second_byte(first, second) {
455 let kind = CharsetDecodeErrorKind::MalformedSequence {
456 value: Some(second as u32),
457 };
458 return Err(CharsetDecodeError::new(
459 Charset::UTF_8,
460 kind,
461 required_index(index, 1),
462 )
463 .with_consumed(2));
464 }
465 if !Utf8::is_continuation_byte(third) {
466 let kind = CharsetDecodeErrorKind::MalformedSequence {
467 value: Some(third as u32),
468 };
469 return Err(CharsetDecodeError::new(
470 Charset::UTF_8,
471 kind,
472 required_index(index, 2),
473 )
474 .with_consumed(3));
475 }
476 if !Utf8::is_continuation_byte(fourth) {
477 let kind = CharsetDecodeErrorKind::MalformedSequence {
478 value: Some(fourth as u32),
479 };
480 return Err(CharsetDecodeError::new(
481 Charset::UTF_8,
482 kind,
483 required_index(index, 3),
484 )
485 .with_consumed(4));
486 }
487 Ok((((first & 0x07) as u32) << 18)
488 | (((second & 0x3f) as u32) << 12)
489 | (((third & 0x3f) as u32) << 6)
490 | ((fourth & 0x3f) as u32))
491}