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 if self.options.reject_non_alphabet {
134 self.update_strict(input, output)
135 } else {
136 Ok(self.update_lenient(input, output))
137 }
138 }
139
140 fn update_lenient(&mut self, input: &[u8], output: &mut [u8]) -> usize {
144 let mut queue = self.queue;
145 let mut nbits = u32::from(self.nbits);
146 let mut val = match self.pending {
147 Some(v) => u32::from(v),
148 None => u32::MAX,
149 };
150 let mut written = 0;
151 for &byte in input {
152 let d = u32::from(DECODE_TABLE[usize::from(byte)]);
153 if d == 91 {
154 continue;
155 }
156 if val == u32::MAX {
157 val = d;
158 } else {
159 let combined = val + d * 91;
160 val = u32::MAX;
161 queue |= combined << nbits;
162 nbits += if combined & 8191 > 88 { 13 } else { 14 };
163 output[written] = queue as u8;
165 written += 1;
166 queue >>= 8;
167 nbits -= 8;
168 if nbits >= 8 {
169 output[written] = queue as u8;
170 written += 1;
171 queue >>= 8;
172 nbits -= 8;
173 }
174 }
175 }
176 self.queue = queue;
177 self.nbits = nbits as u8;
178 self.pending = if val == u32::MAX {
179 None
180 } else {
181 Some(val as u8)
182 };
183 written
184 }
185
186 fn update_strict(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
190 let mut working = self.clone();
191 let mut written = 0;
192 for (offset, &byte) in input.iter().enumerate() {
193 let value = DECODE_TABLE[usize::from(byte)];
194 if value == 91 {
195 return Err(DecodeError::InvalidByte { byte, offset });
196 }
197 if let Some(first) = working.pending.take() {
198 let combined = u32::from(first) + u32::from(value) * 91;
199 working.queue |= combined << working.nbits;
200 working.nbits += if combined & 8191 > 88 { 13 } else { 14 };
201 while working.nbits > 7 {
202 output[written] = working.queue as u8;
203 written += 1;
204 working.queue >>= 8;
205 working.nbits -= 8;
206 }
207 } else {
208 working.pending = Some(value);
209 }
210 }
211 *self = working;
212 Ok(written)
213 }
214
215 pub fn finish(self) -> Result<Option<u8>, DecodeError> {
217 Ok(self
218 .pending
219 .map(|first| (self.queue | (u32::from(first) << self.nbits)) as u8))
220 }
221
222 pub(crate) fn decode_into(
223 mut self,
224 input: &[u8],
225 output: &mut [u8],
226 ) -> Result<usize, DecodeError> {
227 let required = one_shot_capacity(input.len());
228 if output.len() < required {
229 return Err(DecodeError::OutputTooSmall(OutputTooSmall::new(required)));
230 }
231
232 let written = self.update(input, output)?;
233 if let Some(byte) = self.finish()? {
234 output[written] = byte;
235 Ok(written + 1)
236 } else {
237 Ok(written)
238 }
239 }
240
241 fn update_capacity(&self, input_len: usize) -> usize {
242 let pairs = input_len / 2
243 + if self.pending.is_some() {
244 input_len % 2
245 } else {
246 0
247 };
248 let prefix = (pairs / 4).saturating_mul(7);
249 let remaining_bits = usize::from(self.nbits) + (pairs % 4) * 14;
250 prefix.saturating_add(remaining_bits / 8)
251 }
252}
253
254const fn one_shot_capacity(input_len: usize) -> usize {
255 let pairs = input_len / 2;
256 let prefix = (pairs / 4).saturating_mul(7);
257 let remaining_bytes = ((pairs % 4) * 14) / 8;
258 prefix
259 .saturating_add(remaining_bytes)
260 .saturating_add(input_len % 2)
261}
262
263#[cfg(feature = "alloc")]
264pub fn decode(input: &[u8], options: DecodeOptions) -> Result<alloc::vec::Vec<u8>, DecodeError> {
265 let capacity = max_decoded_len(input.len()).ok_or(DecodeError::AllocationFailed)?;
266 if capacity > isize::MAX as usize {
267 return Err(DecodeError::AllocationFailed);
268 }
269
270 let mut output = alloc::vec::Vec::new();
271 output
272 .try_reserve_exact(capacity)
273 .map_err(|_| DecodeError::AllocationFailed)?;
274 output.resize(capacity, 0);
275 let written = Decoder::new(options).decode_into(input, &mut output)?;
276 output.truncate(written);
277 Ok(output)
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn all_abstract_state_classes_are_reachable_and_shifts_fit_u32() {
286 let mut reachable = [[false; 8]; 2];
287 reachable[0][0] = true;
288
289 for _ in 0..32 {
290 let mut next = reachable;
291 for nbits in 0..=7 {
292 if reachable[0][nbits] {
293 next[1][nbits] = true;
294 }
295 if reachable[1][nbits] {
296 next[0][(nbits + 13) % 8] = true;
297 next[0][(nbits + 14) % 8] = true;
298 }
299 }
300 reachable = next;
301 }
302
303 assert!(reachable.into_iter().flatten().all(|state| state));
304 for nbits in 0..=7 {
305 let shifted = 8280_u32 << nbits;
306 assert!(shifted < (1_u32 << 21));
307 }
308 }
309
310 #[test]
311 fn update_capacity_matches_wide_formula() {
312 for pending in [None, Some(0)] {
313 for nbits in 0..=7 {
314 let decoder = Decoder {
315 options: DecodeOptions::new(),
316 pending,
317 queue: 0,
318 nbits,
319 };
320 for input_len in [0, 1, 2, 7, 8, 9, usize::MAX] {
321 let pairs = input_len as u128 / 2
322 + u128::from(pending.is_some()) * (input_len as u128 % 2);
323 let wide = (u128::from(nbits) + 14 * pairs) / 8;
324 let expected = usize::try_from(wide).unwrap_or(usize::MAX);
325 assert_eq!(decoder.update_capacity(input_len), expected);
326 }
327 }
328 }
329 }
330}