Skip to main content

qubit_codec_text/charset/
utf8.rs

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