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