blob: 9fbf9779e8219c68be7f4ab9ec1b63c2782ec8dc [file] [log] [blame] [edit]
use crate::protocol::commands::Command;
use crate::protocol::common::hex::decode_hex;
use crate::target::Target;
/// Packet parse error.
#[derive(Debug)]
pub enum PacketParseError {
ChecksumMismatched { checksum: u8, calculated: u8 },
EmptyBuf,
MissingChecksum,
MalformedChecksum,
MalformedCommand,
NotAscii,
UnexpectedHeader(u8),
}
/// Top-Level GDB packet
pub enum Packet<'a> {
Ack,
Nack,
Interrupt,
Command(Command<'a>),
}
pub struct PacketBuf<'a> {
buf: &'a mut [u8],
body_range: core::ops::Range<usize>,
}
impl<'a> PacketBuf<'a> {
/// Validate the contents of the raw packet buffer, checking for checksum
/// consistency, structural correctness, and ASCII validation.
pub fn new(pkt_buf: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> {
if pkt_buf.is_empty() {
return Err(PacketParseError::EmptyBuf);
}
// split buffer into body and checksum components
let mut parts = pkt_buf[1..].split(|b| *b == b'#');
let body = parts.next().unwrap(); // spit iter always returns at least one elem
let checksum = parts
.next()
.ok_or(PacketParseError::MissingChecksum)?
.get(..2)
.ok_or(PacketParseError::MalformedChecksum)?;
// validate that the body is valid ASCII
if !body.is_ascii() {
return Err(PacketParseError::NotAscii);
}
// validate the checksum
let checksum = decode_hex(checksum).map_err(|_| PacketParseError::MalformedChecksum)?;
let calculated = body.iter().fold(0u8, |a, x| a.wrapping_add(*x));
if calculated != checksum {
return Err(PacketParseError::ChecksumMismatched {
checksum,
calculated,
});
}
let end_of_body = 1 + body.len();
Ok(PacketBuf {
buf: pkt_buf,
body_range: 1..end_of_body,
})
}
/// (used for tests) Create a packet buffer from a raw body buffer, skipping
/// the header/checksum trimming stage. ASCII validation is still performed.
#[cfg(test)]
pub fn new_with_raw_body(body: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> {
// validate the packet is valid ASCII
if !body.is_ascii() {
return Err(PacketParseError::NotAscii);
}
let len = body.len();
Ok(PacketBuf {
buf: body,
body_range: 0..len,
})
}
pub fn strip_prefix(&mut self, prefix: &[u8]) -> bool {
if self.buf[self.body_range.clone()].starts_with(prefix) {
self.body_range = (self.body_range.start + prefix.len())..self.body_range.end;
true
} else {
false
}
}
/// Return a mut reference to slice of the packet buffer corresponding to
/// the current body.
pub fn into_body(self) -> &'a mut [u8] {
&mut self.buf[self.body_range]
}
/// Return a mut reference to the _entire_ underlying packet buffer, and the
/// current body's range.
pub fn into_raw_buf(self) -> (&'a mut [u8], core::ops::Range<usize>) {
(self.buf, self.body_range)
}
/// Returns the length of the _entire_ underlying packet buffer - not just
/// the length of the current range.
///
/// This method is used when handing the `qSupported` packet.
pub fn full_len(&self) -> usize {
self.buf.len()
}
}
impl<'a> Packet<'a> {
pub fn from_buf(
target: &mut impl Target,
buf: &'a mut [u8],
) -> Result<Packet<'a>, PacketParseError> {
// cannot have empty packet
if buf.is_empty() {
return Err(PacketParseError::EmptyBuf);
}
match buf[0] {
b'$' => Ok(Packet::Command(
Command::from_packet(target, PacketBuf::new(buf)?)
.ok_or(PacketParseError::MalformedCommand)?,
)),
b'+' => Ok(Packet::Ack),
b'-' => Ok(Packet::Nack),
0x03 => Ok(Packet::Interrupt),
_ => Err(PacketParseError::UnexpectedHeader(buf[0])),
}
}
}