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}