1#![no_std]
2
3extern crate alloc;
4
5#[cfg(test)]
6extern crate quickcheck;
7
8use alloc::string::String;
9use alloc::vec::Vec;
10use core::cmp::min;
11
12#[derive(Copy, Clone)]
13pub enum Alphabet {
14 Crockford,
15 Rfc4648 { padding: bool },
16 Rfc4648Lower { padding: bool },
17 Rfc4648Hex { padding: bool },
18 Rfc4648HexLower { padding: bool },
19 Z,
20}
21
22const CROCKFORD: &'static [u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
23const RFC4648: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
24const RFC4648_LOWER: &'static [u8] = b"abcdefghijklmnopqrstuvwxyz234567";
25const RFC4648_HEX: &'static [u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUV";
26const RFC4648_HEX_LOWER: &'static [u8] = b"0123456789abcdefghijklmnopqrstuv";
27const Z: &'static [u8] = b"ybndrfg8ejkmcpqxot1uwisza345h769";
28
29pub fn encode(alphabet: Alphabet, data: &[u8]) -> String {
30 let (alphabet, padding) = match alphabet {
31 Alphabet::Crockford => (CROCKFORD, false),
32 Alphabet::Rfc4648 { padding } => (RFC4648, padding),
33 Alphabet::Rfc4648Lower { padding } => (RFC4648_LOWER, padding),
34 Alphabet::Rfc4648Hex { padding } => (RFC4648_HEX, padding),
35 Alphabet::Rfc4648HexLower { padding } => (RFC4648_HEX_LOWER, padding),
36 Alphabet::Z => (Z, false),
37 };
38 let mut ret = Vec::with_capacity((data.len() + 3) / 4 * 5);
39
40 for chunk in data.chunks(5) {
41 let buf = {
42 let mut buf = [0u8; 5];
43 for (i, &b) in chunk.iter().enumerate() {
44 buf[i] = b;
45 }
46 buf
47 };
48 ret.push(alphabet[((buf[0] & 0xF8) >> 3) as usize]);
49 ret.push(alphabet[(((buf[0] & 0x07) << 2) | ((buf[1] & 0xC0) >> 6)) as usize]);
50 ret.push(alphabet[((buf[1] & 0x3E) >> 1) as usize]);
51 ret.push(alphabet[(((buf[1] & 0x01) << 4) | ((buf[2] & 0xF0) >> 4)) as usize]);
52 ret.push(alphabet[(((buf[2] & 0x0F) << 1) | (buf[3] >> 7)) as usize]);
53 ret.push(alphabet[((buf[3] & 0x7C) >> 2) as usize]);
54 ret.push(alphabet[(((buf[3] & 0x03) << 3) | ((buf[4] & 0xE0) >> 5)) as usize]);
55 ret.push(alphabet[(buf[4] & 0x1F) as usize]);
56 }
57
58 if data.len() % 5 != 0 {
59 let len = ret.len();
60 let num_extra = 8 - (data.len() % 5 * 8 + 4) / 5;
61 if padding {
62 for i in 1..num_extra + 1 {
63 ret[len - i] = b'=';
64 }
65 } else {
66 ret.truncate(len - num_extra);
67 }
68 }
69
70 String::from_utf8(ret).unwrap()
71}
72
73const CROCKFORD_INV: [i8; 75] = [
81 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12,
82 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, 0, 22, 23, 24, 25, 26, -1, 27, 28,
83 29, 30, 31, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19,
84 1, 20, 21, 0, 22, 23, 24, 25, 26, -1, 27, 28, 29, 30, 31,
85];
86const RFC4648_INV: [i8; 75] = [
87 -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2,
88 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
89 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
90 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
91];
92const RFC4648_INV_PAD: [i8; 75] = [
93 -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2,
94 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
95 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
96 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
97];
98const RFC4648_INV_LOWER: [i8; 75] = [
99 -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
100 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
101 -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
102 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
103];
104const RFC4648_INV_LOWER_PAD: [i8; 75] = [
105 -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1,
106 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
107 -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
108 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
109];
110const RFC4648_INV_HEX: [i8; 75] = [
111 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12,
112 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, -1,
113 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
114 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
115];
116const RFC4648_INV_HEX_PAD: [i8; 75] = [
117 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, 0, -1, -1, -1, 10, 11, 12,
118 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, -1,
119 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
121];
122const RFC4648_INV_HEX_LOWER: [i8; 75] = [
123 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
124 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
125 -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
126 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
127];
128const RFC4648_INV_HEX_LOWER_PAD: [i8; 75] = [
129 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1,
130 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
131 -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
132 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
133];
134const Z_INV: [i8; 75] = [
135 -1, 18, -1, 25, 26, 27, 30, 29, 7, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
136 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
137 -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 1, 12, 3, 8, 5, 6, 28, 21, 9, 10,
138 -1, 11, 2, 16, 13, 14, 4, 22, 17, 19, -1, 20, 15, 0, 23,
139];
140
141pub fn decode(alphabet: Alphabet, data: &str) -> Option<Vec<u8>> {
142 if !data.is_ascii() {
143 return None;
144 }
145 let data = data.as_bytes();
146 let alphabet = match alphabet {
147 Alphabet::Crockford => CROCKFORD_INV, Alphabet::Rfc4648 { padding } => if padding { RFC4648_INV_PAD } else { RFC4648_INV }
149 Alphabet::Rfc4648Lower { padding } => if padding { RFC4648_INV_LOWER_PAD } else { RFC4648_INV_LOWER }
150 Alphabet::Rfc4648Hex { padding } => if padding { RFC4648_INV_HEX_PAD } else { RFC4648_INV_HEX }
151 Alphabet::Rfc4648HexLower { padding } => if padding { RFC4648_INV_HEX_LOWER_PAD } else { RFC4648_INV_HEX_LOWER }
152 Alphabet::Z => Z_INV,
153 };
154 let mut unpadded_data_length = data.len();
155 for i in 1..min(6, data.len()) + 1 {
156 if data[data.len() - i] != b'=' {
157 break;
158 }
159 unpadded_data_length -= 1;
160 }
161 let output_length = unpadded_data_length * 5 / 8;
162 let mut ret = Vec::with_capacity((output_length + 4) / 5 * 5);
163 for chunk in data.chunks(8) {
164 let buf = {
165 let mut buf = [0u8; 8];
166 for (i, &c) in chunk.iter().enumerate() {
167 match alphabet.get(c.wrapping_sub(b'0') as usize) {
168 Some(&-1) | None => return None,
169 Some(&value) => buf[i] = value as u8,
170 };
171 }
172 buf
173 };
174 ret.push((buf[0] << 3) | (buf[1] >> 2));
175 ret.push((buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4));
176 ret.push((buf[3] << 4) | (buf[4] >> 1));
177 ret.push((buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3));
178 ret.push((buf[6] << 5) | buf[7]);
179 }
180 ret.truncate(output_length);
181 Some(ret)
182}
183
184#[cfg(test)]
185#[allow(dead_code, unused_attributes)]
186mod test {
187 use super::Alphabet::{Crockford, Rfc4648, Rfc4648Hex, Rfc4648HexLower, Rfc4648Lower, Z};
188 use super::{decode, encode};
189 use alloc::string::String;
190 use alloc::vec::Vec;
191 use core::fmt::{Debug, Error, Formatter};
192 use quickcheck::{Arbitrary, Gen};
193
194 #[derive(Clone)]
195 struct B32 {
196 c: u8,
197 }
198
199 impl Arbitrary for B32 {
200 fn arbitrary(g: &mut Gen) -> B32 {
201 B32 {
202 c: *g.choose(b"0123456789ABCDEFGHJKMNPQRSTVWXYZ").unwrap(),
203 }
204 }
205 }
206
207 impl Debug for B32 {
208 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
209 (self.c as char).fmt(f)
210 }
211 }
212
213 #[test]
214 fn masks_crockford() {
215 assert_eq!(
216 encode(Crockford, &[0xF8, 0x3E, 0x0F, 0x83, 0xE0]),
217 "Z0Z0Z0Z0"
218 );
219 assert_eq!(
220 encode(Crockford, &[0x07, 0xC1, 0xF0, 0x7C, 0x1F]),
221 "0Z0Z0Z0Z"
222 );
223 assert_eq!(
224 decode(Crockford, "Z0Z0Z0Z0").unwrap(),
225 [0xF8, 0x3E, 0x0F, 0x83, 0xE0]
226 );
227 assert_eq!(
228 decode(Crockford, "0Z0Z0Z0Z").unwrap(),
229 [0x07, 0xC1, 0xF0, 0x7C, 0x1F]
230 );
231 }
232
233 #[test]
234 fn masks_rfc4648() {
235 assert_eq!(
236 encode(Rfc4648 { padding: false }, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]),
237 "7A7H7A7H"
238 );
239 assert_eq!(
240 encode(Rfc4648 { padding: false }, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]),
241 "O7A7O7A7"
242 );
243 assert_eq!(
244 decode(Rfc4648 { padding: false }, "7A7H7A7H").unwrap(),
245 [0xF8, 0x3E, 0x7F, 0x83, 0xE7]
246 );
247 assert_eq!(
248 decode(Rfc4648 { padding: false }, "O7A7O7A7").unwrap(),
249 [0x77, 0xC1, 0xF7, 0x7C, 0x1F]
250 );
251 assert_eq!(
252 encode(Rfc4648 { padding: false }, &[0xF8, 0x3E, 0x7F, 0x83]),
253 "7A7H7AY"
254 );
255 }
256
257 #[test]
258 fn masks_rfc4648_pad() {
259 assert_eq!(
260 encode(Rfc4648 { padding: true }, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]),
261 "7A7H7A7H"
262 );
263 assert_eq!(
264 encode(Rfc4648 { padding: true }, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]),
265 "O7A7O7A7"
266 );
267 assert_eq!(
268 decode(Rfc4648 { padding: true }, "7A7H7A7H").unwrap(),
269 [0xF8, 0x3E, 0x7F, 0x83, 0xE7]
270 );
271 assert_eq!(
272 decode(Rfc4648 { padding: true }, "O7A7O7A7").unwrap(),
273 [0x77, 0xC1, 0xF7, 0x7C, 0x1F]
274 );
275 assert_eq!(
276 encode(Rfc4648 { padding: true }, &[0xF8, 0x3E, 0x7F, 0x83]),
277 "7A7H7AY="
278 );
279 }
280
281 #[test]
282 fn masks_rfc4648_lower() {
283 assert_eq!(
284 encode(Rfc4648Lower { padding: false }, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]),
285 "7a7h7a7h"
286 );
287 assert_eq!(
288 encode(Rfc4648Lower { padding: false }, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]),
289 "o7a7o7a7"
290 );
291 assert_eq!(
292 decode(Rfc4648Lower { padding: false }, "7a7h7a7h").unwrap(),
293 [0xF8, 0x3E, 0x7F, 0x83, 0xE7]
294 );
295 assert_eq!(
296 decode(Rfc4648Lower { padding: false }, "o7a7o7a7").unwrap(),
297 [0x77, 0xC1, 0xF7, 0x7C, 0x1F]
298 );
299 assert_eq!(
300 encode(Rfc4648Lower { padding: false }, &[0xF8, 0x3E, 0x7F, 0x83]),
301 "7a7h7ay"
302 );
303 }
304
305 #[test]
306 fn masks_rfc4648_lower_pad() {
307 assert_eq!(
308 encode(Rfc4648Lower { padding: true }, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]),
309 "7a7h7a7h"
310 );
311 assert_eq!(
312 encode(Rfc4648Lower { padding: true }, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]),
313 "o7a7o7a7"
314 );
315 assert_eq!(
316 decode(Rfc4648Lower { padding: true }, "7a7h7a7h").unwrap(),
317 [0xF8, 0x3E, 0x7F, 0x83, 0xE7]
318 );
319 assert_eq!(
320 decode(Rfc4648Lower { padding: true }, "o7a7o7a7").unwrap(),
321 [0x77, 0xC1, 0xF7, 0x7C, 0x1F]
322 );
323 assert_eq!(
324 encode(Rfc4648Lower { padding: true }, &[0xF8, 0x3E, 0x7F, 0x83]),
325 "7a7h7ay="
326 );
327 }
328
329 #[test]
330 fn masks_rfc4648_hex() {
331 assert_eq!(
332 encode(Rfc4648Hex { padding: false }, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]),
333 "V0V7V0V7"
334 );
335 assert_eq!(
336 encode(Rfc4648Hex { padding: false }, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]),
337 "EV0VEV0V"
338 );
339 assert_eq!(
340 decode(Rfc4648Hex { padding: false }, "7A7H7A7H").unwrap(),
341 [0x3A, 0x8F, 0x13, 0xA8, 0xF1]
342 );
343 assert_eq!(
344 decode(Rfc4648Hex { padding: false }, "O7A7O7A7").unwrap(),
345 [0xC1, 0xD4, 0x7C, 0x1D, 0x47]
346 );
347 assert_eq!(
348 encode(Rfc4648Hex { padding: false }, &[0xF8, 0x3E, 0x7F, 0x83]),
349 "V0V7V0O"
350 );
351 }
352
353 #[test]
354 fn masks_rfc4648_hex_pad() {
355 assert_eq!(
356 encode(Rfc4648Hex { padding: true }, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]),
357 "V0V7V0V7"
358 );
359 assert_eq!(
360 encode(Rfc4648Hex { padding: true }, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]),
361 "EV0VEV0V"
362 );
363 assert_eq!(
364 decode(Rfc4648Hex { padding: true }, "7A7H7A7H").unwrap(),
365 [0x3A, 0x8F, 0x13, 0xA8, 0xF1]
366 );
367 assert_eq!(
368 decode(Rfc4648Hex { padding: true }, "O7A7O7A7").unwrap(),
369 [0xC1, 0xD4, 0x7C, 0x1D, 0x47]
370 );
371 assert_eq!(
372 encode(Rfc4648Hex { padding: true }, &[0xF8, 0x3E, 0x7F, 0x83]),
373 "V0V7V0O="
374 );
375 }
376
377 #[test]
378 fn masks_rfc4648_hex_lower() {
379 assert_eq!(
380 encode(Rfc4648HexLower { padding: false }, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]),
381 "v0v7v0v7"
382 );
383 assert_eq!(
384 encode(Rfc4648HexLower { padding: false }, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]),
385 "ev0vev0v"
386 );
387 assert_eq!(
388 decode(Rfc4648HexLower { padding: false }, "7a7h7a7h").unwrap(),
389 [0x3A, 0x8F, 0x13, 0xA8, 0xF1]
390 );
391 assert_eq!(
392 decode(Rfc4648HexLower { padding: false }, "o7a7o7a7").unwrap(),
393 [0xC1, 0xD4, 0x7C, 0x1D, 0x47]
394 );
395 assert_eq!(
396 encode(Rfc4648HexLower { padding: false }, &[0xF8, 0x3E, 0x7F, 0x83]),
397 "v0v7v0o"
398 );
399 }
400
401 #[test]
402 fn masks_rfc4648_hex_lower_pad() {
403 assert_eq!(
404 encode(Rfc4648HexLower { padding: true }, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]),
405 "v0v7v0v7"
406 );
407 assert_eq!(
408 encode(Rfc4648HexLower { padding: true }, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]),
409 "ev0vev0v"
410 );
411 assert_eq!(
412 decode(Rfc4648HexLower { padding: true }, "7a7h7a7h").unwrap(),
413 [0x3A, 0x8F, 0x13, 0xA8, 0xF1]
414 );
415 assert_eq!(
416 decode(Rfc4648HexLower { padding: true }, "o7a7o7a7").unwrap(),
417 [0xC1, 0xD4, 0x7C, 0x1D, 0x47]
418 );
419 assert_eq!(
420 encode(Rfc4648HexLower { padding: true }, &[0xF8, 0x3E, 0x7F, 0x83]),
421 "v0v7v0o="
422 );
423 }
424
425 #[test]
426 fn masks_z() {
427 assert_eq!(
428 encode(Z, &[0xF8, 0x3E, 0x0F, 0x83, 0xE0]),
429 "9y9y9y9y"
430 );
431 assert_eq!(
432 encode(Z, &[0x07, 0xC1, 0xF0, 0x7C, 0x1F]),
433 "y9y9y9y9"
434 );
435 assert_eq!(
436 decode(Z, "9y9y9y9y").unwrap(),
437 [0xF8, 0x3E, 0x0F, 0x83, 0xE0]
438 );
439 assert_eq!(
440 decode(Z, "y9y9y9y9").unwrap(),
441 [0x07, 0xC1, 0xF0, 0x7C, 0x1F]
442 );
443 }
444
445 #[test]
446 fn padding() {
447 let num_padding = [0, 6, 4, 3, 1];
448 for i in 1..6 {
449 let encoded = encode(
450 Rfc4648 { padding: true },
451 (0..(i as u8)).collect::<Vec<u8>>().as_ref(),
452 );
453 assert_eq!(encoded.len(), 8);
454 for j in 0..(num_padding[i % 5]) {
455 assert_eq!(encoded.as_bytes()[encoded.len() - j - 1], b'=');
456 }
457 for j in 0..(8 - num_padding[i % 5]) {
458 assert!(encoded.as_bytes()[j] != b'=');
459 }
460 }
461 }
462
463 #[test]
464 fn invertible_crockford() {
465 fn test(data: Vec<u8>) -> bool {
466 decode(Crockford, encode(Crockford, data.as_ref()).as_ref()).unwrap() == data
467 }
468 quickcheck::quickcheck(test as fn(Vec<u8>) -> bool)
469 }
470
471 #[test]
472 fn invertible_rfc4648() {
473 fn test(data: Vec<u8>) -> bool {
474 decode(
475 Rfc4648 { padding: true },
476 encode(Rfc4648 { padding: true }, data.as_ref()).as_ref(),
477 )
478 .unwrap()
479 == data
480 }
481 quickcheck::quickcheck(test as fn(Vec<u8>) -> bool)
482 }
483 #[test]
484 fn invertible_unpadded_rfc4648() {
485 fn test(data: Vec<u8>) -> bool {
486 decode(
487 Rfc4648 { padding: false },
488 encode(Rfc4648 { padding: false }, data.as_ref()).as_ref(),
489 )
490 .unwrap()
491 == data
492 }
493 quickcheck::quickcheck(test as fn(Vec<u8>) -> bool)
494 }
495
496 #[test]
497 fn lower_case() {
498 fn test(data: Vec<B32>) -> bool {
499 let data: String = data.iter().map(|e| e.c as char).collect();
500 decode(Crockford, data.as_ref())
501 == decode(Crockford, data.to_ascii_lowercase().as_ref())
502 }
503 quickcheck::quickcheck(test as fn(Vec<B32>) -> bool)
504 }
505
506 #[test]
507 #[allow(non_snake_case)]
508 fn iIlL1_oO0() {
509 assert_eq!(decode(Crockford, "IiLlOo"), decode(Crockford, "111100"));
510 }
511
512 #[test]
513 fn invalid_chars_crockford() {
514 assert_eq!(decode(Crockford, ","), None)
515 }
516
517 #[test]
518 fn invalid_chars_rfc4648() {
519 assert_eq!(decode(Rfc4648 { padding: true }, ","), None)
520 }
521
522 #[test]
523 fn invalid_chars_unpadded_rfc4648() {
524 assert_eq!(decode(Rfc4648 { padding: false }, ","), None)
525 }
526}
527
528#[cfg(doctest)]
529#[doc = include_str!("../README.md")]
530struct Readme;