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}