use core::num::NonZeroUsize;
use qubit_codec::Codec;
use qubit_codec::DecodeFailure;
use qubit_utils::nonzero;
use crate::MiscCodecError;
use crate::MiscCodecResult;
use crate::misc_codec_error::map_misc_decode_failure_with_consumed;
#[must_use]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Base64QuantumCodec {
url_safe: bool,
}
impl Base64QuantumCodec {
#[inline]
pub fn standard() -> Self {
Self { url_safe: false }
}
#[inline]
pub fn url_safe() -> Self {
Self { url_safe: true }
}
#[inline(always)]
fn alphabet(&self) -> &'static [u8; 64] {
if self.url_safe {
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
} else {
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
}
}
fn decode_unit(&self, unit: u8, index: usize) -> MiscCodecResult<u8> {
match unit {
b'A'..=b'Z' => Ok(unit - b'A'),
b'a'..=b'z' => Ok(unit - b'a' + 26),
b'0'..=b'9' => Ok(unit - b'0' + 52),
b'+' if !self.url_safe => Ok(62),
b'/' if !self.url_safe => Ok(63),
b'-' if self.url_safe => Ok(62),
b'_' if self.url_safe => Ok(63),
_ => Err(MiscCodecError::InvalidInput {
codec: "base64-quantum",
reason: format!("invalid Base64 unit '{}' at index {}", char::from(unit), index),
}),
}
}
}
impl Default for Base64QuantumCodec {
#[inline]
fn default() -> Self {
Self::standard()
}
}
impl Codec for Base64QuantumCodec {
type Value = [u8; 3];
type Unit = u8;
type DecodeError = MiscCodecError;
type EncodeError = MiscCodecError;
const MIN_UNITS_PER_VALUE: usize = 4;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 4;
const MAX_DECODE_UNITS_PER_VALUE: usize = 4;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<([u8; 3], NonZeroUsize), DecodeFailure<Self::DecodeError>> {
debug_assert!(input_index + 4 <= input.len());
let first = self
.decode_unit(input[input_index], input_index)
.map_err(|error| map_misc_decode_failure_with_consumed(error, nonzero(4)))?;
let second = self
.decode_unit(input[input_index + 1], input_index + 1)
.map_err(|error| map_misc_decode_failure_with_consumed(error, nonzero(4)))?;
let third = self
.decode_unit(input[input_index + 2], input_index + 2)
.map_err(|error| map_misc_decode_failure_with_consumed(error, nonzero(4)))?;
let fourth = self
.decode_unit(input[input_index + 3], input_index + 3)
.map_err(|error| map_misc_decode_failure_with_consumed(error, nonzero(4)))?;
Ok((
[
(first << 2) | (second >> 4),
(second << 4) | (third >> 2),
(third << 6) | fourth,
],
nonzero(4),
))
}
unsafe fn encode(
&mut self,
value: &[u8; 3],
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
debug_assert!(output_index + 4 <= output.len());
let alphabet = self.alphabet();
output[output_index] = alphabet[(value[0] >> 2) as usize];
output[output_index + 1] = alphabet[(((value[0] & 0x03) << 4) | (value[1] >> 4)) as usize];
output[output_index + 2] = alphabet[(((value[1] & 0x0f) << 2) | (value[2] >> 6)) as usize];
output[output_index + 3] = alphabet[(value[2] & 0x3f) as usize];
Ok(4)
}
}