Skip to main content

qubit_codec_text/charset/
ascii.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// =============================================================================
8use crate::charset::ascii_folding;
9
10/// Namespace for ASCII character and code point helpers.
11pub enum Ascii {}
12
13impl Ascii {
14    /// Maximum valid ASCII character.
15    pub const MAX_CHAR: char = '\u{007f}';
16
17    /// Maximum valid ASCII byte.
18    pub const MAX_BYTE: u8 = Self::MAX_CHAR as u8;
19
20    /// Maximum number of ASCII characters emitted by [`Self::fold`].
21    pub const MAX_FOLDING_COUNT: usize = 4;
22
23    /// ASCII NUL.
24    pub const NULL_CHAR: char = '\0';
25
26    /// ASCII NUL as a byte.
27    pub const NULL_BYTE: u8 = Self::NULL_CHAR as u8;
28
29    /// ASCII SOH.
30    pub const START_OF_HEADER_CHAR: char = '\u{0001}';
31
32    /// ASCII SOH as a byte.
33    pub const START_OF_HEADER_BYTE: u8 = Self::START_OF_HEADER_CHAR as u8;
34
35    /// ASCII STX.
36    pub const START_OF_TEXT_CHAR: char = '\u{0002}';
37
38    /// ASCII STX as a byte.
39    pub const START_OF_TEXT_BYTE: u8 = Self::START_OF_TEXT_CHAR as u8;
40
41    /// ASCII ETX.
42    pub const END_OF_TEXT_CHAR: char = '\u{0003}';
43
44    /// ASCII ETX as a byte.
45    pub const END_OF_TEXT_BYTE: u8 = Self::END_OF_TEXT_CHAR as u8;
46
47    /// ASCII EOT.
48    pub const START_OF_TRANSMISSION_CHAR: char = '\u{0004}';
49
50    /// ASCII EOT as a byte.
51    pub const START_OF_TRANSMISSION_BYTE: u8 =
52        Self::START_OF_TRANSMISSION_CHAR as u8;
53
54    /// ASCII ENQ.
55    pub const ENQUIRY_CHAR: char = '\u{0005}';
56
57    /// ASCII ENQ as a byte.
58    pub const ENQUIRY_BYTE: u8 = Self::ENQUIRY_CHAR as u8;
59
60    /// ASCII ACK.
61    pub const ACKNOWLEDGMENT_CHAR: char = '\u{0006}';
62
63    /// ASCII ACK as a byte.
64    pub const ACKNOWLEDGMENT_BYTE: u8 = Self::ACKNOWLEDGMENT_CHAR as u8;
65
66    /// ASCII BEL.
67    pub const BELL_CHAR: char = '\u{0007}';
68
69    /// ASCII BEL as a byte.
70    pub const BELL_BYTE: u8 = Self::BELL_CHAR as u8;
71
72    /// ASCII BS.
73    pub const BACKSPACE_CHAR: char = '\u{0008}';
74
75    /// ASCII BS as a byte.
76    pub const BACKSPACE_BYTE: u8 = Self::BACKSPACE_CHAR as u8;
77
78    /// ASCII HT.
79    pub const HORIZONTAL_TAB_CHAR: char = '\t';
80
81    /// ASCII HT as a byte.
82    pub const HORIZONTAL_TAB_BYTE: u8 = Self::HORIZONTAL_TAB_CHAR as u8;
83
84    /// ASCII LF.
85    pub const LINE_FEED_CHAR: char = '\n';
86
87    /// ASCII LF as a byte.
88    pub const LINE_FEED_BYTE: u8 = Self::LINE_FEED_CHAR as u8;
89
90    /// ASCII VT.
91    pub const VERTICAL_TAB_CHAR: char = '\u{000b}';
92
93    /// ASCII VT as a byte.
94    pub const VERTICAL_TAB_BYTE: u8 = Self::VERTICAL_TAB_CHAR as u8;
95
96    /// ASCII FF.
97    pub const FORM_FEED_CHAR: char = '\u{000c}';
98
99    /// ASCII FF as a byte.
100    pub const FORM_FEED_BYTE: u8 = Self::FORM_FEED_CHAR as u8;
101
102    /// ASCII CR.
103    pub const CARRIAGE_RETURN_CHAR: char = '\r';
104
105    /// ASCII CR as a byte.
106    pub const CARRIAGE_RETURN_BYTE: u8 = Self::CARRIAGE_RETURN_CHAR as u8;
107
108    /// ASCII SO.
109    pub const SHIFT_OUT_CHAR: char = '\u{000e}';
110
111    /// ASCII SO as a byte.
112    pub const SHIFT_OUT_BYTE: u8 = Self::SHIFT_OUT_CHAR as u8;
113
114    /// ASCII SI.
115    pub const SHIFT_IN_CHAR: char = '\u{000f}';
116
117    /// ASCII SI as a byte.
118    pub const SHIFT_IN_BYTE: u8 = Self::SHIFT_IN_CHAR as u8;
119
120    /// ASCII DLE.
121    pub const DATA_LINK_ESCAPE_CHAR: char = '\u{0010}';
122
123    /// ASCII DLE as a byte.
124    pub const DATA_LINK_ESCAPE_BYTE: u8 = Self::DATA_LINK_ESCAPE_CHAR as u8;
125
126    /// ASCII DC1.
127    pub const DEVICE_CONTROL_1_CHAR: char = '\u{0011}';
128
129    /// ASCII DC1 as a byte.
130    pub const DEVICE_CONTROL_1_BYTE: u8 = Self::DEVICE_CONTROL_1_CHAR as u8;
131
132    /// ASCII DC2.
133    pub const DEVICE_CONTROL_2_CHAR: char = '\u{0012}';
134
135    /// ASCII DC2 as a byte.
136    pub const DEVICE_CONTROL_2_BYTE: u8 = Self::DEVICE_CONTROL_2_CHAR as u8;
137
138    /// ASCII DC3.
139    pub const DEVICE_CONTROL_3_CHAR: char = '\u{0013}';
140
141    /// ASCII DC3 as a byte.
142    pub const DEVICE_CONTROL_3_BYTE: u8 = Self::DEVICE_CONTROL_3_CHAR as u8;
143
144    /// ASCII DC4.
145    pub const DEVICE_CONTROL_4_CHAR: char = '\u{0014}';
146
147    /// ASCII DC4 as a byte.
148    pub const DEVICE_CONTROL_4_BYTE: u8 = Self::DEVICE_CONTROL_4_CHAR as u8;
149
150    /// ASCII NAK.
151    pub const NEGATIVE_ACKNOWLEDGEMENT_CHAR: char = '\u{0015}';
152
153    /// ASCII NAK as a byte.
154    pub const NEGATIVE_ACKNOWLEDGEMENT_BYTE: u8 =
155        Self::NEGATIVE_ACKNOWLEDGEMENT_CHAR as u8;
156
157    /// ASCII SYN.
158    pub const SYNCHRONOUS_IDLE_CHAR: char = '\u{0016}';
159
160    /// ASCII SYN as a byte.
161    pub const SYNCHRONOUS_IDLE_BYTE: u8 = Self::SYNCHRONOUS_IDLE_CHAR as u8;
162
163    /// ASCII ETB.
164    pub const END_OF_TRANS_BLOCK_CHAR: char = '\u{0017}';
165
166    /// ASCII ETB as a byte.
167    pub const END_OF_TRANS_BLOCK_BYTE: u8 = Self::END_OF_TRANS_BLOCK_CHAR as u8;
168
169    /// ASCII CAN.
170    pub const CANCEL_CHAR: char = '\u{0018}';
171
172    /// ASCII CAN as a byte.
173    pub const CANCEL_BYTE: u8 = Self::CANCEL_CHAR as u8;
174
175    /// ASCII EM.
176    pub const END_OF_MEDIUM_CHAR: char = '\u{0019}';
177
178    /// ASCII EM as a byte.
179    pub const END_OF_MEDIUM_BYTE: u8 = Self::END_OF_MEDIUM_CHAR as u8;
180
181    /// ASCII SUB.
182    pub const SUBSTITUTE_CHAR: char = '\u{001a}';
183
184    /// ASCII SUB as a byte.
185    pub const SUBSTITUTE_BYTE: u8 = Self::SUBSTITUTE_CHAR as u8;
186
187    /// ASCII ESC.
188    pub const ESCAPE_CHAR: char = '\u{001b}';
189
190    /// ASCII ESC as a byte.
191    pub const ESCAPE_BYTE: u8 = Self::ESCAPE_CHAR as u8;
192
193    /// ASCII FS.
194    pub const FILE_SEPARATOR_CHAR: char = '\u{001c}';
195
196    /// ASCII FS as a byte.
197    pub const FILE_SEPARATOR_BYTE: u8 = Self::FILE_SEPARATOR_CHAR as u8;
198
199    /// ASCII GS.
200    pub const GROUP_SEPARATOR_CHAR: char = '\u{001d}';
201
202    /// ASCII GS as a byte.
203    pub const GROUP_SEPARATOR_BYTE: u8 = Self::GROUP_SEPARATOR_CHAR as u8;
204
205    /// ASCII RS.
206    pub const RECORD_SEPARATOR_CHAR: char = '\u{001e}';
207
208    /// ASCII RS as a byte.
209    pub const RECORD_SEPARATOR_BYTE: u8 = Self::RECORD_SEPARATOR_CHAR as u8;
210
211    /// ASCII US.
212    pub const UNIT_SEPARATOR_CHAR: char = '\u{001f}';
213
214    /// ASCII US as a byte.
215    pub const UNIT_SEPARATOR_BYTE: u8 = Self::UNIT_SEPARATOR_CHAR as u8;
216
217    /// ASCII DEL.
218    pub const DELETE_CHAR: char = '\u{007f}';
219
220    /// ASCII DEL as a byte.
221    pub const DELETE_BYTE: u8 = Self::DELETE_CHAR as u8;
222
223    /// ASCII space.
224    pub const SPACE_CHAR: char = ' ';
225
226    /// ASCII space as a byte.
227    pub const SPACE_BYTE: u8 = Self::SPACE_CHAR as u8;
228
229    /// ASCII exclamation mark.
230    pub const EXCLAMATION_CHAR: char = '!';
231
232    /// ASCII exclamation mark as a byte.
233    pub const EXCLAMATION_BYTE: u8 = Self::EXCLAMATION_CHAR as u8;
234
235    /// ASCII double quote.
236    pub const DOUBLE_QUOTE_CHAR: char = '"';
237
238    /// ASCII double quote as a byte.
239    pub const DOUBLE_QUOTE_BYTE: u8 = Self::DOUBLE_QUOTE_CHAR as u8;
240
241    /// ASCII number sign.
242    pub const SHARP_CHAR: char = '#';
243
244    /// ASCII number sign as a byte.
245    pub const SHARP_BYTE: u8 = Self::SHARP_CHAR as u8;
246
247    /// ASCII dollar sign.
248    pub const DOLLAR_CHAR: char = '$';
249
250    /// ASCII dollar sign as a byte.
251    pub const DOLLAR_BYTE: u8 = Self::DOLLAR_CHAR as u8;
252
253    /// ASCII percent sign.
254    pub const PERCENT_CHAR: char = '%';
255
256    /// ASCII percent sign as a byte.
257    pub const PERCENT_BYTE: u8 = Self::PERCENT_CHAR as u8;
258
259    /// ASCII ampersand.
260    pub const AMPERSAND_CHAR: char = '&';
261
262    /// ASCII ampersand as a byte.
263    pub const AMPERSAND_BYTE: u8 = Self::AMPERSAND_CHAR as u8;
264
265    /// ASCII tab.
266    pub const TAB_CHAR: char = '\t';
267
268    /// ASCII tab as a byte.
269    pub const TAB_BYTE: u8 = Self::TAB_CHAR as u8;
270
271    /// ASCII backslash.
272    pub const BACKSLASH_CHAR: char = '\\';
273
274    /// ASCII backslash as a byte.
275    pub const BACKSLASH_BYTE: u8 = Self::BACKSLASH_CHAR as u8;
276
277    /// ASCII single quote.
278    pub const SINGLE_QUOTE_CHAR: char = '\'';
279
280    /// ASCII single quote as a byte.
281    pub const SINGLE_QUOTE_BYTE: u8 = Self::SINGLE_QUOTE_CHAR as u8;
282
283    /// ASCII back quote.
284    pub const BACK_QUOTE_CHAR: char = '`';
285
286    /// ASCII back quote as a byte.
287    pub const BACK_QUOTE_BYTE: u8 = Self::BACK_QUOTE_CHAR as u8;
288
289    /// ASCII comma.
290    pub const COMMA_CHAR: char = ',';
291
292    /// ASCII comma as a byte.
293    pub const COMMA_BYTE: u8 = Self::COMMA_CHAR as u8;
294
295    /// ASCII period.
296    pub const PERIOD_CHAR: char = '.';
297
298    /// ASCII period as a byte.
299    pub const PERIOD_BYTE: u8 = Self::PERIOD_CHAR as u8;
300
301    /// Minimum printable ASCII character.
302    pub const MIN_PRINTABLE_CHAR: char = ' ';
303
304    /// Minimum printable ASCII character as a byte.
305    pub const MIN_PRINTABLE_BYTE: u8 = Self::MIN_PRINTABLE_CHAR as u8;
306
307    /// Maximum printable ASCII character.
308    pub const MAX_PRINTABLE_CHAR: char = '~';
309
310    /// Maximum printable ASCII character as a byte.
311    pub const MAX_PRINTABLE_BYTE: u8 = Self::MAX_PRINTABLE_CHAR as u8;
312
313    /// All printable ASCII characters.
314    pub const PRINTABLE_CHARS: [char; 95] = [
315        ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-',
316        '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';',
317        '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
318        'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
319        'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e',
320        'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
321        't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
322    ];
323
324    /// All printable ASCII characters as bytes.
325    pub const PRINTABLE_BYTES: [u8; 95] =
326        *b" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
327
328    /// All ASCII letter characters.
329    pub const LETTER_CHARS: [char; 52] = [
330        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
331        'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
332        'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
333        'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
334    ];
335
336    /// All ASCII letter characters as bytes.
337    pub const LETTER_BYTES: [u8; 52] =
338        *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
339
340    /// All ASCII letter and digit characters.
341    pub const LETTER_DIGIT_CHARS: [char; 62] = [
342        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
343        'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
344        'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
345        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
346        'u', 'v', 'w', 'x', 'y', 'z',
347    ];
348
349    /// All ASCII letter and digit characters as bytes.
350    pub const LETTER_DIGIT_BYTES: [u8; 62] =
351        *b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
352
353    /// ASCII digit characters.
354    pub const DIGIT_CHARS: [char; 10] =
355        ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
356
357    /// ASCII digit bytes.
358    pub const DIGIT_BYTES: [u8; 10] = *b"0123456789";
359
360    /// Lowercase ASCII hexadecimal digit characters.
361    pub const LOWERCASE_HEX_DIGIT_CHARS: [char; 16] = [
362        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
363        'e', 'f',
364    ];
365
366    /// Lowercase ASCII hexadecimal digit bytes.
367    pub const LOWERCASE_HEX_DIGIT_BYTES: [u8; 16] = *b"0123456789abcdef";
368
369    /// Uppercase ASCII hexadecimal digit characters.
370    pub const UPPERCASE_HEX_DIGIT_CHARS: [char; 16] = [
371        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
372        'E', 'F',
373    ];
374
375    /// Uppercase ASCII hexadecimal digit bytes.
376    pub const UPPERCASE_HEX_DIGIT_BYTES: [u8; 16] = *b"0123456789ABCDEF";
377
378    const CASE_DIFFERENCE: u32 = ('a' as u32) - ('A' as u32);
379
380    /// Tests whether a byte is an ASCII byte.
381    ///
382    /// # Parameters
383    ///
384    /// - `ch`: The byte to test.
385    ///
386    /// # Returns
387    ///
388    /// Returns `true` if `ch` is in the ASCII range `0x00..=0x7F`.
389    #[inline(always)]
390    #[must_use]
391    pub const fn is_ascii_byte(ch: u8) -> bool {
392        ch <= Self::MAX_BYTE
393    }
394
395    /// Tests whether a character is an ASCII character.
396    ///
397    /// # Parameters
398    ///
399    /// - `ch`: The character to test.
400    ///
401    /// # Returns
402    ///
403    /// Returns `true` if `ch` is in the ASCII range `U+0000..=U+007F`.
404    #[inline(always)]
405    #[must_use]
406    pub const fn is_ascii_char(ch: char) -> bool {
407        ch <= Self::MAX_CHAR
408    }
409
410    /// Tests whether an integer value is an ASCII code point.
411    ///
412    /// # Parameters
413    ///
414    /// - `ch`: The raw code point value to test.
415    ///
416    /// # Returns
417    ///
418    /// Returns `true` if `ch` is in the ASCII range `0x00..=0x7F`.
419    /// Negative values and values above `0x7F` return `false`.
420    #[inline(always)]
421    #[must_use]
422    pub const fn is_ascii_code_point(ch: u32) -> bool {
423        ch <= Self::MAX_CHAR as u32
424    }
425
426    /// Tests whether a byte is Java-style ASCII whitespace.
427    ///
428    /// # Parameters
429    ///
430    /// - `ch`: The byte to test.
431    ///
432    /// # Returns
433    ///
434    /// Returns `true` for tab, line feed, form feed, carriage return, or space.
435    #[inline(always)]
436    #[must_use]
437    pub const fn is_whitespace_byte(ch: u8) -> bool {
438        ch == b'\t' || ch == b'\n' || ch == b'\x0c' || ch == b'\r' || ch == b' '
439    }
440
441    /// Tests whether a character is Java-style ASCII whitespace.
442    ///
443    /// # Parameters
444    ///
445    /// - `ch`: The character to test.
446    ///
447    /// # Returns
448    ///
449    /// Returns `true` for tab, line feed, form feed, carriage return, or space.
450    #[inline(always)]
451    #[must_use]
452    pub const fn is_whitespace_char(ch: char) -> bool {
453        ch == '\t' || ch == '\n' || ch == '\u{000c}' || ch == '\r' || ch == ' '
454    }
455
456    /// Tests whether a raw code point is Java-style ASCII whitespace.
457    ///
458    /// # Parameters
459    ///
460    /// - `ch`: The raw code point value to test.
461    ///
462    /// # Returns
463    ///
464    /// Returns `true` for tab, line feed, form feed, carriage return, or space.
465    /// Other values, including negative values, return `false`.
466    #[inline(always)]
467    #[must_use]
468    pub const fn is_whitespace_code_point(ch: u32) -> bool {
469        ch == '\t' as u32
470            || ch == '\n' as u32
471            || ch == '\u{000c}' as u32
472            || ch == '\r' as u32
473            || ch == ' ' as u32
474    }
475
476    /// Tests whether a byte is an ASCII letter.
477    ///
478    /// # Parameters
479    ///
480    /// - `ch`: The byte to test.
481    ///
482    /// # Returns
483    ///
484    /// Returns `true` if `ch` is in `A..=Z` or `a..=z`.
485    #[inline(always)]
486    #[must_use]
487    pub const fn is_letter_byte(ch: u8) -> bool {
488        Self::is_uppercase_letter_byte(ch) || Self::is_lowercase_letter_byte(ch)
489    }
490
491    /// Tests whether a character is an ASCII letter.
492    ///
493    /// # Parameters
494    ///
495    /// - `ch`: The character to test.
496    ///
497    /// # Returns
498    ///
499    /// Returns `true` if `ch` is in `A..=Z` or `a..=z`.
500    #[inline(always)]
501    #[must_use]
502    pub const fn is_letter_char(ch: char) -> bool {
503        Self::is_uppercase_letter_char(ch) || Self::is_lowercase_letter_char(ch)
504    }
505
506    /// Tests whether a raw code point is an ASCII letter.
507    ///
508    /// # Parameters
509    ///
510    /// - `ch`: The raw code point value to test.
511    ///
512    /// # Returns
513    ///
514    /// Returns `true` if `ch` is in `A..=Z` or `a..=z`.
515    #[inline(always)]
516    #[must_use]
517    pub const fn is_letter_code_point(ch: u32) -> bool {
518        Self::is_uppercase_letter_code_point(ch)
519            || Self::is_lowercase_letter_code_point(ch)
520    }
521
522    /// Tests whether a byte is an uppercase ASCII letter.
523    ///
524    /// # Parameters
525    ///
526    /// - `ch`: The byte to test.
527    ///
528    /// # Returns
529    ///
530    /// Returns `true` if `ch` is in `A..=Z`.
531    #[inline(always)]
532    #[must_use]
533    pub const fn is_uppercase_letter_byte(ch: u8) -> bool {
534        ch >= b'A' && ch <= b'Z'
535    }
536
537    /// Tests whether a character is an uppercase ASCII letter.
538    ///
539    /// # Parameters
540    ///
541    /// - `ch`: The character to test.
542    ///
543    /// # Returns
544    ///
545    /// Returns `true` if `ch` is in `A..=Z`.
546    #[inline(always)]
547    #[must_use]
548    pub const fn is_uppercase_letter_char(ch: char) -> bool {
549        ch >= 'A' && ch <= 'Z'
550    }
551
552    /// Tests whether a raw code point is an uppercase ASCII letter.
553    ///
554    /// # Parameters
555    ///
556    /// - `ch`: The raw code point value to test.
557    ///
558    /// # Returns
559    ///
560    /// Returns `true` if `ch` is in `A..=Z`.
561    #[inline(always)]
562    #[must_use]
563    pub const fn is_uppercase_letter_code_point(ch: u32) -> bool {
564        ch >= 'A' as u32 && ch <= 'Z' as u32
565    }
566
567    /// Tests whether a byte is a lowercase ASCII letter.
568    ///
569    /// # Parameters
570    ///
571    /// - `ch`: The byte to test.
572    ///
573    /// # Returns
574    ///
575    /// Returns `true` if `ch` is in `a..=z`.
576    #[inline(always)]
577    #[must_use]
578    pub const fn is_lowercase_letter_byte(ch: u8) -> bool {
579        ch >= b'a' && ch <= b'z'
580    }
581
582    /// Tests whether a character is a lowercase ASCII letter.
583    ///
584    /// # Parameters
585    ///
586    /// - `ch`: The character to test.
587    ///
588    /// # Returns
589    ///
590    /// Returns `true` if `ch` is in `a..=z`.
591    #[inline(always)]
592    #[must_use]
593    pub const fn is_lowercase_letter_char(ch: char) -> bool {
594        ch >= 'a' && ch <= 'z'
595    }
596
597    /// Tests whether a raw code point is a lowercase ASCII letter.
598    ///
599    /// # Parameters
600    ///
601    /// - `ch`: The raw code point value to test.
602    ///
603    /// # Returns
604    ///
605    /// Returns `true` if `ch` is in `a..=z`.
606    #[inline(always)]
607    #[must_use]
608    pub const fn is_lowercase_letter_code_point(ch: u32) -> bool {
609        ch >= 'a' as u32 && ch <= 'z' as u32
610    }
611
612    /// Tests whether a byte is an ASCII decimal digit.
613    ///
614    /// # Parameters
615    ///
616    /// - `ch`: The byte to test.
617    ///
618    /// # Returns
619    ///
620    /// Returns `true` if `ch` is in `0..=9`.
621    #[inline(always)]
622    #[must_use]
623    pub const fn is_digit_byte(ch: u8) -> bool {
624        ch >= b'0' && ch <= b'9'
625    }
626
627    /// Tests whether a character is an ASCII decimal digit.
628    ///
629    /// # Parameters
630    ///
631    /// - `ch`: The character to test.
632    ///
633    /// # Returns
634    ///
635    /// Returns `true` if `ch` is in `0..=9`.
636    #[inline(always)]
637    #[must_use]
638    pub const fn is_digit_char(ch: char) -> bool {
639        ch >= '0' && ch <= '9'
640    }
641
642    /// Tests whether a raw code point is an ASCII decimal digit.
643    ///
644    /// # Parameters
645    ///
646    /// - `ch`: The raw code point value to test.
647    ///
648    /// # Returns
649    ///
650    /// Returns `true` if `ch` is in `0..=9`.
651    #[inline(always)]
652    #[must_use]
653    pub const fn is_digit_code_point(ch: u32) -> bool {
654        ch >= '0' as u32 && ch <= '9' as u32
655    }
656
657    /// Tests whether a byte is an ASCII hexadecimal digit.
658    ///
659    /// # Parameters
660    ///
661    /// - `ch`: The byte to test.
662    ///
663    /// # Returns
664    ///
665    /// Returns `true` if `ch` is in `0..=9`, `A..=F`, or `a..=f`.
666    #[inline(always)]
667    #[must_use]
668    pub const fn is_hex_digit_byte(ch: u8) -> bool {
669        Self::is_digit_byte(ch)
670            || (ch >= b'a' && ch <= b'f')
671            || (ch >= b'A' && ch <= b'F')
672    }
673
674    /// Tests whether a character is an ASCII hexadecimal digit.
675    ///
676    /// # Parameters
677    ///
678    /// - `ch`: The character to test.
679    ///
680    /// # Returns
681    ///
682    /// Returns `true` if `ch` is in `0..=9`, `A..=F`, or `a..=f`.
683    #[inline(always)]
684    #[must_use]
685    pub const fn is_hex_digit_char(ch: char) -> bool {
686        Self::is_digit_char(ch)
687            || (ch >= 'a' && ch <= 'f')
688            || (ch >= 'A' && ch <= 'F')
689    }
690
691    /// Tests whether a raw code point is an ASCII hexadecimal digit.
692    ///
693    /// # Parameters
694    ///
695    /// - `ch`: The raw code point value to test.
696    ///
697    /// # Returns
698    ///
699    /// Returns `true` if `ch` is in `0..=9`, `A..=F`, or `a..=f`.
700    #[inline(always)]
701    #[must_use]
702    pub const fn is_hex_digit_code_point(ch: u32) -> bool {
703        Self::is_digit_code_point(ch)
704            || (ch >= 'a' as u32 && ch <= 'f' as u32)
705            || (ch >= 'A' as u32 && ch <= 'F' as u32)
706    }
707
708    /// Tests whether a byte is an ASCII octal digit.
709    ///
710    /// # Parameters
711    ///
712    /// - `ch`: The byte to test.
713    ///
714    /// # Returns
715    ///
716    /// Returns `true` if `ch` is in `0..=7`.
717    #[inline(always)]
718    #[must_use]
719    pub const fn is_octal_digit_byte(ch: u8) -> bool {
720        ch >= b'0' && ch <= b'7'
721    }
722
723    /// Tests whether a character is an ASCII octal digit.
724    ///
725    /// # Parameters
726    ///
727    /// - `ch`: The character to test.
728    ///
729    /// # Returns
730    ///
731    /// Returns `true` if `ch` is in `0..=7`.
732    #[inline(always)]
733    #[must_use]
734    pub const fn is_octal_digit_char(ch: char) -> bool {
735        ch >= '0' && ch <= '7'
736    }
737
738    /// Tests whether a raw code point is an ASCII octal digit.
739    ///
740    /// # Parameters
741    ///
742    /// - `ch`: The raw code point value to test.
743    ///
744    /// # Returns
745    ///
746    /// Returns `true` if `ch` is in `0..=7`.
747    #[inline(always)]
748    #[must_use]
749    pub const fn is_octal_digit_code_point(ch: u32) -> bool {
750        ch >= '0' as u32 && ch <= '7' as u32
751    }
752
753    /// Tests whether a byte is an ASCII letter or digit.
754    ///
755    /// # Parameters
756    ///
757    /// - `ch`: The byte to test.
758    ///
759    /// # Returns
760    ///
761    /// Returns `true` if `ch` is an ASCII letter or decimal digit.
762    #[inline(always)]
763    #[must_use]
764    pub const fn is_letter_or_digit_byte(ch: u8) -> bool {
765        Self::is_letter_byte(ch) || Self::is_digit_byte(ch)
766    }
767
768    /// Tests whether a character is an ASCII letter or digit.
769    ///
770    /// # Parameters
771    ///
772    /// - `ch`: The character to test.
773    ///
774    /// # Returns
775    ///
776    /// Returns `true` if `ch` is an ASCII letter or decimal digit.
777    #[inline(always)]
778    #[must_use]
779    pub const fn is_letter_or_digit_char(ch: char) -> bool {
780        Self::is_letter_char(ch) || Self::is_digit_char(ch)
781    }
782
783    /// Tests whether a raw code point is an ASCII letter or digit.
784    ///
785    /// # Parameters
786    ///
787    /// - `ch`: The raw code point value to test.
788    ///
789    /// # Returns
790    ///
791    /// Returns `true` if `ch` is an ASCII letter or decimal digit.
792    #[inline(always)]
793    #[must_use]
794    pub const fn is_letter_or_digit_code_point(ch: u32) -> bool {
795        Self::is_letter_code_point(ch) || Self::is_digit_code_point(ch)
796    }
797
798    /// Tests whether a byte is a printable ASCII character.
799    ///
800    /// # Parameters
801    ///
802    /// - `ch`: The byte to test.
803    ///
804    /// # Returns
805    ///
806    /// Returns `true` if `ch` is in the printable ASCII range `0x20..=0x7E`.
807    #[inline(always)]
808    #[must_use]
809    pub const fn is_printable_byte(ch: u8) -> bool {
810        ch >= Self::MIN_PRINTABLE_BYTE && ch <= Self::MAX_PRINTABLE_BYTE
811    }
812
813    /// Tests whether a character is a printable ASCII character.
814    ///
815    /// # Parameters
816    ///
817    /// - `ch`: The character to test.
818    ///
819    /// # Returns
820    ///
821    /// Returns `true` if `ch` is in the printable ASCII range
822    /// `U+0020..=U+007E`.
823    #[inline(always)]
824    #[must_use]
825    pub const fn is_printable_char(ch: char) -> bool {
826        ch >= Self::MIN_PRINTABLE_CHAR && ch <= Self::MAX_PRINTABLE_CHAR
827    }
828
829    /// Tests whether a raw code point is a printable ASCII character.
830    ///
831    /// # Parameters
832    ///
833    /// - `ch`: The raw code point value to test.
834    ///
835    /// # Returns
836    ///
837    /// Returns `true` if `ch` is in the printable ASCII range `0x20..=0x7E`.
838    #[inline(always)]
839    #[must_use]
840    pub const fn is_printable_code_point(ch: u32) -> bool {
841        ch >= Self::MIN_PRINTABLE_CHAR as u32
842            && ch <= Self::MAX_PRINTABLE_CHAR as u32
843    }
844
845    /// Tests whether a byte is an ASCII control character.
846    ///
847    /// # Parameters
848    ///
849    /// - `ch`: The byte to test.
850    ///
851    /// # Returns
852    ///
853    /// Returns `true` for `0x00..=0x1F` or `0x7F`.
854    #[inline(always)]
855    #[must_use]
856    pub const fn is_control_byte(ch: u8) -> bool {
857        ch < Self::MIN_PRINTABLE_BYTE || ch == Self::DELETE_BYTE
858    }
859
860    /// Tests whether a character is an ASCII control character.
861    ///
862    /// # Parameters
863    ///
864    /// - `ch`: The character to test.
865    ///
866    /// # Returns
867    ///
868    /// Returns `true` for `U+0000..=U+001F` or `U+007F`.
869    #[inline(always)]
870    #[must_use]
871    pub const fn is_control_char(ch: char) -> bool {
872        (ch < Self::MIN_PRINTABLE_CHAR) || ch == Self::DELETE_CHAR
873    }
874
875    /// Tests whether a raw code point is an ASCII control character.
876    ///
877    /// # Parameters
878    ///
879    /// - `ch`: The raw code point value to test.
880    ///
881    /// # Returns
882    ///
883    /// Returns `true` for `0x00..=0x1F` or `0x7F`.
884    #[inline(always)]
885    #[must_use]
886    pub const fn is_control_code_point(ch: u32) -> bool {
887        (ch < Self::MIN_PRINTABLE_CHAR as u32) || ch == Self::DELETE_CHAR as u32
888    }
889
890    /// Compares two bytes while ignoring ASCII case.
891    ///
892    /// # Parameters
893    ///
894    /// - `ch1`: The first byte to compare.
895    /// - `ch2`: The second byte to compare.
896    ///
897    /// # Returns
898    ///
899    /// Returns `true` if the bytes are equal after converting ASCII uppercase
900    /// letters to lowercase. Non-ASCII bytes are compared unchanged.
901    #[inline(always)]
902    #[must_use]
903    pub const fn equals_ignore_case_byte(ch1: u8, ch2: u8) -> bool {
904        if ch1 == ch2 {
905            true
906        } else {
907            Self::byte_to_lowercase(ch1) == Self::byte_to_lowercase(ch2)
908        }
909    }
910
911    /// Compares two characters while ignoring ASCII case.
912    ///
913    /// # Parameters
914    ///
915    /// - `ch1`: The first character to compare.
916    /// - `ch2`: The second character to compare.
917    ///
918    /// # Returns
919    ///
920    /// Returns `true` if the characters are equal after converting ASCII
921    /// uppercase letters to lowercase. Non-ASCII characters are compared
922    /// unchanged.
923    #[inline(always)]
924    #[must_use]
925    pub const fn equals_ignore_case_char(ch1: char, ch2: char) -> bool {
926        if ch1 == ch2 {
927            true
928        } else {
929            Self::char_to_lowercase(ch1) == Self::char_to_lowercase(ch2)
930        }
931    }
932
933    /// Compares two raw code points while ignoring ASCII case.
934    ///
935    /// # Parameters
936    ///
937    /// - `ch1`: The first raw code point value to compare.
938    /// - `ch2`: The second raw code point value to compare.
939    ///
940    /// # Returns
941    ///
942    /// Returns `true` if the values are equal after converting ASCII uppercase
943    /// letters to lowercase. Values outside ASCII are compared unchanged.
944    #[inline(always)]
945    #[must_use]
946    pub const fn equals_ignore_case_code_point(ch1: u32, ch2: u32) -> bool {
947        if ch1 == ch2 {
948            true
949        } else {
950            Self::code_point_to_lowercase(ch1)
951                == Self::code_point_to_lowercase(ch2)
952        }
953    }
954
955    /// Converts a byte to uppercase using ASCII case rules.
956    ///
957    /// # Parameters
958    ///
959    /// - `ch`: The byte to convert.
960    ///
961    /// # Returns
962    ///
963    /// Returns the uppercase ASCII equivalent for `a..=z`; all other bytes are
964    /// returned unchanged.
965    #[inline(always)]
966    #[must_use]
967    pub const fn byte_to_uppercase(ch: u8) -> u8 {
968        if ch >= b'a' && ch <= b'z' {
969            ch - (Self::CASE_DIFFERENCE as u8)
970        } else {
971            ch
972        }
973    }
974
975    /// Converts a character to uppercase using ASCII case rules.
976    ///
977    /// # Parameters
978    ///
979    /// - `ch`: The character to convert.
980    ///
981    /// # Returns
982    ///
983    /// Returns the uppercase ASCII equivalent for `a..=z`; all other characters
984    /// are returned unchanged.
985    #[inline(always)]
986    #[must_use]
987    pub const fn char_to_uppercase(ch: char) -> char {
988        if ch >= 'a' && ch <= 'z' {
989            ((ch as u8) - (Self::CASE_DIFFERENCE as u8)) as char
990        } else {
991            ch
992        }
993    }
994
995    /// Converts a raw code point to uppercase using ASCII case rules.
996    ///
997    /// # Parameters
998    ///
999    /// - `ch`: The raw code point value to convert.
1000    ///
1001    /// # Returns
1002    ///
1003    /// Returns the uppercase ASCII equivalent for `a..=z`; all other values are
1004    /// returned unchanged.
1005    #[inline(always)]
1006    #[must_use]
1007    pub const fn code_point_to_uppercase(ch: u32) -> u32 {
1008        if ch >= 'a' as u32 && ch <= 'z' as u32 {
1009            ch - Self::CASE_DIFFERENCE
1010        } else {
1011            ch
1012        }
1013    }
1014
1015    /// Converts a byte to lowercase using ASCII case rules.
1016    ///
1017    /// # Parameters
1018    ///
1019    /// - `ch`: The byte to convert.
1020    ///
1021    /// # Returns
1022    ///
1023    /// Returns the lowercase ASCII equivalent for `A..=Z`; all other bytes are
1024    /// returned unchanged.
1025    #[inline(always)]
1026    #[must_use]
1027    pub const fn byte_to_lowercase(ch: u8) -> u8 {
1028        if ch >= b'A' && ch <= b'Z' {
1029            ch + (Self::CASE_DIFFERENCE as u8)
1030        } else {
1031            ch
1032        }
1033    }
1034
1035    /// Converts a character to lowercase using ASCII case rules.
1036    ///
1037    /// # Parameters
1038    ///
1039    /// - `ch`: The character to convert.
1040    ///
1041    /// # Returns
1042    ///
1043    /// Returns the lowercase ASCII equivalent for `A..=Z`; all other characters
1044    /// are returned unchanged.
1045    #[inline(always)]
1046    #[must_use]
1047    pub const fn char_to_lowercase(ch: char) -> char {
1048        if ch >= 'A' && ch <= 'Z' {
1049            ((ch as u8) + (Self::CASE_DIFFERENCE as u8)) as char
1050        } else {
1051            ch
1052        }
1053    }
1054
1055    /// Converts a raw code point to lowercase using ASCII case rules.
1056    ///
1057    /// # Parameters
1058    ///
1059    /// - `ch`: The raw code point value to convert.
1060    ///
1061    /// # Returns
1062    ///
1063    /// Returns the lowercase ASCII equivalent for `A..=Z`; all other values are
1064    /// returned unchanged.
1065    #[inline(always)]
1066    #[must_use]
1067    pub const fn code_point_to_lowercase(ch: u32) -> u32 {
1068        if ch >= 'A' as u32 && ch <= 'Z' as u32 {
1069            ch + Self::CASE_DIFFERENCE
1070        } else {
1071            ch
1072        }
1073    }
1074
1075    /// Converts an ASCII decimal digit byte into its numeric value.
1076    ///
1077    /// # Parameters
1078    ///
1079    /// - `ch`: The byte to convert.
1080    ///
1081    /// # Returns
1082    ///
1083    /// Returns `Some(0..=9)` for `0..=9`; returns `None` otherwise.
1084    #[inline(always)]
1085    #[must_use]
1086    pub const fn byte_to_digit(ch: u8) -> Option<u8> {
1087        if Self::is_digit_byte(ch) {
1088            Some(ch - b'0')
1089        } else {
1090            None
1091        }
1092    }
1093
1094    /// Converts an ASCII decimal digit character into its numeric value.
1095    ///
1096    /// # Parameters
1097    ///
1098    /// - `ch`: The character to convert.
1099    ///
1100    /// # Returns
1101    ///
1102    /// Returns `Some(0..=9)` for `0..=9`; returns `None` otherwise.
1103    #[inline(always)]
1104    #[must_use]
1105    pub const fn char_to_digit(ch: char) -> Option<u8> {
1106        if Self::is_digit_char(ch) {
1107            Some((ch as u8) - b'0')
1108        } else {
1109            None
1110        }
1111    }
1112
1113    /// Converts an ASCII decimal digit code point into its numeric value.
1114    ///
1115    /// # Parameters
1116    ///
1117    /// - `ch`: The raw code point value to convert.
1118    ///
1119    /// # Returns
1120    ///
1121    /// Returns `Some(0..=9)` for `0..=9`; returns `None` otherwise.
1122    #[inline(always)]
1123    #[must_use]
1124    pub const fn code_point_to_digit(ch: u32) -> Option<u8> {
1125        if Self::is_digit_code_point(ch) {
1126            Some((ch - '0' as u32) as u8)
1127        } else {
1128            None
1129        }
1130    }
1131
1132    /// Converts an ASCII hexadecimal digit byte into its numeric value.
1133    ///
1134    /// # Parameters
1135    ///
1136    /// - `ch`: The byte to convert.
1137    ///
1138    /// # Returns
1139    ///
1140    /// Returns `Some(0..=15)` for `0..=9`, `A..=F`, or `a..=f`; returns `None`
1141    /// otherwise.
1142    #[inline(always)]
1143    #[must_use]
1144    pub const fn byte_to_hex_digit(ch: u8) -> Option<u8> {
1145        if ch >= b'0' && ch <= b'9' {
1146            Some(ch - b'0')
1147        } else if ch >= b'A' && ch <= b'F' {
1148            Some(ch - (b'A' - 10))
1149        } else if ch >= b'a' && ch <= b'f' {
1150            Some(ch - (b'a' - 10))
1151        } else {
1152            None
1153        }
1154    }
1155
1156    /// Converts an ASCII hexadecimal digit character into its numeric value.
1157    ///
1158    /// # Parameters
1159    ///
1160    /// - `ch`: The character to convert.
1161    ///
1162    /// # Returns
1163    ///
1164    /// Returns `Some(0..=15)` for `0..=9`, `A..=F`, or `a..=f`; returns `None`
1165    /// otherwise.
1166    #[inline(always)]
1167    #[must_use]
1168    pub const fn char_to_hex_digit(ch: char) -> Option<u8> {
1169        if ch >= '0' && ch <= '9' {
1170            Some((ch as u8) - b'0')
1171        } else if ch >= 'A' && ch <= 'F' {
1172            Some((ch as u8) - (b'A' - 10))
1173        } else if ch >= 'a' && ch <= 'f' {
1174            Some((ch as u8) - (b'a' - 10))
1175        } else {
1176            None
1177        }
1178    }
1179
1180    /// Converts an ASCII hexadecimal digit code point into its numeric value.
1181    ///
1182    /// # Parameters
1183    ///
1184    /// - `ch`: The raw code point value to convert.
1185    ///
1186    /// # Returns
1187    ///
1188    /// Returns `Some(0..=15)` for `0..=9`, `A..=F`, or `a..=f`; returns `None`
1189    /// otherwise.
1190    #[inline(always)]
1191    #[must_use]
1192    pub const fn code_point_to_hex_digit(ch: u32) -> Option<u8> {
1193        if ch >= '0' as u32 && ch <= '9' as u32 {
1194            Some((ch - '0' as u32) as u8)
1195        } else if ch >= 'A' as u32 && ch <= 'F' as u32 {
1196            Some((ch - ('A' as u32 - 10)) as u8)
1197        } else if ch >= 'a' as u32 && ch <= 'f' as u32 {
1198            Some((ch - ('a' as u32 - 10)) as u8)
1199        } else {
1200            None
1201        }
1202    }
1203
1204    /// Folds a Unicode character to its ASCII replacement.
1205    ///
1206    /// # Parameters
1207    ///
1208    /// - `ch`: The character to fold.
1209    /// - `result`: The caller-provided output buffer that receives the folded
1210    ///   characters.
1211    /// - `offset`: The starting index in `result` at which folded characters
1212    ///   are written.
1213    ///
1214    /// # Returns
1215    ///
1216    /// Returns the number of characters written to `result` starting at
1217    /// `offset`. ASCII characters and unmapped non-ASCII characters write one
1218    /// character. Mapped characters write up to [`Self::MAX_FOLDING_COUNT`]
1219    /// ASCII characters.
1220    ///
1221    /// # Panics
1222    ///
1223    /// Panics if `result` has fewer than [`Self::MAX_FOLDING_COUNT`] writable
1224    /// slots after `offset`.
1225    pub fn fold(ch: char, result: &mut [char], offset: usize) -> usize {
1226        assert!(
1227            result.len().saturating_sub(offset) >= Self::MAX_FOLDING_COUNT,
1228            "ASCII folding output needs at least MAX_FOLDING_COUNT slots"
1229        );
1230        if ch.is_ascii() {
1231            result[offset] = ch;
1232            return 1;
1233        }
1234        match ascii_folding::fold_replacement(ch) {
1235            Some(replacement) => {
1236                for (index, replacement_char) in replacement.chars().enumerate()
1237                {
1238                    result[offset + index] = replacement_char;
1239                }
1240                replacement.len()
1241            }
1242            None => {
1243                result[offset] = ch;
1244                1
1245            }
1246        }
1247    }
1248
1249    /// Folds a Unicode character into an owned string.
1250    ///
1251    /// # Parameters
1252    ///
1253    /// - `ch`: The character to fold.
1254    ///
1255    /// # Returns
1256    ///
1257    /// Returns the folded ASCII replacement as a `String`. If `ch` has no
1258    /// folding mapping, the returned string contains `ch` unchanged.
1259    #[must_use]
1260    pub fn fold_to_string(ch: char) -> String {
1261        let mut buffer = ['\0'; Self::MAX_FOLDING_COUNT];
1262        let count = Self::fold(ch, &mut buffer, 0);
1263        buffer[..count].iter().collect()
1264    }
1265}