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 queue = self.queue;
91 let mut nbits = self.nbits;
92 let mut written = 0;
93 for &byte in input {
94 queue |= u32::from(byte) << nbits;
95 nbits += 8;
96 if nbits <= 13 {
97 continue;
98 }
99 let low13 = queue & 8191;
100 if low13 > 88 {
101 let value = low13 as usize;
102 queue >>= 13;
103 nbits -= 13;
104 output[written] = ALPHABET[value % 91];
105 output[written + 1] = ALPHABET[value / 91];
106 written += 2;
107 } else {
108 let value = (queue & 16383) as usize;
109 queue >>= 14;
110 nbits -= 14;
111 output[written] = ALPHABET[value % 91];
112 output[written + 1] = ALPHABET[value / 91];
113 written += 2;
114 }
115 }
116 self.queue = queue;
117 self.nbits = nbits;
118 Ok(written)
119 }
120
121 #[must_use]
123 pub fn finish(self) -> ([u8; 2], usize) {
124 let mut tail = [0; 2];
125 if self.nbits == 0 {
126 return (tail, 0);
127 }
128
129 tail[0] = ALPHABET[self.queue as usize % 91];
130 if self.nbits > 7 || self.queue > 90 {
131 tail[1] = ALPHABET[self.queue as usize / 91];
132 (tail, 2)
133 } else {
134 (tail, 1)
135 }
136 }
137
138 fn update_capacity(&self, input_len: usize) -> usize {
139 let quotient = input_len / 13;
142 let remainder = input_len % 13;
143 let prefix = quotient.saturating_mul(16);
144 let remaining_bits = usize::from(self.nbits) + remainder * 8;
145 prefix.saturating_add((remaining_bits / 13) * 2)
146 }
147
148 pub(crate) fn encode_into(
149 mut self,
150 input: &[u8],
151 output: &mut [u8],
152 ) -> Result<usize, OutputTooSmall> {
153 let tail_capacity = if self.nbits == 0 && input.is_empty() {
154 0
155 } else {
156 2
157 };
158 let required = self
159 .update_capacity(input.len())
160 .saturating_add(tail_capacity);
161 if output.len() < required {
162 return Err(OutputTooSmall::new(required));
163 }
164
165 let written = self.update(input, output)?;
166 let (tail, tail_len) = self.finish();
167 let end = written + tail_len;
168 output[written..end].copy_from_slice(&tail[..tail_len]);
169 Ok(end)
170 }
171}
172
173#[cfg(feature = "alloc")]
174pub fn encode(input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
175 let capacity = max_encoded_len(input.len()).ok_or(EncodeError::AllocationFailed)?;
176 if capacity > isize::MAX as usize {
177 return Err(EncodeError::AllocationFailed);
178 }
179
180 let mut output = alloc::vec::Vec::new();
181 output
182 .try_reserve_exact(capacity)
183 .map_err(|_| EncodeError::AllocationFailed)?;
184 output.resize(capacity, 0);
185 let written = Encoder::new()
186 .encode_into(input, &mut output)
187 .map_err(EncodeError::OutputTooSmall)?;
188 output.truncate(written);
189 Ok(output)
190}
191
192#[non_exhaustive]
194#[derive(Clone, Copy, Debug, Eq, PartialEq)]
195pub struct OutputTooSmall {
196 required: usize,
197}
198
199impl OutputTooSmall {
200 pub(crate) const fn new(required: usize) -> Self {
201 Self { required }
202 }
203
204 pub const fn required(self) -> usize {
206 self.required
207 }
208}
209
210impl fmt::Display for OutputTooSmall {
211 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
212 write!(
213 formatter,
214 "output buffer is too small; need {} bytes",
215 self.required
216 )
217 }
218}
219
220impl core::error::Error for OutputTooSmall {}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn update_capacity_matches_wide_formula_and_saturates() {
228 for nbits in [0, 7, 13] {
229 let encoder = Encoder { queue: 0, nbits };
230 for input_len in [0, 1, 12, 13, 14, usize::MAX] {
231 let wide = 2 * ((u128::from(nbits) + 8 * input_len as u128) / 13);
232 let expected = usize::try_from(wide).unwrap_or(usize::MAX);
233 assert_eq!(encoder.update_capacity(input_len), expected);
234 }
235 }
236 }
237
238 #[test]
239 fn allocation_limit_is_distinct_from_math_overflow() {
240 let bound = max_encoded_len(isize::MAX as usize).expect("bound fits usize");
241 assert!(bound > isize::MAX as usize);
242 assert_eq!(max_encoded_len(usize::MAX), None);
243 }
244}