qubit_text_codec/codec/ascii_codec.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::{
11 Ascii,
12 Charset,
13 CharsetCodec,
14 CharsetDecodeError,
15 CharsetDecodeErrorKind,
16 CharsetDecodeResult,
17 CharsetEncodeError,
18 CharsetEncodeErrorKind,
19 CharsetEncodeResult,
20 DecodeStatus,
21};
22
23/// Single-byte ASCII codec for bytes.
24///
25/// `AsciiCodec` converts between one-byte ASCII-encoded data and Unicode scalar
26/// values.
27#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
28pub struct AsciiCodec;
29
30impl AsciiCodec {
31 /// Returns the ASCII charset descriptor.
32 ///
33 /// # Returns
34 ///
35 /// Returns [`Charset::ASCII`].
36 #[must_use]
37 #[inline]
38 pub const fn charset(self) -> Charset {
39 Charset::ASCII
40 }
41
42 /// Returns the maximum number of bytes needed for one ASCII character.
43 ///
44 /// # Returns
45 ///
46 /// Returns `1`.
47 #[must_use]
48 #[inline]
49 pub const fn max_units_per_char(self) -> usize {
50 1
51 }
52}
53
54impl CharsetCodec for AsciiCodec {
55 type Unit = u8;
56 /// Returns the charset descriptor for this codec.
57 ///
58 /// # Returns
59 ///
60 /// Returns [`Charset::ASCII`].
61 #[inline]
62 fn charset(&self) -> Charset {
63 Charset::ASCII
64 }
65
66 /// Returns the maximum number of output bytes for one character.
67 ///
68 /// # Returns
69 ///
70 /// Returns `1`.
71 #[inline]
72 fn max_units_per_char(&self) -> usize {
73 1
74 }
75
76 /// Decodes one ASCII byte into a `char`.
77 ///
78 /// # Parameters
79 ///
80 /// - `input`: Complete input byte slice.
81 /// - `index`: Absolute byte index at which decoding starts.
82 ///
83 /// # Returns
84 ///
85 /// `Ok(DecodeStatus::Complete { value, consumed: 1 })` for ASCII bytes.
86 ///
87 /// # Errors
88 ///
89 /// Returns [`CharsetDecodeErrorKind::MalformedSequence`] when:
90 /// - `index` is out of range, or
91 /// - current byte is not in the ASCII range `0x00..=0x7F`.
92 #[inline]
93 fn decode_one(&self, input: &[u8], index: usize) -> CharsetDecodeResult<DecodeStatus> {
94 if index > input.len() {
95 let kind = CharsetDecodeErrorKind::MalformedSequence { value: None };
96 return Err(CharsetDecodeError::new(Charset::ASCII, kind, index));
97 }
98
99 if index == input.len() {
100 return Ok(DecodeStatus::NeedMore {
101 required: index + 1,
102 available: 0,
103 });
104 }
105
106 let value = input[index];
107 if value > Ascii::MAX_BYTE {
108 let kind = CharsetDecodeErrorKind::MalformedSequence {
109 value: Some(value as u32),
110 };
111 return Err(CharsetDecodeError::new(Charset::ASCII, kind, index));
112 }
113
114 Ok(DecodeStatus::Complete {
115 value: value as char,
116 consumed: 1,
117 })
118 }
119
120 /// Encodes one `char` into one ASCII byte.
121 ///
122 /// # Parameters
123 ///
124 /// - `ch`: The character to encode.
125 /// - `output`: Output byte slice.
126 /// - `index`: Absolute output index where writing starts.
127 ///
128 /// # Returns
129 ///
130 /// `Ok(1)` when one byte is written.
131 ///
132 /// # Errors
133 ///
134 /// * `CharsetEncodeErrorKind::BufferTooSmall` if `index >= output.len()`.
135 /// * `CharsetEncodeErrorKind::UnmappableCharacter` if `ch` is not ASCII.
136 #[inline]
137 fn encode_one(&self, ch: char, output: &mut [u8], index: usize) -> CharsetEncodeResult<usize> {
138 if index >= output.len() {
139 let kind = CharsetEncodeErrorKind::BufferTooSmall {
140 required: index + 1,
141 available: 0,
142 };
143 return Err(CharsetEncodeError::new(Charset::ASCII, kind, index));
144 }
145
146 if ch > Ascii::MAX_CHAR {
147 let kind = CharsetEncodeErrorKind::UnmappableCharacter { value: ch as u32 };
148 return Err(CharsetEncodeError::new(Charset::ASCII, kind, index));
149 }
150 // Since we validated `ch`, the cast is safe.
151 output[index] = ch as u8;
152 Ok(1)
153 }
154}