g60/
decoding.rs

1use std::io::Write;
2
3use crate::constants::UTF8_TO_ENCODED_MAP;
4use crate::errors::{DecodingError, VerificationError};
5use crate::utils::div_rem;
6
7/// Decodes a G60 encoded string.
8pub 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
16/// Decodes a G60 encoded string.
17/// The result is placed into `slice` and returns the number of elements written.
18///
19/// # Errors
20/// An error will be thrown if `slice` does not have enough space to store the decoded string.
21pub 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
35/// Decodes a G60 encoded string.
36/// The result is written in `writer`.
37///
38/// # Errors
39/// An error will be thrown if the writing process fails.
40pub 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    // Check length.
45    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    // Complete groups.
53    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    // Last incomplete group.
62    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// ----------------------------------------------------------------------------
78// AUX METHODS ----------------------------------------------------------------
79// ----------------------------------------------------------------------------
80
81/// Computes `ceil(8 * encoded_length / 11)` faster using only integers.
82#[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// ----------------------------------------------------------------------------
151// TESTS ----------------------------------------------------------------------
152// ----------------------------------------------------------------------------
153
154#[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    /// This will test also `decode_in_slice_unchecked` and `decode_in_writer_unchecked`.
202    #[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    /// This test checks that the decoding only works with canonical values.
249    #[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}