wowlab_parsers/parsers/parcel/
base64.rs1use base64::{Engine as _, engine::general_purpose::STANDARD};
4
5use super::errors::ParcelError;
6
7pub(super) fn is_base64_alphabet(s: &str) -> bool {
9 s.bytes().all(|b| {
10 matches!(
11 b,
12 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'+' | b'/' | b'='
13 )
14 })
15}
16
17pub(super) fn encode(bytes: &[u8]) -> String {
18 STANDARD.encode(bytes)
19}
20
21pub(super) fn decode(s: &str) -> Result<Vec<u8>, ParcelError> {
22 Ok(STANDARD.decode(s)?)
23}
24
25#[cfg(test)]
26mod tests {
27 use googletest::prelude::*;
28
29 use super::*;
30
31 #[gtest]
32 fn round_trip_basic() -> Result<()> {
33 verify_that!(encode(b""), eq(""))?;
34 verify_that!(encode(b"hello"), eq("aGVsbG8="))?;
35 verify_that!(decode("aGVsbG8=").or_fail()?, eq(b"hello"))?;
36 verify_that!(decode("").or_fail()?, eq(b""))?;
37
38 Ok(())
39 }
40
41 #[gtest]
42 fn rejects_missing_or_extra_padding() -> Result<()> {
43 verify_true!(decode("aGVsbG8").is_err())?;
44 verify_true!(decode("aGVsbG8==").is_err())?;
45
46 Ok(())
47 }
48
49 #[gtest]
50 fn rejects_non_canonical_padding() -> Result<()> {
51 for s in ["AB==", "AC==", "AD==", "AAB=", "AAC=", "AAD="] {
52 verify_true!(decode(s).is_err())?;
53 }
54
55 Ok(())
56 }
57
58 #[gtest]
59 fn accepts_canonical_padding() -> Result<()> {
60 verify_that!(decode("AA==").or_fail()?, eq(b"\0"))?;
61 verify_that!(decode("AAA=").or_fail()?, eq(b"\0\0"))?;
62 verify_that!(decode("AAAA").or_fail()?, eq(b"\0\0\0"))?;
63
64 Ok(())
65 }
66
67 #[gtest]
68 fn alphabet_check() -> Result<()> {
69 verify_true!(is_base64_alphabet("aGVsbG8="))?;
70 verify_true!(is_base64_alphabet("ABCabc012+/="))?;
71 verify_true!(is_base64_alphabet(""))?;
72 verify_false!(is_base64_alphabet("aGVs*G8="))?;
73 verify_false!(is_base64_alphabet("aGVs G8="))?;
74 verify_false!(is_base64_alphabet("aGVs\nG8="))?;
75 verify_false!(is_base64_alphabet("aGVs-G8="))?;
76
77 Ok(())
78 }
79}