qubit_codec_misc/
base64_quantum_codec.rs1use crate::{
11 MiscCodecError,
12 MiscCodecResult,
13 misc_codec_error::map_misc_decode_failure_with_consumed,
14};
15use core::num::NonZeroUsize;
16use qubit_codec::{
17 Codec,
18 DecodeFailure,
19};
20
21#[must_use]
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct Base64QuantumCodec {
29 url_safe: bool,
30}
31
32impl Base64QuantumCodec {
33 #[inline]
38 pub fn standard() -> Self {
39 Self { url_safe: false }
40 }
41
42 #[inline]
47 pub fn url_safe() -> Self {
48 Self { url_safe: true }
49 }
50
51 #[inline(always)]
56 fn alphabet(&self) -> &'static [u8; 64] {
57 if self.url_safe {
58 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
59 } else {
60 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
61 }
62 }
63
64 fn decode_unit(&self, unit: u8, index: usize) -> MiscCodecResult<u8> {
77 match unit {
78 b'A'..=b'Z' => Ok(unit - b'A'),
79 b'a'..=b'z' => Ok(unit - b'a' + 26),
80 b'0'..=b'9' => Ok(unit - b'0' + 52),
81 b'+' if !self.url_safe => Ok(62),
82 b'/' if !self.url_safe => Ok(63),
83 b'-' if self.url_safe => Ok(62),
84 b'_' if self.url_safe => Ok(63),
85 _ => Err(MiscCodecError::InvalidInput {
86 codec: "base64-quantum",
87 reason: format!(
88 "invalid Base64 unit '{}' at index {}",
89 char::from(unit),
90 index
91 ),
92 }),
93 }
94 }
95}
96
97impl Default for Base64QuantumCodec {
98 #[inline]
100 fn default() -> Self {
101 Self::standard()
102 }
103}
104
105impl Codec for Base64QuantumCodec {
106 type Value = [u8; 3];
107 type Unit = u8;
108 type DecodeError = MiscCodecError;
109 type EncodeError = MiscCodecError;
110
111 const MIN_UNITS_PER_VALUE: usize = 4;
112 const MAX_ENCODE_UNITS_PER_VALUE: usize = 4;
113
114 const MAX_DECODE_UNITS_PER_VALUE: usize = 4;
115
116 unsafe fn decode(
121 &mut self,
122 input: &[u8],
123 input_index: usize,
124 ) -> Result<([u8; 3], NonZeroUsize), DecodeFailure<Self::DecodeError>> {
125 debug_assert!(input_index + 4 <= input.len());
126
127 let first = self.decode_unit(input[input_index], input_index).map_err(
128 |error| {
129 map_misc_decode_failure_with_consumed(
130 error,
131 qubit_codec::nz!(4),
132 )
133 },
134 )?;
135 let second = self
136 .decode_unit(input[input_index + 1], input_index + 1)
137 .map_err(|error| {
138 map_misc_decode_failure_with_consumed(
139 error,
140 qubit_codec::nz!(4),
141 )
142 })?;
143 let third = self
144 .decode_unit(input[input_index + 2], input_index + 2)
145 .map_err(|error| {
146 map_misc_decode_failure_with_consumed(error, qubit_codec::nz!(4))
147 })?;
148 let fourth = self
149 .decode_unit(input[input_index + 3], input_index + 3)
150 .map_err(|error| {
151 map_misc_decode_failure_with_consumed(
152 error,
153 qubit_codec::nz!(4),
154 )
155 })?;
156 Ok((
157 [
158 (first << 2) | (second >> 4),
159 (second << 4) | (third >> 2),
160 (third << 6) | fourth,
161 ],
162 qubit_codec::nz!(4),
163 ))
164 }
165
166 unsafe fn encode(
171 &mut self,
172 value: &[u8; 3],
173 output: &mut [u8],
174 output_index: usize,
175 ) -> Result<usize, Self::EncodeError> {
176 debug_assert!(output_index + 4 <= output.len());
177
178 let alphabet = self.alphabet();
179 output[output_index] = alphabet[(value[0] >> 2) as usize];
180 output[output_index + 1] =
181 alphabet[(((value[0] & 0x03) << 4) | (value[1] >> 4)) as usize];
182 output[output_index + 2] =
183 alphabet[(((value[1] & 0x0f) << 2) | (value[2] >> 6)) as usize];
184 output[output_index + 3] = alphabet[(value[2] & 0x3f) as usize];
185 Ok(4)
186 }
187}