/rust/registry/src/index.crates.io-1949cf8c6b5b557f/base64-0.13.1/src/encode.rs
Line | Count | Source |
1 | | use crate::{Config, PAD_BYTE}; |
2 | | #[cfg(any(feature = "alloc", feature = "std", test))] |
3 | | use crate::{chunked_encoder, STANDARD}; |
4 | | #[cfg(any(feature = "alloc", feature = "std", test))] |
5 | | use alloc::{string::String, vec}; |
6 | | use core::convert::TryInto; |
7 | | |
8 | | ///Encode arbitrary octets as base64. |
9 | | ///Returns a String. |
10 | | ///Convenience for `encode_config(input, base64::STANDARD);`. |
11 | | /// |
12 | | ///# Example |
13 | | /// |
14 | | ///```rust |
15 | | ///extern crate base64; |
16 | | /// |
17 | | ///fn main() { |
18 | | /// let b64 = base64::encode(b"hello world"); |
19 | | /// println!("{}", b64); |
20 | | ///} |
21 | | ///``` |
22 | | #[cfg(any(feature = "alloc", feature = "std", test))] |
23 | 0 | pub fn encode<T: AsRef<[u8]>>(input: T) -> String { |
24 | 0 | encode_config(input, STANDARD) |
25 | 0 | } Unexecuted instantiation: base64::encode::encode::<&[u8]> Unexecuted instantiation: base64::encode::encode::<_> |
26 | | |
27 | | ///Encode arbitrary octets as base64. |
28 | | ///Returns a String. |
29 | | /// |
30 | | ///# Example |
31 | | /// |
32 | | ///```rust |
33 | | ///extern crate base64; |
34 | | /// |
35 | | ///fn main() { |
36 | | /// let b64 = base64::encode_config(b"hello world~", base64::STANDARD); |
37 | | /// println!("{}", b64); |
38 | | /// |
39 | | /// let b64_url = base64::encode_config(b"hello internet~", base64::URL_SAFE); |
40 | | /// println!("{}", b64_url); |
41 | | ///} |
42 | | ///``` |
43 | | #[cfg(any(feature = "alloc", feature = "std", test))] |
44 | 0 | pub fn encode_config<T: AsRef<[u8]>>(input: T, config: Config) -> String { |
45 | 0 | let mut buf = match encoded_size(input.as_ref().len(), config) { |
46 | 0 | Some(n) => vec![0; n], |
47 | 0 | None => panic!("integer overflow when calculating buffer size"), |
48 | | }; |
49 | | |
50 | 0 | encode_with_padding(input.as_ref(), config, buf.len(), &mut buf[..]); |
51 | | |
52 | 0 | String::from_utf8(buf).expect("Invalid UTF8") |
53 | 0 | } Unexecuted instantiation: base64::encode::encode_config::<&[u8]> Unexecuted instantiation: base64::encode::encode_config::<_> |
54 | | |
55 | | ///Encode arbitrary octets as base64. |
56 | | ///Writes into the supplied output buffer, which will grow the buffer if needed. |
57 | | /// |
58 | | ///# Example |
59 | | /// |
60 | | ///```rust |
61 | | ///extern crate base64; |
62 | | /// |
63 | | ///fn main() { |
64 | | /// let mut buf = String::new(); |
65 | | /// base64::encode_config_buf(b"hello world~", base64::STANDARD, &mut buf); |
66 | | /// println!("{}", buf); |
67 | | /// |
68 | | /// buf.clear(); |
69 | | /// base64::encode_config_buf(b"hello internet~", base64::URL_SAFE, &mut buf); |
70 | | /// println!("{}", buf); |
71 | | ///} |
72 | | ///``` |
73 | | #[cfg(any(feature = "alloc", feature = "std", test))] |
74 | 5.45M | pub fn encode_config_buf<T: AsRef<[u8]>>(input: T, config: Config, buf: &mut String) { |
75 | 5.45M | let input_bytes = input.as_ref(); |
76 | | |
77 | | { |
78 | 5.45M | let mut sink = chunked_encoder::StringSink::new(buf); |
79 | 5.45M | let encoder = chunked_encoder::ChunkedEncoder::new(config); |
80 | | |
81 | 5.45M | encoder |
82 | 5.45M | .encode(input_bytes, &mut sink) |
83 | 5.45M | .expect("Writing to a String shouldn't fail") |
84 | | } |
85 | 5.45M | } base64::encode::encode_config_buf::<&[u8]> Line | Count | Source | 74 | 5.45M | pub fn encode_config_buf<T: AsRef<[u8]>>(input: T, config: Config, buf: &mut String) { | 75 | 5.45M | let input_bytes = input.as_ref(); | 76 | | | 77 | | { | 78 | 5.45M | let mut sink = chunked_encoder::StringSink::new(buf); | 79 | 5.45M | let encoder = chunked_encoder::ChunkedEncoder::new(config); | 80 | | | 81 | 5.45M | encoder | 82 | 5.45M | .encode(input_bytes, &mut sink) | 83 | 5.45M | .expect("Writing to a String shouldn't fail") | 84 | | } | 85 | 5.45M | } |
Unexecuted instantiation: base64::encode::encode_config_buf::<_> |
86 | | |
87 | | /// Encode arbitrary octets as base64. |
88 | | /// Writes into the supplied output buffer. |
89 | | /// |
90 | | /// This is useful if you wish to avoid allocation entirely (e.g. encoding into a stack-resident |
91 | | /// or statically-allocated buffer). |
92 | | /// |
93 | | /// # Panics |
94 | | /// |
95 | | /// If `output` is too small to hold the encoded version of `input`, a panic will result. |
96 | | /// |
97 | | /// # Example |
98 | | /// |
99 | | /// ```rust |
100 | | /// extern crate base64; |
101 | | /// |
102 | | /// fn main() { |
103 | | /// let s = b"hello internet!"; |
104 | | /// let mut buf = Vec::new(); |
105 | | /// // make sure we'll have a slice big enough for base64 + padding |
106 | | /// buf.resize(s.len() * 4 / 3 + 4, 0); |
107 | | /// |
108 | | /// let bytes_written = base64::encode_config_slice(s, |
109 | | /// base64::STANDARD, &mut buf); |
110 | | /// |
111 | | /// // shorten our vec down to just what was written |
112 | | /// buf.resize(bytes_written, 0); |
113 | | /// |
114 | | /// assert_eq!(s, base64::decode(&buf).unwrap().as_slice()); |
115 | | /// } |
116 | | /// ``` |
117 | 0 | pub fn encode_config_slice<T: AsRef<[u8]>>(input: T, config: Config, output: &mut [u8]) -> usize { |
118 | 0 | let input_bytes = input.as_ref(); |
119 | | |
120 | 0 | let encoded_size = encoded_size(input_bytes.len(), config) |
121 | 0 | .expect("usize overflow when calculating buffer size"); |
122 | | |
123 | 0 | let mut b64_output = &mut output[0..encoded_size]; |
124 | | |
125 | 0 | encode_with_padding(&input_bytes, config, encoded_size, &mut b64_output); |
126 | | |
127 | 0 | encoded_size |
128 | 0 | } |
129 | | |
130 | | /// B64-encode and pad (if configured). |
131 | | /// |
132 | | /// This helper exists to avoid recalculating encoded_size, which is relatively expensive on short |
133 | | /// inputs. |
134 | | /// |
135 | | /// `encoded_size` is the encoded size calculated for `input`. |
136 | | /// |
137 | | /// `output` must be of size `encoded_size`. |
138 | | /// |
139 | | /// All bytes in `output` will be written to since it is exactly the size of the output. |
140 | 0 | fn encode_with_padding(input: &[u8], config: Config, encoded_size: usize, output: &mut [u8]) { |
141 | 0 | debug_assert_eq!(encoded_size, output.len()); |
142 | | |
143 | 0 | let b64_bytes_written = encode_to_slice(input, output, config.char_set.encode_table()); |
144 | | |
145 | 0 | let padding_bytes = if config.pad { |
146 | 0 | add_padding(input.len(), &mut output[b64_bytes_written..]) |
147 | | } else { |
148 | 0 | 0 |
149 | | }; |
150 | | |
151 | 0 | let encoded_bytes = b64_bytes_written |
152 | 0 | .checked_add(padding_bytes) |
153 | 0 | .expect("usize overflow when calculating b64 length"); |
154 | | |
155 | 0 | debug_assert_eq!(encoded_size, encoded_bytes); |
156 | 0 | } |
157 | | |
158 | | #[inline] |
159 | 82.1M | fn read_u64(s: &[u8]) -> u64 { |
160 | 82.1M | u64::from_be_bytes(s[..8].try_into().unwrap()) |
161 | 82.1M | } |
162 | | |
163 | | /// Encode input bytes to utf8 base64 bytes. Does not pad. |
164 | | /// `output` must be long enough to hold the encoded `input` without padding. |
165 | | /// Returns the number of bytes written. |
166 | | #[inline] |
167 | 5.80M | pub fn encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64]) -> usize { |
168 | 5.80M | let mut input_index: usize = 0; |
169 | | |
170 | | const BLOCKS_PER_FAST_LOOP: usize = 4; |
171 | | const LOW_SIX_BITS: u64 = 0x3F; |
172 | | |
173 | | // we read 8 bytes at a time (u64) but only actually consume 6 of those bytes. Thus, we need |
174 | | // 2 trailing bytes to be available to read.. |
175 | 5.80M | let last_fast_index = input.len().saturating_sub(BLOCKS_PER_FAST_LOOP * 6 + 2); |
176 | 5.80M | let mut output_index = 0; |
177 | | |
178 | 5.80M | if last_fast_index > 0 { |
179 | 26.1M | while input_index <= last_fast_index { |
180 | 20.5M | // Major performance wins from letting the optimizer do the bounds check once, mostly |
181 | 20.5M | // on the output side |
182 | 20.5M | let input_chunk = &input[input_index..(input_index + (BLOCKS_PER_FAST_LOOP * 6 + 2))]; |
183 | 20.5M | let output_chunk = &mut output[output_index..(output_index + BLOCKS_PER_FAST_LOOP * 8)]; |
184 | 20.5M | |
185 | 20.5M | // Hand-unrolling for 32 vs 16 or 8 bytes produces yields performance about equivalent |
186 | 20.5M | // to unsafe pointer code on a Xeon E5-1650v3. 64 byte unrolling was slightly better for |
187 | 20.5M | // large inputs but significantly worse for 50-byte input, unsurprisingly. I suspect |
188 | 20.5M | // that it's a not uncommon use case to encode smallish chunks of data (e.g. a 64-byte |
189 | 20.5M | // SHA-512 digest), so it would be nice if that fit in the unrolled loop at least once. |
190 | 20.5M | // Plus, single-digit percentage performance differences might well be quite different |
191 | 20.5M | // on different hardware. |
192 | 20.5M | |
193 | 20.5M | let input_u64 = read_u64(&input_chunk[0..]); |
194 | 20.5M | |
195 | 20.5M | output_chunk[0] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize]; |
196 | 20.5M | output_chunk[1] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize]; |
197 | 20.5M | output_chunk[2] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize]; |
198 | 20.5M | output_chunk[3] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize]; |
199 | 20.5M | output_chunk[4] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize]; |
200 | 20.5M | output_chunk[5] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize]; |
201 | 20.5M | output_chunk[6] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize]; |
202 | 20.5M | output_chunk[7] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize]; |
203 | 20.5M | |
204 | 20.5M | let input_u64 = read_u64(&input_chunk[6..]); |
205 | 20.5M | |
206 | 20.5M | output_chunk[8] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize]; |
207 | 20.5M | output_chunk[9] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize]; |
208 | 20.5M | output_chunk[10] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize]; |
209 | 20.5M | output_chunk[11] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize]; |
210 | 20.5M | output_chunk[12] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize]; |
211 | 20.5M | output_chunk[13] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize]; |
212 | 20.5M | output_chunk[14] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize]; |
213 | 20.5M | output_chunk[15] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize]; |
214 | 20.5M | |
215 | 20.5M | let input_u64 = read_u64(&input_chunk[12..]); |
216 | 20.5M | |
217 | 20.5M | output_chunk[16] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize]; |
218 | 20.5M | output_chunk[17] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize]; |
219 | 20.5M | output_chunk[18] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize]; |
220 | 20.5M | output_chunk[19] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize]; |
221 | 20.5M | output_chunk[20] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize]; |
222 | 20.5M | output_chunk[21] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize]; |
223 | 20.5M | output_chunk[22] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize]; |
224 | 20.5M | output_chunk[23] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize]; |
225 | 20.5M | |
226 | 20.5M | let input_u64 = read_u64(&input_chunk[18..]); |
227 | 20.5M | |
228 | 20.5M | output_chunk[24] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize]; |
229 | 20.5M | output_chunk[25] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize]; |
230 | 20.5M | output_chunk[26] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize]; |
231 | 20.5M | output_chunk[27] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize]; |
232 | 20.5M | output_chunk[28] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize]; |
233 | 20.5M | output_chunk[29] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize]; |
234 | 20.5M | output_chunk[30] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize]; |
235 | 20.5M | output_chunk[31] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize]; |
236 | 20.5M | |
237 | 20.5M | output_index += BLOCKS_PER_FAST_LOOP * 8; |
238 | 20.5M | input_index += BLOCKS_PER_FAST_LOOP * 6; |
239 | 20.5M | } |
240 | 217k | } |
241 | | |
242 | | // Encode what's left after the fast loop. |
243 | | |
244 | | const LOW_SIX_BITS_U8: u8 = 0x3F; |
245 | | |
246 | 5.80M | let rem = input.len() % 3; |
247 | 5.80M | let start_of_rem = input.len() - rem; |
248 | | |
249 | | // start at the first index not handled by fast loop, which may be 0. |
250 | | |
251 | 21.0M | while input_index < start_of_rem { |
252 | 15.2M | let input_chunk = &input[input_index..(input_index + 3)]; |
253 | 15.2M | let output_chunk = &mut output[output_index..(output_index + 4)]; |
254 | 15.2M | |
255 | 15.2M | output_chunk[0] = encode_table[(input_chunk[0] >> 2) as usize]; |
256 | 15.2M | output_chunk[1] = |
257 | 15.2M | encode_table[((input_chunk[0] << 4 | input_chunk[1] >> 4) & LOW_SIX_BITS_U8) as usize]; |
258 | 15.2M | output_chunk[2] = |
259 | 15.2M | encode_table[((input_chunk[1] << 2 | input_chunk[2] >> 6) & LOW_SIX_BITS_U8) as usize]; |
260 | 15.2M | output_chunk[3] = encode_table[(input_chunk[2] & LOW_SIX_BITS_U8) as usize]; |
261 | 15.2M | |
262 | 15.2M | input_index += 3; |
263 | 15.2M | output_index += 4; |
264 | 15.2M | } |
265 | | |
266 | 5.80M | if rem == 2 { |
267 | 4.83M | output[output_index] = encode_table[(input[start_of_rem] >> 2) as usize]; |
268 | 4.83M | output[output_index + 1] = encode_table[((input[start_of_rem] << 4 |
269 | 4.83M | | input[start_of_rem + 1] >> 4) |
270 | 4.83M | & LOW_SIX_BITS_U8) as usize]; |
271 | 4.83M | output[output_index + 2] = |
272 | 4.83M | encode_table[((input[start_of_rem + 1] << 2) & LOW_SIX_BITS_U8) as usize]; |
273 | 4.83M | output_index += 3; |
274 | 4.83M | } else if rem == 1 { |
275 | 317k | output[output_index] = encode_table[(input[start_of_rem] >> 2) as usize]; |
276 | 317k | output[output_index + 1] = |
277 | 317k | encode_table[((input[start_of_rem] << 4) & LOW_SIX_BITS_U8) as usize]; |
278 | 317k | output_index += 2; |
279 | 652k | } |
280 | | |
281 | 5.80M | output_index |
282 | 5.80M | } |
283 | | |
284 | | /// calculate the base64 encoded string size, including padding if appropriate |
285 | 0 | pub fn encoded_size(bytes_len: usize, config: Config) -> Option<usize> { |
286 | 0 | let rem = bytes_len % 3; |
287 | | |
288 | 0 | let complete_input_chunks = bytes_len / 3; |
289 | 0 | let complete_chunk_output = complete_input_chunks.checked_mul(4); |
290 | | |
291 | 0 | if rem > 0 { |
292 | 0 | if config.pad { |
293 | 0 | complete_chunk_output.and_then(|c| c.checked_add(4)) |
294 | | } else { |
295 | 0 | let encoded_rem = match rem { |
296 | 0 | 1 => 2, |
297 | 0 | 2 => 3, |
298 | 0 | _ => unreachable!("Impossible remainder"), |
299 | | }; |
300 | 0 | complete_chunk_output.and_then(|c| c.checked_add(encoded_rem)) |
301 | | } |
302 | | } else { |
303 | 0 | complete_chunk_output |
304 | | } |
305 | 0 | } |
306 | | |
307 | | /// Write padding characters. |
308 | | /// `output` is the slice where padding should be written, of length at least 2. |
309 | | /// |
310 | | /// Returns the number of padding bytes written. |
311 | 5.45M | pub fn add_padding(input_len: usize, output: &mut [u8]) -> usize { |
312 | 5.45M | let rem = input_len % 3; |
313 | 5.45M | let mut bytes_written = 0; |
314 | 5.47M | for _ in 0..((3 - rem) % 3) { |
315 | 5.47M | output[bytes_written] = PAD_BYTE; |
316 | 5.47M | bytes_written += 1; |
317 | 5.47M | } |
318 | | |
319 | 5.45M | bytes_written |
320 | 5.45M | } |
321 | | |
322 | | #[cfg(test)] |
323 | | mod tests { |
324 | | use super::*; |
325 | | use crate::{ |
326 | | decode::decode_config_buf, |
327 | | tests::{assert_encode_sanity, random_config}, |
328 | | Config, STANDARD, URL_SAFE_NO_PAD, |
329 | | }; |
330 | | |
331 | | use rand::{ |
332 | | distributions::{Distribution, Uniform}, |
333 | | FromEntropy, Rng, |
334 | | }; |
335 | | use std; |
336 | | use std::str; |
337 | | |
338 | | #[test] |
339 | | fn encoded_size_correct_standard() { |
340 | | assert_encoded_length(0, 0, STANDARD); |
341 | | |
342 | | assert_encoded_length(1, 4, STANDARD); |
343 | | assert_encoded_length(2, 4, STANDARD); |
344 | | assert_encoded_length(3, 4, STANDARD); |
345 | | |
346 | | assert_encoded_length(4, 8, STANDARD); |
347 | | assert_encoded_length(5, 8, STANDARD); |
348 | | assert_encoded_length(6, 8, STANDARD); |
349 | | |
350 | | assert_encoded_length(7, 12, STANDARD); |
351 | | assert_encoded_length(8, 12, STANDARD); |
352 | | assert_encoded_length(9, 12, STANDARD); |
353 | | |
354 | | assert_encoded_length(54, 72, STANDARD); |
355 | | |
356 | | assert_encoded_length(55, 76, STANDARD); |
357 | | assert_encoded_length(56, 76, STANDARD); |
358 | | assert_encoded_length(57, 76, STANDARD); |
359 | | |
360 | | assert_encoded_length(58, 80, STANDARD); |
361 | | } |
362 | | |
363 | | #[test] |
364 | | fn encoded_size_correct_no_pad() { |
365 | | assert_encoded_length(0, 0, URL_SAFE_NO_PAD); |
366 | | |
367 | | assert_encoded_length(1, 2, URL_SAFE_NO_PAD); |
368 | | assert_encoded_length(2, 3, URL_SAFE_NO_PAD); |
369 | | assert_encoded_length(3, 4, URL_SAFE_NO_PAD); |
370 | | |
371 | | assert_encoded_length(4, 6, URL_SAFE_NO_PAD); |
372 | | assert_encoded_length(5, 7, URL_SAFE_NO_PAD); |
373 | | assert_encoded_length(6, 8, URL_SAFE_NO_PAD); |
374 | | |
375 | | assert_encoded_length(7, 10, URL_SAFE_NO_PAD); |
376 | | assert_encoded_length(8, 11, URL_SAFE_NO_PAD); |
377 | | assert_encoded_length(9, 12, URL_SAFE_NO_PAD); |
378 | | |
379 | | assert_encoded_length(54, 72, URL_SAFE_NO_PAD); |
380 | | |
381 | | assert_encoded_length(55, 74, URL_SAFE_NO_PAD); |
382 | | assert_encoded_length(56, 75, URL_SAFE_NO_PAD); |
383 | | assert_encoded_length(57, 76, URL_SAFE_NO_PAD); |
384 | | |
385 | | assert_encoded_length(58, 78, URL_SAFE_NO_PAD); |
386 | | } |
387 | | |
388 | | #[test] |
389 | | fn encoded_size_overflow() { |
390 | | assert_eq!(None, encoded_size(std::usize::MAX, STANDARD)); |
391 | | } |
392 | | |
393 | | #[test] |
394 | | fn encode_config_buf_into_nonempty_buffer_doesnt_clobber_prefix() { |
395 | | let mut orig_data = Vec::new(); |
396 | | let mut prefix = String::new(); |
397 | | let mut encoded_data_no_prefix = String::new(); |
398 | | let mut encoded_data_with_prefix = String::new(); |
399 | | let mut decoded = Vec::new(); |
400 | | |
401 | | let prefix_len_range = Uniform::new(0, 1000); |
402 | | let input_len_range = Uniform::new(0, 1000); |
403 | | |
404 | | let mut rng = rand::rngs::SmallRng::from_entropy(); |
405 | | |
406 | | for _ in 0..10_000 { |
407 | | orig_data.clear(); |
408 | | prefix.clear(); |
409 | | encoded_data_no_prefix.clear(); |
410 | | encoded_data_with_prefix.clear(); |
411 | | decoded.clear(); |
412 | | |
413 | | let input_len = input_len_range.sample(&mut rng); |
414 | | |
415 | | for _ in 0..input_len { |
416 | | orig_data.push(rng.gen()); |
417 | | } |
418 | | |
419 | | let prefix_len = prefix_len_range.sample(&mut rng); |
420 | | for _ in 0..prefix_len { |
421 | | // getting convenient random single-byte printable chars that aren't base64 is |
422 | | // annoying |
423 | | prefix.push('#'); |
424 | | } |
425 | | encoded_data_with_prefix.push_str(&prefix); |
426 | | |
427 | | let config = random_config(&mut rng); |
428 | | encode_config_buf(&orig_data, config, &mut encoded_data_no_prefix); |
429 | | encode_config_buf(&orig_data, config, &mut encoded_data_with_prefix); |
430 | | |
431 | | assert_eq!( |
432 | | encoded_data_no_prefix.len() + prefix_len, |
433 | | encoded_data_with_prefix.len() |
434 | | ); |
435 | | assert_encode_sanity(&encoded_data_no_prefix, config, input_len); |
436 | | assert_encode_sanity(&encoded_data_with_prefix[prefix_len..], config, input_len); |
437 | | |
438 | | // append plain encode onto prefix |
439 | | prefix.push_str(&mut encoded_data_no_prefix); |
440 | | |
441 | | assert_eq!(prefix, encoded_data_with_prefix); |
442 | | |
443 | | decode_config_buf(&encoded_data_no_prefix, config, &mut decoded).unwrap(); |
444 | | assert_eq!(orig_data, decoded); |
445 | | } |
446 | | } |
447 | | |
448 | | #[test] |
449 | | fn encode_config_slice_into_nonempty_buffer_doesnt_clobber_suffix() { |
450 | | let mut orig_data = Vec::new(); |
451 | | let mut encoded_data = Vec::new(); |
452 | | let mut encoded_data_original_state = Vec::new(); |
453 | | let mut decoded = Vec::new(); |
454 | | |
455 | | let input_len_range = Uniform::new(0, 1000); |
456 | | |
457 | | let mut rng = rand::rngs::SmallRng::from_entropy(); |
458 | | |
459 | | for _ in 0..10_000 { |
460 | | orig_data.clear(); |
461 | | encoded_data.clear(); |
462 | | encoded_data_original_state.clear(); |
463 | | decoded.clear(); |
464 | | |
465 | | let input_len = input_len_range.sample(&mut rng); |
466 | | |
467 | | for _ in 0..input_len { |
468 | | orig_data.push(rng.gen()); |
469 | | } |
470 | | |
471 | | // plenty of existing garbage in the encoded buffer |
472 | | for _ in 0..10 * input_len { |
473 | | encoded_data.push(rng.gen()); |
474 | | } |
475 | | |
476 | | encoded_data_original_state.extend_from_slice(&encoded_data); |
477 | | |
478 | | let config = random_config(&mut rng); |
479 | | |
480 | | let encoded_size = encoded_size(input_len, config).unwrap(); |
481 | | |
482 | | assert_eq!( |
483 | | encoded_size, |
484 | | encode_config_slice(&orig_data, config, &mut encoded_data) |
485 | | ); |
486 | | |
487 | | assert_encode_sanity( |
488 | | std::str::from_utf8(&encoded_data[0..encoded_size]).unwrap(), |
489 | | config, |
490 | | input_len, |
491 | | ); |
492 | | |
493 | | assert_eq!( |
494 | | &encoded_data[encoded_size..], |
495 | | &encoded_data_original_state[encoded_size..] |
496 | | ); |
497 | | |
498 | | decode_config_buf(&encoded_data[0..encoded_size], config, &mut decoded).unwrap(); |
499 | | assert_eq!(orig_data, decoded); |
500 | | } |
501 | | } |
502 | | |
503 | | #[test] |
504 | | fn encode_config_slice_fits_into_precisely_sized_slice() { |
505 | | let mut orig_data = Vec::new(); |
506 | | let mut encoded_data = Vec::new(); |
507 | | let mut decoded = Vec::new(); |
508 | | |
509 | | let input_len_range = Uniform::new(0, 1000); |
510 | | |
511 | | let mut rng = rand::rngs::SmallRng::from_entropy(); |
512 | | |
513 | | for _ in 0..10_000 { |
514 | | orig_data.clear(); |
515 | | encoded_data.clear(); |
516 | | decoded.clear(); |
517 | | |
518 | | let input_len = input_len_range.sample(&mut rng); |
519 | | |
520 | | for _ in 0..input_len { |
521 | | orig_data.push(rng.gen()); |
522 | | } |
523 | | |
524 | | let config = random_config(&mut rng); |
525 | | |
526 | | let encoded_size = encoded_size(input_len, config).unwrap(); |
527 | | |
528 | | encoded_data.resize(encoded_size, 0); |
529 | | |
530 | | assert_eq!( |
531 | | encoded_size, |
532 | | encode_config_slice(&orig_data, config, &mut encoded_data) |
533 | | ); |
534 | | |
535 | | assert_encode_sanity( |
536 | | std::str::from_utf8(&encoded_data[0..encoded_size]).unwrap(), |
537 | | config, |
538 | | input_len, |
539 | | ); |
540 | | |
541 | | decode_config_buf(&encoded_data[0..encoded_size], config, &mut decoded).unwrap(); |
542 | | assert_eq!(orig_data, decoded); |
543 | | } |
544 | | } |
545 | | |
546 | | #[test] |
547 | | fn encode_to_slice_random_valid_utf8() { |
548 | | let mut input = Vec::new(); |
549 | | let mut output = Vec::new(); |
550 | | |
551 | | let input_len_range = Uniform::new(0, 1000); |
552 | | |
553 | | let mut rng = rand::rngs::SmallRng::from_entropy(); |
554 | | |
555 | | for _ in 0..10_000 { |
556 | | input.clear(); |
557 | | output.clear(); |
558 | | |
559 | | let input_len = input_len_range.sample(&mut rng); |
560 | | |
561 | | for _ in 0..input_len { |
562 | | input.push(rng.gen()); |
563 | | } |
564 | | |
565 | | let config = random_config(&mut rng); |
566 | | |
567 | | // fill up the output buffer with garbage |
568 | | let encoded_size = encoded_size(input_len, config).unwrap(); |
569 | | for _ in 0..encoded_size { |
570 | | output.push(rng.gen()); |
571 | | } |
572 | | |
573 | | let orig_output_buf = output.to_vec(); |
574 | | |
575 | | let bytes_written = |
576 | | encode_to_slice(&input, &mut output, config.char_set.encode_table()); |
577 | | |
578 | | // make sure the part beyond bytes_written is the same garbage it was before |
579 | | assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]); |
580 | | |
581 | | // make sure the encoded bytes are UTF-8 |
582 | | let _ = str::from_utf8(&output[0..bytes_written]).unwrap(); |
583 | | } |
584 | | } |
585 | | |
586 | | #[test] |
587 | | fn encode_with_padding_random_valid_utf8() { |
588 | | let mut input = Vec::new(); |
589 | | let mut output = Vec::new(); |
590 | | |
591 | | let input_len_range = Uniform::new(0, 1000); |
592 | | |
593 | | let mut rng = rand::rngs::SmallRng::from_entropy(); |
594 | | |
595 | | for _ in 0..10_000 { |
596 | | input.clear(); |
597 | | output.clear(); |
598 | | |
599 | | let input_len = input_len_range.sample(&mut rng); |
600 | | |
601 | | for _ in 0..input_len { |
602 | | input.push(rng.gen()); |
603 | | } |
604 | | |
605 | | let config = random_config(&mut rng); |
606 | | |
607 | | // fill up the output buffer with garbage |
608 | | let encoded_size = encoded_size(input_len, config).unwrap(); |
609 | | for _ in 0..encoded_size + 1000 { |
610 | | output.push(rng.gen()); |
611 | | } |
612 | | |
613 | | let orig_output_buf = output.to_vec(); |
614 | | |
615 | | encode_with_padding(&input, config, encoded_size, &mut output[0..encoded_size]); |
616 | | |
617 | | // make sure the part beyond b64 is the same garbage it was before |
618 | | assert_eq!(orig_output_buf[encoded_size..], output[encoded_size..]); |
619 | | |
620 | | // make sure the encoded bytes are UTF-8 |
621 | | let _ = str::from_utf8(&output[0..encoded_size]).unwrap(); |
622 | | } |
623 | | } |
624 | | |
625 | | #[test] |
626 | | fn add_padding_random_valid_utf8() { |
627 | | let mut output = Vec::new(); |
628 | | |
629 | | let mut rng = rand::rngs::SmallRng::from_entropy(); |
630 | | |
631 | | // cover our bases for length % 3 |
632 | | for input_len in 0..10 { |
633 | | output.clear(); |
634 | | |
635 | | // fill output with random |
636 | | for _ in 0..10 { |
637 | | output.push(rng.gen()); |
638 | | } |
639 | | |
640 | | let orig_output_buf = output.to_vec(); |
641 | | |
642 | | let bytes_written = add_padding(input_len, &mut output); |
643 | | |
644 | | // make sure the part beyond bytes_written is the same garbage it was before |
645 | | assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]); |
646 | | |
647 | | // make sure the encoded bytes are UTF-8 |
648 | | let _ = str::from_utf8(&output[0..bytes_written]).unwrap(); |
649 | | } |
650 | | } |
651 | | |
652 | | fn assert_encoded_length(input_len: usize, encoded_len: usize, config: Config) { |
653 | | assert_eq!(encoded_len, encoded_size(input_len, config).unwrap()); |
654 | | |
655 | | let mut bytes: Vec<u8> = Vec::new(); |
656 | | let mut rng = rand::rngs::SmallRng::from_entropy(); |
657 | | |
658 | | for _ in 0..input_len { |
659 | | bytes.push(rng.gen()); |
660 | | } |
661 | | |
662 | | let encoded = encode_config(&bytes, config); |
663 | | assert_encode_sanity(&encoded, config, input_len); |
664 | | |
665 | | assert_eq!(encoded_len, encoded.len()); |
666 | | } |
667 | | |
668 | | #[test] |
669 | | fn encode_imap() { |
670 | | assert_eq!( |
671 | | encode_config(b"\xFB\xFF", crate::IMAP_MUTF7), |
672 | | encode_config(b"\xFB\xFF", crate::STANDARD_NO_PAD).replace("/", ",") |
673 | | ); |
674 | | } |
675 | | } |