1use std::{
2 borrow::Borrow,
3 fmt,
4 ops::{Deref, DerefMut},
5 str::FromStr,
6};
7
8use crate::{
9 lexical::{self, LexicalFormOf},
10 Datatype, ParseXsd, XsdValue,
11};
12
13const CHARS: [char; 64] = [
14 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
15 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
16 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
17 '5', '6', '7', '8', '9', '+', '/',
18];
19
20const PADDING: char = '=';
21
22#[derive(Debug, thiserror::Error)]
23#[error("invalid base64")]
24pub struct InvalidBase64;
25
26#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct Base64BinaryBuf(Vec<u8>);
28
29impl Base64BinaryBuf {
30 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn from_bytes(bytes: Vec<u8>) -> Self {
35 Self(bytes)
36 }
37
38 pub fn decode(input: impl AsRef<[u8]>) -> Result<Self, InvalidBase64> {
39 let input = input.as_ref();
40 let mut bytes = Vec::with_capacity(input.len() * 3 / 4);
41 let mut buffer = 0u16;
42 let mut buffer_len = 0u16;
43 let mut padding = false;
44
45 for &c in input {
46 if c == 0x20 {
47 continue;
48 }
49
50 if padding {
51 if c != b'=' {
52 return Err(InvalidBase64);
53 }
54 } else if c == b'=' {
55 padding = true
56 } else {
57 buffer_len += 6;
58 buffer = buffer << 6 | decode_char(c)? as u16;
59
60 while buffer_len >= 8 {
61 buffer_len -= 8;
62 let b = buffer >> buffer_len;
63 bytes.push(b as u8)
64 }
65 }
66 }
67
68 Ok(Self(bytes))
69 }
70
71 pub fn into_bytes(self) -> Vec<u8> {
72 self.0
73 }
74
75 pub fn as_bytes(&self) -> &[u8] {
76 &self.0
77 }
78
79 pub fn as_base64_binary(&self) -> &Base64Binary {
80 Base64Binary::new(&self.0)
81 }
82
83 pub fn as_base64_binary_mut(&mut self) -> &mut Base64Binary {
84 Base64Binary::new_mut(&mut self.0)
85 }
86}
87
88fn decode_char(c: u8) -> Result<u8, InvalidBase64> {
89 match c {
90 b'A'..=b'Z' => Ok(c - b'A'),
91 b'a'..=b'z' => Ok(c - b'a' + 26),
92 b'0'..=b'9' => Ok(c - b'0' + 52),
93 b'+' => Ok(62),
94 b'/' => Ok(63),
95 _ => Err(InvalidBase64),
96 }
97}
98
99impl fmt::Display for Base64BinaryBuf {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 self.as_base64_binary().fmt(f)
102 }
103}
104
105impl From<Vec<u8>> for Base64BinaryBuf {
106 fn from(value: Vec<u8>) -> Self {
107 Base64BinaryBuf::from_bytes(value)
108 }
109}
110
111impl FromStr for Base64BinaryBuf {
112 type Err = InvalidBase64;
113
114 fn from_str(s: &str) -> Result<Self, Self::Err> {
115 Self::decode(s)
116 }
117}
118
119impl AsRef<[u8]> for Base64BinaryBuf {
120 fn as_ref(&self) -> &[u8] {
121 self.as_bytes()
122 }
123}
124
125impl AsRef<Base64Binary> for Base64BinaryBuf {
126 fn as_ref(&self) -> &Base64Binary {
127 self.as_base64_binary()
128 }
129}
130
131impl Borrow<Base64Binary> for Base64BinaryBuf {
132 fn borrow(&self) -> &Base64Binary {
133 self.as_base64_binary()
134 }
135}
136
137impl Deref for Base64BinaryBuf {
138 type Target = Base64Binary;
139
140 fn deref(&self) -> &Self::Target {
141 self.as_base64_binary()
142 }
143}
144
145impl DerefMut for Base64BinaryBuf {
146 fn deref_mut(&mut self) -> &mut Self::Target {
147 self.as_base64_binary_mut()
148 }
149}
150
151impl XsdValue for Base64BinaryBuf {
152 fn datatype(&self) -> Datatype {
153 Datatype::Base64Binary
154 }
155}
156
157impl ParseXsd for Base64BinaryBuf {
158 type LexicalForm = lexical::Base64Binary;
159}
160
161impl LexicalFormOf<Base64BinaryBuf> for lexical::Base64Binary {
162 type ValueError = std::convert::Infallible;
163
164 fn try_as_value(&self) -> Result<Base64BinaryBuf, Self::ValueError> {
165 Ok(self.value())
166 }
167}
168
169#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
170pub struct Base64Binary([u8]);
171
172impl Base64Binary {
173 pub fn new(bytes: &[u8]) -> &Self {
174 unsafe { std::mem::transmute(bytes) }
175 }
176
177 pub fn new_mut(bytes: &mut [u8]) -> &mut Self {
178 unsafe { std::mem::transmute(bytes) }
179 }
180
181 pub fn chars(&self) -> Chars {
182 Chars {
183 offset: 0,
184 rest: 0,
185 padding: false,
186 bytes: self.0.iter(),
187 }
188 }
189
190 pub fn as_bytes(&self) -> &[u8] {
191 &self.0
192 }
193}
194
195impl<'a> From<&'a [u8]> for &'a Base64Binary {
196 fn from(value: &'a [u8]) -> Self {
197 Base64Binary::new(value)
198 }
199}
200
201impl<'a> From<&'a mut [u8]> for &'a Base64Binary {
202 fn from(value: &'a mut [u8]) -> Self {
203 Base64Binary::new(value)
204 }
205}
206
207impl<'a> From<&'a mut [u8]> for &'a mut Base64Binary {
208 fn from(value: &'a mut [u8]) -> Self {
209 Base64Binary::new_mut(value)
210 }
211}
212
213impl fmt::Display for Base64Binary {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 for c in self.chars() {
216 c.fmt(f)?;
217 }
218
219 Ok(())
220 }
221}
222
223impl ToOwned for Base64Binary {
224 type Owned = Base64BinaryBuf;
225
226 fn to_owned(&self) -> Self::Owned {
227 Base64BinaryBuf::from_bytes(self.as_bytes().to_vec())
228 }
229}
230
231pub struct Chars<'a> {
232 offset: u8,
233 rest: u8,
234 padding: bool,
235 bytes: std::slice::Iter<'a, u8>,
236}
237
238impl<'a> Iterator for Chars<'a> {
239 type Item = char;
240
241 fn next(&mut self) -> Option<Self::Item> {
242 if self.padding {
243 if self.offset <= 6 {
244 self.offset += 2;
245 Some(PADDING)
246 } else {
247 None
248 }
249 } else if self.offset == 6 {
250 let sextet = self.rest;
251 self.rest = 0;
252 self.offset = 0;
253 Some(CHARS[sextet as usize])
254 } else {
255 match self.bytes.next() {
256 Some(b) => {
257 let sextet = self.rest | (b >> 2 >> self.offset & 0b111111);
258 self.offset += 2;
259 self.rest = b << (6 - self.offset) & 0b111111;
260 Some(CHARS[sextet as usize])
261 }
262 None => {
263 if self.offset > 0 {
264 let sextet = self.rest;
265 self.offset += 2;
266 self.padding = true;
267 Some(CHARS[sextet as usize])
268 } else {
269 None
270 }
271 }
272 }
273 }
274 }
275}
276
277impl XsdValue for Base64Binary {
278 fn datatype(&self) -> Datatype {
279 Datatype::Base64Binary
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 const TESTS: [(&'static [u8], &'static str); 9] = [
288 (b"M", "TQ=="),
289 (b"Ma", "TWE="),
290 (b"Man", "TWFu"),
291 (b"light w", "bGlnaHQgdw=="),
292 (b"light wo", "bGlnaHQgd28="),
293 (b"light wor", "bGlnaHQgd29y"),
294 (b"light work", "bGlnaHQgd29yaw=="),
295 (b"light work.", "bGlnaHQgd29yay4="),
296 (
297 b"Many hands make light work.",
298 "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu",
299 ),
300 ];
301
302 #[test]
303 fn encoding() {
304 for (input, expected) in TESTS {
305 let output = Base64Binary::new(input).to_string();
306 assert_eq!(output, expected)
307 }
308 }
309
310 #[test]
311 fn decoding() {
312 for (expected, input) in TESTS {
313 let output = Base64BinaryBuf::decode(input).unwrap();
314 assert_eq!(output.as_bytes(), expected)
315 }
316 }
317}