fastbase91_core/
encode.rs1use core::fmt;
2
3use crate::tables::ALPHABET;
4
5pub const fn max_encoded_len(input_len: usize) -> Option<usize> {
16 let quotient = input_len / 13;
17 let remainder = input_len % 13;
18 let prefix = match quotient.checked_mul(16) {
19 Some(value) => value,
20 None => return None,
21 };
22 let remaining_bits = remainder * 8 + 13;
23 let remaining_groups = remaining_bits.div_ceil(13);
24 prefix.checked_add(remaining_groups * 2)
25}
26
27#[non_exhaustive]
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub enum EncodeError {
31 OutputTooSmall(OutputTooSmall),
33 #[cfg(feature = "alloc")]
35 AllocationFailed,
36}
37
38impl fmt::Display for EncodeError {
39 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
40 match self {
41 Self::OutputTooSmall(error) => error.fmt(formatter),
42 #[cfg(feature = "alloc")]
43 Self::AllocationFailed => formatter.write_str("encoding allocation failed"),
44 }
45 }
46}
47
48impl core::error::Error for EncodeError {}
49
50#[derive(Clone, Debug, Default, Eq, PartialEq)]
68pub struct Encoder {
69 queue: u32,
70 nbits: u8,
71}
72
73impl Encoder {
74 pub const fn new() -> Self {
76 Self { queue: 0, nbits: 0 }
77 }
78
79 pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, OutputTooSmall> {
85 let required = self.update_capacity(input.len());
86 if output.len() < required {
87 return Err(OutputTooSmall::new(required));
88 }
89
90 let mut working = self.clone();
91 let mut written = 0;
92 for &byte in input {
93 if let Some(value) = working.push(byte) {
94 output[written] = ALPHABET[value % 91];
95 output[written + 1] = ALPHABET[value / 91];
96 written += 2;
97 }
98 }
99 *self = working;
100 Ok(written)
101 }
102
103 #[must_use]
105 pub fn finish(self) -> ([u8; 2], usize) {
106 let mut tail = [0; 2];
107 if self.nbits == 0 {
108 return (tail, 0);
109 }
110
111 tail[0] = ALPHABET[self.queue as usize % 91];
112 if self.nbits > 7 || self.queue > 90 {
113 tail[1] = ALPHABET[self.queue as usize / 91];
114 (tail, 2)
115 } else {
116 (tail, 1)
117 }
118 }
119
120 fn push(&mut self, byte: u8) -> Option<usize> {
121 self.queue |= u32::from(byte) << self.nbits;
122 self.nbits += 8;
123 if self.nbits <= 13 {
124 return None;
125 }
126
127 let mut value = self.queue & 8191;
128 let consumed = if value > 88 {
129 13
130 } else {
131 value = self.queue & 16383;
132 14
133 };
134 self.queue >>= consumed;
135 self.nbits -= consumed;
136 Some(value as usize)
137 }
138
139 fn update_capacity(&self, input_len: usize) -> usize {
140 let quotient = input_len / 13;
143 let remainder = input_len % 13;
144 let prefix = quotient.saturating_mul(16);
145 let remaining_bits = usize::from(self.nbits) + remainder * 8;
146 prefix.saturating_add((remaining_bits / 13) * 2)
147 }
148
149 pub(crate) fn encode_into(
150 mut self,
151 input: &[u8],
152 output: &mut [u8],
153 ) -> Result<usize, OutputTooSmall> {
154 let tail_capacity = if self.nbits == 0 && input.is_empty() {
155 0
156 } else {
157 2
158 };
159 let required = self
160 .update_capacity(input.len())
161 .saturating_add(tail_capacity);
162 if output.len() < required {
163 return Err(OutputTooSmall::new(required));
164 }
165
166 let written = self.update(input, output)?;
167 let (tail, tail_len) = self.finish();
168 let end = written + tail_len;
169 output[written..end].copy_from_slice(&tail[..tail_len]);
170 Ok(end)
171 }
172}
173
174#[cfg(feature = "alloc")]
175pub fn encode(input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
176 let capacity = max_encoded_len(input.len()).ok_or(EncodeError::AllocationFailed)?;
177 if capacity > isize::MAX as usize {
178 return Err(EncodeError::AllocationFailed);
179 }
180
181 let mut output = alloc::vec::Vec::new();
182 output
183 .try_reserve_exact(capacity)
184 .map_err(|_| EncodeError::AllocationFailed)?;
185 output.resize(capacity, 0);
186 let written = Encoder::new()
187 .encode_into(input, &mut output)
188 .map_err(EncodeError::OutputTooSmall)?;
189 output.truncate(written);
190 Ok(output)
191}
192
193#[non_exhaustive]
195#[derive(Clone, Copy, Debug, Eq, PartialEq)]
196pub struct OutputTooSmall {
197 required: usize,
198}
199
200impl OutputTooSmall {
201 pub(crate) const fn new(required: usize) -> Self {
202 Self { required }
203 }
204
205 pub const fn required(self) -> usize {
207 self.required
208 }
209}
210
211impl fmt::Display for OutputTooSmall {
212 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
213 write!(
214 formatter,
215 "output buffer is too small; need {} bytes",
216 self.required
217 )
218 }
219}
220
221impl core::error::Error for OutputTooSmall {}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn update_capacity_matches_wide_formula_and_saturates() {
229 for nbits in [0, 7, 13] {
230 let encoder = Encoder { queue: 0, nbits };
231 for input_len in [0, 1, 12, 13, 14, usize::MAX] {
232 let wide = 2 * ((u128::from(nbits) + 8 * input_len as u128) / 13);
233 let expected = usize::try_from(wide).unwrap_or(usize::MAX);
234 assert_eq!(encoder.update_capacity(input_len), expected);
235 }
236 }
237 }
238
239 #[test]
240 fn allocation_limit_is_distinct_from_math_overflow() {
241 let bound = max_encoded_len(isize::MAX as usize).expect("bound fits usize");
242 assert!(bound > isize::MAX as usize);
243 assert_eq!(max_encoded_len(usize::MAX), None);
244 }
245}