Skip to main content

chromaprint/codec/
base64.rs

1use crate::error::{Error, Result};
2
3const ENCODE_TABLE: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4
5#[rustfmt::skip]
6static DECODE_TABLE: [u8; 256] = {
7    let mut table = [0u8; 256];
8    table[b'A' as usize] = 0;  table[b'B' as usize] = 1;  table[b'C' as usize] = 2;
9    table[b'D' as usize] = 3;  table[b'E' as usize] = 4;  table[b'F' as usize] = 5;
10    table[b'G' as usize] = 6;  table[b'H' as usize] = 7;  table[b'I' as usize] = 8;
11    table[b'J' as usize] = 9;  table[b'K' as usize] = 10; table[b'L' as usize] = 11;
12    table[b'M' as usize] = 12; table[b'N' as usize] = 13; table[b'O' as usize] = 14;
13    table[b'P' as usize] = 15; table[b'Q' as usize] = 16; table[b'R' as usize] = 17;
14    table[b'S' as usize] = 18; table[b'T' as usize] = 19; table[b'U' as usize] = 20;
15    table[b'V' as usize] = 21; table[b'W' as usize] = 22; table[b'X' as usize] = 23;
16    table[b'Y' as usize] = 24; table[b'Z' as usize] = 25;
17    table[b'a' as usize] = 26; table[b'b' as usize] = 27; table[b'c' as usize] = 28;
18    table[b'd' as usize] = 29; table[b'e' as usize] = 30; table[b'f' as usize] = 31;
19    table[b'g' as usize] = 32; table[b'h' as usize] = 33; table[b'i' as usize] = 34;
20    table[b'j' as usize] = 35; table[b'k' as usize] = 36; table[b'l' as usize] = 37;
21    table[b'm' as usize] = 38; table[b'n' as usize] = 39; table[b'o' as usize] = 40;
22    table[b'p' as usize] = 41; table[b'q' as usize] = 42; table[b'r' as usize] = 43;
23    table[b's' as usize] = 44; table[b't' as usize] = 45; table[b'u' as usize] = 46;
24    table[b'v' as usize] = 47; table[b'w' as usize] = 48; table[b'x' as usize] = 49;
25    table[b'y' as usize] = 50; table[b'z' as usize] = 51;
26    table[b'0' as usize] = 52; table[b'1' as usize] = 53; table[b'2' as usize] = 54;
27    table[b'3' as usize] = 55; table[b'4' as usize] = 56; table[b'5' as usize] = 57;
28    table[b'6' as usize] = 58; table[b'7' as usize] = 59; table[b'8' as usize] = 60;
29    table[b'9' as usize] = 61;
30    table[b'-' as usize] = 62;
31    table[b'_' as usize] = 63;
32    table
33};
34
35/// Encode binary data to chromaprint's URL-safe base64 (no padding).
36pub fn encode(data: &[u8]) -> String {
37    let mut result = String::with_capacity((data.len() * 4 + 2) / 3);
38    let mut i = 0;
39    let size = data.len();
40
41    while i + 3 <= size {
42        let s0 = data[i];
43        let s1 = data[i + 1];
44        let s2 = data[i + 2];
45        result.push(ENCODE_TABLE[((s0 >> 2) & 63) as usize] as char);
46        result.push(ENCODE_TABLE[(((s0 << 4) | (s1 >> 4)) & 63) as usize] as char);
47        result.push(ENCODE_TABLE[(((s1 << 2) | (s2 >> 6)) & 63) as usize] as char);
48        result.push(ENCODE_TABLE[(s2 & 63) as usize] as char);
49        i += 3;
50    }
51
52    let remaining = size - i;
53    if remaining == 2 {
54        let s0 = data[i];
55        let s1 = data[i + 1];
56        result.push(ENCODE_TABLE[((s0 >> 2) & 63) as usize] as char);
57        result.push(ENCODE_TABLE[(((s0 << 4) | (s1 >> 4)) & 63) as usize] as char);
58        result.push(ENCODE_TABLE[((s1 << 2) & 63) as usize] as char);
59    } else if remaining == 1 {
60        let s0 = data[i];
61        result.push(ENCODE_TABLE[((s0 >> 2) & 63) as usize] as char);
62        result.push(ENCODE_TABLE[((s0 << 4) & 63) as usize] as char);
63    }
64
65    result
66}
67
68/// Decode chromaprint's URL-safe base64 (no padding) to binary data.
69pub fn decode(input: &str) -> Result<Vec<u8>> {
70    let bytes = input.as_bytes();
71    let size = bytes.len();
72    let mut result = Vec::with_capacity(size * 3 / 4);
73    let mut i = 0;
74
75    while i + 4 <= size {
76        let b0 = DECODE_TABLE[bytes[i] as usize];
77        let b1 = DECODE_TABLE[bytes[i + 1] as usize];
78        let b2 = DECODE_TABLE[bytes[i + 2] as usize];
79        let b3 = DECODE_TABLE[bytes[i + 3] as usize];
80        result.push((b0 << 2) | (b1 >> 4));
81        result.push((b1 << 4) | (b2 >> 2));
82        result.push((b2 << 6) | b3);
83        i += 4;
84    }
85
86    let remaining = size - i;
87    if remaining == 3 {
88        let b0 = DECODE_TABLE[bytes[i] as usize];
89        let b1 = DECODE_TABLE[bytes[i + 1] as usize];
90        let b2 = DECODE_TABLE[bytes[i + 2] as usize];
91        result.push((b0 << 2) | (b1 >> 4));
92        result.push((b1 << 4) | (b2 >> 2));
93    } else if remaining == 2 {
94        let b0 = DECODE_TABLE[bytes[i] as usize];
95        let b1 = DECODE_TABLE[bytes[i + 1] as usize];
96        result.push((b0 << 2) | (b1 >> 4));
97    } else if remaining == 1 {
98        return Err(Error::InvalidFingerprint);
99    }
100
101    Ok(result)
102}