gdb_protocol/
packet.rs

1use crate::{parser::CHECKSUM_LEN, Error};
2
3use std::{cmp, io::{self, prelude::*}, ops::Deref};
4
5#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
6pub enum Kind {
7    Notification, // %
8    Packet,       // $
9}
10
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct UncheckedPacket {
13    pub kind: Kind,
14    pub data: Vec<u8>,
15    pub checksum: [u8; CHECKSUM_LEN as usize],
16}
17impl UncheckedPacket {
18    /// Return the integer parsed from the hexadecimal expected
19    /// checksum.
20    ///
21    /// ```rust
22    /// # use gdb_protocol::packet::{Kind, UncheckedPacket};
23    /// let packet = UncheckedPacket {
24    ///     kind: Kind::Packet,
25    ///     data: b"Hello, World!".to_vec(),
26    ///     checksum: *b"BA",
27    /// };
28    /// assert_eq!(packet.expected_checksum().unwrap(), 186);
29    /// ```
30    pub fn expected_checksum(&self) -> Result<u8, Error> {
31        let string = std::str::from_utf8(&self.checksum)
32            .map_err(|err| Error::NonUtf8(self.checksum.to_vec(), err))?;
33        u8::from_str_radix(string, 16).map_err(|err| Error::NonNumber(string.to_owned(), err))
34    }
35
36    /// Return the actual checksum, derived from the data.
37    ///
38    /// ```rust
39    /// # use gdb_protocol::packet::{Kind, UncheckedPacket};
40    /// let packet = UncheckedPacket {
41    ///     kind: Kind::Packet,
42    ///     data: b"Hello, World!".to_vec(),
43    ///     checksum: *b"BA",
44    /// };
45    /// assert_eq!(packet.actual_checksum(), 105);
46    /// ```
47    ///
48    /// As per the GDB specification, this is currently a sum of all characters, modulo 256.
49    /// The same result can be compared with
50    ///
51    /// ```rust
52    /// # use gdb_protocol::packet::{Kind, UncheckedPacket};
53    /// # fn test(input: &str) {
54    /// #     assert_eq!(
55    /// #         UncheckedPacket {
56    /// #             kind: Kind::Packet,
57    /// #             data: input.as_bytes().to_owned(),
58    /// #             checksum: *b"00",
59    /// #         }.actual_checksum(),
60    /// (input.bytes().map(|x| usize::from(x)).sum::<usize>() % 256) as u8
61    /// #     );
62    /// # }
63    /// # test("Hello, World!");
64    /// # test("The history books say you live up to be 86 years old, Mr. Queen.");
65    /// # test("All you care about is money. This town deserves a better class of criminals.");
66    /// # test("Hello. I'm the Doctor. So basically, run.");
67    /// # test("Batman! What are you doing? You're *completely* outnumbered here. Are you *nuts*?");
68    /// ```
69    ///
70    /// however, this function is more efficient and won't go out of
71    /// bounds.
72    pub fn actual_checksum(&self) -> u8 {
73        let mut hash: u8 = 0;
74        for &b in &self.data {
75            hash = hash.wrapping_add(b);
76        }
77        hash
78    }
79
80    /// Returns true if the checksums match.
81    ///
82    /// ```rust
83    /// # use gdb_protocol::packet::{Kind, UncheckedPacket};
84    /// assert_eq!(UncheckedPacket {
85    ///     kind: Kind::Packet,
86    ///     data: b"Rust is an amazing programming language".to_vec(),
87    ///     checksum: *b"00",
88    /// }.is_valid(), false);
89    /// assert_eq!(UncheckedPacket {
90    ///     kind: Kind::Packet,
91    ///     data: b"Rust is an amazing programming language".to_vec(),
92    ///     checksum: *b"ZZ",
93    /// }.is_valid(), false);
94    /// assert_eq!(UncheckedPacket {
95    ///     kind: Kind::Packet,
96    ///     data: b"Rust is an amazing programming language".to_vec(),
97    ///     checksum: *b"C7",
98    /// }.is_valid(), true);
99    /// ```
100    pub fn is_valid(&self) -> bool {
101        self.expected_checksum().ok() == Some(self.actual_checksum())
102    }
103
104    /// Will return a checked packet if, and only if, the checksums
105    /// match. If you know the packet wasn't corrupted and want to
106    /// bypass the check, use `CheckedPacket::assume_checked`.
107    pub fn check(self) -> Option<CheckedPacket> {
108        if self.is_valid() {
109            Some(CheckedPacket::assume_checked(self))
110        } else {
111            None
112        }
113    }
114
115    /// Encode the packet into a long binary string, written to a
116    /// writer of choice. You can receive a Vec<u8> by taking
117    /// advantage of the fact that they implement io::Write:
118    ///
119    /// ```rust
120    /// # use gdb_protocol::packet::{Kind, UncheckedPacket};
121    /// let mut encoded = Vec::new();
122    /// UncheckedPacket {
123    ///     kind: Kind::Packet,
124    ///     data: b"these must be escaped: # $ } *".to_vec(),
125    ///     checksum: *b"00",
126    /// }.encode(&mut encoded);
127    /// assert_eq!(
128    ///     encoded,
129    ///     b"$these must be escaped: }\x03 }\x04 }] }\x0a#00".to_vec()
130    /// );
131    /// ```
132    ///
133    /// Currently multiple series repeated characters aren't
134    /// shortened, however, this may change at any time and you should
135    /// not rely on the output of this function being exactly one of
136    /// multiple representations.
137    pub fn encode<W>(&self, w: &mut W) -> io::Result<()>
138    where
139        W: Write,
140    {
141        w.write_all(&[match self.kind {
142            Kind::Notification => b'%',
143            Kind::Packet => b'$',
144        }])?;
145
146        let mut remaining: &[u8] = &self.data;
147        while !remaining.is_empty() {
148            let escape1 = memchr::memchr3(b'#', b'$', b'}', remaining);
149            let escape2 = memchr::memchr(b'*', remaining);
150
151            let escape = cmp::min(
152                escape1.unwrap_or_else(|| remaining.len()),
153                escape2.unwrap_or_else(|| remaining.len()),
154            );
155
156            w.write_all(&remaining[..escape])?;
157            remaining = &remaining[escape..];
158
159            if let Some(&b) = remaining.first() {
160                dbg!(b as char);
161                // memchr found a character that needs escaping, so let's do that
162                w.write_all(&[b'}', b ^ 0x20])?;
163                remaining = &remaining[1..];
164            }
165        }
166
167        w.write_all(&[b'#'])?;
168        w.write_all(&self.checksum)?;
169        Ok(())
170    }
171}
172
173#[derive(Clone, Debug, PartialEq, Eq)]
174pub struct CheckedPacket {
175    unchecked: UncheckedPacket,
176}
177impl CheckedPacket {
178    /// If you know the packet isn't corrupted, this function bypasses
179    /// the checksum verification.
180    pub fn assume_checked(unchecked: UncheckedPacket) -> Self {
181        Self { unchecked }
182    }
183    /// If you intend to mutate the packet's internals, you must first
184    /// convert it to an unchecked packet so it isn't marked as
185    /// checked.
186    pub fn invalidate_check(self) -> UncheckedPacket {
187        self.unchecked
188    }
189
190    /// The empty packet is used when you get a packet which you just
191    /// don't understand. Replying an empty packet means "I don't
192    /// support this feature".
193    ///
194    /// ```rust
195    /// # use gdb_protocol::packet::CheckedPacket;
196    /// let mut encoded = Vec::new();
197    /// CheckedPacket::empty().encode(&mut encoded);
198    /// assert_eq!(encoded, b"$#00")
199    /// ```
200    pub fn empty() -> Self {
201        Self::assume_checked(UncheckedPacket {
202            kind: Kind::Packet,
203            data: Vec::new(),
204            checksum: *b"00",
205        })
206    }
207
208    /// Creates a packet from the inputted binary data, and generates
209    /// the checksum from it.
210    /// ```rust
211    /// # use gdb_protocol::packet::{CheckedPacket, Kind, UncheckedPacket};
212    /// assert_eq!(
213    ///     CheckedPacket::from_data(Kind::Packet, b"Hello, World!".to_vec()).invalidate_check(),
214    ///     UncheckedPacket {
215    ///         kind: Kind::Packet,
216    ///         data: b"Hello, World!".to_vec(),
217    ///         checksum: *b"69"
218    ///     },
219    /// )
220    /// ```
221    pub fn from_data(kind: Kind, data: Vec<u8>) -> Self {
222        let mut packet = UncheckedPacket {
223            kind,
224            data,
225            checksum: [0; CHECKSUM_LEN as usize],
226        };
227        let actual = packet.actual_checksum();
228        write!(&mut packet.checksum[..], "{:02X}", actual).unwrap();
229        Self::assume_checked(packet)
230    }
231}
232// No DerefMut, because then the checksum/data could be modified
233impl Deref for CheckedPacket {
234    type Target = UncheckedPacket;
235
236    fn deref(&self) -> &Self::Target {
237        &self.unchecked
238    }
239}