in rust-runtime/aws-smithy-types/src/base64.rs [109:152]
fn decode_inner(inp: &str) -> Result<Vec<u8>, DecodeError> {
// one base64 character is only 6 bits so it can't produce valid data.
if inp.len() == 1 {
return Err(DecodeError::InvalidLength);
}
// when there's padding, we might slightly over allocate but it significantly simplifies
// the code to just ignore it.
let mut ret = Vec::with_capacity((inp.len() + 3) / 4 * 3);
// 4 base-64 characters = 3 bytes
// 1. Break the input into 4 character segments
// 2. Write those segments into an i32
// 3. Read u8s back out of the i32
let chunks = inp.as_bytes().chunks(4);
let mut padding = 0;
for chunk in chunks {
// padding should only be set on the last input
if padding != 0 {
return Err(DecodeError::InvalidPadding);
}
let mut block = 0_i32;
for (idx, chunk) in chunk.iter().enumerate() {
let bits = BASE64_DECODE_TABLE[*chunk as usize].ok_or(DecodeError::InvalidByte)?;
if bits == 0xFF {
padding += 1;
} else if padding > 0 {
// Once you've started padding, you can't stop.
return Err(DecodeError::InvalidPadding);
}
block |= (bits as i32) << (18 - (idx * 6));
}
// if we got a short slice, its because of implied padding
let missing_chars = 4 - chunk.len();
for i in (padding + missing_chars..3).rev() {
let byte = ((block >> (i * 8)) & 0xFF) as u8;
ret.push(byte)
}
}
// The code is much simpler if we _slightly_ over allocate in certain cases
debug_assert!(ret.capacity() - ret.len() < 4);
Ok(ret)
}