1use std::{marker::PhantomData, ops};
4
5use super::{NumStr, StringExtT, StringT};
6
7pub mod b64_padding {
8 use super::{
14 Base64EncoderT, Base64Str, Decode, DecodeToAny, DecodeToHex, Encode, NumStr, PhantomData,
15 StringExtT, StringT,
16 };
17
18 macro_rules! enum_padding {
19 ($($name:ident) *) => {
20 $(
21 #[derive(Debug)]
22 #[allow(non_camel_case_types)]
23 #[doc = "Base64 Padding: "]
24 #[doc = stringify!($name) ]
25 pub struct $name;
26
27 impl<T: AsRef<[u8]>> Base64EncoderT for Base64Str<T, $name, Encode> {}
28
29 impl $name {
30 #[inline]
31 pub fn encode<T: AsRef<[u8]>>(inner: T) -> Base64Str<T, $name, Encode> {
33 Base64Str {
34 inner,
35 padding: PhantomData,
36 command: PhantomData,
37 }
38 }
39
40 #[inline]
41 pub fn decode<T: AsRef<[u8]>>(inner: T) -> Base64Str<T, $name, Decode> {
46 Base64Str {
47 inner,
48 padding: PhantomData,
49 command: PhantomData,
50 }
51 }
52
53 #[allow(unsafe_code)]
54 #[inline]
55 pub unsafe fn decode_to_any<T: AsRef<[u8]>>(inner: T) -> Base64Str<T, $name, DecodeToAny> {
61 Base64Str {
62 inner,
63 padding: PhantomData,
64 command: PhantomData,
65 }
66 }
67
68 #[inline]
69 pub fn decode_to_hex<T: AsRef<[u8]>>(inner: T) -> Base64Str<T, $name, DecodeToHex> {
73 Base64Str {
74 inner,
75 padding: PhantomData,
76 command: PhantomData,
77 }
78 }
79 }
80
81 impl<T: AsRef<[u8]>> StringT for Base64Str<T, $name, Encode> {
82 #[inline]
83 fn encode_to_buf(self, string: &mut Vec<u8>) {
84 let inner = self.inner.as_ref();
85
86 let current_len = string.len();
87 let base64_len = inner.len() * 4 / 3 + 4;
88 let target_len = current_len + base64_len;
89
90 string.reserve_exact(base64_len);
91 #[allow(unsafe_code)]
92 unsafe {
94 string.set_len(target_len);
95 }
96
97 let bytes_written = base64::Engine::encode_slice(
98 &base64::engine::general_purpose::$name,
99 self.inner,
100 &mut string[current_len..target_len],
101 )
102 .unwrap_or(0);
103
104 string.truncate(current_len + bytes_written);
105 }
106
107 #[inline]
108 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
109 self.encode_to_buf(string);
110 string.extend(separator.as_bytes());
111 }
112
113 #[inline]
114 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
115 let inner = self.inner.as_ref();
116
117 let current_len = string.len();
118 let base64_len = inner.len() * 4 / 3 + 4;
119 let target_len = current_len + base64_len;
120
121 string.reserve(base64_len);
122 #[allow(unsafe_code)]
123 unsafe {
125 string.set_len(target_len);
126 }
127
128 let bytes_written = base64::Engine::encode_slice(
129 &base64::engine::general_purpose::$name,
130 self.inner,
131 &mut string[current_len..target_len],
132 )
133 .unwrap_or(0);
134
135 string.truncate(current_len + bytes_written);
136 }
137
138 #[inline]
139 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
140 self.encode_to_bytes_buf(string);
141 string.extend(separator.as_bytes());
142 }
143 }
144
145 impl<T: AsRef<[u8]>> StringExtT for Base64Str<T, $name, Encode> {}
146
147 impl<T: AsRef<[u8]>> StringT for Base64Str<T, $name, Decode> {
148 #[inline]
149 fn encode_to_buf(self, string: &mut Vec<u8>) {
150 use base64::Engine;
151
152 let data = base64::engine::general_purpose::$name
153 .decode(self.inner.as_ref())
154 .unwrap_or_default();
155
156 if std::str::from_utf8(&data).is_ok() {
157 string.extend(data)
158 }
159 }
160
161 #[inline]
162 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
163 self.encode_to_buf(string);
164 string.extend(separator.as_bytes());
165 }
166
167 #[inline]
168 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
169 use base64::Engine;
170
171 let data = base64::engine::general_purpose::$name
172 .decode(self.inner.as_ref())
173 .unwrap_or_default();
174
175 if std::str::from_utf8(&data).is_ok() {
176 string.extend(data)
177 }
178 }
179
180 #[inline]
181 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
182 self.encode_to_bytes_buf(string);
183 string.extend(separator.as_bytes());
184 }
185 }
186
187 impl<T: AsRef<[u8]>> StringExtT for Base64Str<T, $name, Decode> {}
188
189 impl<T: AsRef<[u8]>> StringT for Base64Str<T, $name, DecodeToAny> {
190 #[inline]
191 fn encode_to_buf(self, string: &mut Vec<u8>) {
192 use base64::Engine;
193
194 let _ = base64::engine::general_purpose::$name
195 .decode_vec(self.inner.as_ref(), string);
196 }
197
198 #[inline]
199 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
200 self.encode_to_buf(string);
201 string.extend(separator.as_bytes());
202 }
203
204 #[inline]
205 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
206 use base64::Engine;
207
208 if let Ok(data) = base64::engine::general_purpose::$name.decode(self.inner.as_ref()) {
209 string.extend(data);
210 }
211 }
212
213 #[inline]
214 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
215 self.encode_to_bytes_buf(string);
216 string.extend(separator.as_bytes());
217 }
218 }
219
220 impl<T: AsRef<[u8]>> StringExtT for Base64Str<T, $name, DecodeToAny> {}
221
222 impl<T: AsRef<[u8]>> StringT for Base64Str<T, $name, DecodeToHex> {
223 #[inline]
224 fn encode_to_buf(self, string: &mut Vec<u8>) {
225 use base64::Engine;
226
227 base64::engine::general_purpose::$name
228 .decode(self.inner.as_ref())
229 .unwrap_or_default()
230 .into_iter()
231 .map(NumStr::hex_byte_default)
232 .encode_to_buf(string);
233 }
234
235 #[inline]
236 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
237 self.encode_to_buf(string);
238 string.extend(separator.as_bytes());
239 }
240
241 #[inline]
242 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
243 use base64::Engine;
244
245 base64::engine::general_purpose::$name
246 .decode(self.inner.as_ref())
247 .unwrap_or_default()
248 .into_iter()
249 .map(NumStr::hex_byte_default)
250 .encode_to_bytes_buf(string);
251 }
252
253 #[inline]
254 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
255 self.encode_to_bytes_buf(string);
256 string.extend(separator.as_bytes());
257 }
258 }
259
260 impl<T: AsRef<[u8]>> StringExtT for Base64Str<T, $name, DecodeToHex> {}
261 )*
262 };
263 }
264
265 enum_padding!(STANDARD STANDARD_NO_PAD URL_SAFE URL_SAFE_NO_PAD);
266}
267
268pub trait Base64EncoderT: StringExtT {}
270
271#[derive(Debug)]
272pub struct Encode;
274
275#[derive(Debug)]
276pub struct Decode;
280
281#[derive(Debug)]
282pub struct DecodeToAny;
286
287#[derive(Debug)]
288pub struct DecodeToHex;
292
293#[derive(Debug)]
294#[repr(transparent)]
295pub struct Base64Str<T: AsRef<[u8]>, P = b64_padding::STANDARD, C = Encode> {
301 inner: T,
302 padding: PhantomData<P>,
303 command: PhantomData<C>,
304}
305
306impl<T: AsRef<[u8]>, P, C> ops::Deref for Base64Str<T, P, C> {
307 type Target = T;
308
309 fn deref(&self) -> &Self::Target {
310 &self.inner
311 }
312}
313
314impl<T: AsRef<[u8]>, P, C> AsRef<[u8]> for Base64Str<T, P, C> {
315 fn as_ref(&self) -> &[u8] {
316 self.inner.as_ref()
317 }
318}
319
320#[allow(unsafe_code)]
321#[cfg(test)]
322mod test {
323 use super::*;
324
325 #[test]
326 fn test_base64() {
327 assert_eq!(
328 b64_padding::STANDARD::encode(b"hello world").to_string_ext(),
329 "aGVsbG8gd29ybGQ="
330 );
331 assert_eq!(
332 b64_padding::STANDARD::encode(b"hello world").to_string_ext(),
333 "aGVsbG8gd29ybGQ="
334 );
335 assert_eq!(
336 b64_padding::STANDARD::encode("hello world").to_string_ext(),
337 "aGVsbG8gd29ybGQ="
338 );
339 assert_eq!(
340 b64_padding::STANDARD::encode("hello world").to_string_ext(),
341 "aGVsbG8gd29ybGQ="
342 );
343 assert_eq!(
344 b64_padding::STANDARD::decode(b"aGVsbG8gd29ybGQ=").to_string_ext(),
345 "hello world"
346 );
347 assert_eq!(
348 unsafe { b64_padding::STANDARD::decode_to_any(b"aGVsbG8gd29ybGQ=") }.to_string_ext(),
349 "hello world"
350 );
351 assert_eq!(
352 b64_padding::STANDARD::decode_to_hex(
353 b64_padding::STANDARD::encode(vec![0x11, 0x45, 0x14, 0x19, 0x19, 0x81, 0x00])
354 .to_string_ext()
355 )
356 .to_string_ext(),
357 "11451419198100"
358 );
359 }
360}