wiwi/encoding/hex/decode.rs
1//! Internal decoding implementations
2use crate::num::*;
3use crate::prelude::*;
4use super::{ DecodeError, UnsafeBufWriteGuard };
5
6/// Length of the table decoder (256)
7const TABLE_DECODER_LEN: usize = 256;
8
9/// Decoding table (with mappings for both upper and lower hex)
10// TODO: this table is mostly empty... I wonder what we could do here to shrink it,
11// without compromising speed (we could do what we did with z85 with None == 0xff)?
12static TABLE_DECODER: &[Option<u8>; TABLE_DECODER_LEN] = &[
13 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
14 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
15 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
16 Some(0x00), Some(0x01), Some(0x02), Some(0x03), Some(0x04), Some(0x05), Some(0x06), Some(0x07), Some(0x08), Some(0x09), None, None, None, None, None, None,
17 None, Some(0x0a), Some(0x0b), Some(0x0c), Some(0x0d), Some(0x0e), Some(0x0f), None, None, None, None, None, None, None, None, None,
18 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
19 None, Some(0x0a), Some(0x0b), Some(0x0c), Some(0x0d), Some(0x0e), Some(0x0f), None, None, None, None, None, None, None, None, None,
20 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
21 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
22 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
23 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
24 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
25 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
26 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
27 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
28 None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None
29];
30
31/// Reads `rounds * 2` bytes from bytes_ptr, encoding pairs of chars into
32/// bytes, then writes the decoded bytes into `dest`
33///
34/// # Safety
35///
36/// - `bytes_ptr` must be valid for reads for `rounds * 2` bytes
37/// - `dest` must have enough capacity to write at least `rounds` bytes
38pub(super) unsafe fn generic(
39 mut bytes_ptr: *const u8,
40 dest: &mut UnsafeBufWriteGuard,
41 rounds: usize
42) -> Result<(), DecodeError> {
43 let table_ptr = TABLE_DECODER.as_ptr();
44
45 for _ in 0..rounds {
46 // SAFETY: bytes_ptr must be valid for `rounds * 2` bytes, and we loop
47 // `rounds` times, each time reading 2 bytes (n, and n + 1)
48 let next_byte_ptr = unsafe { bytes_ptr.add(1) };
49
50 // SAFETY: caller promises `bytes_ptr` is valid for at least `rounds * 2`
51 // bytes, see prev comment for details
52 let byte1 = unsafe { (*bytes_ptr).into_usize() };
53 // SAFETY: same as above
54 let byte2 = unsafe { (*next_byte_ptr).into_usize() };
55
56 /// # Safety
57 ///
58 /// The valud stored in the variable passed in must be within the range of
59 /// 0..=255 (for indexing the decode table)
60 macro_rules! decode_byte_unsafe {
61 ($byte:ident) => {
62 // SAFETY: macro caller promises var is within 0..=84,
63 // ie. within range to index table ptr
64 let ptr = unsafe { table_ptr.add($byte) };
65
66 // SAFETY: as described above, ptr is valid to read
67 let $byte = unsafe { *ptr };
68
69 let $byte = match $byte {
70 Some(byte) => { byte }
71 None => { return Err(DecodeError::InvalidChar) }
72 };
73 }
74 }
75
76 // SAFETY: a byte can only be between `0..256`, which fits
77 // within the lookup table
78
79 // SAFETY: both vars were casted from bytes, which only has a range of 0..=255
80 decode_byte_unsafe!(byte1);
81 decode_byte_unsafe!(byte2);
82
83 let byte = (byte1 << 4) | byte2;
84
85 // SAFETY: we loop `rounds` times, and caller promises `dest` is writeable
86 // for at least `rounds` bytes, so writing 1 per iteration is good
87 unsafe { dest.write_bytes_const::<1>(&byte) }
88
89 // SAFETY: caller promises `bytes_ptr` is readable from for at least
90 // `num_rounds * 2` bytes, adding 2 per iter is sound. Also it's sound to
91 // have the pointer pointing to the end of the memory section (as long as
92 // it isn't dereferenced)
93 unsafe { bytes_ptr = bytes_ptr.add(2) }
94 }
95
96 Ok(())
97}