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