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