rspamd_base32/
alphabet.rs1use std::{error, fmt};
3
4pub const ALPHABET_SIZE: usize = 32;
5
6#[derive(Clone, Debug, Eq, PartialEq)]
7pub enum EncodeOrder {
8 OrderInversed,
9 OrderNormal
10}
11
12#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct Alphabet {
15 pub(crate) encode_symbols: [u8; ALPHABET_SIZE],
16 pub(crate) decode_bytes: [u8; u8::MAX as usize],
17 pub(crate) encode_order : EncodeOrder,
18}
19
20impl Alphabet {
21 const fn from_str_unsafe(alphabet: &str, encode_order: EncodeOrder) -> Self {
24 let mut symbols = [0_u8; ALPHABET_SIZE];
25 let source_bytes = alphabet.as_bytes();
26 let mut decode_bytes = [0xff_u8; u8::MAX as usize];
27
28 let mut index = 0;
29 while index < ALPHABET_SIZE {
30 let sym = source_bytes[index];
31 symbols[index] = sym;
32 decode_bytes[sym as usize] = index as u8;
33 index = index + 1;
34 }
35 Alphabet { encode_symbols: symbols, decode_bytes, encode_order }
36 }
37
38 pub const fn from_str_order(alphabet: &str, encode_order: EncodeOrder) -> Result<Self, ParseAlphabetError> {
40 const FIRST_PRINTABLE: u8 = 32;
41 const LAST_PRINTABLE: u8 = 126;
42 const DUPS_SIZE: usize = (LAST_PRINTABLE - FIRST_PRINTABLE) as usize;
43 let source_bytes = alphabet.as_bytes();
44 let mut dups : [bool; DUPS_SIZE] = [false; DUPS_SIZE];
45
46 if source_bytes.len() != ALPHABET_SIZE {
47 return Err(ParseAlphabetError::InvalidLength);
48 }
49
50 let mut index = 0;
51 while index < ALPHABET_SIZE {
52 let byte = source_bytes[index];
53 if !(byte >= FIRST_PRINTABLE as u8 && byte <= LAST_PRINTABLE as u8) {
55 return Err(ParseAlphabetError::UnprintableByte(byte));
56 }
57
58 let dup_idx = (byte - FIRST_PRINTABLE) as usize;
59 if dups[dup_idx] {
60 return Err(ParseAlphabetError::DuplicatedByte(byte));
61 }
62
63 dups[dup_idx] = true;
64 index = index + 1;
65 }
66
67 Ok(Self::from_str_unsafe(alphabet, encode_order))
68 }
69
70 pub const fn from_str(alphabet: &str) -> Result<Self, ParseAlphabetError> {
71 Self::from_str_order(alphabet, EncodeOrder::OrderNormal)
72 }
73}
74
75#[derive(Debug, Eq, PartialEq)]
76pub enum ParseAlphabetError {
77 InvalidLength,
79 DuplicatedByte(u8),
81 UnprintableByte(u8),
83}
84
85#[cfg(any(feature = "std", test))]
86impl fmt::Display for ParseAlphabetError {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 match self {
89 ParseAlphabetError::InvalidLength => write!(f, "Invalid length - must be {} bytes", ALPHABET_SIZE),
90 ParseAlphabetError::DuplicatedByte(b) => write!(f, "Duplicated byte: {:#04x}", b),
91 ParseAlphabetError::UnprintableByte(b) => write!(f, "Unprintable byte: {:#04x}", b),
92 }
93 }
94}
95
96#[cfg(any(feature = "std", test))]
97impl error::Error for ParseAlphabetError {}
98
99pub const ZBASE32: Alphabet = Alphabet::from_str_unsafe(
102 "ybndrfg8ejkmcpqxot1uwisza345h769",
103 EncodeOrder::OrderInversed,
104);
105
106pub const BECH32: Alphabet = Alphabet::from_str_unsafe(
108 "qpzry9x8gf2tvdw0s3jn54khce6mua7l",
109 EncodeOrder::OrderNormal,
110);
111
112pub const RFC: Alphabet = Alphabet::from_str_unsafe(
114 "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
115 EncodeOrder::OrderNormal,
116);
117
118#[cfg(test)]
119mod tests {
120 use crate::alphabet::*;
121
122 #[test]
123 fn detects_duplicate_start() {
124 assert_eq!(
125 ParseAlphabetError::DuplicatedByte(b'A'),
126 Alphabet::from_str("AACDEFGHIJKLMNOPQRSTUVWXYZ234567")
127 .unwrap_err()
128 );
129 }
130
131 #[test]
132 fn detects_duplicate_end() {
133 assert_eq!(
134 ParseAlphabetError::DuplicatedByte(b'7'),
135 Alphabet::from_str("ABCDEFGHIJKLMNOPQRSTUVWXYZ234577")
136 .unwrap_err()
137 );
138 }
139
140 #[test]
141 fn detects_duplicate_middle() {
142 assert_eq!(
143 ParseAlphabetError::DuplicatedByte(b'Z'),
144 Alphabet::from_str("ABCDEFGHIJKLMNOPQRSTUVWXZZ234567")
145 .unwrap_err()
146 );
147 }
148
149 #[test]
150 fn detects_length() {
151 assert_eq!(
152 ParseAlphabetError::InvalidLength,
153 Alphabet::from_str("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567xxx")
154 .unwrap_err()
155 );
156 }
157
158 #[test]
159 fn detects_unprintable() {
160 assert_eq!(
162 ParseAlphabetError::UnprintableByte(0xc),
163 Alphabet::from_str(
164 "\x0cBCDEFGHIJKLMNOPQRSTUVWXYZ234567"
165 )
166 .unwrap_err()
167 );
168 }
169
170 #[test]
171 fn same_as_unchecked() {
172 assert_eq!(
173 RFC,
174 Alphabet::from_str("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
175 .unwrap()
176 )
177 }
178}