fastbase91_core/
decode.rs1use core::fmt;
2
3use crate::tables::DECODE_TABLE;
4use crate::OutputTooSmall;
5
6#[non_exhaustive]
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub struct DecodeOptions {
10 pub reject_non_alphabet: bool,
12}
13
14impl DecodeOptions {
15 pub const fn new() -> Self {
17 Self {
18 reject_non_alphabet: false,
19 }
20 }
21}
22
23impl Default for DecodeOptions {
24 fn default() -> Self {
25 Self::new()
26 }
27}
28
29#[non_exhaustive]
31#[derive(Clone, Copy, Debug, Eq, PartialEq)]
32pub enum DecodeError {
33 OutputTooSmall(OutputTooSmall),
35 InvalidByte { byte: u8, offset: usize },
37 #[cfg(feature = "alloc")]
39 AllocationFailed,
40}
41
42impl fmt::Display for DecodeError {
43 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Self::OutputTooSmall(error) => error.fmt(formatter),
46 Self::InvalidByte { byte, offset } => {
47 write!(formatter, "invalid byte 0x{byte:02x} at offset {offset}")
48 }
49 #[cfg(feature = "alloc")]
50 Self::AllocationFailed => formatter.write_str("decoding allocation failed"),
51 }
52 }
53}
54
55impl core::error::Error for DecodeError {}
56
57pub const fn max_decoded_len(input_len: usize) -> Option<usize> {
71 let pairs = input_len / 2 + input_len % 2;
72 let prefix = match (pairs / 4).checked_mul(7) {
73 Some(value) => value,
74 None => return None,
75 };
76 let remaining_bytes = ((pairs % 4) * 14).div_ceil(8);
77 let tail = if input_len % 2 == 0 { 1 } else { 0 };
78 match prefix.checked_add(remaining_bytes) {
79 Some(value) => value.checked_add(tail),
80 None => None,
81 }
82}
83
84#[derive(Clone, Debug, Eq, PartialEq)]
100pub struct Decoder {
101 options: DecodeOptions,
102 pending: Option<u8>,
103 queue: u32,
104 nbits: u8,
105}
106
107impl Decoder {
108 pub const fn new(options: DecodeOptions) -> Self {
110 Self {
111 options,
112 pending: None,
113 queue: 0,
114 nbits: 0,
115 }
116 }
117
118 pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
128 let required = self.update_capacity(input.len());
129 if output.len() < required {
130 return Err(DecodeError::OutputTooSmall(OutputTooSmall::new(required)));
131 }
132
133 let mut working = self.clone();
134 let mut written = 0;
135 for (offset, &byte) in input.iter().enumerate() {
136 let value = DECODE_TABLE[usize::from(byte)];
137 if value == 91 {
138 if self.options.reject_non_alphabet {
139 return Err(DecodeError::InvalidByte { byte, offset });
140 }
141 continue;
142 }
143 if let Some(first) = working.pending.take() {
144 let combined = u32::from(first) + u32::from(value) * 91;
145 working.queue |= combined << working.nbits;
146 working.nbits += if combined & 8191 > 88 { 13 } else { 14 };
147 while working.nbits > 7 {
148 output[written] = working.queue as u8;
149 written += 1;
150 working.queue >>= 8;
151 working.nbits -= 8;
152 }
153 } else {
154 working.pending = Some(value);
155 }
156 }
157 *self = working;
158 Ok(written)
159 }
160
161 pub fn finish(self) -> Result<Option<u8>, DecodeError> {
163 Ok(self
164 .pending
165 .map(|first| (self.queue | (u32::from(first) << self.nbits)) as u8))
166 }
167
168 pub(crate) fn decode_into(
169 mut self,
170 input: &[u8],
171 output: &mut [u8],
172 ) -> Result<usize, DecodeError> {
173 let required = one_shot_capacity(input.len());
174 if output.len() < required {
175 return Err(DecodeError::OutputTooSmall(OutputTooSmall::new(required)));
176 }
177
178 let written = self.update(input, output)?;
179 if let Some(byte) = self.finish()? {
180 output[written] = byte;
181 Ok(written + 1)
182 } else {
183 Ok(written)
184 }
185 }
186
187 fn update_capacity(&self, input_len: usize) -> usize {
188 let pairs = input_len / 2
189 + if self.pending.is_some() {
190 input_len % 2
191 } else {
192 0
193 };
194 let prefix = (pairs / 4).saturating_mul(7);
195 let remaining_bits = usize::from(self.nbits) + (pairs % 4) * 14;
196 prefix.saturating_add(remaining_bits / 8)
197 }
198}
199
200const fn one_shot_capacity(input_len: usize) -> usize {
201 let pairs = input_len / 2;
202 let prefix = (pairs / 4).saturating_mul(7);
203 let remaining_bytes = ((pairs % 4) * 14) / 8;
204 prefix
205 .saturating_add(remaining_bytes)
206 .saturating_add(input_len % 2)
207}
208
209#[cfg(feature = "alloc")]
210pub fn decode(input: &[u8], options: DecodeOptions) -> Result<alloc::vec::Vec<u8>, DecodeError> {
211 let capacity = max_decoded_len(input.len()).ok_or(DecodeError::AllocationFailed)?;
212 if capacity > isize::MAX as usize {
213 return Err(DecodeError::AllocationFailed);
214 }
215
216 let mut output = alloc::vec::Vec::new();
217 output
218 .try_reserve_exact(capacity)
219 .map_err(|_| DecodeError::AllocationFailed)?;
220 output.resize(capacity, 0);
221 let written = Decoder::new(options).decode_into(input, &mut output)?;
222 output.truncate(written);
223 Ok(output)
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
231 fn all_abstract_state_classes_are_reachable_and_shifts_fit_u32() {
232 let mut reachable = [[false; 8]; 2];
233 reachable[0][0] = true;
234
235 for _ in 0..32 {
236 let mut next = reachable;
237 for nbits in 0..=7 {
238 if reachable[0][nbits] {
239 next[1][nbits] = true;
240 }
241 if reachable[1][nbits] {
242 next[0][(nbits + 13) % 8] = true;
243 next[0][(nbits + 14) % 8] = true;
244 }
245 }
246 reachable = next;
247 }
248
249 assert!(reachable.into_iter().flatten().all(|state| state));
250 for nbits in 0..=7 {
251 let shifted = 8280_u32 << nbits;
252 assert!(shifted < (1_u32 << 21));
253 }
254 }
255
256 #[test]
257 fn update_capacity_matches_wide_formula() {
258 for pending in [None, Some(0)] {
259 for nbits in 0..=7 {
260 let decoder = Decoder {
261 options: DecodeOptions::new(),
262 pending,
263 queue: 0,
264 nbits,
265 };
266 for input_len in [0, 1, 2, 7, 8, 9, usize::MAX] {
267 let pairs = input_len as u128 / 2
268 + u128::from(pending.is_some()) * (input_len as u128 % 2);
269 let wide = (u128::from(nbits) + 14 * pairs) / 8;
270 let expected = usize::try_from(wide).unwrap_or(usize::MAX);
271 assert_eq!(decoder.update_capacity(input_len), expected);
272 }
273 }
274 }
275 }
276}