1#![doc=vsimd::shared_docs!()]
20#![cfg_attr(not(any(feature = "std", test)), no_std)]
22#![cfg_attr(feature = "unstable", feature(arm_target_feature))]
23#![cfg_attr(docsrs, feature(doc_cfg))]
24#![cfg_attr(test, deny(warnings))]
25#![deny(
27 missing_debug_implementations,
28 missing_docs,
29 clippy::all,
30 clippy::pedantic,
31 clippy::cargo,
32 clippy::missing_inline_in_public_items
33)]
34#![warn(clippy::todo)]
35#![allow(
36 clippy::inline_always,
37 clippy::wildcard_imports,
38 clippy::module_name_repetitions,
39 clippy::cast_sign_loss,
40 clippy::cast_possible_truncation,
41 clippy::cast_lossless,
42 clippy::cast_possible_wrap,
43 clippy::items_after_statements,
44 clippy::match_same_arms,
45 clippy::verbose_bit_mask
46)]
47
48#[cfg(feature = "alloc")]
49extern crate alloc;
50
51#[macro_use]
52mod error;
53pub use self::error::Error;
54
55mod alsw;
56mod ascii;
57mod check;
58mod decode;
59mod encode;
60
61mod multiversion;
62
63#[cfg(feature = "alloc")]
64mod heap;
65
66mod forgiving;
67pub use self::forgiving::*;
68
69pub use outref::{AsOut, Out};
70
71use crate::decode::decoded_length;
74use crate::encode::encoded_length_unchecked;
75
76use vsimd::tools::{slice_mut, slice_parts};
77
78#[cfg(feature = "alloc")]
79use alloc::{string::String, vec::Vec};
80
81const STANDARD_CHARSET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
82const URL_SAFE_CHARSET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
83
84#[derive(Debug)]
86pub struct Base64 {
87 config: Config,
88}
89
90#[derive(Debug, Clone, Copy)]
91enum Kind {
92 Standard,
93 UrlSafe,
94}
95
96#[derive(Debug, Clone, Copy)]
97struct Config {
98 kind: Kind,
99 extra: Extra,
100}
101
102#[derive(Debug, Clone, Copy)]
103enum Extra {
104 Pad,
105 NoPad,
106 Forgiving,
107}
108
109impl Extra {
110 #[inline(always)]
112 #[must_use]
113 const fn padding(self) -> bool {
114 match self {
115 Extra::Pad => true,
116 Extra::NoPad => false,
117 Extra::Forgiving => true,
118 }
119 }
120
121 #[inline(always)]
122 #[must_use]
123 const fn forgiving(self) -> bool {
124 match self {
125 Extra::Pad => false,
126 Extra::NoPad => false,
127 Extra::Forgiving => true,
128 }
129 }
130}
131
132pub const STANDARD: Base64 = Base64 {
134 config: Config {
135 kind: Kind::Standard,
136 extra: Extra::Pad,
137 },
138};
139
140pub const URL_SAFE: Base64 = Base64 {
142 config: Config {
143 kind: Kind::UrlSafe,
144 extra: Extra::Pad,
145 },
146};
147
148pub const STANDARD_NO_PAD: Base64 = Base64 {
150 config: Config {
151 kind: Kind::Standard,
152 extra: Extra::NoPad,
153 },
154};
155
156pub const URL_SAFE_NO_PAD: Base64 = Base64 {
158 config: Config {
159 kind: Kind::UrlSafe,
160 extra: Extra::NoPad,
161 },
162};
163
164const STANDARD_FORGIVING: Base64 = Base64 {
165 config: Config {
166 kind: Kind::Standard,
167 extra: Extra::Forgiving,
168 },
169};
170
171impl Base64 {
172 #[inline]
174 #[must_use]
175 pub const fn charset(&self) -> &[u8; 64] {
176 match self.config.kind {
177 Kind::Standard => STANDARD_CHARSET,
178 Kind::UrlSafe => URL_SAFE_CHARSET,
179 }
180 }
181
182 #[inline]
187 #[must_use]
188 pub const fn encoded_length(&self, n: usize) -> usize {
189 assert!(n <= usize::MAX / 2);
190 encoded_length_unchecked(n, self.config)
191 }
192
193 #[inline]
197 #[must_use]
198 pub const fn estimated_decoded_length(&self, n: usize) -> usize {
199 if n % 4 == 0 {
200 n / 4 * 3
201 } else {
202 (n / 4 + 1) * 3
203 }
204 }
205
206 #[inline]
213 pub fn decoded_length(&self, data: &[u8]) -> Result<usize, Error> {
214 let (_, m) = decoded_length(data, self.config)?;
215 Ok(m)
216 }
217
218 #[inline]
223 pub fn check(&self, data: &[u8]) -> Result<(), Error> {
224 let (n, _) = decoded_length(data, self.config)?;
225 unsafe { crate::multiversion::check::auto(data.as_ptr(), n, self.config) }
226 }
227
228 #[inline]
233 #[must_use]
234 pub fn encode<'d>(&self, src: &[u8], mut dst: Out<'d, [u8]>) -> &'d mut [u8] {
235 unsafe {
236 let m = encoded_length_unchecked(src.len(), self.config);
237 assert!(dst.len() >= m);
238
239 let (src, len) = slice_parts(src);
240 let dst = dst.as_mut_ptr();
241 self::multiversion::encode::auto(src, len, dst, self.config);
242
243 slice_mut(dst, m)
244 }
245 }
246
247 #[inline]
252 #[must_use]
253 pub fn encode_as_str<'d>(&self, src: &[u8], dst: Out<'d, [u8]>) -> &'d mut str {
254 let ans = self.encode(src, dst);
255 unsafe { core::str::from_utf8_unchecked_mut(ans) }
256 }
257
258 #[inline]
266 pub fn decode<'d>(&self, src: &[u8], mut dst: Out<'d, [u8]>) -> Result<&'d mut [u8], Error> {
267 unsafe {
268 let (n, m) = decoded_length(src, self.config)?;
269 assert!(dst.len() >= m);
270
271 let src = src.as_ptr();
272 let dst = dst.as_mut_ptr();
273 self::multiversion::decode::auto(src, dst, n, self.config)?;
274
275 Ok(slice_mut(dst, m))
276 }
277 }
278
279 #[inline]
284 pub fn decode_inplace<'d>(&self, data: &'d mut [u8]) -> Result<&'d mut [u8], Error> {
285 unsafe {
286 let (n, m) = decoded_length(data, self.config)?;
287
288 let dst: *mut u8 = data.as_mut_ptr();
289 let src: *const u8 = dst;
290 self::multiversion::decode::auto(src, dst, n, self.config)?;
291
292 Ok(slice_mut(dst, m))
293 }
294 }
295
296 #[inline]
298 #[must_use]
299 pub fn encode_type<T: FromBase64Encode>(&self, data: impl AsRef<[u8]>) -> T {
300 T::from_base64_encode(self, data.as_ref())
301 }
302
303 #[inline]
308 pub fn decode_type<T: FromBase64Decode>(&self, data: impl AsRef<[u8]>) -> Result<T, Error> {
309 T::from_base64_decode(self, data.as_ref())
310 }
311
312 #[inline]
314 pub fn encode_append<T: AppendBase64Encode>(&self, src: impl AsRef<[u8]>, dst: &mut T) {
315 T::append_base64_encode(self, src.as_ref(), dst);
316 }
317
318 #[inline]
323 pub fn decode_append<T: AppendBase64Decode>(&self, src: impl AsRef<[u8]>, dst: &mut T) -> Result<(), Error> {
324 T::append_base64_decode(self, src.as_ref(), dst)
325 }
326
327 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
329 #[cfg(feature = "alloc")]
330 #[inline]
331 #[must_use]
332 pub fn encode_to_string(&self, data: impl AsRef<[u8]>) -> String {
333 self.encode_type(data)
334 }
335
336 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
341 #[cfg(feature = "alloc")]
342 #[inline]
343 pub fn decode_to_vec(&self, data: impl AsRef<[u8]>) -> Result<Vec<u8>, Error> {
344 self.decode_type(data)
345 }
346}
347
348pub trait FromBase64Encode: Sized {
350 fn from_base64_encode(base64: &Base64, data: &[u8]) -> Self;
352}
353
354pub trait FromBase64Decode: Sized {
356 fn from_base64_decode(base64: &Base64, data: &[u8]) -> Result<Self, Error>;
361}
362
363pub trait AppendBase64Encode: FromBase64Encode {
365 fn append_base64_encode(base64: &Base64, src: &[u8], dst: &mut Self);
367}
368
369pub trait AppendBase64Decode: FromBase64Decode {
371 fn append_base64_decode(base64: &Base64, src: &[u8], dst: &mut Self) -> Result<(), Error>;
376}