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}