qubit_text_codec/charset/utf8.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10/// Namespace for UTF-8 constants and byte classification helpers.
11pub enum Utf8 {}
12
13impl Utf8 {
14 /// Maximum number of UTF-8 bytes needed for one Unicode scalar value.
15 pub const MAX_UNITS_PER_CHAR: usize = 4;
16
17 /// Maximum number of UTF-8 bytes needed for one Unicode scalar value.
18 pub const MAX_BYTES_PER_CHAR: usize = Self::MAX_UNITS_PER_CHAR;
19
20 /// Tests whether a byte encodes an ASCII scalar value by itself.
21 ///
22 /// # Parameters
23 ///
24 /// - `byte`: The byte to test.
25 ///
26 /// # Returns
27 ///
28 /// Returns `true` if `byte` is in `0x00..=0x7F`.
29 #[inline]
30 pub const fn is_single_byte(byte: u8) -> bool {
31 byte <= 0x7f
32 }
33
34 /// Tests whether a byte can lead a multi-byte UTF-8 sequence.
35 ///
36 /// # Parameters
37 ///
38 /// - `byte`: The byte to test.
39 ///
40 /// # Returns
41 ///
42 /// Returns `true` if `byte` is in `0xC2..=0xF4`.
43 #[inline]
44 pub const fn is_leading_byte(byte: u8) -> bool {
45 byte >= 0xc2 && byte <= 0xf4
46 }
47
48 /// Tests whether a byte is a UTF-8 continuation byte.
49 ///
50 /// # Parameters
51 ///
52 /// - `byte`: The byte to test.
53 ///
54 /// # Returns
55 ///
56 /// Returns `true` if `byte` matches the `10xxxxxx` continuation pattern.
57 #[inline]
58 pub const fn is_continuation_byte(byte: u8) -> bool {
59 (byte & 0xc0) == 0x80
60 }
61
62 /// Returns the complete UTF-8 sequence length implied by a leading byte.
63 ///
64 /// # Parameters
65 ///
66 /// - `byte`: The first byte of the UTF-8 sequence.
67 ///
68 /// # Returns
69 ///
70 /// Returns `Some(1..=4)` for valid ASCII or leading bytes, and `None` for
71 /// continuation bytes or invalid leading bytes.
72 #[inline]
73 pub const fn byte_len_from_leading_byte(byte: u8) -> Option<usize> {
74 if byte <= 0x7f {
75 Some(1)
76 } else if byte >= 0xc2 && byte <= 0xdf {
77 Some(2)
78 } else if byte >= 0xe0 && byte <= 0xef {
79 Some(3)
80 } else if byte >= 0xf0 && byte <= 0xf4 {
81 Some(4)
82 } else {
83 None
84 }
85 }
86
87 /// Returns the number of UTF-8 bytes needed for `ch`.
88 ///
89 /// # Parameters
90 ///
91 /// - `ch`: The character to size.
92 ///
93 /// # Returns
94 ///
95 /// Returns `1`, `2`, `3`, or `4`.
96 #[inline]
97 pub const fn byte_len(ch: char) -> usize {
98 let code_point = ch as u32;
99 if code_point <= 0x7f {
100 1
101 } else if code_point <= 0x7ff {
102 2
103 } else if code_point <= 0xffff {
104 3
105 } else {
106 4
107 }
108 }
109}