1use crate::alphabet::Symbol;
2#[cfg(any(feature = "alloc", test))]
3use crate::engine::general_purpose::STANDARD;
4use crate::engine::{Config, Engine};
5#[cfg(any(feature = "alloc", test))]
6use alloc::string::String;
7use core::fmt;
8#[cfg(any(feature = "std", test))]
9use std::error;
10
11#[allow(unused)]
15#[deprecated(since = "0.21.0", note = "Use Engine::encode")]
16#[cfg(any(feature = "alloc", test))]
17pub fn encode<T: AsRef<[u8]>>(input: T) -> String {
18 STANDARD.encode(input)
19}
20
21#[allow(unused)]
25#[deprecated(since = "0.21.0", note = "Use Engine::encode")]
26#[cfg(any(feature = "alloc", test))]
27pub fn encode_engine<E: Engine, T: AsRef<[u8]>>(input: T, engine: &E) -> String {
28 engine.encode(input)
29}
30
31#[allow(unused)]
35#[deprecated(since = "0.21.0", note = "Use Engine::encode_string")]
36#[cfg(any(feature = "alloc", test))]
37pub fn encode_engine_string<E: Engine, T: AsRef<[u8]>>(
38 input: T,
39 output_buf: &mut String,
40 engine: &E,
41) {
42 engine.encode_string(input, output_buf);
43}
44
45#[allow(unused)]
49#[deprecated(since = "0.21.0", note = "Use Engine::encode_slice")]
50pub fn encode_engine_slice<E: Engine, T: AsRef<[u8]>>(
51 input: T,
52 output_buf: &mut [u8],
53 engine: &E,
54) -> Result<usize, EncodeSliceError> {
55 engine.encode_slice(input, output_buf)
56}
57
58pub(crate) fn encode_with_padding<E: Engine + ?Sized>(
69 input: &[u8],
70 output: &mut [u8],
71 engine: &E,
72 expected_encoded_size: usize,
73) {
74 debug_assert_eq!(expected_encoded_size, output.len());
75
76 let b64_bytes_written = engine.internal_encode(input, output);
77
78 let padding_bytes = if engine.config().encode_padding() {
79 add_padding(
80 b64_bytes_written,
81 engine.padding(),
82 &mut output[b64_bytes_written..],
83 )
84 } else {
85 0
86 };
87
88 let encoded_bytes = b64_bytes_written
89 .checked_add(padding_bytes)
90 .expect("usize overflow when calculating b64 length");
91
92 debug_assert_eq!(expected_encoded_size, encoded_bytes);
93}
94
95#[must_use]
101pub const fn encoded_len(bytes_len: usize, padding: bool) -> Option<usize> {
102 let rem = bytes_len % 3;
103
104 let complete_input_chunks = bytes_len / 3;
105 let complete_chunk_output =
108 if let Some(complete_chunk_output) = complete_input_chunks.checked_mul(4) {
109 complete_chunk_output
110 } else {
111 return None;
112 };
113
114 if rem > 0 {
115 if padding {
116 complete_chunk_output.checked_add(4)
117 } else {
118 let encoded_rem = match rem {
119 1 => 2,
120 _ => 3,
123 };
124 complete_chunk_output.checked_add(encoded_rem)
125 }
126 } else {
127 Some(complete_chunk_output)
128 }
129}
130
131pub(crate) fn add_padding(unpadded_output_len: usize, padding: Symbol, output: &mut [u8]) -> usize {
137 let pad_bytes = (4 - (unpadded_output_len % 4)) % 4;
138 #[allow(clippy::needless_range_loop)]
141 for i in 0..pad_bytes {
142 output[i] = padding.as_u8();
143 }
144
145 pad_bytes
146}
147
148#[derive(Clone, Debug, PartialEq, Eq)]
150pub enum EncodeSliceError {
151 OutputSliceTooSmall,
153}
154
155impl fmt::Display for EncodeSliceError {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 match self {
158 Self::OutputSliceTooSmall => write!(f, "Output slice too small"),
159 }
160 }
161}
162
163#[cfg(any(feature = "std", test))]
164impl error::Error for EncodeSliceError {}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 use crate::alphabet::PADDING_SYMBOL;
171 use crate::{
172 alphabet,
173 engine::general_purpose::{GeneralPurpose, NO_PAD, STANDARD},
174 tests::{assert_encode_sanity, random_config, random_engine},
175 };
176 use rand::distr::{Distribution, Uniform};
177 use rand::{rngs, RngExt};
178 use std::str;
179
180 const URL_SAFE_NO_PAD_ENGINE: GeneralPurpose = GeneralPurpose::new(&alphabet::URL_SAFE, NO_PAD);
181
182 #[test]
183 fn encoded_size_correct_standard() {
184 assert_encoded_length(0, 0, &STANDARD, true);
185
186 assert_encoded_length(1, 4, &STANDARD, true);
187 assert_encoded_length(2, 4, &STANDARD, true);
188 assert_encoded_length(3, 4, &STANDARD, true);
189
190 assert_encoded_length(4, 8, &STANDARD, true);
191 assert_encoded_length(5, 8, &STANDARD, true);
192 assert_encoded_length(6, 8, &STANDARD, true);
193
194 assert_encoded_length(7, 12, &STANDARD, true);
195 assert_encoded_length(8, 12, &STANDARD, true);
196 assert_encoded_length(9, 12, &STANDARD, true);
197
198 assert_encoded_length(54, 72, &STANDARD, true);
199
200 assert_encoded_length(55, 76, &STANDARD, true);
201 assert_encoded_length(56, 76, &STANDARD, true);
202 assert_encoded_length(57, 76, &STANDARD, true);
203
204 assert_encoded_length(58, 80, &STANDARD, true);
205 }
206
207 #[test]
208 fn encoded_size_correct_no_pad() {
209 assert_encoded_length(0, 0, &URL_SAFE_NO_PAD_ENGINE, false);
210
211 assert_encoded_length(1, 2, &URL_SAFE_NO_PAD_ENGINE, false);
212 assert_encoded_length(2, 3, &URL_SAFE_NO_PAD_ENGINE, false);
213 assert_encoded_length(3, 4, &URL_SAFE_NO_PAD_ENGINE, false);
214
215 assert_encoded_length(4, 6, &URL_SAFE_NO_PAD_ENGINE, false);
216 assert_encoded_length(5, 7, &URL_SAFE_NO_PAD_ENGINE, false);
217 assert_encoded_length(6, 8, &URL_SAFE_NO_PAD_ENGINE, false);
218
219 assert_encoded_length(7, 10, &URL_SAFE_NO_PAD_ENGINE, false);
220 assert_encoded_length(8, 11, &URL_SAFE_NO_PAD_ENGINE, false);
221 assert_encoded_length(9, 12, &URL_SAFE_NO_PAD_ENGINE, false);
222
223 assert_encoded_length(54, 72, &URL_SAFE_NO_PAD_ENGINE, false);
224
225 assert_encoded_length(55, 74, &URL_SAFE_NO_PAD_ENGINE, false);
226 assert_encoded_length(56, 75, &URL_SAFE_NO_PAD_ENGINE, false);
227 assert_encoded_length(57, 76, &URL_SAFE_NO_PAD_ENGINE, false);
228
229 assert_encoded_length(58, 78, &URL_SAFE_NO_PAD_ENGINE, false);
230 }
231
232 #[test]
233 fn encoded_size_overflow() {
234 assert_eq!(None, encoded_len(usize::MAX, true));
235 }
236
237 #[test]
238 fn encode_engine_string_into_nonempty_buffer_doesnt_clobber_prefix() {
239 let mut orig_data = Vec::new();
240 let mut prefix = String::new();
241 let mut encoded_data_no_prefix = String::new();
242 let mut encoded_data_with_prefix = String::new();
243 let mut decoded = Vec::new();
244
245 let prefix_len_range = Uniform::new(0, 1000).unwrap();
246 let input_len_range = Uniform::new(0, 1000).unwrap();
247
248 let mut rng = rand::make_rng::<rngs::SmallRng>();
249
250 for _ in 0..10_000 {
251 orig_data.clear();
252 prefix.clear();
253 encoded_data_no_prefix.clear();
254 encoded_data_with_prefix.clear();
255 decoded.clear();
256
257 let input_len = input_len_range.sample(&mut rng);
258
259 for _ in 0..input_len {
260 orig_data.push(rng.random());
261 }
262
263 let prefix_len = prefix_len_range.sample(&mut rng);
264 for _ in 0..prefix_len {
265 prefix.push('#');
268 }
269 encoded_data_with_prefix.push_str(&prefix);
270
271 let engine = random_engine(&mut rng);
272 engine.encode_string(&orig_data, &mut encoded_data_no_prefix);
273 engine.encode_string(&orig_data, &mut encoded_data_with_prefix);
274
275 assert_eq!(
276 encoded_data_no_prefix.len() + prefix_len,
277 encoded_data_with_prefix.len()
278 );
279 assert_encode_sanity(&encoded_data_no_prefix, &engine, input_len);
280 assert_encode_sanity(&encoded_data_with_prefix[prefix_len..], &engine, input_len);
281
282 prefix.push_str(&encoded_data_no_prefix);
284
285 assert_eq!(prefix, encoded_data_with_prefix);
286
287 engine
288 .decode_vec(&encoded_data_no_prefix, &mut decoded)
289 .unwrap();
290 assert_eq!(orig_data, decoded);
291 }
292 }
293
294 #[test]
295 fn encode_engine_slice_into_nonempty_buffer_doesnt_clobber_suffix() {
296 let mut orig_data = Vec::new();
297 let mut encoded_data = Vec::new();
298 let mut encoded_data_original_state = Vec::new();
299 let mut decoded = Vec::new();
300
301 let input_len_range = Uniform::new(0, 1000).unwrap();
302
303 let mut rng = rand::make_rng::<rngs::SmallRng>();
304
305 for _ in 0..10_000 {
306 orig_data.clear();
307 encoded_data.clear();
308 encoded_data_original_state.clear();
309 decoded.clear();
310
311 let input_len = input_len_range.sample(&mut rng);
312
313 for _ in 0..input_len {
314 orig_data.push(rng.random());
315 }
316
317 for _ in 0..10 * input_len {
319 encoded_data.push(rng.random());
320 }
321
322 encoded_data_original_state.extend_from_slice(&encoded_data);
323
324 let engine = random_engine(&mut rng);
325
326 let encoded_size = encoded_len(input_len, engine.config().encode_padding()).unwrap();
327
328 assert_eq!(
329 encoded_size,
330 engine.encode_slice(&orig_data, &mut encoded_data).unwrap()
331 );
332
333 assert_encode_sanity(
334 str::from_utf8(&encoded_data[0..encoded_size]).unwrap(),
335 &engine,
336 input_len,
337 );
338
339 assert_eq!(
340 &encoded_data[encoded_size..],
341 &encoded_data_original_state[encoded_size..]
342 );
343
344 engine
345 .decode_vec(&encoded_data[0..encoded_size], &mut decoded)
346 .unwrap();
347 assert_eq!(orig_data, decoded);
348 }
349 }
350
351 #[test]
352 fn encode_to_slice_random_valid_utf8() {
353 let mut input = Vec::new();
354 let mut output = Vec::new();
355
356 let input_len_range = Uniform::new(0, 1000).unwrap();
357
358 let mut rng = rand::make_rng::<rngs::SmallRng>();
359
360 for _ in 0..10_000 {
361 input.clear();
362 output.clear();
363
364 let input_len = input_len_range.sample(&mut rng);
365
366 for _ in 0..input_len {
367 input.push(rng.random());
368 }
369
370 let config = random_config(&mut rng);
371 let engine = random_engine(&mut rng);
372
373 let encoded_size = encoded_len(input_len, config.encode_padding()).unwrap();
375 for _ in 0..encoded_size {
376 output.push(rng.random());
377 }
378
379 let orig_output_buf = output.clone();
380
381 let bytes_written = engine.internal_encode(&input, &mut output);
382
383 assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]);
385
386 let _ = str::from_utf8(&output[0..bytes_written]).unwrap();
388 }
389 }
390
391 #[test]
392 fn encode_with_padding_random_valid_utf8() {
393 let mut input = Vec::new();
394 let mut output = Vec::new();
395
396 let input_len_range = Uniform::new(0, 1000).unwrap();
397
398 let mut rng = rand::make_rng::<rngs::SmallRng>();
399
400 for _ in 0..10_000 {
401 input.clear();
402 output.clear();
403
404 let input_len = input_len_range.sample(&mut rng);
405
406 for _ in 0..input_len {
407 input.push(rng.random());
408 }
409
410 let engine = random_engine(&mut rng);
411
412 let encoded_size = encoded_len(input_len, engine.config().encode_padding()).unwrap();
414 for _ in 0..encoded_size + 1000 {
415 output.push(rng.random());
416 }
417
418 let orig_output_buf = output.clone();
419
420 encode_with_padding(&input, &mut output[0..encoded_size], &engine, encoded_size);
421
422 assert_eq!(orig_output_buf[encoded_size..], output[encoded_size..]);
424
425 let _ = str::from_utf8(&output[0..encoded_size]).unwrap();
427 }
428 }
429
430 #[test]
431 fn add_padding_random_valid_utf8() {
432 let mut output = Vec::new();
433
434 let mut rng = rand::make_rng::<rngs::SmallRng>();
435
436 for unpadded_output_len in 0..20 {
438 output.clear();
439
440 for _ in 0..100 {
442 output.push(rng.random());
443 }
444
445 let orig_output_buf = output.clone();
446
447 let bytes_written = add_padding(unpadded_output_len, PADDING_SYMBOL, &mut output);
448
449 assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]);
451
452 let _ = str::from_utf8(&output[0..bytes_written]).unwrap();
454 }
455 }
456
457 fn assert_encoded_length<E: Engine>(
458 input_len: usize,
459 enc_len: usize,
460 engine: &E,
461 padded: bool,
462 ) {
463 assert_eq!(enc_len, encoded_len(input_len, padded).unwrap());
464
465 let mut bytes: Vec<u8> = Vec::new();
466 let mut rng = rand::make_rng::<rngs::SmallRng>();
467
468 for _ in 0..input_len {
469 bytes.push(rng.random());
470 }
471
472 let encoded = engine.encode(&bytes);
473 assert_encode_sanity(&encoded, engine, input_len);
474
475 assert_eq!(enc_len, encoded.len());
476 }
477
478 #[test]
479 fn encode_imap() {
480 assert_eq!(
481 &GeneralPurpose::new(&alphabet::IMAP_MUTF7, NO_PAD).encode(b"\xFB\xFF"),
482 &GeneralPurpose::new(&alphabet::STANDARD, NO_PAD)
483 .encode(b"\xFB\xFF")
484 .replace('/', ",")
485 );
486 }
487}