1 use num_traits::CheckedAdd;
2 use num_traits::CheckedMul;
3 use num_traits::FromPrimitive;
4 use num_traits::Zero;
5 
6 #[derive(Debug)]
7 pub enum DecodeHexError {
8     NotAscii,
9     Empty,
10     Overflow,
11     InvalidOutput,
12 }
13 
14 /// Decode a GDB hex string into the specified integer.
15 ///
16 /// GDB hex strings may include "xx", which represent "missing" data. This
17 /// method simply treats "xx" as 0x00.
decode_hex<I>(buf: &[u8]) -> Result<I, DecodeHexError> where I: FromPrimitive + Zero + CheckedAdd + CheckedMul,18 pub fn decode_hex<I>(buf: &[u8]) -> Result<I, DecodeHexError>
19 where
20     I: FromPrimitive + Zero + CheckedAdd + CheckedMul,
21 {
22     use DecodeHexError::*;
23 
24     let radix = I::from_u8(16).ok_or(InvalidOutput)?;
25 
26     if buf.is_empty() {
27         return Err(Empty);
28     }
29 
30     let mut result = I::zero();
31 
32     for &digit in buf {
33         let x = I::from_u8(ascii2byte(digit).ok_or(NotAscii)?).ok_or(InvalidOutput)?;
34         result = result.checked_mul(&radix).ok_or(Overflow)?;
35         result = result.checked_add(&x).ok_or(Overflow)?
36     }
37 
38     Ok(result)
39 }
40 
41 /// Wrapper around a raw hex string. Enables "late" calls to `decode` from
42 /// outside the `crate::protocol` module.
43 #[derive(Debug, Clone, Copy)]
44 pub struct HexString<'a>(pub &'a [u8]);
45 
46 impl HexString<'_> {
decode<I>(&self) -> Result<I, DecodeHexError> where I: FromPrimitive + Zero + CheckedAdd + CheckedMul,47     pub fn decode<I>(&self) -> Result<I, DecodeHexError>
48     where
49         I: FromPrimitive + Zero + CheckedAdd + CheckedMul,
50     {
51         decode_hex(self.0)
52     }
53 }
54 
55 #[derive(Debug)]
56 pub enum DecodeHexBufError {
57     NotAscii,
58 }
59 
60 #[inline]
ascii2byte(c: u8) -> Option<u8>61 fn ascii2byte(c: u8) -> Option<u8> {
62     match c {
63         b'0'..=b'9' => Some(c - b'0'),
64         b'a'..=b'f' => Some(c - b'a' + 10),
65         b'A'..=b'F' => Some(c - b'A' + 10),
66         b'x' | b'X' => Some(0),
67         _ => None,
68     }
69 }
70 
71 /// Check if the byte `c` is a valid GDB hex digit `[0-9a-fA-FxX]`
72 #[inline]
is_hex(c: u8) -> bool73 pub fn is_hex(c: u8) -> bool {
74     #[allow(clippy::match_like_matches_macro)] // mirror ascii2byte
75     match c {
76         b'0'..=b'9' => true,
77         b'a'..=b'f' => true,
78         b'A'..=b'F' => true,
79         b'x' | b'X' => true,
80         _ => false,
81     }
82 }
83 
84 /// Decode a GDB hex string into a byte slice _in place_.
85 ///
86 /// GDB hex strings may include "xx", which represent "missing" data. This
87 /// method simply treats "xx" as 0x00.
88 // TODO: maybe don't blindly translate "xx" as 0x00?
89 #[cfg(not(feature = "paranoid_unsafe"))]
decode_hex_buf(base_buf: &mut [u8]) -> Result<&mut [u8], DecodeHexBufError>90 pub fn decode_hex_buf(base_buf: &mut [u8]) -> Result<&mut [u8], DecodeHexBufError> {
91     use DecodeHexBufError::*;
92 
93     if base_buf.is_empty() {
94         return Ok(&mut []);
95     }
96 
97     let odd_adust = base_buf.len() % 2;
98     if odd_adust != 0 {
99         base_buf[0] = ascii2byte(base_buf[0]).ok_or(NotAscii)?;
100     }
101 
102     let buf = &mut base_buf[odd_adust..];
103 
104     let decoded_len = buf.len() / 2;
105     for i in 0..decoded_len {
106         // SAFETY: rustc isn't smart enough to automatically elide these bound checks.
107         //
108         // If buf.len() == 0 or 1: trivially safe, since the for block is never taken
109         // If buf.len() >= 2: the range of values for `i` is 0..(buf.len() / 2 - 1)
110         let (hi, lo, b) = unsafe {
111             (
112                 //    (buf.len() / 2 - 1) * 2
113                 // == (buf.len() - 2)
114                 // since buf.len() is >2, this is in-bounds
115                 *buf.get_unchecked(i * 2),
116                 //    (buf.len() / 2 - 1) * 2 + 1
117                 // == (buf.len() - 1)
118                 // since buf.len() is >2, this is in-bounds
119                 *buf.get_unchecked(i * 2 + 1),
120                 // since buf.len() is >2, (buf.len() / 2 - 1) is always in-bounds
121                 buf.get_unchecked_mut(i),
122             )
123         };
124 
125         let hi = ascii2byte(hi).ok_or(NotAscii)?;
126         let lo = ascii2byte(lo).ok_or(NotAscii)?;
127         *b = hi << 4 | lo;
128     }
129 
130     // SAFETY: rustc isn't smart enough to automatically elide this bound check.
131     //
132     // Consider the different values (decoded_len + odd_adust) can take:
133     //
134     //  buf.len() | (decoded_len + odd_adust)
135     // -----------|---------------------------
136     //      0     | (0 + 0) == 0
137     //      1     | (0 + 1) == 1
138     //      2     | (1 + 0) == 1
139     //      3     | (1 + 1) == 2
140     //      4     | (2 + 0) == 2
141     //      5     | (2 + 1) == 3
142     //
143     // Note that the computed index is always in-bounds.
144     //
145     // If I were still in undergrad, I could probably have whipped up a proper
146     // mathematical proof by induction or whatnot, but hopefully this "proof by
147     // example" ought to suffice.
148     unsafe { Ok(base_buf.get_unchecked_mut(..decoded_len + odd_adust)) }
149 }
150 
151 /// Decode a GDB hex string into a byte slice _in place_.
152 ///
153 /// GDB hex strings may include "xx", which represent "missing" data. This
154 /// method simply treats "xx" as 0x00.
155 // TODO: maybe don't blindly translate "xx" as 0x00?
156 #[cfg(feature = "paranoid_unsafe")]
decode_hex_buf(base_buf: &mut [u8]) -> Result<&mut [u8], DecodeHexBufError>157 pub fn decode_hex_buf(base_buf: &mut [u8]) -> Result<&mut [u8], DecodeHexBufError> {
158     use DecodeHexBufError::*;
159 
160     let odd_adust = base_buf.len() % 2;
161     if odd_adust != 0 {
162         base_buf[0] = ascii2byte(base_buf[0]).ok_or(NotAscii)?;
163     }
164     let buf = &mut base_buf[odd_adust..];
165 
166     let decoded_len = buf.len() / 2;
167     for i in 0..decoded_len {
168         let b = ascii2byte(buf[i * 2]).ok_or(NotAscii)? << 4
169             | ascii2byte(buf[i * 2 + 1]).ok_or(NotAscii)?;
170         buf[i] = b;
171     }
172 
173     Ok(&mut base_buf[..decoded_len + odd_adust])
174 }
175 
176 /// Decode GDB escaped binary bytes into origin bytes _in place_.
177 //
178 // Thanks reddit!
179 // https://www.reddit.com/r/rust/comments/110qzq9/any_idea_why_rust_isnt_able_to_elide_this_bounds/
decode_bin_buf(buf: &mut [u8]) -> Option<&mut [u8]>180 pub fn decode_bin_buf(buf: &mut [u8]) -> Option<&mut [u8]> {
181     let mut i = 0;
182     let len = buf.len();
183     for j in 0..len {
184         if i >= len {
185             return Some(&mut buf[..j]);
186         }
187 
188         if buf[i] == b'}' {
189             buf[j] = buf.get(i + 1)? ^ 0x20;
190             i += 1;
191         } else {
192             buf[j] = buf[i];
193         }
194         i += 1;
195     }
196 
197     Some(buf)
198 }
199 
200 #[cfg(test)]
201 mod tests {
202     use super::*;
203 
204     #[test]
decode_hex_buf_odd()205     fn decode_hex_buf_odd() {
206         let mut payload = b"ffffff4".to_vec();
207         let res = decode_hex_buf(&mut payload).unwrap();
208         assert_eq!(res, [0xf, 0xff, 0xff, 0xf4]);
209     }
210 
211     #[test]
decode_hex_buf_even()212     fn decode_hex_buf_even() {
213         let mut payload = b"0123456789abcdef".to_vec();
214         let res = decode_hex_buf(&mut payload).unwrap();
215         assert_eq!(res, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
216     }
217 
218     #[test]
decode_hex_buf_odd_alt()219     fn decode_hex_buf_odd_alt() {
220         let mut payload = b"12345".to_vec();
221         let res = decode_hex_buf(&mut payload).unwrap();
222         assert_eq!(res, [0x1, 0x23, 0x45]);
223     }
224 
225     #[test]
decode_hex_buf_short()226     fn decode_hex_buf_short() {
227         let mut payload = b"1".to_vec();
228         let res = decode_hex_buf(&mut payload).unwrap();
229         assert_eq!(res, [0x1]);
230     }
231 
232     #[test]
decode_hex_buf_empty()233     fn decode_hex_buf_empty() {
234         let mut payload = b"".to_vec();
235         let res = decode_hex_buf(&mut payload).unwrap();
236         assert_eq!(res, []);
237     }
238 
239     #[test]
decode_bin_buf_escaped()240     fn decode_bin_buf_escaped() {
241         let mut payload = b"}\x03}\x04}]}\n".to_vec();
242         let res = decode_bin_buf(&mut payload).unwrap();
243         assert_eq!(res, [0x23, 0x24, 0x7d, 0x2a]);
244     }
245 }
246