fn decode_inner()

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)
}