Let emulator tell debugger to stop
This commit is contained in:
+124
-60
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user