1use std::io::Write;
2
3use crate::constants::UTF8_TO_ENCODED_MAP;
4use crate::errors::{DecodingError, VerificationError};
5use crate::utils::div_rem;
6
7pub fn decode(encoded: &str) -> Result<Vec<u8>, DecodingError> {
9 let mut slice = vec![0; compute_decoded_size(encoded.len())];
10
11 decode_in_slice(encoded, &mut slice)?;
12
13 Ok(slice)
14}
15
16pub fn decode_in_slice(encoded: &str, slice: &mut [u8]) -> Result<usize, DecodingError> {
22 let bytes = encoded.as_bytes();
23 let required_slice_size = compute_decoded_size(bytes.len());
24
25 if slice.len() < required_slice_size {
26 return Err(DecodingError::NotEnoughSpaceInSlice {
27 actual: slice.len(),
28 required: required_slice_size,
29 });
30 }
31
32 decode_in_writer(encoded, &mut std::io::Cursor::new(slice))
33}
34
35pub fn decode_in_writer<T: Write>(encoded: &str, writer: &mut T) -> Result<usize, DecodingError> {
41 let bytes = encoded.as_bytes();
42 let required_slice_size = compute_decoded_size(bytes.len());
43
44 let last_group_length = bytes.len() - bytes.len() / 11 * 11;
46 if let 1 | 4 | 8 = last_group_length {
47 return Err(DecodingError::Verification(
48 VerificationError::InvalidLength,
49 ));
50 }
51
52 let mut chunk_index = 0;
54 for chunk in bytes.chunks_exact(11) {
55 let decoded = compute_chunk(chunk_index, chunk)?;
56
57 writer.write_all(&decoded).unwrap();
58 chunk_index += 11;
59 }
60
61 if last_group_length != 0 {
63 let chunk = &bytes[bytes.len() - last_group_length..];
64 let decoded = compute_chunk(chunk_index, chunk)?;
65 let elements_to_write = compute_decoded_size(last_group_length);
66
67 if decoded[elements_to_write..].iter().any(|v| *v != 0) {
68 return Err(DecodingError::Verification(VerificationError::NotCanonical));
69 }
70
71 writer.write_all(&decoded[..elements_to_write]).unwrap();
72 }
73
74 Ok(required_slice_size)
75}
76
77#[inline(always)]
83pub(crate) fn compute_decoded_size(encoded_length: usize) -> usize {
84 (encoded_length << 3) / 11
85}
86
87#[inline]
88pub(crate) fn map_utf8_to_encoded(
89 chunk_index: usize,
90 index: usize,
91 chunk: &[u8],
92) -> Result<usize, VerificationError> {
93 match chunk.get(index) {
94 Some(v) => {
95 let encoded = *UTF8_TO_ENCODED_MAP.get(*v as usize).unwrap_or(&255) as usize;
96 if encoded == 255 {
97 Err(VerificationError::InvalidByte {
98 index: chunk_index + index,
99 byte: *v,
100 })
101 } else {
102 Ok(encoded)
103 }
104 }
105 None => Ok(0),
106 }
107}
108
109#[inline]
110pub(crate) fn compute_chunk(
111 chunk_index: usize,
112 chunk: &[u8],
113) -> Result<[u8; 8], VerificationError> {
114 let c0 = map_utf8_to_encoded(chunk_index, 0, chunk)?;
115 let c1 = map_utf8_to_encoded(chunk_index, 1, chunk)?;
116 let c2 = map_utf8_to_encoded(chunk_index, 2, chunk)?;
117 let c3 = map_utf8_to_encoded(chunk_index, 3, chunk)?;
118 let c4 = map_utf8_to_encoded(chunk_index, 4, chunk)?;
119 let c5 = map_utf8_to_encoded(chunk_index, 5, chunk)?;
120 let c6 = map_utf8_to_encoded(chunk_index, 6, chunk)?;
121 let c7 = map_utf8_to_encoded(chunk_index, 7, chunk)?;
122 let c8 = map_utf8_to_encoded(chunk_index, 8, chunk)?;
123 let c9 = map_utf8_to_encoded(chunk_index, 9, chunk)?;
124 let c10 = map_utf8_to_encoded(chunk_index, 10, chunk)?;
125
126 let (b1, r1) = div_rem(60 * c0 + c1, 14);
127 let (b2, r2) = div_rem(c2, 3);
128 let (b3, r3) = div_rem(c4, 20);
129 let aux = 3 * c3 + b3;
130 let b3_bis = aux >> 1;
131 let r3_bis = aux & 0x1;
132 let (b4, r4) = div_rem(60 * r3 + c5, 9);
133 let b5 = c6 >> 1;
134 let r5 = c6 & 0x1;
135 let (b6, r6) = div_rem(60 * c7 + c8, 24);
136 let (b7, r7) = div_rem(c9, 5);
137
138 let c_a = u8::try_from(b1).map_err(|_| VerificationError::NotCanonical)?;
139 let c_b = u8::try_from(r1 * 20 + b2).map_err(|_| VerificationError::NotCanonical)?;
140 let c_c = u8::try_from(r2 * 90 + b3_bis).map_err(|_| VerificationError::NotCanonical)?;
141 let c_d = u8::try_from(128 * r3_bis + b4).map_err(|_| VerificationError::NotCanonical)?;
142 let c_e = u8::try_from(r4 * 30 + b5).map_err(|_| VerificationError::NotCanonical)?;
143 let c_f = u8::try_from(r5 * 150 + b6).map_err(|_| VerificationError::NotCanonical)?;
144 let c_g = u8::try_from(r6 * 12 + b7).map_err(|_| VerificationError::NotCanonical)?;
145 let c_h = u8::try_from(60 * r7 + c10).map_err(|_| VerificationError::NotCanonical)?;
146
147 Ok([c_a, c_b, c_c, c_d, c_e, c_f, c_g, c_h])
148}
149
150#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::constants::ENCODED_TO_UTF8_MAP;
158 use crate::encode;
159 use std::collections::HashSet;
160
161 #[test]
162 fn test_compute_decoded_size() {
163 for encoded_length in 0usize..100 {
164 let real_value = (8.0 * encoded_length as f64 / 11.0).floor() as usize;
165 let computed_value = compute_decoded_size(encoded_length);
166
167 assert_eq!(
168 computed_value, real_value,
169 "Incorrect for {}",
170 encoded_length
171 );
172 }
173 }
174
175 #[test]
176 fn test_decoded_correct_values() {
177 for length in 0..16 {
178 for byte in 0..=255 {
179 let bytes = vec![byte; length];
180 let encoded = encode(&bytes);
181 let decoded = decode(&encoded).expect("The decoding must succeed");
182
183 assert_eq!(bytes, decoded, "Incorrect for length {length}, byte {byte}",);
184 }
185 }
186 }
187
188 #[test]
189 fn test_decode_in_writer() {
190 let test = "Gt4CGFiHehzRzjCF16";
191 let mut result_vector = Vec::new();
192 let decoded_chars =
193 decode_in_writer(test, &mut result_vector).expect("The decoding must succeed");
194
195 let result = b"Hello, world!".to_vec();
196
197 assert_eq!(decoded_chars, 13, "Incorrect chars");
198 assert_eq!(result_vector, result, "Incorrect slice result");
199 }
200
201 #[test]
203 fn test_decode_in_slice_exact_slice() {
204 let test = "Gt4CGFiHehzRzjCF16";
205 let mut result_slice = vec![0; 13];
206 let decoded_chars =
207 decode_in_slice(test, &mut result_slice).expect("The decoding must succeed");
208
209 let result = b"Hello, world!".to_vec();
210
211 assert_eq!(decoded_chars, 13, "Incorrect chars");
212 assert_eq!(result_slice, result, "Incorrect slice result");
213 }
214
215 #[test]
216 fn test_decode_in_slice_bigger_slice() {
217 let test = "Gt4CGFiHehzRzjCF16";
218 let mut result_slice = vec![0; 15];
219 let decoded_chars =
220 decode_in_slice(test, &mut result_slice).expect("The decoding must succeed");
221
222 let mut result = b"Hello, world!".to_vec();
223 result.push(0);
224 result.push(0);
225
226 assert_eq!(decoded_chars, 13, "Incorrect chars");
227 assert_eq!(result_slice, result, "Incorrect slice result");
228 }
229
230 #[test]
231 fn test_decode_in_slice_shorter_slice() {
232 let test = "Gt4CGFiHehzRzjCF16";
233 let mut result_slice = vec![0; 10];
234 let error =
235 decode_in_slice(test, &mut result_slice).expect_err("The decoding cannot succeed");
236
237 assert_eq!(
238 error,
239 DecodingError::NotEnoughSpaceInSlice {
240 actual: 10,
241 required: 13,
242 },
243 "Incorrect for '{}'",
244 test
245 );
246 }
247
248 #[test]
250 fn test_do_not_decode_non_canonical_values() {
251 let mut ok_values = HashSet::new();
252
253 for i in ENCODED_TO_UTF8_MAP {
254 for j in ENCODED_TO_UTF8_MAP {
255 let encoded = format!("{}{}", *i as char, *j as char);
256 let decoded = match decode(&encoded) {
257 Ok(v) => v,
258 Err(_) => continue,
259 };
260 let encoded_2 = encode(&decoded);
261
262 assert_eq!(encoded, encoded_2, "[Incorrect]\n - Encoded : {encoded} - {:?}\n - Encoded 2: {encoded_2} - {:?}\n - Decoded: {:?}",
263 encoded.as_bytes(),
264 encoded_2.as_bytes(),
265 decoded);
266
267 ok_values.insert(encoded);
268 }
269 }
270
271 for i in 0..=255 {
272 let decoded = vec![i];
273 let encoded = encode(&decoded);
274
275 if encoded.len() == 2 {
276 assert!(
277 ok_values.remove(&encoded),
278 "Not found encoding for {:?}. Encoded: {encoded}",
279 decoded
280 );
281 }
282
283 for j in 0..=255 {
284 let decoded = vec![i, j];
285 let encoded = encode(&decoded);
286
287 if encoded.len() == 2 {
288 assert!(
289 ok_values.remove(&encoded),
290 "Not found encoding for {:?}. Encoded: {encoded}",
291 decoded
292 );
293 }
294 }
295 }
296
297 assert!(
298 ok_values.is_empty(),
299 "Not found encoding for {:?}",
300 ok_values
301 );
302 }
303}