Clean up decoder and add documentation
This commit is contained in:
parent
841160d2d5
commit
313b877553
49
README.md
Normal file
49
README.md
Normal file
@ -0,0 +1,49 @@
|
||||
Contents
|
||||
========
|
||||
- [Introduction](#introduction)
|
||||
- [Usage](#usage)
|
||||
|
||||
# Introduction
|
||||
This is a Base64 encoding library as described in [RFC4648](https://www.rfc-editor.org/rfc/rfc4648).
|
||||
This library encodes and decodes random data into ASCII characters using an appropriate
|
||||
Base64 library. It is designed to leverage the abstractions in Rust's std library
|
||||
in order to be generic over io streams. That is to say, you can read from anything
|
||||
implementing **Read** and write the encoded or decoded output to anything which
|
||||
implements **Write**.
|
||||
|
||||
# Usage
|
||||
Add the crate to your `Cargo.toml` file.
|
||||
```Toml
|
||||
# Cargo.toml
|
||||
[dependencies]
|
||||
b64 = { git = "https://git.hitchhiker-linux.org/jeang3nie/b64_rs.git" }
|
||||
```
|
||||
## Encoding
|
||||
Encoding 'Hello, World!' into a new `String`, wrapping at 76 characters and using
|
||||
the default alphabet.
|
||||
```
|
||||
use {
|
||||
std::io::Read,
|
||||
b64::Encoder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut encoder = Encoder::new("Hello, World!".as_bytes(), String::new(), None, Some(76));
|
||||
encoder.encode().unwrap();
|
||||
assert_eq!(encoder.output(), "SGVsbG8sIFdvcmxkIQ==");
|
||||
}
|
||||
```
|
||||
|
||||
## Decoding
|
||||
```
|
||||
use {
|
||||
std::io::Read,
|
||||
b64::Decoder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut decoder = Decoder::new("SGVsbG8sIFdvcmxkIQ==".as_bytes(), vec![], None);
|
||||
let output = decoder.decode().unwrap();
|
||||
assert_eq!(&output, b"Hello, World!");
|
||||
}
|
||||
```
|
@ -3,6 +3,7 @@ use {
|
||||
std::io::{ErrorKind, Read, Write},
|
||||
};
|
||||
|
||||
/// Decodes b64 encoded data
|
||||
pub struct Decoder<R: Read, W: Write> {
|
||||
reader: R,
|
||||
writer: W,
|
||||
@ -20,6 +21,10 @@ impl<R: Read, W: Write> Decoder<R, W> {
|
||||
|
||||
/// Decodes the bytes provided by `self.reader` and writes the resulting
|
||||
/// bytes into `self.writer`.
|
||||
/// # Important
|
||||
/// Both reader and writer should be buffered for good performance, as the
|
||||
/// process of decoding will perform a series of very short read and write
|
||||
/// operations.
|
||||
/// # Errors
|
||||
/// - io failure will return `Error::Io`
|
||||
/// - a character not in the alphabet, or encountering any character after
|
||||
@ -36,23 +41,19 @@ impl<R: Read, W: Write> Decoder<R, W> {
|
||||
let mut num = 0_u32;
|
||||
let mut n_bytes = 0;
|
||||
while n_bytes < 4 {
|
||||
if let Some(byte) = byte_reader.next() {
|
||||
match byte {
|
||||
Ok(b) => {
|
||||
if b == b'\n' {
|
||||
match byte_reader.next() {
|
||||
Some(Ok(b)) => {
|
||||
if b == b'\n' || b == b'\r' {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
in_buf[n_bytes] = b;
|
||||
n_bytes += 1;
|
||||
}
|
||||
Err(e) => match e.kind() {
|
||||
ErrorKind::UnexpectedEof => break,
|
||||
ErrorKind::Interrupted => continue,
|
||||
_ => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
Some(Err(e)) if e.kind() == ErrorKind::UnexpectedEof => break,
|
||||
Some(Err(e)) if e.kind() == ErrorKind::Interrupted => continue,
|
||||
Some(Err(e)) => return Err(e.into()),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
match n_bytes {
|
||||
@ -90,12 +91,15 @@ impl<R: Read, W: Write> Decoder<R, W> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufReader, Read},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn decode() {
|
||||
let mut output = vec![];
|
||||
let mut decoder = Decoder::new("SGVsbG8sIFdvcmxk".as_bytes(), output, None);
|
||||
output = decoder.decode().unwrap();
|
||||
let mut decoder = Decoder::new("SGVsbG8sIFdvcmxk".as_bytes(), vec![], None);
|
||||
let mut output = decoder.decode().unwrap();
|
||||
assert_eq!(String::from_utf8(output.clone()).unwrap(), "Hello, World");
|
||||
output.clear();
|
||||
decoder = Decoder::new("SGVsbG8sIFdvcmxkIQ==".as_bytes(), output, None);
|
||||
@ -104,9 +108,19 @@ mod tests {
|
||||
output.clear();
|
||||
decoder = Decoder::new("SGVsbG8sIFdvcmxkIQo=".as_bytes(), output, None);
|
||||
output = decoder.decode().unwrap();
|
||||
assert_eq!(
|
||||
String::from_utf8(output).unwrap(),
|
||||
"Hello, World!\n"
|
||||
);
|
||||
assert_eq!(String::from_utf8(output).unwrap(), "Hello, World!\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_wrapped() {
|
||||
let infile = File::open("src/testdata/lorem_b64.txt").unwrap();
|
||||
let reader = BufReader::new(infile);
|
||||
let mut outfile = vec![];
|
||||
File::open("src/testdata/lorem.txt")
|
||||
.unwrap()
|
||||
.read_to_end(&mut outfile)
|
||||
.unwrap();
|
||||
let decoder = Decoder::new(reader, vec![], None);
|
||||
assert_eq!(decoder.decode().unwrap(), outfile);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
/// Encodes arbitraty data as b64 encoded ASCII text
|
||||
pub struct Encoder<R: Read, W: Write> {
|
||||
reader: R,
|
||||
writer: W,
|
||||
@ -16,6 +17,10 @@ pub struct Encoder<R: Read, W: Write> {
|
||||
impl<R: Read, W: Write> Encoder<R, W> {
|
||||
/// Creates a new encoder with the given reader and writer. If alphabet is
|
||||
/// `None` the encoder will use the default rfc4648 alphabet.
|
||||
/// # Important
|
||||
/// Both reader and writer should be buffered for best performance, as the
|
||||
/// encoder will pull single bytes from the reader and write three bytes at
|
||||
/// a time into the writer.
|
||||
pub fn new(reader: R, writer: W, alphabet: Option<B64Alphabet>, wrap: Option<usize>) -> Self {
|
||||
Self {
|
||||
reader,
|
||||
@ -25,6 +30,7 @@ impl<R: Read, W: Write> Encoder<R, W> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
pub fn encode(&mut self) -> Result<(), Error> {
|
||||
let mut total: usize = 0;
|
||||
loop {
|
||||
@ -88,7 +94,10 @@ impl<R: Read> Encoder<R, String> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::{self, File};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::BufReader,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn encode() {
|
||||
@ -111,8 +120,9 @@ mod tests {
|
||||
#[test]
|
||||
fn wrapping() {
|
||||
let infile = File::open("src/testdata/lorem.txt").unwrap();
|
||||
let reader = BufReader::new(infile);
|
||||
let outfile = fs::read_to_string("src/testdata/lorem_b64.txt").unwrap();
|
||||
let mut encoder = Encoder::new(infile, String::new(), None, Some(76));
|
||||
let mut encoder = Encoder::new(reader, String::new(), None, Some(76));
|
||||
encoder.encode().unwrap();
|
||||
assert_eq!(encoder.output(), outfile);
|
||||
}
|
||||
|
14
src/lib.rs
14
src/lib.rs
@ -1,14 +1,20 @@
|
||||
#[warn(clippy::all, clippy::pedantic)]
|
||||
pub mod decode;
|
||||
pub mod encode;
|
||||
pub mod error;
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
mod decode;
|
||||
mod encode;
|
||||
mod error;
|
||||
|
||||
pub use {decode::Decoder, encode::Encoder, error::Error as B64Error};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
/// Defines the character set used for encoding and decoding
|
||||
pub struct B64Alphabet {
|
||||
items: [char; 64],
|
||||
pad: char,
|
||||
}
|
||||
|
||||
/// The most common Base64 alphabet as defined in [RFC4648](https://www.rfc-editor.org/rfc/rfc4648)
|
||||
pub static B64_RFC4648_ALPHABET: B64Alphabet = B64Alphabet {
|
||||
items: [
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
|
||||
|
10
src/testdata/lorem.txt
vendored
10
src/testdata/lorem.txt
vendored
@ -1 +1,9 @@
|
||||
Lorem ipsum odor amet, consectetuer adipiscing elit. Mauris praesent senectus vivamus urna donec magna bibendum aenean. Nam nec sodales ex cursus inceptos vitae non habitant consectetur. Ex montes quis pellentesque tempor neque. Class ipsum fringilla mauris quam tempus. Nullam habitasse arcu ac nascetur class ad in. Senectus nostra arcu habitant arcu blandit potenti. Congue penatibus sollicitudin ullamcorper ridiculus semper. Ac porttitor bibendum nam; rhoncus pellentesque natoque pretium sit feugiat. Litora convallis maecenas elit netus blandit mus duis gravida sollicitudin.
|
||||
Lorem ipsum odor amet, consectetuer adipiscing elit. Hendrerit eu semper lectus amet est proin. Tempus himenaeos at fusce lacus habitasse sociosqu torquent varius arcu. Odio netus fermentum nisl natoque per cubilia himenaeos urna. Elit venenatis eros risus vulputate duis interdum. Mauris dis ad pharetra dui aptent. Imperdiet orci auctor condimentum adipiscing arcu. Nullam tempor magna odio volutpat nisi. Condimentum scelerisque risus faucibus; duis sollicitudin nullam.
|
||||
|
||||
Condimentum ex fringilla tempus ultricies consectetur odio egestas venenatis vulputate. Sem sapien pellentesque ornare; viverra scelerisque consequat. Platea posuere mi senectus felis dolor dictumst mattis ultricies est. Malesuada elit adipiscing suspendisse est a. Justo luctus turpis tempus leo finibus rutrum nostra magnis aliquam. Felis posuere molestie praesent porta justo nisi. Etiam aptent conubia consectetur dis; penatibus elementum ut.
|
||||
|
||||
Morbi nostra netus ad nunc egestas neque nascetur fusce. Consequat ultrices scelerisque ut rhoncus volutpat, hendrerit convallis massa. Tortor himenaeos nullam malesuada suspendisse consectetur potenti volutpat. In suscipit sit maximus; lacinia eu nulla porta. Tempus commodo libero auctor cras bibendum sit. Metus aptent mauris nascetur venenatis diam metus. Inceptos mi ultrices class morbi donec. Orci pulvinar risus accumsan faucibus litora duis commodo commodo.
|
||||
|
||||
Aliquam etiam habitasse fames massa, hendrerit purus interdum nullam. Curabitur placerat tortor placerat nec adipiscing habitasse. Massa litora interdum pretium fusce nascetur primis faucibus magna. Dui curabitur molestie justo ullamcorper leo molestie mauris. Inceptos malesuada taciti litora libero maecenas neque nam laoreet. Tristique vivamus eget maecenas; porta fames curae mollis cras. Ipsum vulputate ullamcorper et nascetur nascetur vel orci.
|
||||
|
||||
Curae et odio; urna eros pulvinar malesuada eget dignissim. Cursus fermentum lectus phasellus tempor posuere vulputate quam. Mauris commodo commodo mauris inceptos metus varius urna sagittis. Ligula duis erat ipsum pretium eu facilisis. Duis lobortis proin facilisi ad suscipit vestibulum hac tortor interdum. Ultrices tincidunt maximus aptent phasellus in ullamcorper nisl varius dis. Venenatis enim potenti potenti sodales massa id elementum.
|
||||
|
52
src/testdata/lorem_b64.txt
vendored
52
src/testdata/lorem_b64.txt
vendored
@ -1,11 +1,41 @@
|
||||
TG9yZW0gaXBzdW0gb2RvciBhbWV0LCBjb25zZWN0ZXR1ZXIgYWRpcGlzY2luZyBlbGl0LiBNYXVy
|
||||
aXMgcHJhZXNlbnQgc2VuZWN0dXMgdml2YW11cyB1cm5hIGRvbmVjIG1hZ25hIGJpYmVuZHVtIGFl
|
||||
bmVhbi4gTmFtIG5lYyBzb2RhbGVzIGV4IGN1cnN1cyBpbmNlcHRvcyB2aXRhZSBub24gaGFiaXRh
|
||||
bnQgY29uc2VjdGV0dXIuIEV4IG1vbnRlcyBxdWlzIHBlbGxlbnRlc3F1ZSB0ZW1wb3IgbmVxdWUu
|
||||
IENsYXNzIGlwc3VtIGZyaW5naWxsYSBtYXVyaXMgcXVhbSB0ZW1wdXMuIE51bGxhbSBoYWJpdGFz
|
||||
c2UgYXJjdSBhYyBuYXNjZXR1ciBjbGFzcyBhZCBpbi4gU2VuZWN0dXMgbm9zdHJhIGFyY3UgaGFi
|
||||
aXRhbnQgYXJjdSBibGFuZGl0IHBvdGVudGkuIENvbmd1ZSBwZW5hdGlidXMgc29sbGljaXR1ZGlu
|
||||
IHVsbGFtY29ycGVyIHJpZGljdWx1cyBzZW1wZXIuIEFjIHBvcnR0aXRvciBiaWJlbmR1bSBuYW07
|
||||
IHJob25jdXMgcGVsbGVudGVzcXVlIG5hdG9xdWUgcHJldGl1bSBzaXQgZmV1Z2lhdC4gTGl0b3Jh
|
||||
IGNvbnZhbGxpcyBtYWVjZW5hcyBlbGl0IG5ldHVzIGJsYW5kaXQgbXVzIGR1aXMgZ3JhdmlkYSBz
|
||||
b2xsaWNpdHVkaW4uCg==
|
||||
TG9yZW0gaXBzdW0gb2RvciBhbWV0LCBjb25zZWN0ZXR1ZXIgYWRpcGlzY2luZyBlbGl0LiBIZW5k
|
||||
cmVyaXQgZXUgc2VtcGVyIGxlY3R1cyBhbWV0IGVzdCBwcm9pbi4gVGVtcHVzIGhpbWVuYWVvcyBh
|
||||
dCBmdXNjZSBsYWN1cyBoYWJpdGFzc2Ugc29jaW9zcXUgdG9ycXVlbnQgdmFyaXVzIGFyY3UuIE9k
|
||||
aW8gbmV0dXMgZmVybWVudHVtIG5pc2wgbmF0b3F1ZSBwZXIgY3ViaWxpYSBoaW1lbmFlb3MgdXJu
|
||||
YS4gRWxpdCB2ZW5lbmF0aXMgZXJvcyByaXN1cyB2dWxwdXRhdGUgZHVpcyBpbnRlcmR1bS4gTWF1
|
||||
cmlzIGRpcyBhZCBwaGFyZXRyYSBkdWkgYXB0ZW50LiBJbXBlcmRpZXQgb3JjaSBhdWN0b3IgY29u
|
||||
ZGltZW50dW0gYWRpcGlzY2luZyBhcmN1LiBOdWxsYW0gdGVtcG9yIG1hZ25hIG9kaW8gdm9sdXRw
|
||||
YXQgbmlzaS4gQ29uZGltZW50dW0gc2NlbGVyaXNxdWUgcmlzdXMgZmF1Y2lidXM7IGR1aXMgc29s
|
||||
bGljaXR1ZGluIG51bGxhbS4KCkNvbmRpbWVudHVtIGV4IGZyaW5naWxsYSB0ZW1wdXMgdWx0cmlj
|
||||
aWVzIGNvbnNlY3RldHVyIG9kaW8gZWdlc3RhcyB2ZW5lbmF0aXMgdnVscHV0YXRlLiBTZW0gc2Fw
|
||||
aWVuIHBlbGxlbnRlc3F1ZSBvcm5hcmU7IHZpdmVycmEgc2NlbGVyaXNxdWUgY29uc2VxdWF0LiBQ
|
||||
bGF0ZWEgcG9zdWVyZSBtaSBzZW5lY3R1cyBmZWxpcyBkb2xvciBkaWN0dW1zdCBtYXR0aXMgdWx0
|
||||
cmljaWVzIGVzdC4gTWFsZXN1YWRhIGVsaXQgYWRpcGlzY2luZyBzdXNwZW5kaXNzZSBlc3QgYS4g
|
||||
SnVzdG8gbHVjdHVzIHR1cnBpcyB0ZW1wdXMgbGVvIGZpbmlidXMgcnV0cnVtIG5vc3RyYSBtYWdu
|
||||
aXMgYWxpcXVhbS4gRmVsaXMgcG9zdWVyZSBtb2xlc3RpZSBwcmFlc2VudCBwb3J0YSBqdXN0byBu
|
||||
aXNpLiBFdGlhbSBhcHRlbnQgY29udWJpYSBjb25zZWN0ZXR1ciBkaXM7IHBlbmF0aWJ1cyBlbGVt
|
||||
ZW50dW0gdXQuCgpNb3JiaSBub3N0cmEgbmV0dXMgYWQgbnVuYyBlZ2VzdGFzIG5lcXVlIG5hc2Nl
|
||||
dHVyIGZ1c2NlLiBDb25zZXF1YXQgdWx0cmljZXMgc2NlbGVyaXNxdWUgdXQgcmhvbmN1cyB2b2x1
|
||||
dHBhdCwgaGVuZHJlcml0IGNvbnZhbGxpcyBtYXNzYS4gVG9ydG9yIGhpbWVuYWVvcyBudWxsYW0g
|
||||
bWFsZXN1YWRhIHN1c3BlbmRpc3NlIGNvbnNlY3RldHVyIHBvdGVudGkgdm9sdXRwYXQuIEluIHN1
|
||||
c2NpcGl0IHNpdCBtYXhpbXVzOyBsYWNpbmlhIGV1IG51bGxhIHBvcnRhLiBUZW1wdXMgY29tbW9k
|
||||
byBsaWJlcm8gYXVjdG9yIGNyYXMgYmliZW5kdW0gc2l0LiBNZXR1cyBhcHRlbnQgbWF1cmlzIG5h
|
||||
c2NldHVyIHZlbmVuYXRpcyBkaWFtIG1ldHVzLiBJbmNlcHRvcyBtaSB1bHRyaWNlcyBjbGFzcyBt
|
||||
b3JiaSBkb25lYy4gT3JjaSBwdWx2aW5hciByaXN1cyBhY2N1bXNhbiBmYXVjaWJ1cyBsaXRvcmEg
|
||||
ZHVpcyBjb21tb2RvIGNvbW1vZG8uCgpBbGlxdWFtIGV0aWFtIGhhYml0YXNzZSBmYW1lcyBtYXNz
|
||||
YSwgaGVuZHJlcml0IHB1cnVzIGludGVyZHVtIG51bGxhbS4gQ3VyYWJpdHVyIHBsYWNlcmF0IHRv
|
||||
cnRvciBwbGFjZXJhdCBuZWMgYWRpcGlzY2luZyBoYWJpdGFzc2UuIE1hc3NhIGxpdG9yYSBpbnRl
|
||||
cmR1bSBwcmV0aXVtIGZ1c2NlIG5hc2NldHVyIHByaW1pcyBmYXVjaWJ1cyBtYWduYS4gRHVpIGN1
|
||||
cmFiaXR1ciBtb2xlc3RpZSBqdXN0byB1bGxhbWNvcnBlciBsZW8gbW9sZXN0aWUgbWF1cmlzLiBJ
|
||||
bmNlcHRvcyBtYWxlc3VhZGEgdGFjaXRpIGxpdG9yYSBsaWJlcm8gbWFlY2VuYXMgbmVxdWUgbmFt
|
||||
IGxhb3JlZXQuIFRyaXN0aXF1ZSB2aXZhbXVzIGVnZXQgbWFlY2VuYXM7IHBvcnRhIGZhbWVzIGN1
|
||||
cmFlIG1vbGxpcyBjcmFzLiBJcHN1bSB2dWxwdXRhdGUgdWxsYW1jb3JwZXIgZXQgbmFzY2V0dXIg
|
||||
bmFzY2V0dXIgdmVsIG9yY2kuCgpDdXJhZSBldCBvZGlvOyB1cm5hIGVyb3MgcHVsdmluYXIgbWFs
|
||||
ZXN1YWRhIGVnZXQgZGlnbmlzc2ltLiBDdXJzdXMgZmVybWVudHVtIGxlY3R1cyBwaGFzZWxsdXMg
|
||||
dGVtcG9yIHBvc3VlcmUgdnVscHV0YXRlIHF1YW0uIE1hdXJpcyBjb21tb2RvIGNvbW1vZG8gbWF1
|
||||
cmlzIGluY2VwdG9zIG1ldHVzIHZhcml1cyB1cm5hIHNhZ2l0dGlzLiBMaWd1bGEgZHVpcyBlcmF0
|
||||
IGlwc3VtIHByZXRpdW0gZXUgZmFjaWxpc2lzLiBEdWlzIGxvYm9ydGlzIHByb2luIGZhY2lsaXNp
|
||||
IGFkIHN1c2NpcGl0IHZlc3RpYnVsdW0gaGFjIHRvcnRvciBpbnRlcmR1bS4gVWx0cmljZXMgdGlu
|
||||
Y2lkdW50IG1heGltdXMgYXB0ZW50IHBoYXNlbGx1cyBpbiB1bGxhbWNvcnBlciBuaXNsIHZhcml1
|
||||
cyBkaXMuIFZlbmVuYXRpcyBlbmltIHBvdGVudGkgcG90ZW50aSBzb2RhbGVzIG1hc3NhIGlkIGVs
|
||||
ZW1lbnR1bS4K
|
Loading…
Reference in New Issue
Block a user