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