Separate GDB request and response into structs
This commit is contained in:
		
							parent
							
								
									24487b21b7
								
							
						
					
					
						commit
						9519897711
					
				
							
								
								
									
										251
									
								
								src/gdbserver.rs
								
								
								
								
							
							
						
						
									
										251
									
								
								src/gdbserver.rs
								
								
								
								
							| 
						 | 
					@ -1,10 +1,12 @@
 | 
				
			||||||
use anyhow::{bail, Result};
 | 
					use anyhow::{bail, Result};
 | 
				
			||||||
 | 
					use request::{Request, RequestKind};
 | 
				
			||||||
 | 
					use response::Response;
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
    sync::{Arc, Mutex},
 | 
					    sync::{Arc, Mutex},
 | 
				
			||||||
    thread,
 | 
					    thread,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use tokio::{
 | 
					use tokio::{
 | 
				
			||||||
    io::{AsyncReadExt, AsyncWriteExt as _, BufReader, BufWriter},
 | 
					    io::{AsyncWriteExt as _, BufReader, BufWriter},
 | 
				
			||||||
    net::{
 | 
					    net::{
 | 
				
			||||||
        tcp::{OwnedReadHalf, OwnedWriteHalf},
 | 
					        tcp::{OwnedReadHalf, OwnedWriteHalf},
 | 
				
			||||||
        TcpListener, TcpStream,
 | 
					        TcpListener, TcpStream,
 | 
				
			||||||
| 
						 | 
					@ -15,6 +17,9 @@ use tokio::{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::emulator::{EmulatorClient, EmulatorCommand, SimId};
 | 
					use crate::emulator::{EmulatorClient, EmulatorCommand, SimId};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod request;
 | 
				
			||||||
 | 
					mod response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct GdbServer {
 | 
					pub struct GdbServer {
 | 
				
			||||||
    sim_id: SimId,
 | 
					    sim_id: SimId,
 | 
				
			||||||
    client: EmulatorClient,
 | 
					    client: EmulatorClient,
 | 
				
			||||||
| 
						 | 
					@ -126,6 +131,8 @@ struct GdbConnection {
 | 
				
			||||||
    stream_in: BufReader<OwnedReadHalf>,
 | 
					    stream_in: BufReader<OwnedReadHalf>,
 | 
				
			||||||
    stream_out: BufWriter<OwnedWriteHalf>,
 | 
					    stream_out: BufWriter<OwnedWriteHalf>,
 | 
				
			||||||
    ack_messages: bool,
 | 
					    ack_messages: bool,
 | 
				
			||||||
 | 
					    request_buf: Vec<u8>,
 | 
				
			||||||
 | 
					    response_buf: Option<Vec<u8>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl GdbConnection {
 | 
					impl GdbConnection {
 | 
				
			||||||
| 
						 | 
					@ -137,208 +144,94 @@ impl GdbConnection {
 | 
				
			||||||
            stream_in: BufReader::new(rx),
 | 
					            stream_in: BufReader::new(rx),
 | 
				
			||||||
            stream_out: BufWriter::new(tx),
 | 
					            stream_out: BufWriter::new(tx),
 | 
				
			||||||
            ack_messages: true,
 | 
					            ack_messages: true,
 | 
				
			||||||
 | 
					            request_buf: vec![],
 | 
				
			||||||
 | 
					            response_buf: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    async fn run(mut self) -> Result<()> {
 | 
					    async fn run(mut self) -> Result<()> {
 | 
				
			||||||
        println!("Connected for {}", self.sim_id);
 | 
					        println!("Connected for {}", self.sim_id);
 | 
				
			||||||
        self.client.send_command(EmulatorCommand::Pause);
 | 
					        self.client.send_command(EmulatorCommand::Pause);
 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            let message = self.read_message().await?;
 | 
					            let mut req = Request::read(&mut self.stream_in, &mut self.request_buf).await?;
 | 
				
			||||||
            println!("received {:?}", message);
 | 
					            println!("received {:?}", req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //let mut res = ResponseWriter::new(&mut self.stream_out);
 | 
					            if req.kind == RequestKind::Signal {
 | 
				
			||||||
            //res.init(self.ack_messages).await?;
 | 
					                self.client.send_command(EmulatorCommand::Pause);
 | 
				
			||||||
 | 
					                let res = self
 | 
				
			||||||
 | 
					                    .response()
 | 
				
			||||||
 | 
					                    .write_str("T05;thread:p1.t1;threads:p1.t1;reason:trap;");
 | 
				
			||||||
 | 
					                self.send(res).await?;
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let body = match &message {
 | 
					            if req.match_str("QStartNoAckMode") {
 | 
				
			||||||
                Message::String(str) => str.as_str(),
 | 
					                let res = self.response().write_str("OK");
 | 
				
			||||||
                Message::Signal => {
 | 
					                self.send(res).await?;
 | 
				
			||||||
                    self.client.send_command(EmulatorCommand::Pause);
 | 
					 | 
				
			||||||
                    let mut res = self.respond().await?;
 | 
					 | 
				
			||||||
                    res.write_str("T05;thread:p1.t1;threads:p1.t1;reason:trap;")
 | 
					 | 
				
			||||||
                        .await?;
 | 
					 | 
				
			||||||
                    res.send().await?;
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if body == "QStartNoAckMode" {
 | 
					 | 
				
			||||||
                let mut res = ResponseWriter::new(&mut self.stream_out);
 | 
					 | 
				
			||||||
                res.init(self.ack_messages).await?;
 | 
					 | 
				
			||||||
                self.ack_messages = false;
 | 
					                self.ack_messages = false;
 | 
				
			||||||
                res.send_ok().await?;
 | 
					            } else if req.match_str("qSupported:") {
 | 
				
			||||||
            } else if body.starts_with("qSupported:") {
 | 
					                let res = self
 | 
				
			||||||
                let mut res = self.respond().await?;
 | 
					                    .response()
 | 
				
			||||||
                res.write_str("multiprocess+;swbreak+;vContSupported+")
 | 
					                    .write_str("multiprocess+;swbreak+;vContSupported+");
 | 
				
			||||||
                    .await?;
 | 
					                self.send(res).await?;
 | 
				
			||||||
                res.send().await?;
 | 
					            } else if req.match_str("QThreadSuffixSupported")
 | 
				
			||||||
            } else if body == "QThreadSuffixSupported"
 | 
					                || req.match_str("QListThreadsInStopReply")
 | 
				
			||||||
                || body == "QListThreadsInStopReply"
 | 
					                || req.match_str("QEnableErrorStrings")
 | 
				
			||||||
                || body == "QEnableErrorStrings"
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                let res = self.respond().await?;
 | 
					                let res = self.response().write_str("OK");
 | 
				
			||||||
                res.send_ok().await?;
 | 
					                self.send(res).await?;
 | 
				
			||||||
            } else if body == "qHostInfo" {
 | 
					            } else if req.match_str("qHostInfo") {
 | 
				
			||||||
                let mut res = self.respond().await?;
 | 
					                let res = self.response().write_str(&format!(
 | 
				
			||||||
                res.write_str(&format!(
 | 
					 | 
				
			||||||
                    "triple:{};endian:little;ptrsize:4;",
 | 
					                    "triple:{};endian:little;ptrsize:4;",
 | 
				
			||||||
                    hex::encode("v810-unknown-vb")
 | 
					                    hex::encode("v810-unknown-vb")
 | 
				
			||||||
                ))
 | 
					                ));
 | 
				
			||||||
                .await?;
 | 
					                self.send(res).await?;
 | 
				
			||||||
                res.send().await?;
 | 
					            } else if req.match_str("qProcessInfo") {
 | 
				
			||||||
            } else if body == "qProcessInfo" {
 | 
					                let res = self.response().write_str(&format!(
 | 
				
			||||||
                let mut res = self.respond().await?;
 | 
					 | 
				
			||||||
                res.write_str(&format!(
 | 
					 | 
				
			||||||
                    "pid:1;triple:{};endian:little;ptrsize:4;",
 | 
					                    "pid:1;triple:{};endian:little;ptrsize:4;",
 | 
				
			||||||
                    hex::encode("v810-unknown-vb")
 | 
					                    hex::encode("v810-unknown-vb")
 | 
				
			||||||
                ))
 | 
					                ));
 | 
				
			||||||
                .await?;
 | 
					                self.send(res).await?;
 | 
				
			||||||
                res.send().await?;
 | 
					            } else if req.match_str("vCont?") {
 | 
				
			||||||
            } else if body == "vCont?" {
 | 
					                let res = self.response().write_str("vCont;c;");
 | 
				
			||||||
                let mut res = self.respond().await?;
 | 
					                self.send(res).await?;
 | 
				
			||||||
                res.write_str("vCont;c;").await?;
 | 
					            } else if req.match_str("qC") {
 | 
				
			||||||
                res.send().await?;
 | 
					 | 
				
			||||||
            } else if body == "qC" {
 | 
					 | 
				
			||||||
                // The v810 has no threads, so report that the "current thread" is 1.
 | 
					                // The v810 has no threads, so report that the "current thread" is 1.
 | 
				
			||||||
                let mut res = self.respond().await?;
 | 
					                let res = self.response().write_str("QCp1.t1");
 | 
				
			||||||
                res.write_str("QCp1.t1").await?;
 | 
					                self.send(res).await?;
 | 
				
			||||||
                res.send().await?;
 | 
					            } else if req.match_str("qfThreadInfo") {
 | 
				
			||||||
            } else if body == "qfThreadInfo" {
 | 
					                let res = self.response().write_str("mp1.t1");
 | 
				
			||||||
                let mut res = self.respond().await?;
 | 
					                self.send(res).await?;
 | 
				
			||||||
                res.write_str("mp1.t1").await?;
 | 
					            } else if req.match_str("qsThreadInfo") {
 | 
				
			||||||
                res.send().await?;
 | 
					                let res = self.response().write_str("l");
 | 
				
			||||||
            } else if body == "qsThreadInfo" {
 | 
					                self.send(res).await?;
 | 
				
			||||||
                let mut res = self.respond().await?;
 | 
					            } else if req.match_str("k") {
 | 
				
			||||||
                res.write_str("l").await?;
 | 
					 | 
				
			||||||
                res.send().await?;
 | 
					 | 
				
			||||||
            } else if body == "k" {
 | 
					 | 
				
			||||||
                bail!("debug process was killed");
 | 
					                bail!("debug process was killed");
 | 
				
			||||||
            } else if body == "?" {
 | 
					            } else if req.match_str("?") {
 | 
				
			||||||
                let mut res = self.respond().await?;
 | 
					                let res = self.response().write_str("T00;thread:p1.t1;threads:p1.t1;");
 | 
				
			||||||
                res.write_str("T00;thread:p1.t1;threads:p1.t1;").await?;
 | 
					                self.send(res).await?;
 | 
				
			||||||
                res.send().await?;
 | 
					            } else if req.match_str("c") || req.match_str("vCont;c:") {
 | 
				
			||||||
            } else if body == "c" || body.starts_with("vCont;c:") {
 | 
					 | 
				
			||||||
                // Continue running the game until we're interrupted again
 | 
					 | 
				
			||||||
                self.client.send_command(EmulatorCommand::Resume);
 | 
					                self.client.send_command(EmulatorCommand::Resume);
 | 
				
			||||||
                continue;
 | 
					                // Don't send a response until we hit a breakpoint or get interrupted
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                // unrecognized command
 | 
					                // unrecognized command
 | 
				
			||||||
                let res = self.respond().await?;
 | 
					                let res = self.response();
 | 
				
			||||||
                res.send().await?;
 | 
					                self.send(res).await?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn read_message(&mut self) -> Result<Message> {
 | 
					    fn response(&mut self) -> Response {
 | 
				
			||||||
        let mut char = self.read_byte().await?;
 | 
					        Response::new(
 | 
				
			||||||
        while char == b'+' {
 | 
					            self.response_buf.take().unwrap_or_default(),
 | 
				
			||||||
            // just ignore positive acks
 | 
					            self.ack_messages,
 | 
				
			||||||
            char = self.read_byte().await?;
 | 
					        )
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if char == b'-' {
 | 
					 | 
				
			||||||
            bail!("no support for negative acks");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if char == 0x03 {
 | 
					 | 
				
			||||||
            // This is how the client "cancels an in-flight request"
 | 
					 | 
				
			||||||
            return Ok(Message::Signal);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if char != b'$' {
 | 
					 | 
				
			||||||
            // Messages are supposed to start with a dollar sign
 | 
					 | 
				
			||||||
            bail!("malformed message");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // now read the body
 | 
					 | 
				
			||||||
        let mut checksum = 0u8;
 | 
					 | 
				
			||||||
        let mut body = vec![];
 | 
					 | 
				
			||||||
        char = self.read_byte().await?;
 | 
					 | 
				
			||||||
        while char != b'#' {
 | 
					 | 
				
			||||||
            if char == b'}' {
 | 
					 | 
				
			||||||
                // escape character
 | 
					 | 
				
			||||||
                checksum = checksum.wrapping_add(char);
 | 
					 | 
				
			||||||
                char = self.read_byte().await?;
 | 
					 | 
				
			||||||
                checksum = checksum.wrapping_add(char);
 | 
					 | 
				
			||||||
                body.push(char ^ 0x20);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                checksum = checksum.wrapping_add(char);
 | 
					 | 
				
			||||||
                body.push(char);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            char = self.read_byte().await?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut checksum_bytes = [b'0'; 2];
 | 
					 | 
				
			||||||
        self.stream_in.read_exact(&mut checksum_bytes).await?;
 | 
					 | 
				
			||||||
        let checksum_str = std::str::from_utf8(&checksum_bytes)?;
 | 
					 | 
				
			||||||
        let real_checksum = u8::from_str_radix(checksum_str, 16)?;
 | 
					 | 
				
			||||||
        if checksum != real_checksum {
 | 
					 | 
				
			||||||
            bail!("invalid checksum");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let string = String::from_utf8(body)?;
 | 
					 | 
				
			||||||
        Ok(Message::String(string))
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn read_byte(&mut self) -> std::io::Result<u8> {
 | 
					    async fn send(&mut self, res: Response) -> std::io::Result<()> {
 | 
				
			||||||
        self.stream_in.read_u8().await
 | 
					        let buffer = res.finish();
 | 
				
			||||||
    }
 | 
					        println!("{:?}", std::str::from_utf8(&buffer));
 | 
				
			||||||
 | 
					        self.stream_out.write_all(&buffer).await?;
 | 
				
			||||||
    async fn respond(&mut self) -> std::io::Result<ResponseWriter<'_>> {
 | 
					        self.response_buf = Some(buffer);
 | 
				
			||||||
        let mut res = ResponseWriter::new(&mut self.stream_out);
 | 
					        self.stream_out.flush().await
 | 
				
			||||||
        res.init(self.ack_messages).await?;
 | 
					 | 
				
			||||||
        Ok(res)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
struct ResponseWriter<'a> {
 | 
					 | 
				
			||||||
    inner: &'a mut BufWriter<OwnedWriteHalf>,
 | 
					 | 
				
			||||||
    checksum: u8,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
impl<'a> ResponseWriter<'a> {
 | 
					 | 
				
			||||||
    fn new(inner: &'a mut BufWriter<OwnedWriteHalf>) -> Self {
 | 
					 | 
				
			||||||
        Self { inner, checksum: 0 }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn init(&mut self, ack: bool) -> std::io::Result<()> {
 | 
					 | 
				
			||||||
        if ack {
 | 
					 | 
				
			||||||
            self.inner.write_u8(b'+').await?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        self.inner.write_u8(b'$').await
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn write_str(&mut self, str: &str) -> std::io::Result<()> {
 | 
					 | 
				
			||||||
        for byte in str.bytes() {
 | 
					 | 
				
			||||||
            self.checksum = self.checksum.wrapping_add(byte);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        self.inner.write_all(str.as_bytes()).await
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn write_hex_u8(&mut self, value: u8) -> std::io::Result<()> {
 | 
					 | 
				
			||||||
        for digit in [(value >> 4), (value & 0xf)] {
 | 
					 | 
				
			||||||
            let char = if digit > 9 {
 | 
					 | 
				
			||||||
                b'a' + digit - 10
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                b'0' + digit
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
            self.checksum = self.checksum.wrapping_add(char);
 | 
					 | 
				
			||||||
            self.inner.write_u8(char).await?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn send_ok(mut self) -> std::io::Result<()> {
 | 
					 | 
				
			||||||
        self.write_str("OK").await?;
 | 
					 | 
				
			||||||
        self.send().await
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async fn send(mut self) -> std::io::Result<()> {
 | 
					 | 
				
			||||||
        let final_checksum = self.checksum;
 | 
					 | 
				
			||||||
        self.inner.write_u8(b'#').await?;
 | 
					 | 
				
			||||||
        self.write_hex_u8(final_checksum).await?;
 | 
					 | 
				
			||||||
        println!("{:?}", std::str::from_utf8(self.inner.buffer()));
 | 
					 | 
				
			||||||
        self.inner.flush().await
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
enum Message {
 | 
					 | 
				
			||||||
    String(String),
 | 
					 | 
				
			||||||
    Signal,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,84 @@
 | 
				
			||||||
 | 
					use anyhow::{bail, Result};
 | 
				
			||||||
 | 
					use tokio::io::{AsyncRead, AsyncReadExt as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub enum RequestKind {
 | 
				
			||||||
 | 
					    Signal,
 | 
				
			||||||
 | 
					    Command,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct Request<'a> {
 | 
				
			||||||
 | 
					    pub kind: RequestKind,
 | 
				
			||||||
 | 
					    body: &'a str,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					            let body = std::str::from_utf8(buffer)?;
 | 
				
			||||||
 | 
					            return Ok(Self {
 | 
				
			||||||
 | 
					                kind: RequestKind::Signal,
 | 
				
			||||||
 | 
					                body,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        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 checksum_str = std::str::from_utf8(&checksum_bytes)?;
 | 
				
			||||||
 | 
					        let real_checksum = u8::from_str_radix(checksum_str, 16)?;
 | 
				
			||||||
 | 
					        if checksum != real_checksum {
 | 
				
			||||||
 | 
					            bail!("invalid checksum");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let body = std::str::from_utf8(buffer)?;
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            kind: RequestKind::Command,
 | 
				
			||||||
 | 
					            body,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn match_str(&mut self, prefix: &str) -> bool {
 | 
				
			||||||
 | 
					        if let Some(new_body) = self.body.strip_prefix(prefix) {
 | 
				
			||||||
 | 
					            self.body = new_body;
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,45 @@
 | 
				
			||||||
 | 
					pub struct Response {
 | 
				
			||||||
 | 
					    buffer: Vec<u8>,
 | 
				
			||||||
 | 
					    checksum: u8,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Response {
 | 
				
			||||||
 | 
					    pub fn new(mut buffer: Vec<u8>, ack: bool) -> Self {
 | 
				
			||||||
 | 
					        buffer.clear();
 | 
				
			||||||
 | 
					        if ack {
 | 
				
			||||||
 | 
					            buffer.push(b'+');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        buffer.push(b'$');
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            buffer,
 | 
				
			||||||
 | 
					            checksum: 0,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn write_str(mut self, str: &str) -> Self {
 | 
				
			||||||
 | 
					        for char in str.as_bytes() {
 | 
				
			||||||
 | 
					            self.buffer.push(*char);
 | 
				
			||||||
 | 
					            self.checksum = self.checksum.wrapping_add(*char);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn write_hex_u8(mut self, value: u8) -> Self {
 | 
				
			||||||
 | 
					        for digit in [(value >> 4), (value & 0xf)] {
 | 
				
			||||||
 | 
					            let char = if digit > 9 {
 | 
				
			||||||
 | 
					                b'a' + digit - 10
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                b'0' + digit
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            self.buffer.push(char);
 | 
				
			||||||
 | 
					            self.checksum = self.checksum.wrapping_add(char);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn finish(mut self) -> Vec<u8> {
 | 
				
			||||||
 | 
					        let checksum = self.checksum;
 | 
				
			||||||
 | 
					        self.buffer.push(b'#');
 | 
				
			||||||
 | 
					        self.write_hex_u8(checksum).buffer
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue