1 extern crate base64;
2 extern crate rand;
3 
4 use rand::{FromEntropy, Rng};
5 
6 use base64::*;
7 
8 mod helpers;
9 use helpers::*;
10 
11 // generate random contents of the specified length and test encode/decode roundtrip
roundtrip_random( byte_buf: &mut Vec<u8>, str_buf: &mut String, config: Config, byte_len: usize, approx_values_per_byte: u8, max_rounds: u64, )12 fn roundtrip_random(
13     byte_buf: &mut Vec<u8>,
14     str_buf: &mut String,
15     config: Config,
16     byte_len: usize,
17     approx_values_per_byte: u8,
18     max_rounds: u64,
19 ) {
20     // let the short ones be short but don't let it get too crazy large
21     let num_rounds = calculate_number_of_rounds(byte_len, approx_values_per_byte, max_rounds);
22     let mut r = rand::rngs::SmallRng::from_entropy();
23     let mut decode_buf = Vec::new();
24 
25     for _ in 0..num_rounds {
26         byte_buf.clear();
27         str_buf.clear();
28         decode_buf.clear();
29         while byte_buf.len() < byte_len {
30             byte_buf.push(r.gen::<u8>());
31         }
32 
33         encode_config_buf(&byte_buf, config, str_buf);
34         decode_config_buf(&str_buf, config, &mut decode_buf).unwrap();
35 
36         assert_eq!(byte_buf, &decode_buf);
37     }
38 }
39 
calculate_number_of_rounds(byte_len: usize, approx_values_per_byte: u8, max: u64) -> u6440 fn calculate_number_of_rounds(byte_len: usize, approx_values_per_byte: u8, max: u64) -> u64 {
41     // don't overflow
42     let mut prod = approx_values_per_byte as u64;
43 
44     for _ in 0..byte_len {
45         if prod > max {
46             return max;
47         }
48 
49         prod = prod.saturating_mul(prod);
50     }
51 
52     prod
53 }
54 
no_pad_config() -> Config55 fn no_pad_config() -> Config {
56     Config::new(CharacterSet::Standard, false)
57 }
58 
59 #[test]
roundtrip_random_short_standard()60 fn roundtrip_random_short_standard() {
61     let mut byte_buf: Vec<u8> = Vec::new();
62     let mut str_buf = String::new();
63 
64     for input_len in 0..40 {
65         roundtrip_random(&mut byte_buf, &mut str_buf, STANDARD, input_len, 4, 10000);
66     }
67 }
68 
69 #[test]
roundtrip_random_with_fast_loop_standard()70 fn roundtrip_random_with_fast_loop_standard() {
71     let mut byte_buf: Vec<u8> = Vec::new();
72     let mut str_buf = String::new();
73 
74     for input_len in 40..100 {
75         roundtrip_random(&mut byte_buf, &mut str_buf, STANDARD, input_len, 4, 1000);
76     }
77 }
78 
79 #[test]
roundtrip_random_short_no_padding()80 fn roundtrip_random_short_no_padding() {
81     let mut byte_buf: Vec<u8> = Vec::new();
82     let mut str_buf = String::new();
83 
84     for input_len in 0..40 {
85         roundtrip_random(
86             &mut byte_buf,
87             &mut str_buf,
88             no_pad_config(),
89             input_len,
90             4,
91             10000,
92         );
93     }
94 }
95 
96 #[test]
roundtrip_random_no_padding()97 fn roundtrip_random_no_padding() {
98     let mut byte_buf: Vec<u8> = Vec::new();
99     let mut str_buf = String::new();
100 
101     for input_len in 40..100 {
102         roundtrip_random(
103             &mut byte_buf,
104             &mut str_buf,
105             no_pad_config(),
106             input_len,
107             4,
108             1000,
109         );
110     }
111 }
112 
113 #[test]
roundtrip_decode_trailing_10_bytes()114 fn roundtrip_decode_trailing_10_bytes() {
115     // This is a special case because we decode 8 byte blocks of input at a time as much as we can,
116     // ideally unrolled to 32 bytes at a time, in stages 1 and 2. Since we also write a u64's worth
117     // of bytes (8) to the output, we always write 2 garbage bytes that then will be overwritten by
118     // the NEXT block. However, if the next block only contains 2 bytes, it will decode to 1 byte,
119     // and therefore be too short to cover up the trailing 2 garbage bytes. Thus, we have stage 3
120     // to handle that case.
121 
122     for num_quads in 0..25 {
123         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
124         s.push_str("EFGHIJKLZg");
125 
126         let decoded = decode(&s).unwrap();
127         assert_eq!(num_quads * 3 + 7, decoded.len());
128 
129         assert_eq!(s, encode_config(&decoded, STANDARD_NO_PAD));
130     }
131 }
132 
133 #[test]
display_wrapper_matches_normal_encode()134 fn display_wrapper_matches_normal_encode() {
135     let mut bytes = Vec::<u8>::with_capacity(256);
136 
137     for i in 0..255 {
138         bytes.push(i);
139     }
140     bytes.push(255);
141 
142     assert_eq!(
143         encode(&bytes),
144         format!(
145             "{}",
146             base64::display::Base64Display::with_config(&bytes, STANDARD)
147         )
148     );
149 }
150 
151 #[test]
because_we_can()152 fn because_we_can() {
153     compare_decode("alice", "YWxpY2U=");
154     compare_decode("alice", &encode(b"alice"));
155     compare_decode("alice", &encode(&decode(&encode(b"alice")).unwrap()));
156 }
157 
158 #[test]
encode_config_slice_can_use_inline_buffer()159 fn encode_config_slice_can_use_inline_buffer() {
160     let mut buf: [u8; 22] = [0; 22];
161     let mut larger_buf: [u8; 24] = [0; 24];
162     let mut input: [u8; 16] = [0; 16];
163 
164     let mut rng = rand::rngs::SmallRng::from_entropy();
165     for elt in &mut input {
166         *elt = rng.gen();
167     }
168 
169     assert_eq!(22, encode_config_slice(&input, STANDARD_NO_PAD, &mut buf));
170     let decoded = decode_config(&buf, STANDARD_NO_PAD).unwrap();
171 
172     assert_eq!(decoded, input);
173 
174     // let's try it again with padding
175 
176     assert_eq!(24, encode_config_slice(&input, STANDARD, &mut larger_buf));
177     let decoded = decode_config(&buf, STANDARD).unwrap();
178 
179     assert_eq!(decoded, input);
180 }
181 
182 #[test]
183 #[should_panic(expected = "index 24 out of range for slice of length 22")]
encode_config_slice_panics_when_buffer_too_small()184 fn encode_config_slice_panics_when_buffer_too_small() {
185     let mut buf: [u8; 22] = [0; 22];
186     let mut input: [u8; 16] = [0; 16];
187 
188     let mut rng = rand::rngs::SmallRng::from_entropy();
189     for elt in &mut input {
190         *elt = rng.gen();
191     }
192 
193     encode_config_slice(&input, STANDARD, &mut buf);
194 }
195