Let emulator tell debugger to stop

This commit is contained in:
2025-01-04 01:04:21 -05:00
parent a5b5f8e80f
commit 11df670ff4
4 changed files with 392 additions and 246 deletions
+124 -60
View File
@@ -35,66 +35,7 @@ impl std::fmt::Debug for Request<'_> {
}
}
impl<'a> Request<'a> {
pub async fn read<R: AsyncRead + Unpin>(
reader: &mut R,
buffer: &'a mut Vec<u8>,
) -> Result<Self> {
buffer.clear();
let mut char = reader.read_u8().await?;
while char == b'+' {
// just ignore positive acks
char = reader.read_u8().await?;
}
if char == b'-' {
bail!("no support for negative acks");
}
if char == 0x03 {
// This is how the client "cancels an in-flight request"
buffer.push(char);
return Ok(Self {
kind: RequestKind::Signal,
buffer,
});
}
if char != b'$' {
// Messages are supposed to start with a dollar sign
bail!("malformed message");
}
// now read the body
let mut checksum = 0u8;
char = reader.read_u8().await?;
while char != b'#' {
if char == b'}' {
// escape character
checksum = checksum.wrapping_add(char);
char = reader.read_u8().await?;
checksum = checksum.wrapping_add(char);
buffer.push(char ^ 0x20);
} else {
checksum = checksum.wrapping_add(char);
buffer.push(char);
}
char = reader.read_u8().await?;
}
let mut checksum_bytes = [b'0'; 2];
reader.read_exact(&mut checksum_bytes).await?;
let (real_checksum, 2) = u8::from_radix_16(&checksum_bytes) else {
bail!("invalid checksum");
};
if checksum != real_checksum {
bail!("mismatched checksum");
}
Ok(Self {
kind: RequestKind::Command,
buffer,
})
}
impl Request<'_> {
pub fn match_str(&mut self, prefix: &str) -> bool {
if let Some(new_buffer) = self.buffer.strip_prefix(prefix.as_bytes()) {
self.buffer = new_buffer;
@@ -103,6 +44,13 @@ impl<'a> Request<'a> {
false
}
pub fn match_some_str<'a, I: IntoIterator<Item = &'a str>>(
&mut self,
prefixes: I,
) -> Option<&'a str> {
prefixes.into_iter().find(|&prefix| self.match_str(prefix))
}
pub fn match_hex<I: FromRadix16>(&mut self) -> Option<I> {
match I::from_radix_16(self.buffer) {
(_, 0) => None,
@@ -113,3 +61,119 @@ impl<'a> Request<'a> {
}
}
}
pub struct RequestSource<R> {
reader: R,
buffer: Vec<u8>,
state: RequestReadState,
}
impl<R: AsyncRead + Unpin> RequestSource<R> {
pub fn new(reader: R) -> Self {
Self {
reader,
buffer: vec![],
state: RequestReadState::Header,
}
}
pub async fn recv(&mut self) -> Result<Request<'_>> {
let mut char = self.reader.read_u8().await?;
if matches!(self.state, RequestReadState::Start) {
self.buffer.clear();
self.state = RequestReadState::Header;
}
if matches!(self.state, RequestReadState::Header) {
// Just ignore positive acks
while char == b'+' {
char = self.reader.read_u8().await?;
}
if char == b'-' {
bail!("no support for negative acks");
}
if char == 0x03 {
// This is how the client "cancels an in-flight request"
self.buffer.push(char);
self.state = RequestReadState::Start;
return Ok(Request {
kind: RequestKind::Signal,
buffer: &self.buffer,
});
}
if char != b'$' {
// Messages are supposed to start with a dollar sign
bail!("malformed message");
}
self.state = RequestReadState::Body {
checksum: 0,
escaping: false,
};
char = self.reader.read_u8().await?;
}
while let RequestReadState::Body { checksum, escaping } = &mut self.state {
if char == b'#' && !*escaping {
self.state = RequestReadState::Checksum {
expected: *checksum,
actual: 0,
digits: 0,
};
char = self.reader.read_u8().await?;
break;
}
*checksum = checksum.wrapping_add(char);
if *escaping {
// escaped character
self.buffer.push(char ^ 0x20);
*escaping = false;
} else if char == b'}' {
// next character will be escaped
*escaping = true;
} else {
self.buffer.push(char);
}
char = self.reader.read_u8().await?;
}
while let RequestReadState::Checksum {
expected,
actual,
digits,
} = &mut self.state
{
let digit = match char {
b'0'..=b'9' => char - b'0',
b'a'..=b'f' => char - b'a' + 10,
b'A'..=b'F' => char - b'A' + 10,
_ => bail!("invalid checksum"),
};
*actual = (*actual << 4) + digit;
*digits += 1;
if *digits == 2 {
if *expected != *actual {
bail!("mismatched checksum");
}
self.state = RequestReadState::Start;
return Ok(Request {
kind: RequestKind::Command,
buffer: &self.buffer,
});
}
char = self.reader.read_u8().await?;
}
unreachable!();
}
}
enum RequestReadState {
Start,
Header,
Body {
checksum: u8,
escaping: bool,
},
Checksum {
expected: u8,
actual: u8,
digits: u8,
},
}