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