1use std::collections::HashMap;
2
3pub fn encode(bytes: &Vec<u8>) -> String {
17 return encode_with_alphabet(&bytes, &String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="));
18}
19
20pub fn decode(base64_string: &String) -> Vec<u8> {
34 return decode_with_alphabet(&base64_string, &String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="));
35}
36
37pub fn encode_with_alphabet(bytes: &Vec<u8>, alphabet: &String) -> String {
53 if bytes.len() == 0 {
54 return String::from("");
55 }
56
57 if !validate_alphabet(&alphabet) {
58 panic!("Invalid alphabet!");
59 }
60
61 let pad_char: char = get_pad_char(&alphabet);
62 let fixed_alphabet: String = remove_pad_char(&alphabet);
63 let lookup_table = get_encode_lookup(&fixed_alphabet);
64 let pads: usize = get_number_of_pads(&bytes);
65
66 let mut buffer: Vec<u8> = vec![0; bytes.len() + pads];
67 let mut output: String = String::from("");
68
69 for i in 0..bytes.len() {
70 buffer[i] = bytes[i];
71 }
72
73 let mut i = 0;
74 while i < buffer.len() {
75 let mut number: usize = 0;
76 let mut segment: usize;
77 let mask: usize = 0x3f;
78
79 number += buffer[i] as usize;
80 number = number << 8;
81 number += buffer[i+1] as usize;
82 number = number << 8;
83 number += buffer[i+2] as usize;
84
85 if i == buffer.len() - 3 && pads != 0 {
86 if pads == 1 {
87 segment = (number >> 18) & mask;
88 output.push(lookup_table[segment]);
89 segment = (number >> 12) & mask;
90 output.push(lookup_table[segment]);
91 segment = (number >> 6) & mask;
92 output.push(lookup_table[segment]);
93 output.push(pad_char);
94 } else if pads == 2 {
95 segment = (number >> 18) & mask;
96 output.push(lookup_table[segment]);
97 segment = (number >> 12) & mask;
98 output.push(lookup_table[segment]);
99 output.push(pad_char);
100 output.push(pad_char);
101 } else {
102 panic!("pads had invalid value???");
103 }
104 } else {
105 segment = (number >> 18) & mask;
106 output.push(lookup_table[segment]);
107 segment = (number >> 12) & mask;
108 output.push(lookup_table[segment]);
109 segment = (number >> 6) & mask;
110 output.push(lookup_table[segment]);
111 segment = number & mask;
112 output.push(lookup_table[segment]);
113 }
114
115 i = i + 3;
116 }
117
118 return output;
119}
120
121pub fn decode_with_alphabet(base64_string: &String, alphabet: &String) -> Vec<u8> {
137 if base64_string == "" {
138 let empty_vec: Vec<u8> = vec![];
139 return empty_vec;
140 }
141
142 if !validate_alphabet(&alphabet) {
143 panic!("Invalid alphabet!");
144 }
145
146 let pad_char: char = get_pad_char(&alphabet);
147 let fixed_alphabet: String = remove_pad_char(&alphabet);
148 let decode_lookup: HashMap<char, usize> = get_decode_lookup(&fixed_alphabet, &pad_char);
149 let base64_vector: Vec<char> = base64_string.chars().collect();
150 let mut output_vector: Vec<u8> = vec![];
151 let pad_count: usize = get_pad_count(&base64_vector, &pad_char);
152
153 let mut i: usize = 0;
154 while i < base64_vector.len() {
155 let mut number: usize = 0;
156
157 number += match decode_lookup.get(&base64_vector[i]) {
158 Some(value) => *value,
159 None => panic!("Didn't find that key! {}", &base64_vector[i])
160 };
161 number = number << 6;
162 number += match decode_lookup.get(&base64_vector[i+1]) {
163 Some(value) => *value,
164 None => panic!("Didn't find that key! {}", &base64_vector[i+1])
165 };
166 number = number << 6;
167 number += match decode_lookup.get(&base64_vector[i+2]) {
168 Some(value) => *value,
169 None => panic!("Didn't find that key! {}", &base64_vector[i+2])
170 };
171 number = number << 6;
172 number += match decode_lookup.get(&base64_vector[i+3]) {
173 Some(value) => *value,
174 None => panic!("Didn't find that key! {}", &base64_vector[i+3])
175 };
176
177 if i != base64_vector.len() - 4 {
178 output_vector.push(((number & 0xff0000) >> 16) as u8);
179 output_vector.push(((number & 0x00ff00) >> 8) as u8);
180 output_vector.push((number & 0x0000ff) as u8);
181 } else {
182 if pad_count == 0 {
183 output_vector.push(((number & 0xff0000) >> 16) as u8);
184 output_vector.push(((number & 0x00ff00) >> 8) as u8);
185 output_vector.push((number & 0x0000ff) as u8);
186 } else if pad_count == 1 {
187 output_vector.push(((number & 0xff0000) >> 16) as u8);
188 output_vector.push(((number & 0x00ff00) >> 8) as u8);
189 } else if pad_count == 2 {
190 output_vector.push(((number & 0xff0000) >> 16) as u8);
191 } else {
192 panic!("Invalid pad_count");
193 }
194 }
195
196 i = i + 4;
197 }
198
199 return output_vector;
200}
201
202fn get_encode_lookup(alphabet: &String) -> Vec<char> {
204 let lookup: Vec<char> = alphabet.chars().collect();
205
206 return lookup;
207}
208
209fn get_decode_lookup(alphabet: &String, pad_char: &char) -> HashMap<char, usize> {
211 let alphabet_vector: Vec<char> = alphabet.chars().collect();
212 let mut reverse_lookup = HashMap::new();
213
214 let mut i: usize = 0;
215
216 for c in alphabet_vector {
217 reverse_lookup.insert(c, i);
218 i = i + 1;
219 }
220
221 reverse_lookup.insert(*pad_char, 0);
222
223 return reverse_lookup;
224}
225
226fn remove_pad_char(alphabet: &String) -> String {
228 let mut alphabet_vector: Vec<char> = alphabet.chars().collect();
229 let index = alphabet_vector.len() - 1;
230 let mut result: String = String::from("");
231
232 alphabet_vector.remove(index);
233
234 for c in alphabet_vector {
235 result.push(c);
236 }
237
238 return result;
239}
240
241fn get_number_of_pads(bytes: &Vec<u8>) -> usize {
243
244 if bytes.len() % 3 != 0 {
245 return 3 - (bytes.len() % 3);
246 }
247
248 return 0;
249}
250
251fn get_pad_char(alphabet: &String) -> char {
253 let alphabet_vector: Vec<char> = alphabet.chars().collect();
255
256 return alphabet_vector[alphabet_vector.len() - 1];
257}
258
259fn get_pad_count(bytes: &Vec<char>, pad_char: &char) -> usize {
261 let mut pad_count = 0;
262 let mut i: usize = 0;
263
264 while i < bytes.len() {
265 if bytes[i] == *pad_char {
266 pad_count += 1;
267 }
268 i = i + 1;
269 }
270
271 return pad_count;
272}
273
274fn validate_alphabet(alphabet: &String) -> bool {
276 let alphabet_vector: Vec<char> = alphabet.chars().collect();
277
278 if alphabet_vector.len() != 65 {
279 println!("Invalid alphabet length! {}", alphabet_vector.len());
280 return false;
281 }
282
283 let mut lookup_map: HashMap<char, bool> = HashMap::new();
284
285 for c in &alphabet_vector {
286 if lookup_map.contains_key(c) {
287 println!("Duplicate key! {}", &c);
288 return false;
289 } else {
290 lookup_map.insert(*c, true);
291 }
292 }
293
294 return true;
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 fn get_plain_strings() -> Vec<String> {
303 let plain_strings: Vec<String> = vec![
304 String::from("Foo Bar"),
305 String::from("This string\nhas newlines and\ttabs"),
306 String::from("mañana"),
307 String::from("Iñtërnâtiônàlizætiøn☃💩"),
308 String::from("🇺🇸🇺🇸")
309 ];
310
311 return plain_strings;
312 }
313
314 fn get_base64_strings() -> Vec<String> {
316 let base64_strings: Vec<String> = vec![
317 String::from("Rm9vIEJhcg=="),
318 String::from("VGhpcyBzdHJpbmcKaGFzIG5ld2xpbmVzIGFuZAl0YWJz"),
319 String::from("bWHDsWFuYQ=="),
320 String::from("ScOxdMOrcm7DonRpw7Ruw6BsaXrDpnRpw7hu4piD8J+SqQ=="),
321 String::from("8J+HuvCfh7jwn4e68J+HuA==")
322 ];
323
324 return base64_strings;
325 }
326
327 fn get_nonstandard_base64_strings() -> Vec<String> {
329 let nonstandard_base64_strings: Vec<String> = vec![
330 String::from("РёэоЗДИбЬа++"),
331 String::from("ХЁбиЬсБтЭЖИиЫёЬЙЪЁЕтЗЁщеЭцриЫёХтЗЁЕнЩАефШЦИт"),
332 String::from("ЫЦЖГлЦЕнШП++"),
333 String::from("СЬНрЭЛНкЬёыГзжРипыРнпъБлЪЧкГижРипыбншивГьИюСйП++"),
334 String::from("ьИюЖноВЯбыгпжшЮъьИюЖнА++")
335 ];
336
337 return nonstandard_base64_strings;
338 }
339
340 fn get_alphabets() -> Vec<String> {
342 let alphabet_strings: Vec<String> = vec![
343 String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="),
345 String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/="),
346 String::from("АБВГДЕЁЖЗИЙКЛМНОПРСТФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстфхцчшщъыьэюя+"),
347 String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZабвгдеёжзийклмнопрстyфхцчшщъыьэюя01234+"),
348 String::from("Foo"),
350 String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/#$%"),
351 String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123abcdefghijklmnopqrstuvwxyz0123456789+/")
352 ];
353
354 return alphabet_strings;
355 }
356
357 mod encode_tests {
358 use super::*;
359
360 #[test]
362 fn should_encode_empty_vector_correctly() {
363 let actual = encode(&vec![]);
364 assert!(actual == String::from(""));
365 }
366
367 #[test]
368 fn should_encode_plain_strings_correctly() {
369 let plain_strings = get_plain_strings();
370 let base64_strings = get_base64_strings();
371
372 let mut i: usize = 0;
373 while i < plain_strings.len() {
374 let test_vector = &plain_strings[i].as_bytes().to_vec();
375 let actual = encode(&test_vector);
376 assert_eq!(actual, base64_strings[i]);
377 i = i + 1;
378 }
379 }
380 }
381
382 mod decode_tests {
383 use super::*;
384
385 #[test]
387 fn decode_empty_string_should_return_empty_vector() {
388 let actual: Vec<u8> = decode(&String::from(""));
389 assert_eq!(actual, []);
390 }
391
392 #[test]
393 fn decode_should_decode_base64_strings_correctly() {
394 let base64_strings = get_base64_strings();
395 let plain_strings = get_plain_strings();
396
397 let mut i: usize = 0;
398 while i < base64_strings.len() {
399 let test_string = &base64_strings[i];
400 let actual: String = match String::from_utf8(decode(&test_string)) {
401 Ok(s) => s,
402 Err(e) => panic!("Invalid utf-8 sequence: {:?}", e),
403 };
404
405 assert_eq!(actual, plain_strings[i]);
406 i = i + 1;
407 }
408 }
409 }
410
411 mod encode_with_alphabet_tests {
412 use super::*;
413
414 #[test]
417 fn encode_with_alphabet_should_encode_empty_vector_correctly() {
418 let alphabet_vector: Vec<String> = get_alphabets();
419 let actual = encode_with_alphabet(&vec![], &alphabet_vector[2]);
420 assert!(actual == String::from(""));
421 }
422
423 #[test]
424 fn encode_with_alphabet_should_encode_plain_strings_correctly() {
425 let alphabet_vector: Vec<String> = get_alphabets();
426 let plain_strings = get_plain_strings();
427 let nonstandard_base64_strings = get_nonstandard_base64_strings();
428
429 let mut i: usize = 0;
430 while i < plain_strings.len() {
431 let test_vector = &plain_strings[i].as_bytes().to_vec();
432 let actual = encode_with_alphabet(&test_vector, &alphabet_vector[2]);
433 assert_eq!(actual, nonstandard_base64_strings[i]);
434 i = i + 1;
435 }
436 }
437 }
438
439 mod decode_with_alphabet_tests {
440 use super::*;
441
442 #[test]
445 fn decode_with_alphabet_should_decode_empty_string_correctly() {
446 let alphabet_vector: Vec<String> = get_alphabets();
447 let actual: Vec<u8> = decode_with_alphabet(&String::from(""), &alphabet_vector[2]);
448 assert_eq!(actual, []);
449 }
450
451 #[test]
452 fn decode_with_alphabet_should_decode_base64_strings_correctly() {
453 let alphabet_vector: Vec<String> = get_alphabets();
454 let nonstandard_base64_strings = get_nonstandard_base64_strings();
455 let plain_strings = get_plain_strings();
456
457 let mut i: usize = 0;
458 while i < nonstandard_base64_strings.len() {
459 let test_string = &nonstandard_base64_strings[i];
460 let actual: String = match String::from_utf8(decode_with_alphabet(&test_string, &alphabet_vector[2])) {
461 Ok(s) => s,
462 Err(e) => panic!("Invalid utf-8 sequence: {:?}", e),
463 };
464
465 println!("actual = {}, plain_strings[{}] = {}", actual, i, plain_strings[i]);
466
467 assert_eq!(actual, plain_strings[i]);
468 i = i + 1;
469 }
470 }
471 }
472
473 mod get_encode_lookup_tests {
474 use super::*;
475
476 #[test]
477 fn should_give_expected_output() {
478 let test_string: String = String::from("Test");
479 let expected: Vec<char> = vec!['T', 'e', 's', 't'];
480
481 assert_eq!(expected, get_encode_lookup(&test_string));
482 }
483 }
484
485 mod get_decode_lookup_tests {
486 use super::*;
487
488 #[test]
489 fn should_build_decode_lookup_correctly() {
490 let mut test_data: HashMap<char, usize> = HashMap::new();
491
492 test_data.insert('A', 0);
493 test_data.insert('B', 1);
494 test_data.insert('C', 2);
495 test_data.insert('+', 0);
496
497 let test_alphabet: String = String::from("ABC");
498 let pad_char: char = '+';
499
500 assert_eq!(test_data, get_decode_lookup(&test_alphabet, &pad_char));
501 }
502 }
503
504 mod get_pad_char_tests {
506 use super::*;
507
508 #[test]
509 fn get_pad_char_should_return_correct_char() {
510 let alphabet = String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
511 assert_eq!(get_pad_char(&alphabet), '=');
512 }
513
514 #[test]
515 fn get_pad_char_should_return_correct_char_nonstandard_alphabet() {
516 let alphabet = String::from("АБВГДЕЁЖЗИЙКЛМНОПРСТФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстфхцчшщъыьэюя+");
517 assert_eq!(get_pad_char(&alphabet), '+');
518 }
519 }
520
521 mod remove_pad_char_tests {
522 use super::*;
523
524 #[test]
525 fn remove_pad_char_should_return_modified_alphabet_standard() {
526 let alphabet = String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
527 let expected = String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
528 assert_eq!(remove_pad_char(&alphabet), expected);
529 }
530
531 #[test]
532 fn remove_pad_char_should_return_modified_alphabet_nonstandard() {
533 let alphabet = String::from("АБВГДЕЁЖЗИЙКЛМНОПРСТФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстфхцчшщъыьэюя+");
534 let expected = String::from("АБВГДЕЁЖЗИЙКЛМНОПРСТФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстфхцчшщъыьэюя");
535 assert_eq!(remove_pad_char(&alphabet), expected);
536 }
537 }
538
539 mod get_number_of_pads_tests {
540 use super::*;
541
542 #[test]
543 fn get_number_of_pads_should_return_zero() {
544 let test_vec = String::from("Foo").into_bytes();
545 assert!(get_number_of_pads(&test_vec) == 0);
546 }
547
548 #[test]
549 fn get_number_of_pads_should_return_one() {
550 let test_vec = String::from("Fooox").into_bytes();
551 assert!(get_number_of_pads(&test_vec) == 1);
552 }
553
554 #[test]
555 fn get_number_of_pads_should_return_two() {
556 let test_vec = String::from("Foo Bar").into_bytes();
557 assert!(get_number_of_pads(&test_vec) == 2);
558 }
559 }
560
561 mod get_pad_count_tests {
562 use super::*;
563
564 #[test]
565 fn get_pad_count_should_return_zero() {
566 let input: Vec<char> = String::from("FooBar").chars().collect();
567 let pad_char: char = '=';
568 let actual: usize = get_pad_count(&input, &pad_char);
569
570 assert_eq!(actual, 0);
571 }
572
573 #[test]
574 fn get_pad_count_should_return_one() {
575 let input: Vec<char> = String::from("FooBar=").chars().collect();
576 let pad_char: char = '=';
577 let actual: usize = get_pad_count(&input, &pad_char);
578
579 assert_eq!(actual, 1);
580 }
581
582 #[test]
583 fn get_pad_count_should_return_two() {
584 let input: Vec<char> = String::from("FooBar==").chars().collect();
585 let pad_char: char = '=';
586 let actual: usize = get_pad_count(&input, &pad_char);
587
588 assert_eq!(actual, 2);
589 }
590 }
591
592 mod validate_alphabet_tests {
593 use super::*;
594
595 #[test]
596 fn validate_alphabet_should_return_true_with_standard_alphabet() {
597 let alphabet_vector: Vec<String> = get_alphabets();
598 assert_eq!(validate_alphabet(&alphabet_vector[0]), true);
599 }
600
601 #[test]
602 fn validate_alphabet_should_return_true_with_nonstandard_alphabet() {
603 let alphabet_vector: Vec<String> = get_alphabets();
604 assert_eq!(validate_alphabet(&alphabet_vector[2]), true);
605 }
606
607 #[test]
608 fn validate_alphabet_should_return_false_with_short_alphabet() {
609 let alphabet_vector: Vec<String> = get_alphabets();
610 assert_eq!(validate_alphabet(&alphabet_vector[4]), false);
611 }
612
613 #[test]
614 fn validate_alphabet_should_return_false_with_long_alphabet() {
615 let alphabet_vector: Vec<String> = get_alphabets();
616 assert_eq!(validate_alphabet(&alphabet_vector[5]), false);
617 }
618
619 #[test]
620 fn validate_alphabet_should_return_false_with_nonunique_symbols() {
621 let alphabet_vector: Vec<String> = get_alphabets();
622 assert_eq!(validate_alphabet(&alphabet_vector[6]), false);
623 }
624 }
625}