1use crate::alphabet::{Alphabet, Standard, UrlSafe};
7use crate::codec;
8use crate::error::{DecodeError, EncodeError};
9use crate::scalar;
10use crate::{Codec, Kind};
11
12pub struct Encoder {
14 kind: Kind,
15 is_padded: bool,
16 held: [u8; 2],
17 held_len: u8,
18}
19
20impl Codec {
21 pub fn encoder(&self) -> Encoder {
23 Encoder {
24 kind: self.kind,
25 is_padded: self.is_padded,
26 held: [0; 2],
27 held_len: 0,
28 }
29 }
30
31 pub fn decoder(&self) -> Decoder {
33 Decoder {
34 kind: self.kind,
35 is_padded: self.is_padded,
36 quad: [0; 3],
37 quad_len: 0,
38 total: 0,
39 run: 0,
40 pending: None,
41 state: State::Reading,
42 }
43 }
44}
45
46impl Encoder {
47 pub fn push(&mut self, input: &[u8], out: &mut [u8]) -> Result<usize, EncodeError> {
49 let groups = (usize::from(self.held_len) + input.len()) / 3;
50 let needed = groups * 4;
51 if out.len() < needed {
52 return Err(EncodeError::BufferTooSmall { needed });
53 }
54 match self.kind {
55 Kind::Standard => self.pushed::<Standard>(input, out, groups),
56 Kind::UrlSafe => self.pushed::<UrlSafe>(input, out, groups),
57 }
58 }
59
60 pub fn finish(self, out: &mut [u8]) -> Result<usize, EncodeError> {
62 let held = usize::from(self.held_len);
63 let count = match (held, self.is_padded) {
64 (0, _) => 0,
65 (_, true) => 4,
66 (_, false) => held + 1,
67 };
68 if out.len() < count {
69 return Err(EncodeError::BufferTooSmall { needed: count });
70 }
71 let written = match self.kind {
72 Kind::Standard => {
73 scalar::encode_rest::<Standard>(&self.held[..held], out, self.is_padded)
74 }
75 Kind::UrlSafe => {
76 scalar::encode_rest::<UrlSafe>(&self.held[..held], out, self.is_padded)
77 }
78 };
79 debug_assert_eq!(written, count);
80 Ok(written)
81 }
82
83 fn pushed<A: Alphabet>(
85 &mut self,
86 input: &[u8],
87 out: &mut [u8],
88 groups: usize,
89 ) -> Result<usize, EncodeError> {
90 let held = usize::from(self.held_len);
91 let mut consumed = 0;
92 let mut written = 0;
93
94 if groups > 0 && held > 0 {
96 let mut group = [0u8; 3];
97 group[..held].copy_from_slice(&self.held[..held]);
98 group[held..].copy_from_slice(&input[..3 - held]);
99 scalar::encode_group::<A>(group, &mut out[..4]);
100 self.held_len = 0;
101 consumed = 3 - held;
102 written = 4;
103 }
104
105 written += codec::encode_groups::<A>(
106 &input[consumed..],
107 &mut out[written..],
108 groups - written / 4,
109 );
110
111 let spare = (held + input.len()) % 3;
113 if groups > 0 {
114 self.held[..spare].copy_from_slice(&input[input.len() - spare..]);
115 self.held_len = spare as u8;
116 } else {
117 self.held[held..held + input.len()].copy_from_slice(input);
118 self.held_len = (held + input.len()) as u8;
119 }
120 Ok(written)
121 }
122}
123
124pub struct Decoder {
126 kind: Kind,
127 is_padded: bool,
128 quad: [u8; 3],
129 quad_len: u8,
130 total: usize,
131 run: u8,
132 pending: Option<Pending>,
133 state: State,
134}
135
136fn take_quad<A: Alphabet>(
138 quad: &[u8; 4],
139 base: usize,
140 out: &mut [u8],
141) -> Result<usize, DecodeError> {
142 if scalar::decode_bulk::<A>(quad, out, 1) == 0 {
144 return Err(scalar::find_invalid::<A>(quad, base));
145 }
146 Ok(3)
147}
148
149fn room_for(chars: usize, out: &[u8]) -> Result<(), DecodeError> {
151 let needed = chars.saturating_sub(1);
152 if out.len() < needed {
153 return Err(DecodeError::BufferTooSmall { needed });
154 }
155 Ok(())
156}
157
158struct Pending {
160 group: [u8; 3],
162 group_len: u8,
164 pad: usize,
166}
167
168enum State {
170 Reading,
172 Ended {
174 at: usize,
176 pad: usize,
178 },
179 Refused(Option<DecodeError>),
181}
182
183impl Decoder {
184 pub fn push(&mut self, input: &[u8], out: &mut [u8]) -> Result<usize, DecodeError> {
186 match self.state {
187 State::Reading => {}
188 State::Ended { .. } | State::Refused(_) => {
189 self.tally(input);
190 return Ok(0);
191 }
192 }
193 if input.is_empty() {
194 return Ok(0);
195 }
196 let chars = usize::from(self.quad_len) + input.len();
198 let mut quads = chars / 4;
199 if self.is_padded && chars.is_multiple_of(4) && input.last() == Some(&b'=') {
200 quads -= 1;
201 }
202 let needed = quads * 3;
203 if out.len() < needed {
204 return Err(DecodeError::BufferTooSmall { needed });
205 }
206 match self.kind {
207 Kind::Standard => self.read::<Standard>(input, out),
208 Kind::UrlSafe => self.read::<UrlSafe>(input, out),
209 }
210 }
211
212 pub fn finish(mut self, out: &mut [u8]) -> Result<usize, DecodeError> {
214 let held = usize::from(self.quad_len);
215 match core::mem::replace(&mut self.state, State::Reading) {
216 State::Reading => {
217 self.check_length()?;
218 let owed = match &self.pending {
219 Some(pending) => usize::from(pending.group_len),
220 None => held,
221 };
222 room_for(owed, out)?;
223 if let Some(pending) = self.pending.take() {
225 return self.settle(pending, out);
226 }
227 if held > 0 {
228 let offset = self.total - held;
229 return match self.kind {
230 Kind::Standard => {
231 scalar::decode_rest::<Standard>(&self.quad[..held], out, offset)
232 }
233 Kind::UrlSafe => {
234 scalar::decode_rest::<UrlSafe>(&self.quad[..held], out, offset)
235 }
236 };
237 }
238 Ok(0)
239 }
240 State::Ended { at, .. } if self.total == at => {
241 self.check_length()?;
243 let owed = self
244 .pending
245 .as_ref()
246 .map_or(0, |pending| usize::from(pending.group_len));
247 room_for(owed, out)?;
248 match self.pending.take() {
249 Some(pending) => self.settle(pending, out),
251 None => Ok(0),
252 }
253 }
254 State::Ended { pad, .. } => self.name(Some(DecodeError::InvalidByte {
255 position: pad,
256 byte: b'=',
257 })),
258 State::Refused(cause) => self.name(cause),
259 }
260 }
261
262 fn read<A: Alphabet>(&mut self, input: &[u8], out: &mut [u8]) -> Result<usize, DecodeError> {
264 let mut consumed = 0;
266 let mut written = 0;
267 if let Some(pending) = self.pending.take() {
268 let Some(&byte) = input.first() else {
269 self.pending = Some(pending);
270 return Ok(0);
271 };
272 consumed = 1;
273 self.total += 1;
274 if byte != b'=' {
275 let cause = DecodeError::InvalidByte {
276 position: pending.pad,
277 byte: b'=',
278 };
279 return Err(self.refuse(Some(cause), 0, &input[consumed..]));
280 }
281 self.state = State::Ended {
283 at: self.total,
284 pad: pending.pad,
285 };
286 self.pending = Some(pending);
287 self.tally(&input[consumed..]);
288 return Ok(written);
289 }
290
291 let held = usize::from(self.quad_len);
292
293 if held > 0 && input.len() >= 4 - held {
295 let take = 4 - held;
296 let is_ending = self.is_padded && input[take - 1] == b'=';
298 if !is_ending {
299 let mut quad = [0u8; 4];
300 quad[..held].copy_from_slice(&self.quad[..held]);
301 quad[held..].copy_from_slice(&input[..take]);
302 match take_quad::<A>(&quad, self.total - held, &mut out[written..]) {
303 Ok(count) => {
304 written += count;
305 self.total += take;
306 }
307 Err(cause) => {
308 self.total += take;
309 return Err(self.refuse(Some(cause), 0, &input[take..]));
310 }
311 }
312 self.quad_len = 0;
313 consumed = take;
314 }
315 }
316
317 let ending = if self.is_padded && input.last() == Some(&b'=') {
319 4
320 } else {
321 0
322 };
323 let whole = (input.len() - consumed).saturating_sub(ending) / 4;
324 if whole > 0 {
325 let base = self.total;
326 let began = consumed;
327 let (bytes, failure) = codec::scan_quads::<A>(
328 &input[consumed..consumed + whole * 4],
329 &mut out[written..],
330 whole,
331 );
332 written += bytes;
333 let taken = bytes / 3 * 4;
334 consumed += taken;
335 self.total += taken;
336
337 if let Some(DecodeError::InvalidByte {
338 position: local,
339 byte,
340 }) = failure
341 {
342 if !self.is_padded || byte != b'=' {
343 self.total += local + 1 - taken;
344 let cause = DecodeError::InvalidByte {
345 position: base + local,
346 byte,
347 };
348 return Err(self.refuse(Some(cause), 0, &input[began + local + 1..]));
349 }
350 }
351 }
352
353 for index in consumed..input.len() {
355 let byte = input[index];
356 let rest = &input[index + 1..];
357 self.total += 1;
358
359 if let Some(pending) = self.pending.take() {
361 if byte != b'=' {
362 let cause = DecodeError::InvalidByte {
363 position: pending.pad,
364 byte: b'=',
365 };
366 return Err(self.refuse(Some(cause), 0, rest));
367 }
368 self.state = State::Ended {
370 at: self.total,
371 pad: pending.pad,
372 };
373 self.pending = Some(pending);
374 self.tally(rest);
375 return Ok(written);
376 }
377
378 let held = usize::from(self.quad_len);
379
380 if self.is_padded && byte == b'=' && held >= 2 {
382 self.pending = Some(Pending {
383 group: [self.quad[0], self.quad[1], self.quad[2]],
384 group_len: held as u8,
385 pad: self.total - 1,
386 });
387 continue;
388 }
389
390 if A::DECODE[byte as usize] == crate::alphabet::INVALID {
392 let position = self.total - 1;
393 return Err(self.refuse(
394 Some(DecodeError::InvalidByte { position, byte }),
395 u8::from(self.is_padded && byte == b'='),
396 rest,
397 ));
398 }
399
400 if held == 3 {
401 let quad = [self.quad[0], self.quad[1], self.quad[2], byte];
403 match take_quad::<A>(&quad, self.total - 4, &mut out[written..]) {
404 Ok(count) => written += count,
405 Err(cause) => {
406 return Err(self.refuse(Some(cause), 0, rest));
407 }
408 }
409 self.quad_len = 0;
410 } else {
411 self.quad[held] = byte;
412 self.quad_len += 1;
413 }
414 }
415 Ok(written)
416 }
417
418 fn settle(&mut self, pending: Pending, out: &mut [u8]) -> Result<usize, DecodeError> {
420 let held = usize::from(pending.group_len);
421 let offset = self.total - 4;
422 let group = &pending.group[..held];
423 let written = match self.kind {
424 Kind::Standard => scalar::decode_rest::<Standard>(group, out, offset)?,
425 Kind::UrlSafe => scalar::decode_rest::<UrlSafe>(group, out, offset)?,
426 };
427 self.state = State::Ended {
428 at: self.total,
429 pad: pending.pad,
430 };
431 Ok(written)
432 }
433
434 fn refuse(&mut self, cause: Option<DecodeError>, run: u8, rest: &[u8]) -> DecodeError {
436 self.state = State::Refused(cause);
437 self.quad_len = 0;
438 self.pending = None;
439 self.run = run;
440 self.tally(rest);
441 cause.unwrap_or(DecodeError::InvalidPadding {
442 position: self.total.saturating_sub(3),
443 })
444 }
445
446 fn tally(&mut self, input: &[u8]) {
448 let Some((&first, rest)) = input.split_first() else {
449 return;
450 };
451 self.total += 1;
452 if let Some(pending) = self.pending.take() {
453 let cause = DecodeError::InvalidByte {
454 position: pending.pad,
455 byte: b'=',
456 };
457 self.state = State::Refused(Some(cause));
458 }
459 self.run = if self.is_padded && first == b'=' {
460 self.run.min(2) + 1
461 } else {
462 0
463 };
464
465 self.total += rest.len();
467 for &byte in &rest[rest.len().saturating_sub(3)..] {
468 self.run = if self.is_padded && byte == b'=' {
469 self.run.min(2) + 1
470 } else {
471 0
472 };
473 }
474 }
475
476 fn name(&self, cause: Option<DecodeError>) -> Result<usize, DecodeError> {
478 self.check_length()?;
479 if self.run >= 3 {
480 return Err(DecodeError::InvalidPadding {
481 position: self.total - 3,
482 });
483 }
484 Err(cause.unwrap_or(DecodeError::InvalidPadding {
485 position: self.total.saturating_sub(3),
486 }))
487 }
488
489 fn check_length(&self) -> Result<(), DecodeError> {
491 if crate::length_ok(self.total, self.is_padded) {
492 Ok(())
493 } else {
494 Err(DecodeError::InvalidLength { length: self.total })
495 }
496 }
497}