Use HexBytes traits for base64

This commit is contained in:
Eryn Wells 2018-03-25 18:50:45 -04:00
parent 2621f30830
commit af8c16dcc4
2 changed files with 106 additions and 29 deletions

View file

@ -1,32 +1,37 @@
use hex::{AsHexBytes, HexResult};
static B64: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\\"; static B64: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\\";
pub fn base64(hex: &str) -> Result<String, String> { pub fn base64(hex: &str) -> Result<String, String> {
let mut out = String::from(""); let mut out = String::from("");
let mut num_bits = 0; let mut num_bits = 0;
let mut acc: u32 = 0; let mut acc: u32 = 0;
for (idx, c) in hex.char_indices() { for (idx, c) in hex.hex_bytes().enumerate() {
if let Some(c_digit) = c.to_digit(16) { match c {
// Accumulate bytes until we have 6 chunks of 4. HexResult::Byte(c) => {
acc = (acc << 4) + c_digit; // Accumulate bytes until we have 6 chunks of 4.
num_bits += 4; acc = (acc << 4) + (c as u32);
if idx % 6 != 5 { num_bits += 4;
continue; if idx % 6 != 5 {
} continue;
// Read out 4 chunks of 6.
for i in (0..4).rev() {
let out_char_idx = ((acc >> (6 * i)) & 0x3F) as usize;
// TODO: I don't like this nth() call.
if let Some(out_char) = B64.chars().nth(out_char_idx) {
out.push(out_char);
} else {
return Err(format!("Couldn't make output char from {}", out_char_idx));
} }
}
acc = 0; // Read out 4 chunks of 6.
num_bits = 0; for i in (0..4).rev() {
} else { let out_char_idx = ((acc >> (6 * i)) & 0x3F) as usize;
return Err(format!("Invalid input: {}", c)); // TODO: I don't like this nth() call.
if let Some(out_char) = B64.chars().nth(out_char_idx) {
out.push(out_char);
} else {
return Err(format!("Couldn't make output char from {}", out_char_idx));
}
}
acc = 0;
num_bits = 0;
},
HexResult::Invalid(c) => {
return Err(format!("Invalid input: {}", c));
},
} }
} }

View file

@ -1,3 +1,4 @@
use std::char;
use std::iter; use std::iter;
use std::str::Chars; use std::str::Chars;
@ -6,17 +7,21 @@ pub enum HexResult {
Invalid(char), Invalid(char),
} }
pub struct HexDecoder<'a> { pub struct HexBytes<'a> {
input: Chars<'a>, input: Chars<'a>,
} }
impl<'a> HexDecoder<'a> { impl<'a> HexBytes<'a> {
fn new(input: &'a str) -> HexDecoder { fn new(input: &'a str) -> HexBytes {
HexDecoder { input: input.chars() } HexBytes { input: input.chars() }
}
pub fn valid(self) -> ValidHexBytes<'a> {
ValidHexBytes::new(self)
} }
} }
impl<'a> iter::Iterator for HexDecoder<'a> { impl<'a> iter::Iterator for HexBytes<'a> {
type Item = HexResult; type Item = HexResult;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -32,14 +37,81 @@ impl<'a> iter::Iterator for HexDecoder<'a> {
} }
} }
/*
* ValidHexBytes --
*/
pub struct ValidHexBytes<'a> {
input: HexBytes<'a>,
}
impl<'a> ValidHexBytes<'a> {
fn new(input: HexBytes) -> ValidHexBytes {
ValidHexBytes { input: input }
}
}
impl<'a> iter::Iterator for ValidHexBytes<'a> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.input.next() {
Some(hex_c) => match hex_c {
HexResult::Byte(c) => return Some(c),
_ => continue,
},
None => return None,
}
}
}
}
/*
* HexDigest --
*/
pub trait HexDigest {
fn hex_digest(self) -> String;
}
impl<'a> HexDigest for HexBytes<'a> {
fn hex_digest(self) -> String {
self.valid().hex_digest()
}
}
impl<'a> HexDigest for ValidHexBytes<'a> {
fn hex_digest(self) -> String {
self.map(|x| char::from_digit(x as u32, 16).unwrap()).collect()
}
}
impl HexDigest for Vec<u8> {
fn hex_digest(self) -> String {
self.into_iter().map(|x| char::from_digit(x as u32, 16).unwrap()).collect()
}
}
/*
* AsHexbytes --
*/
pub trait AsHexBytes<'a> {
fn hex_bytes(&self) -> HexBytes<'a>;
}
impl<'a> AsHexBytes<'a> for &'a str {
fn hex_bytes(&self) -> HexBytes<'a> { HexBytes::new(self) }
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn simple() { fn simple() {
let decoder = HexDecoder::new("123"); let collected: Vec<u8> = "123".hex_bytes().map(|c| match c {
let collected: Vec<u8> = decoder.map(|c| match c {
HexResult::Byte(c) => c, HexResult::Byte(c) => c,
_ => panic!(), _ => panic!(),
}).collect(); }).collect();