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