basekit 0.5.0

Universal library for encoding in various bases
Documentation
use super::super::config::Base64DecodeConfig;
use super::super::error::Base64Error;

/// Decodes the remaining 1–3 base64 characters of a stream.
///
/// Validates padding position and returns the number of output bytes written.
#[inline(always)]
#[allow(unsafe_op_in_unsafe_fn)]
pub(crate) unsafe fn decode_tail_into(
    config: &Base64DecodeConfig,
    dst: &mut [u8],
    dst_offset: usize,
    tail: &[u8],
    src_offset: usize,
    padding_count: usize,
) -> Result<usize, Base64Error> {
    let tail_len = tail.len();

    if tail_len == 0 {
        return Ok(0);
    }

    let bytes_written = match padding_count {
        2 => Ok(1),
        1 => Ok(2),
        0 => match tail_len {
            1 => Ok(0),
            2 => Ok(1),
            3 => Ok(2),
            _ => Err(Base64Error::InvalidPadding),
        },
        _ => Err(Base64Error::InvalidPadding),
    };

    let decode_table = config.decode_table;
    let mut indices: [i8; 4] = [0; 4];
    for (j, &byte) in tail.iter().enumerate() {
        let pos = src_offset + j;

        if let Some(padding) = config.padding {
            if byte == padding {
                let expected_min_pos = 4 - padding_count;
                if j < expected_min_pos {
                    return Err(Base64Error::InvalidPadding);
                }
                indices[j] = 0;
            } else {
                if byte >= 128 {
                    return Err(Base64Error::InvalidCharacter(byte, pos));
                }
                let val = decode_table[byte as usize];
                if val < 0 {
                    return Err(Base64Error::InvalidCharacter(byte, pos));
                }
                indices[j] = val;
            }
        } else {
            if byte >= 128 {
                return Err(Base64Error::InvalidCharacter(byte, pos));
            }
            let val = decode_table[byte as usize];
            if val < 0 {
                return Err(Base64Error::InvalidCharacter(byte, pos));
            }
            indices[j] = val;
        }
    }

    let three_bytes = ((indices[0] as u32) << 18)
        | ((indices[1] as u32) << 12)
        | ((indices[2] as u32) << 6)
        | (indices[3] as u32);

    let bytes_written = bytes_written?;
    let ptr = dst.as_mut_ptr().add(dst_offset);

    match bytes_written {
        0 => {}
        1 => {
            ptr.write((three_bytes >> 16) as u8);
        }
        2 => {
            ptr.write((three_bytes >> 16) as u8);
            ptr.offset(1).write((three_bytes >> 8) as u8);
        }
        3 => {
            ptr.write((three_bytes >> 16) as u8);
            ptr.offset(1).write((three_bytes >> 8) as u8);
            ptr.offset(2).write(three_bytes as u8);
        }
        _ => unreachable!(),
    }

    Ok(bytes_written)
}