• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #[cfg(feature = "alloc")]
2 use alloc::string::String;
3 
4 use num_traits::PrimInt;
5 
6 use crate::internal::BeBytes;
7 use crate::protocol::{IdKind, ThreadId};
8 use crate::Connection;
9 
10 /// Newtype around a Connection error. Having a newtype allows implementing a
11 /// `From<ResponseWriterError<C>> for crate::Error<T, C>`, which greatly
12 /// simplifies some of the error handling in the main gdbstub.
13 #[derive(Debug, Clone)]
14 pub struct Error<C>(C);
15 
16 /// A wrapper around [`Connection`] that computes the single-byte checksum of
17 /// incoming / outgoing data.
18 pub struct ResponseWriter<'a, C: Connection + 'a> {
19     inner: &'a mut C,
20     started: bool,
21     checksum: u8,
22     // buffer outgoing message
23     // TODO: add `write_all` method to Connection, and allow user to optionally pass outgoing
24     // packet buffer? This could improve performance (instead of writing a single byte at a time)
25     #[cfg(feature = "alloc")]
26     msg: String,
27 }
28 
29 impl<'a, C: Connection + 'a> ResponseWriter<'a, C> {
30     /// Creates a new ResponseWriter
new(inner: &'a mut C) -> Self31     pub fn new(inner: &'a mut C) -> Self {
32         Self {
33             inner,
34             started: false,
35             checksum: 0,
36             #[cfg(feature = "alloc")]
37             msg: String::new(),
38         }
39     }
40 
41     /// Consumes self, writing out the final '#' and checksum
flush(mut self) -> Result<(), Error<C::Error>>42     pub fn flush(mut self) -> Result<(), Error<C::Error>> {
43         // don't include '#' in checksum calculation
44         let checksum = self.checksum;
45 
46         #[cfg(feature = "alloc")]
47         trace!("--> ${}#{:02x?}", self.msg, checksum);
48 
49         self.write(b'#')?;
50         self.write_hex(checksum)?;
51 
52         Ok(())
53     }
54 
55     /// Get a mutable reference to the underlying connection.
as_conn(&mut self) -> &mut C56     pub fn as_conn(&mut self) -> &mut C {
57         self.inner
58     }
59 
60     /// Write a single byte.
write(&mut self, byte: u8) -> Result<(), Error<C::Error>>61     pub fn write(&mut self, byte: u8) -> Result<(), Error<C::Error>> {
62         #[cfg(feature = "alloc")]
63         self.msg.push(byte as char);
64 
65         if !self.started {
66             self.started = true;
67             self.inner.write(b'$').map_err(Error)?;
68         }
69 
70         self.checksum = self.checksum.wrapping_add(byte);
71         self.inner.write(byte).map_err(Error)
72     }
73 
74     /// Write an entire buffer over the connection.
write_all(&mut self, data: &[u8]) -> Result<(), Error<C::Error>>75     pub fn write_all(&mut self, data: &[u8]) -> Result<(), Error<C::Error>> {
76         data.iter().try_for_each(|b| self.write(*b))
77     }
78 
79     /// Write an entire string over the connection.
write_str(&mut self, s: &str) -> Result<(), Error<C::Error>>80     pub fn write_str(&mut self, s: &str) -> Result<(), Error<C::Error>> {
81         self.write_all(&s.as_bytes())
82     }
83 
84     /// Write a single byte as a hex string (two ascii chars)
write_hex(&mut self, byte: u8) -> Result<(), Error<C::Error>>85     fn write_hex(&mut self, byte: u8) -> Result<(), Error<C::Error>> {
86         for digit in [(byte & 0xf0) >> 4, byte & 0x0f].iter() {
87             let c = match digit {
88                 0..=9 => b'0' + digit,
89                 10..=15 => b'a' + digit - 10,
90                 _ => unreachable!(),
91             };
92             self.write(c)?;
93         }
94         Ok(())
95     }
96 
97     /// Write a byte-buffer as a hex string (i.e: two ascii chars / byte).
write_hex_buf(&mut self, data: &[u8]) -> Result<(), Error<C::Error>>98     pub fn write_hex_buf(&mut self, data: &[u8]) -> Result<(), Error<C::Error>> {
99         data.iter().try_for_each(|b| self.write_hex(*b))
100     }
101 
102     /// Write data using the binary protocol.
write_binary(&mut self, data: &[u8]) -> Result<(), Error<C::Error>>103     pub fn write_binary(&mut self, data: &[u8]) -> Result<(), Error<C::Error>> {
104         data.iter().try_for_each(|b| match b {
105             b'#' | b'$' | b'}' | b'*' => {
106                 self.write(b'}')?;
107                 self.write(*b ^ 0x20)
108             }
109             _ => self.write(*b),
110         })
111     }
112 
113     /// Write a number as a big-endian hex string using the most compact
114     /// representation possible (i.e: trimming leading zeros).
write_num<D: BeBytes + PrimInt>(&mut self, digit: D) -> Result<(), Error<C::Error>>115     pub fn write_num<D: BeBytes + PrimInt>(&mut self, digit: D) -> Result<(), Error<C::Error>> {
116         if digit.is_zero() {
117             return self.write_hex(0);
118         }
119 
120         let mut buf = [0; 16];
121         // infallible (unless digit is a >128 bit number)
122         let len = digit.to_be_bytes(&mut buf).unwrap();
123         let buf = &buf[..len];
124         buf.iter()
125             .copied()
126             .skip_while(|&b| b == 0)
127             .try_for_each(|b| self.write_hex(b))
128     }
129 
write_id_kind(&mut self, tid: IdKind) -> Result<(), Error<C::Error>>130     pub fn write_id_kind(&mut self, tid: IdKind) -> Result<(), Error<C::Error>> {
131         match tid {
132             IdKind::All => self.write_str("-1")?,
133             IdKind::Any => self.write_str("0")?,
134             IdKind::WithID(id) => self.write_num(id.get())?,
135         };
136         Ok(())
137     }
138 
write_thread_id(&mut self, tid: ThreadId) -> Result<(), Error<C::Error>>139     pub fn write_thread_id(&mut self, tid: ThreadId) -> Result<(), Error<C::Error>> {
140         if let Some(pid) = tid.pid {
141             self.write_str("p")?;
142             self.write_id_kind(pid)?;
143             self.write_str(".")?;
144         }
145         self.write_id_kind(tid.tid)?;
146         Ok(())
147     }
148 }
149