Skip to content

Instantly share code, notes, and snippets.

@HanEmile
Last active March 27, 2024 15:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HanEmile/d805450d52b223c9de538ad823729868 to your computer and use it in GitHub Desktop.
Save HanEmile/d805450d52b223c9de538ad823729868 to your computer and use it in GitHub Desktop.
base64 implemented in rust
/// base64 can be generated by taking chunks of 3 bytes from the input and
/// mapping them onto 4 output chunks as shown below:
///
/// ```raw
/// | M | a | n | ascii
/// | 77 | 97 | 110 | decimal
/// | 0x4d | 0x61 | 0x6e | hex
/// | | | |
/// | a | b | c | chunk name
/// | | | |
/// |0 1 0 0 1 1 0 1|0 1 1 0 0 0 0 1|0 1 1 0 1 1 1 0| binary
/// |0 1 0 0 1 1|0 1 0 1 1 0|0 0 0 1 0 1|1 0 1 1 1 0| binary
/// | | | | |
/// | T | W | F | u | ascii
/// | 84 | 97 | 70 | 117 | decimal
/// | 0x54 | 0x57 | 0x46 | 0x75 | hex
/// | | | | |
/// | d | e | f | g | chunk name
/// ```
///
/// So "Man" is equal to "TWFU" in base64.
///
/// ```bash
/// ; echo -n "Man" | base64
/// TWFu
/// ```
pub fn bytes_to_base64(bytes: Vec<u8>) -> String {
let mut result = String::new();
let alphabet: Vec<_> =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
.chars().collect();
for chunk in bytes.chunks(3) {
let a = chunk[0];
// get the bits 8 to 2 and shift them to the right by to in order to
// get the d we can "mask off" the bits we need using the and operation
// and then define the bits using hex (0xfc == 0b11111100).
let d = (a & 0xfc) >> 2;
result.push(alphabet[d as usize]);
// get the 'a' part of e (the lower two bits of a), as we're unsure if
// there actually is a 'b' we can use for the other part, the following
// condition is based on this and continues "normally" if there is and
// just pushes e1 as the new index if there is nothing following (and
// we pad with two equal signs)
let e1 = (a & 0x3) << 4;
if let Some(b) = chunk.get(1) {
// now we're doing the about two times combining both: first, mask
// off the first two bits (this is e1) of a using 0x3 (remember:
// 0x3 == 0b11). Then do the same for the last four bits of b (0xf0
// == 0b11110000), shift that part to the right and combine them
// both using the or operation.
let e = e1 | ((b & 0xf0) >> 4);
result.push(alphabet[e as usize]);
// as with e1, we have to compute f split up into two parts
let f1 = (b & 0xf) << 2;
if let Some(c) = chunk.get(2) {
// lower four bits of b (f1) combined with the highest two bits
// of c
let f = f1 | (c & 0xc0) >> 6;
result.push(alphabet[f as usize]);
// lower six bits of c
let g = c & 0x3f;
result.push(alphabet[g as usize]);
} else {
result.push(alphabet[f1 as usize]);
result.push('=');
}
} else {
result.push(alphabet[e1 as usize]);
result.push_str("==");
}
}
result
}
fn main() {
let a = bytes_to_base64(vec![0x4d, 0x61, 0x6e, 0x41]);
println!("{}", a);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment