1 #[cfg(any(feature = "alloc", feature = "std", test))]
2 use alloc
::string
::String
;
4 #[cfg(any(feature = "std", test))]
7 #[cfg(any(feature = "alloc", feature = "std", test))]
8 use crate::engine
::general_purpose
::STANDARD
;
9 use crate::engine
::{Config, Engine}
;
12 /// Encode arbitrary octets as base64 using the [`STANDARD` engine](STANDARD).
14 /// See [Engine::encode].
16 #[deprecated(since = "0.21.0", note = "Use Engine::encode")]
17 #[cfg(any(feature = "alloc", feature = "std", test))]
18 pub fn encode
<T
: AsRef
<[u8]>>(input
: T
) -> String
{
19 STANDARD
.encode(input
)
22 ///Encode arbitrary octets as base64 using the provided `Engine` into a new `String`.
24 /// See [Engine::encode].
26 #[deprecated(since = "0.21.0", note = "Use Engine::encode")]
27 #[cfg(any(feature = "alloc", feature = "std", test))]
28 pub fn encode_engine
<E
: Engine
, T
: AsRef
<[u8]>>(input
: T
, engine
: &E
) -> String
{
32 ///Encode arbitrary octets as base64 into a supplied `String`.
34 /// See [Engine::encode_string].
36 #[deprecated(since = "0.21.0", note = "Use Engine::encode_string")]
37 #[cfg(any(feature = "alloc", feature = "std", test))]
38 pub fn encode_engine_string
<E
: Engine
, T
: AsRef
<[u8]>>(
40 output_buf
: &mut String
,
43 engine
.encode_string(input
, output_buf
)
46 /// Encode arbitrary octets as base64 into a supplied slice.
48 /// See [Engine::encode_slice].
50 #[deprecated(since = "0.21.0", note = "Use Engine::encode_slice")]
51 pub fn encode_engine_slice
<E
: Engine
, T
: AsRef
<[u8]>>(
53 output_buf
: &mut [u8],
55 ) -> Result
<usize, EncodeSliceError
> {
56 engine
.encode_slice(input
, output_buf
)
59 /// B64-encode and pad (if configured).
61 /// This helper exists to avoid recalculating encoded_size, which is relatively expensive on short
64 /// `encoded_size` is the encoded size calculated for `input`.
66 /// `output` must be of size `encoded_size`.
68 /// All bytes in `output` will be written to since it is exactly the size of the output.
69 pub(crate) fn encode_with_padding
<E
: Engine
+ ?Sized
>(
73 expected_encoded_size
: usize,
75 debug_assert_eq
!(expected_encoded_size
, output
.len());
77 let b64_bytes_written
= engine
.internal_encode(input
, output
);
79 let padding_bytes
= if engine
.config().encode_padding() {
80 add_padding(input
.len(), &mut output
[b64_bytes_written
..])
85 let encoded_bytes
= b64_bytes_written
86 .checked_add(padding_bytes
)
87 .expect("usize overflow when calculating b64 length");
89 debug_assert_eq
!(expected_encoded_size
, encoded_bytes
);
92 /// Calculate the base64 encoded length for a given input length, optionally including any
93 /// appropriate padding bytes.
95 /// Returns `None` if the encoded length can't be represented in `usize`. This will happen for
96 /// input lengths in approximately the top quarter of the range of `usize`.
97 pub fn encoded_len(bytes_len
: usize, padding
: bool
) -> Option
<usize> {
98 let rem
= bytes_len
% 3;
100 let complete_input_chunks
= bytes_len
/ 3;
101 let complete_chunk_output
= complete_input_chunks
.checked_mul(4);
105 complete_chunk_output
.and_then(|c
| c
.checked_add(4))
107 let encoded_rem
= match rem
{
110 _
=> unreachable
!("Impossible remainder"),
112 complete_chunk_output
.and_then(|c
| c
.checked_add(encoded_rem
))
115 complete_chunk_output
119 /// Write padding characters.
120 /// `input_len` is the size of the original, not encoded, input.
121 /// `output` is the slice where padding should be written, of length at least 2.
123 /// Returns the number of padding bytes written.
124 pub(crate) fn add_padding(input_len
: usize, output
: &mut [u8]) -> usize {
125 // TODO base on encoded len to use cheaper mod by 4 (aka & 7)
126 let rem
= input_len
% 3;
127 let mut bytes_written
= 0;
128 for _
in 0..((3 - rem
) % 3) {
129 output
[bytes_written
] = PAD_BYTE
;
136 /// Errors that can occur while encoding into a slice.
137 #[derive(Clone, Debug, PartialEq, Eq)]
138 pub enum EncodeSliceError
{
139 /// The provided slice is too small.
143 impl fmt
::Display
for EncodeSliceError
{
144 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
146 Self::OutputSliceTooSmall
=> write
!(f
, "Output slice too small"),
151 #[cfg(any(feature = "std", test))]
152 impl error
::Error
for EncodeSliceError
{
153 fn cause(&self) -> Option
<&dyn error
::Error
> {
164 engine
::general_purpose
::{GeneralPurpose, NO_PAD, STANDARD}
,
165 tests
::{assert_encode_sanity, random_config, random_engine}
,
168 distributions
::{Distribution, Uniform}
,
173 const URL_SAFE_NO_PAD_ENGINE
: GeneralPurpose
= GeneralPurpose
::new(&alphabet
::URL_SAFE
, NO_PAD
);
176 fn encoded_size_correct_standard() {
177 assert_encoded_length(0, 0, &STANDARD
, true);
179 assert_encoded_length(1, 4, &STANDARD
, true);
180 assert_encoded_length(2, 4, &STANDARD
, true);
181 assert_encoded_length(3, 4, &STANDARD
, true);
183 assert_encoded_length(4, 8, &STANDARD
, true);
184 assert_encoded_length(5, 8, &STANDARD
, true);
185 assert_encoded_length(6, 8, &STANDARD
, true);
187 assert_encoded_length(7, 12, &STANDARD
, true);
188 assert_encoded_length(8, 12, &STANDARD
, true);
189 assert_encoded_length(9, 12, &STANDARD
, true);
191 assert_encoded_length(54, 72, &STANDARD
, true);
193 assert_encoded_length(55, 76, &STANDARD
, true);
194 assert_encoded_length(56, 76, &STANDARD
, true);
195 assert_encoded_length(57, 76, &STANDARD
, true);
197 assert_encoded_length(58, 80, &STANDARD
, true);
201 fn encoded_size_correct_no_pad() {
202 assert_encoded_length(0, 0, &URL_SAFE_NO_PAD_ENGINE
, false);
204 assert_encoded_length(1, 2, &URL_SAFE_NO_PAD_ENGINE
, false);
205 assert_encoded_length(2, 3, &URL_SAFE_NO_PAD_ENGINE
, false);
206 assert_encoded_length(3, 4, &URL_SAFE_NO_PAD_ENGINE
, false);
208 assert_encoded_length(4, 6, &URL_SAFE_NO_PAD_ENGINE
, false);
209 assert_encoded_length(5, 7, &URL_SAFE_NO_PAD_ENGINE
, false);
210 assert_encoded_length(6, 8, &URL_SAFE_NO_PAD_ENGINE
, false);
212 assert_encoded_length(7, 10, &URL_SAFE_NO_PAD_ENGINE
, false);
213 assert_encoded_length(8, 11, &URL_SAFE_NO_PAD_ENGINE
, false);
214 assert_encoded_length(9, 12, &URL_SAFE_NO_PAD_ENGINE
, false);
216 assert_encoded_length(54, 72, &URL_SAFE_NO_PAD_ENGINE
, false);
218 assert_encoded_length(55, 74, &URL_SAFE_NO_PAD_ENGINE
, false);
219 assert_encoded_length(56, 75, &URL_SAFE_NO_PAD_ENGINE
, false);
220 assert_encoded_length(57, 76, &URL_SAFE_NO_PAD_ENGINE
, false);
222 assert_encoded_length(58, 78, &URL_SAFE_NO_PAD_ENGINE
, false);
226 fn encoded_size_overflow() {
227 assert_eq
!(None
, encoded_len(usize::MAX
, true));
231 fn encode_engine_string_into_nonempty_buffer_doesnt_clobber_prefix() {
232 let mut orig_data
= Vec
::new();
233 let mut prefix
= String
::new();
234 let mut encoded_data_no_prefix
= String
::new();
235 let mut encoded_data_with_prefix
= String
::new();
236 let mut decoded
= Vec
::new();
238 let prefix_len_range
= Uniform
::new(0, 1000);
239 let input_len_range
= Uniform
::new(0, 1000);
241 let mut rng
= rand
::rngs
::SmallRng
::from_entropy();
246 encoded_data_no_prefix
.clear();
247 encoded_data_with_prefix
.clear();
250 let input_len
= input_len_range
.sample(&mut rng
);
252 for _
in 0..input_len
{
253 orig_data
.push(rng
.gen());
256 let prefix_len
= prefix_len_range
.sample(&mut rng
);
257 for _
in 0..prefix_len
{
258 // getting convenient random single-byte printable chars that aren't base64 is
262 encoded_data_with_prefix
.push_str(&prefix
);
264 let engine
= random_engine(&mut rng
);
265 engine
.encode_string(&orig_data
, &mut encoded_data_no_prefix
);
266 engine
.encode_string(&orig_data
, &mut encoded_data_with_prefix
);
269 encoded_data_no_prefix
.len() + prefix_len
,
270 encoded_data_with_prefix
.len()
272 assert_encode_sanity(
273 &encoded_data_no_prefix
,
274 engine
.config().encode_padding(),
277 assert_encode_sanity(
278 &encoded_data_with_prefix
[prefix_len
..],
279 engine
.config().encode_padding(),
283 // append plain encode onto prefix
284 prefix
.push_str(&encoded_data_no_prefix
);
286 assert_eq
!(prefix
, encoded_data_with_prefix
);
289 .decode_vec(&encoded_data_no_prefix
, &mut decoded
)
291 assert_eq
!(orig_data
, decoded
);
296 fn encode_engine_slice_into_nonempty_buffer_doesnt_clobber_suffix() {
297 let mut orig_data
= Vec
::new();
298 let mut encoded_data
= Vec
::new();
299 let mut encoded_data_original_state
= Vec
::new();
300 let mut decoded
= Vec
::new();
302 let input_len_range
= Uniform
::new(0, 1000);
304 let mut rng
= rand
::rngs
::SmallRng
::from_entropy();
308 encoded_data
.clear();
309 encoded_data_original_state
.clear();
312 let input_len
= input_len_range
.sample(&mut rng
);
314 for _
in 0..input_len
{
315 orig_data
.push(rng
.gen());
318 // plenty of existing garbage in the encoded buffer
319 for _
in 0..10 * input_len
{
320 encoded_data
.push(rng
.gen());
323 encoded_data_original_state
.extend_from_slice(&encoded_data
);
325 let engine
= random_engine(&mut rng
);
327 let encoded_size
= encoded_len(input_len
, engine
.config().encode_padding()).unwrap();
331 engine
.encode_slice(&orig_data
, &mut encoded_data
).unwrap()
334 assert_encode_sanity(
335 str::from_utf8(&encoded_data
[0..encoded_size
]).unwrap(),
336 engine
.config().encode_padding(),
341 &encoded_data
[encoded_size
..],
342 &encoded_data_original_state
[encoded_size
..]
346 .decode_vec(&encoded_data
[0..encoded_size
], &mut decoded
)
348 assert_eq
!(orig_data
, decoded
);
353 fn encode_to_slice_random_valid_utf8() {
354 let mut input
= Vec
::new();
355 let mut output
= Vec
::new();
357 let input_len_range
= Uniform
::new(0, 1000);
359 let mut rng
= rand
::rngs
::SmallRng
::from_entropy();
365 let input_len
= input_len_range
.sample(&mut rng
);
367 for _
in 0..input_len
{
368 input
.push(rng
.gen());
371 let config
= random_config(&mut rng
);
372 let engine
= random_engine(&mut rng
);
374 // fill up the output buffer with garbage
375 let encoded_size
= encoded_len(input_len
, config
.encode_padding()).unwrap();
376 for _
in 0..encoded_size
{
377 output
.push(rng
.gen());
380 let orig_output_buf
= output
.clone();
382 let bytes_written
= engine
.internal_encode(&input
, &mut output
);
384 // make sure the part beyond bytes_written is the same garbage it was before
385 assert_eq
!(orig_output_buf
[bytes_written
..], output
[bytes_written
..]);
387 // make sure the encoded bytes are UTF-8
388 let _
= str::from_utf8(&output
[0..bytes_written
]).unwrap();
393 fn encode_with_padding_random_valid_utf8() {
394 let mut input
= Vec
::new();
395 let mut output
= Vec
::new();
397 let input_len_range
= Uniform
::new(0, 1000);
399 let mut rng
= rand
::rngs
::SmallRng
::from_entropy();
405 let input_len
= input_len_range
.sample(&mut rng
);
407 for _
in 0..input_len
{
408 input
.push(rng
.gen());
411 let engine
= random_engine(&mut rng
);
413 // fill up the output buffer with garbage
414 let encoded_size
= encoded_len(input_len
, engine
.config().encode_padding()).unwrap();
415 for _
in 0..encoded_size
+ 1000 {
416 output
.push(rng
.gen());
419 let orig_output_buf
= output
.clone();
421 encode_with_padding(&input
, &mut output
[0..encoded_size
], &engine
, encoded_size
);
423 // make sure the part beyond b64 is the same garbage it was before
424 assert_eq
!(orig_output_buf
[encoded_size
..], output
[encoded_size
..]);
426 // make sure the encoded bytes are UTF-8
427 let _
= str::from_utf8(&output
[0..encoded_size
]).unwrap();
432 fn add_padding_random_valid_utf8() {
433 let mut output
= Vec
::new();
435 let mut rng
= rand
::rngs
::SmallRng
::from_entropy();
437 // cover our bases for length % 3
438 for input_len
in 0..10 {
441 // fill output with random
443 output
.push(rng
.gen());
446 let orig_output_buf
= output
.clone();
448 let bytes_written
= add_padding(input_len
, &mut output
);
450 // make sure the part beyond bytes_written is the same garbage it was before
451 assert_eq
!(orig_output_buf
[bytes_written
..], output
[bytes_written
..]);
453 // make sure the encoded bytes are UTF-8
454 let _
= str::from_utf8(&output
[0..bytes_written
]).unwrap();
458 fn assert_encoded_length
<E
: Engine
>(
464 assert_eq
!(enc_len
, encoded_len(input_len
, padded
).unwrap());
466 let mut bytes
: Vec
<u8> = Vec
::new();
467 let mut rng
= rand
::rngs
::SmallRng
::from_entropy();
469 for _
in 0..input_len
{
470 bytes
.push(rng
.gen());
473 let encoded
= engine
.encode(&bytes
);
474 assert_encode_sanity(&encoded
, padded
, input_len
);
476 assert_eq
!(enc_len
, encoded
.len());
482 &GeneralPurpose
::new(&alphabet
::IMAP_MUTF7
, NO_PAD
).encode(b
"\xFB\xFF"),
483 &GeneralPurpose
::new(&alphabet
::STANDARD
, NO_PAD
)