Skip to main content

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}