From 36e06e5570c217127e047e5224c0479394d101c4 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 25 Mar 2018 16:42:25 -0400 Subject: [PATCH] Move base64 and tests to b64 module --- src/b64.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 92 +----------------------------------------------------- 2 files changed, 92 insertions(+), 91 deletions(-) create mode 100644 src/b64.rs diff --git a/src/b64.rs b/src/b64.rs new file mode 100644 index 0000000..9c3985c --- /dev/null +++ b/src/b64.rs @@ -0,0 +1,91 @@ +static B64: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\\"; + +pub fn base64(hex: &str) -> Result { + let mut out = String::from(""); + let mut num_bits = 0; + let mut acc: u32 = 0; + for (idx, c) in hex.char_indices() { + if let Some(c_digit) = c.to_digit(16) { + // Accumulate bytes until we have 6 chunks of 4. + acc = (acc << 4) + c_digit; + num_bits += 4; + 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; + num_bits = 0; + } else { + return Err(format!("Invalid input: {}", c)); + } + } + + if acc != 0 { + // Pad the string if we have bits remaining. + acc = acc << (24 - num_bits); + let padding = (24 - num_bits) / 6; + for i in (0..4).rev() { + let out_char_idx = ((acc >> (6 * i)) & 0x3F) as usize; + if i < padding { + out.push('='); + } else 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)); + } + } + } + + Ok(out) +} + +#[cfg(test)] +mod tests { + use super::base64; + + #[test] + fn small_wikipedia_example() { + let input = "4d616e"; + let ex_output = "TWFu"; + println!(""); + let output = base64(input).expect("Error converting to base64"); + assert_eq!(output, ex_output); + } + + #[test] + fn one_byte_padding() { + let input = "6f6d"; + let ex_output = "b20="; + println!(""); + let output = base64(input).expect("Error converting to base64"); + assert_eq!(output, ex_output); + } + + #[test] + fn two_byte_padding() { + let input = "6f"; + let ex_output = "bw=="; + println!(""); + let output = base64(input).expect("Error converting to base64"); + assert_eq!(output, ex_output); + } + + #[test] + fn cryptopals_input() { + let input = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"; + let ex_output = "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"; + println!(""); + let output = base64(input).expect("Error converting to base64"); + assert_eq!(output, ex_output); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9c3985c..17ff39e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,91 +1 @@ -static B64: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\\"; - -pub fn base64(hex: &str) -> Result { - let mut out = String::from(""); - let mut num_bits = 0; - let mut acc: u32 = 0; - for (idx, c) in hex.char_indices() { - if let Some(c_digit) = c.to_digit(16) { - // Accumulate bytes until we have 6 chunks of 4. - acc = (acc << 4) + c_digit; - num_bits += 4; - 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; - num_bits = 0; - } else { - return Err(format!("Invalid input: {}", c)); - } - } - - if acc != 0 { - // Pad the string if we have bits remaining. - acc = acc << (24 - num_bits); - let padding = (24 - num_bits) / 6; - for i in (0..4).rev() { - let out_char_idx = ((acc >> (6 * i)) & 0x3F) as usize; - if i < padding { - out.push('='); - } else 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)); - } - } - } - - Ok(out) -} - -#[cfg(test)] -mod tests { - use super::base64; - - #[test] - fn small_wikipedia_example() { - let input = "4d616e"; - let ex_output = "TWFu"; - println!(""); - let output = base64(input).expect("Error converting to base64"); - assert_eq!(output, ex_output); - } - - #[test] - fn one_byte_padding() { - let input = "6f6d"; - let ex_output = "b20="; - println!(""); - let output = base64(input).expect("Error converting to base64"); - assert_eq!(output, ex_output); - } - - #[test] - fn two_byte_padding() { - let input = "6f"; - let ex_output = "bw=="; - println!(""); - let output = base64(input).expect("Error converting to base64"); - assert_eq!(output, ex_output); - } - - #[test] - fn cryptopals_input() { - let input = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"; - let ex_output = "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"; - println!(""); - let output = base64(input).expect("Error converting to base64"); - assert_eq!(output, ex_output); - } -} +pub mod b64;