base32/
lib.rs

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
73/*
74     0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  :,  ;,  <,  =,  >,  ?,  @,  A,  B,  C,
75     D,  E,  F,  G,  H,  I,  J,  K,  L,  M,  N,  O,  P,  Q,  R,  S,  T,  U,  V,  W,
76     X,  Y,  Z,  [,  \,  ],  ^,  _,  `,  a,  b,  c,  d,  e,  f,  g,  h,  i,  j,  k,
77     l,  m,  n,  o,  p,  q,  r,  s,  t,  u,  v,  w,  x,  y,  z,
78*/
79
80const 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, // supports both upper and lower case
148        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;