Skip to content

Commit cfe91f8

Browse files
committed
Add base64 to the public API
1 parent e2b423a commit cfe91f8

File tree

1 file changed

+69
-43
lines changed

1 file changed

+69
-43
lines changed

src/lib.rs

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -302,16 +302,37 @@ fn decode_without_base64<F, E>(encoded_body_plus_fragment: &str, mut write_bytes
302302
/// `decode_without_base64()` composed with
303303
/// <https://infra.spec.whatwg.org/#isomorphic-decode> composed with
304304
/// <https://infra.spec.whatwg.org/#forgiving-base64-decode>.
305-
fn decode_with_base64<F, E>(encoded_body_plus_fragment: &str, mut write_bytes: F)
305+
fn decode_with_base64<F, E>(encoded_body_plus_fragment: &str, write_bytes: F)
306306
-> Result<Option<FragmentIdentifier>, DecodeError<E>>
307307
where F: FnMut(&[u8]) -> Result<(), E>
308308
{
309-
let mut bit_buffer: u32 = 0;
310-
let mut buffer_bit_length: u8 = 0;
311-
let mut padding_symbols: u8 = 0;
309+
let mut decoder = ForgivingBase64Decoder::new(write_bytes);
310+
let fragment = decode_without_base64(encoded_body_plus_fragment, |bytes| decoder.feed(bytes))?;
311+
decoder.finish()?;
312+
Ok(fragment)
313+
}
314+
315+
/// <https://infra.spec.whatwg.org/#forgiving-base64-decode>
316+
pub struct ForgivingBase64Decoder<F, E> where F: FnMut(&[u8]) -> Result<(), E> {
317+
write_bytes: F,
318+
bit_buffer: u32,
319+
buffer_bit_length: u8,
320+
padding_symbols: u8,
321+
}
322+
323+
impl<F, E> ForgivingBase64Decoder<F, E> where F: FnMut(&[u8]) -> Result<(), E> {
324+
pub fn new(write_bytes: F) -> Self {
325+
Self {
326+
write_bytes,
327+
bit_buffer: 0,
328+
buffer_bit_length: 0,
329+
padding_symbols: 0,
330+
}
331+
}
312332

313-
let fragment = decode_without_base64::<_, DecodeError<E>>(encoded_body_plus_fragment, |bytes| {
314-
for &byte in bytes.iter() {
333+
/// Feed to the decoder partial input in an ASCII-compatible encoding
334+
pub fn feed(&mut self, input: &[u8]) -> Result<(), DecodeError<E>> {
335+
for &byte in input.iter() {
315336
let value = BASE64_DECODE_TABLE[byte as usize];
316337
if value < 0 {
317338
// A character that’s not part of the alphabet
@@ -323,64 +344,69 @@ fn decode_with_base64<F, E>(encoded_body_plus_fragment: &str, mut write_bytes: F
323344
}
324345

325346
if byte == b'=' {
326-
padding_symbols = padding_symbols.saturating_add(8);
347+
self.padding_symbols = self.padding_symbols.saturating_add(8);
327348
continue
328349
}
329350

330351
Err(InvalidBase64(()))?
331352
}
332-
if padding_symbols > 0 {
353+
if self.padding_symbols > 0 {
333354
// Alphabet symbols after padding
334355
Err(InvalidBase64(()))?
335356
}
336-
bit_buffer <<= 6;
337-
bit_buffer |= value as u32;
338-
if buffer_bit_length < 24 {
339-
buffer_bit_length += 6;
357+
self.bit_buffer <<= 6;
358+
self.bit_buffer |= value as u32;
359+
if self.buffer_bit_length < 24 {
360+
self.buffer_bit_length += 6;
340361
} else {
341362
// We’ve accumulated four times 6 bits, which equals three times 8 bits.
342363
let byte_buffer = [
343-
(bit_buffer >> 16) as u8,
344-
(bit_buffer >> 8) as u8,
345-
bit_buffer as u8,
364+
(self.bit_buffer >> 16) as u8,
365+
(self.bit_buffer >> 8) as u8,
366+
self.bit_buffer as u8,
346367
];
347-
write_bytes(&byte_buffer).map_err(DecodeError::WriteError)?;
348-
buffer_bit_length = 0;
368+
(self.write_bytes)(&byte_buffer).map_err(DecodeError::WriteError)?;
369+
self.buffer_bit_length = 0;
349370
// No need to reset bit_buffer,
350371
// since next time we’re only gonna read relevant bits.
351372
}
352373
}
353374
Ok(())
354-
})?;
355-
match (buffer_bit_length, padding_symbols) {
356-
(0, 0) => {
357-
// A multiple of four of alphabet symbols, and nothing else.
358-
}
359-
(12, 2) | (12, 0) => {
360-
// A multiple of four of alphabet symbols, followed by two more symbols,
361-
// optionally followed by two padding characters (which make a total multiple of four).
362-
let byte_buffer = [
363-
(bit_buffer >> 4) as u8,
364-
];
365-
write_bytes(&byte_buffer).map_err(DecodeError::WriteError)?;
366-
}
367-
(18, 1) | (18, 0) => {
368-
// A multiple of four of alphabet symbols, followed by three more symbols,
369-
// optionally followed by one padding character (which make a total multiple of four).
370-
let byte_buffer = [
371-
(bit_buffer >> 10) as u8,
372-
(bit_buffer >> 2) as u8,
373-
];
374-
write_bytes(&byte_buffer).map_err(DecodeError::WriteError)?;
375-
}
376-
_ => {
377-
// No other combination is acceptable
378-
Err(InvalidBase64(()))?
375+
}
376+
377+
/// Call this to signal the end of the input
378+
pub fn finish(mut self) -> Result<(), DecodeError<E>> {
379+
match (self.buffer_bit_length, self.padding_symbols) {
380+
(0, 0) => {
381+
// A multiple of four of alphabet symbols, and nothing else.
382+
}
383+
(12, 2) | (12, 0) => {
384+
// A multiple of four of alphabet symbols, followed by two more symbols,
385+
// optionally followed by two padding characters (which make a total multiple of four).
386+
let byte_buffer = [
387+
(self.bit_buffer >> 4) as u8,
388+
];
389+
(self.write_bytes)(&byte_buffer).map_err(DecodeError::WriteError)?;
390+
}
391+
(18, 1) | (18, 0) => {
392+
// A multiple of four of alphabet symbols, followed by three more symbols,
393+
// optionally followed by one padding character (which make a total multiple of four).
394+
let byte_buffer = [
395+
(self.bit_buffer >> 10) as u8,
396+
(self.bit_buffer >> 2) as u8,
397+
];
398+
(self.write_bytes)(&byte_buffer).map_err(DecodeError::WriteError)?;
399+
}
400+
_ => {
401+
// No other combination is acceptable
402+
Err(InvalidBase64(()))?
403+
}
379404
}
405+
Ok(())
380406
}
381-
Ok(fragment)
382407
}
383408

409+
384410
/// Generated by `make_base64_decode_table.py` based on "Table 1: The Base 64 Alphabet"
385411
/// at <https://tools.ietf.org/html/rfc4648#section-4>
386412
///

0 commit comments

Comments
 (0)