threetwo/
lib.rs

1// log2(32) = 5 bits per character (bpc)
2
3// https://en.wikipedia.org/wiki/Base32
4pub mod alphabet {
5    /// https://datatracker.ietf.org/doc/html/rfc4648#section-6
6    pub const RFC4648: &[u8; 32] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
7
8    /// https://datatracker.ietf.org/doc/html/rfc4648#section-7
9    pub const RFC4648_HEX: &[u8; 32] = b"0123456789ABCDEFGHIJKLMNOPQRSTUV";
10
11    /// http://www.crockford.com/base32.html
12    pub const CROCKFORD: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
13
14    /// z-base-32
15    /// See http://www.crockford.com/base32.html
16    pub const Z: &[u8; 32] = b"ybndrfg8ejkmcpqxot1uwisza345h769";
17
18    /// https://en.wikipedia.org/wiki/Geohash#Textual_representation
19    pub const GEOHASH: &[u8; 32] = b"0123456789bcdefghjkmnpqrstuvwxyz";
20
21    /// https://en.wikipedia.org/wiki/Open_Location_Code
22    pub const WORD_SAFE: &[u8; 32] = b"23456789CFGHJMPQRVWXcfghjmpqrvwx";
23}
24
25pub const fn foo(alphabet: &[u8; 32]) -> [u8; 256] {
26    let mut mapping: [u8; 256] = [0; 256];
27
28    let mut i: u8 = 0;
29    while i < 32 {
30        let char = alphabet[i as usize];
31        mapping[char as usize] = i;
32        i += 1;
33    }
34
35    return mapping;
36}
37
38pub fn encode_bytes(alphabet: &[u8; 32], src: &[u8]) -> String {
39    // Encodes the bytes by block
40    // 1 block = 40 bits (LCM of 8 bits in a byte and 5 bits in a char)
41    // 5 = bytes per block
42    // 8 = chars per block
43
44    let src_len = src.len();
45    let dest_len = (src_len * 8 + 4) / 5; // `+ 4` to always round up
46
47    let mut dest = vec![0; dest_len];
48    let mut si = 0;
49    let mut di = 0;
50
51    while si + 5 <= src_len {
52        let src_block = &src[si..(si + 5)];
53        let dest_block = &mut dest[di..(di + 8)];
54        encode_block(
55            alphabet,
56            src_block.try_into().unwrap(),
57            dest_block.try_into().unwrap(),
58        );
59
60        si += 5;
61        di += 8;
62    }
63
64    let rem_bytes = src_len - si;
65    let rem_chars = dest_len - di;
66    if rem_bytes > 0 {
67        let mut last_block = [0u8; 5];
68        let mut last_chars = [0u8; 8];
69
70        last_block[0..rem_bytes].copy_from_slice(&src[si..]);
71        encode_block(alphabet, &last_block, &mut last_chars);
72        dest[di..].copy_from_slice(&last_chars[0..rem_chars]);
73    }
74
75    return unsafe { String::from_utf8_unchecked(dest) };
76}
77
78fn encode_block(alphabet: &[u8; 32], src: &[u8; 5], dest: &mut [u8; 8]) {
79    let block: u64 = ((src[0] as u64) << 32)
80        | ((src[1] as u64) << 24)
81        | ((src[2] as u64) << 16)
82        | ((src[3] as u64) << 8)
83        | ((src[4] as u64) << 0);
84
85    dest[0] = encode_5_bits(alphabet, (block >> 35) as usize);
86    dest[1] = encode_5_bits(alphabet, (block >> 30) as usize);
87    dest[2] = encode_5_bits(alphabet, (block >> 25) as usize);
88    dest[3] = encode_5_bits(alphabet, (block >> 20) as usize);
89    dest[4] = encode_5_bits(alphabet, (block >> 15) as usize);
90    dest[5] = encode_5_bits(alphabet, (block >> 10) as usize);
91    dest[6] = encode_5_bits(alphabet, (block >> 5) as usize);
92    dest[7] = encode_5_bits(alphabet, (block >> 0) as usize);
93}
94
95pub const fn encode_5_bits(alphabet: &[u8; 32], bits: usize) -> u8 {
96    return alphabet[bits & 0b11111];
97}