Skip to main content

xvc_protocol/
codec.rs

1use std::{num::ParseIntError, str::Utf8Error};
2
3use crate::{
4    XvcCommand,
5    error::ParseVersionError,
6    protocol::{Version, XvcInfo},
7};
8
9const XVC_SERVER_PREFIX: &[u8] = b"xvcServer_v";
10
11pub(crate) const CMD_GET_INFO: &[u8] = b"getinfo:";
12pub(crate) const CMD_SET_TCK: &[u8] = b"settck:";
13pub(crate) const CMD_SHIFT: &[u8] = b"shift:";
14
15/// A lightweight cursor over a borrowed byte slice.
16struct SliceReader<'a>(&'a [u8]);
17
18impl<'a> SliceReader<'a> {
19    fn remaining(&self) -> usize {
20        self.0.len()
21    }
22
23    fn advance(&mut self, n: usize) {
24        self.0 = &self.0[n..];
25    }
26
27    fn get_u32_le(&mut self) -> u32 {
28        let v = u32::from_le_bytes([self.0[0], self.0[1], self.0[2], self.0[3]]);
29        self.advance(4);
30        v
31    }
32
33    fn copy_to_boxed_slice(&mut self, n: usize) -> Box<[u8]> {
34        let out: Box<[u8]> = self.0[..n].into();
35        self.advance(n);
36        out
37    }
38}
39
40impl XvcInfo {
41    pub fn parse(buf: &mut &[u8]) -> ParseResult<XvcInfo> {
42        let Some(newline_index) = buf.iter().position(|b| *b == b'\n') else {
43            return Err(ParseErr::Incomplete);
44        };
45        let line = &buf[..newline_index];
46        *buf = &buf[newline_index + 1..];
47        let rest = line
48            .strip_prefix(XVC_SERVER_PREFIX)
49            .ok_or_else(|| ParseErr::InvalidCommand(line.into()))?;
50        let colon_index = rest
51            .iter()
52            .position(|byte| *byte == b':')
53            .ok_or_else(|| ParseErr::InvalidCommand(line.into()))?;
54        let version = core::str::from_utf8(&rest[..colon_index])?.parse::<Version>()?;
55        let max_vector_len = core::str::from_utf8(&rest[colon_index + 1..])?.parse::<u32>()?;
56        Ok(XvcInfo::new(version, max_vector_len))
57    }
58}
59
60/// Errors that happen while parsing a command.
61/// Note that `ParseErr::Incomplete` is usually used to indicate
62/// upstream that it should increase the buffer size and re-try.
63#[derive(Eq, PartialEq, Clone, Debug)]
64pub enum ParseErr {
65    /// The buffer is too small to parse the message.
66    Incomplete,
67    /// A command was recognized, but it does not match any known command.
68    InvalidCommand(Box<[u8]>),
69    /// A command requested more size than is available.
70    /// This can happen in the `Shift` command when the passed `tdi` or `tms`
71    /// vectors are larger than the maximum size negotiated in the beginning.
72    TooManyBytes { max: usize, got: usize },
73    /// Conversion to UTF-8 failed
74    Utf8Error(Utf8Error),
75    /// Parsing an integer failed
76    ParseIntError(ParseIntError),
77    /// Parsing a version failed
78    ParseVersionError(ParseVersionError),
79}
80
81impl From<Utf8Error> for ParseErr {
82    fn from(value: Utf8Error) -> Self {
83        ParseErr::Utf8Error(value)
84    }
85}
86
87impl From<ParseIntError> for ParseErr {
88    fn from(value: ParseIntError) -> Self {
89        ParseErr::ParseIntError(value)
90    }
91}
92
93impl From<ParseVersionError> for ParseErr {
94    fn from(value: ParseVersionError) -> Self {
95        ParseErr::ParseVersionError(value)
96    }
97}
98
99pub type ParseResult<T> = core::result::Result<T, ParseErr>;
100
101impl XvcCommand {
102    /// Parse a command from a buffer.
103    ///
104    /// # Example
105    /// ```
106    /// use xvc_protocol::{XvcCommand};
107    ///
108    /// let mut buf: &[u8] = b"getinfo:";
109    /// let command = XvcCommand::parse(&mut buf).expect("Parsing a large enough buffer should not fail");
110    /// assert_eq!(command, XvcCommand::GetInfo);
111    /// assert_eq!(buf.len(), 0);
112    /// ```
113    ///
114    /// If the buffer is not large enough, `ParseErr::Incomplete` is returned.
115    /// This usually indicates to the caller to read more bytes into the buffer:
116    ///
117    /// A buffer that is too large is permitted. After a successful parse the
118    /// buffer is advanced past the consumed command bytes:
119    /// ```
120    /// use xvc_protocol::XvcCommand;
121    ///
122    /// let mut buf: &[u8] = b"settck:\x64";
123    /// let command = XvcCommand::parse(&mut buf).expect("Parsing a large enough buffer should not fail");
124    /// assert_eq!(command, XvcCommand::SetTck);
125    /// assert_eq!(buf.len(), 1);
126    /// ```
127    pub fn parse(buf: &mut &[u8]) -> ParseResult<XvcCommand> {
128        let (cmd, n) = if buf.starts_with(CMD_GET_INFO) {
129            (XvcCommand::GetInfo, CMD_GET_INFO.len())
130        } else if buf.starts_with(CMD_SET_TCK) {
131            (XvcCommand::SetTck, CMD_SET_TCK.len())
132        } else if buf.starts_with(CMD_SHIFT) {
133            (XvcCommand::Shift, CMD_SHIFT.len())
134        } else {
135            return if CMD_GET_INFO.starts_with(buf)
136                || CMD_SET_TCK.starts_with(buf)
137                || CMD_SHIFT.starts_with(buf)
138            {
139                Err(ParseErr::Incomplete)
140            } else {
141                Err(ParseErr::InvalidCommand((*buf).into()))
142            };
143        };
144        *buf = &buf[n..];
145        Ok(cmd)
146    }
147}
148
149pub struct SetTck {
150    period: u32,
151}
152
153impl SetTck {
154    pub fn period(&self) -> u32 {
155        self.period
156    }
157}
158
159impl SetTck {
160    pub fn parse(buf: &mut &[u8]) -> ParseResult<Self> {
161        let mut r = SliceReader(buf);
162        if r.remaining() < 4 {
163            return Err(ParseErr::Incomplete);
164        }
165        let period = r.get_u32_le();
166        *buf = r.0;
167        Ok(SetTck { period })
168    }
169}
170
171pub struct Shift {
172    num_bits: u32,
173    tdi: Box<[u8]>,
174    tms: Box<[u8]>,
175}
176
177impl Shift {
178    pub fn num_bits(&self) -> u32 {
179        self.num_bits
180    }
181
182    #[cfg(test)]
183    pub fn tdi(&self) -> &[u8] {
184        &self.tdi
185    }
186
187    #[cfg(test)]
188    pub fn tms(&self) -> &[u8] {
189        &self.tms
190    }
191
192    pub fn into_tms_tdi(self) -> (Box<[u8]>, Box<[u8]>) {
193        (self.tms, self.tdi)
194    }
195}
196
197impl Shift {
198    pub fn parse_num_bits(buf: &mut &[u8]) -> ParseResult<u32> {
199        let mut r = SliceReader(buf);
200        if r.remaining() < 4 {
201            return Err(ParseErr::Incomplete);
202        }
203        let n = r.get_u32_le();
204        *buf = r.0;
205        Ok(n)
206    }
207
208    /// This is an internal convenience method when parsing the `Shift` command.
209    pub fn parse_tdi_or_tms(
210        buf: &mut &[u8],
211        num_bytes: usize,
212        max_len: usize,
213    ) -> ParseResult<Box<[u8]>> {
214        if num_bytes > max_len {
215            return Err(ParseErr::TooManyBytes {
216                max: max_len,
217                got: num_bytes,
218            });
219        }
220        let mut r = SliceReader(buf);
221        if r.remaining() < num_bytes {
222            return Err(ParseErr::Incomplete);
223        }
224        let out = r.copy_to_boxed_slice(num_bytes);
225        *buf = r.0;
226        Ok(out)
227    }
228
229    pub fn parse(buf: &mut &[u8], max_len: usize) -> ParseResult<Shift> {
230        let num_bits = Self::parse_num_bits(buf)?;
231        let num_bytes = num_bits.div_ceil(8) as usize;
232        let tms = Self::parse_tdi_or_tms(buf, num_bytes, max_len)?;
233        let tdi = Self::parse_tdi_or_tms(buf, num_bytes, max_len)?;
234        Ok(Shift { num_bits, tdi, tms })
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use std::vec::Vec;
241
242    use super::*;
243
244    #[test]
245    fn parses_valid_xvc_info() {
246        let mut info1: &[u8] = b"xvcServer_v1.0:4\n";
247        assert_eq!(
248            XvcInfo::parse(&mut info1),
249            Ok(XvcInfo::new(Version::new(1, 0), 4))
250        );
251
252        let mut info2: &[u8] = b"xvcServer_v10.2:24\n";
253        assert_eq!(
254            XvcInfo::parse(&mut info2),
255            Ok(XvcInfo::new(Version::new(10, 2), 24))
256        );
257    }
258
259    #[test]
260    fn xvc_info_incomplete_no_newline() {
261        let mut buf: &[u8] = b"xvcServer_v1.0:4"; // no newline
262        assert!(matches!(
263            XvcInfo::parse(&mut buf),
264            Err(ParseErr::Incomplete)
265        ));
266    }
267
268    #[test]
269    fn xvc_info_invalid_prefix() {
270        let mut buf: &[u8] = b"badprefix:1.0:4\n";
271        assert!(matches!(
272            XvcInfo::parse(&mut buf),
273            Err(ParseErr::InvalidCommand(_))
274        ));
275    }
276
277    #[test]
278    fn xvc_info_missing_colon() {
279        let mut buf: &[u8] = b"xvcServer_v1.0\n";
280        assert!(matches!(
281            XvcInfo::parse(&mut buf),
282            Err(ParseErr::InvalidCommand(_))
283        ));
284    }
285
286    #[test]
287    fn xvc_info_malformed_version() {
288        let mut buf: &[u8] = b"xvcServer_v1.a:4\n";
289        assert!(matches!(
290            XvcInfo::parse(&mut buf),
291            Err(ParseErr::ParseVersionError(_))
292        ));
293    }
294
295    #[test]
296    fn xvc_info_invalid_max_vector_len() {
297        let mut buf: &[u8] = b"xvcServer_v1.0:NaN\n";
298        assert!(matches!(
299            XvcInfo::parse(&mut buf),
300            Err(ParseErr::ParseIntError(_))
301        ));
302    }
303
304    #[test]
305    fn xvc_command_parse_valid_and_rest() {
306        let mut buf: &[u8] = b"settck:\x64";
307        let cmd = XvcCommand::parse(&mut buf).expect("should parse settck");
308        assert_eq!(cmd, XvcCommand::SetTck);
309        assert_eq!(buf, b"\x64");
310    }
311
312    #[test]
313    fn xvc_command_parse_incomplete() {
314        let mut buf: &[u8] = b"getin";
315        assert!(matches!(
316            XvcCommand::parse(&mut buf),
317            Err(ParseErr::Incomplete)
318        ));
319    }
320
321    #[test]
322    fn xvc_command_parse_invalid() {
323        let mut buf: &[u8] = b"unknown:";
324        assert!(matches!(
325            XvcCommand::parse(&mut buf),
326            Err(ParseErr::InvalidCommand(_))
327        ));
328    }
329
330    #[test]
331    fn set_tck_parse_ok_and_incomplete() {
332        let mut buf: &[u8] = &[0x01u8, 0x00, 0x00, 0x00];
333        let set = SetTck::parse(&mut buf).expect("should parse period");
334        assert_eq!(set.period(), 1);
335        assert!(buf.is_empty());
336
337        let mut short: &[u8] = &[0u8, 0u8, 0u8];
338        assert!(matches!(
339            SetTck::parse(&mut short),
340            Err(ParseErr::Incomplete)
341        ));
342    }
343
344    #[test]
345    fn shift_parse_num_bits_behaviour() {
346        let mut short: &[u8] = &[0u8, 0, 0];
347        assert!(matches!(
348            Shift::parse_num_bits(&mut short),
349            Err(ParseErr::Incomplete)
350        ));
351        let mut v: &[u8] = &[0x0Cu8, 0, 0, 0]; // 12 bits
352        let num_bits = Shift::parse_num_bits(&mut v).expect("should parse num bits");
353        assert_eq!(num_bits, 12);
354        assert!(v.is_empty());
355    }
356
357    #[test]
358    fn parse_tdi_or_tms_edge_cases() {
359        let mut buf: &[u8] = &[0u8; 4];
360        assert!(matches!(
361            Shift::parse_tdi_or_tms(&mut buf, 5, 4),
362            Err(ParseErr::TooManyBytes { .. })
363        ));
364
365        let mut buf: &[u8] = &[0u8; 1];
366        assert!(matches!(
367            Shift::parse_tdi_or_tms(&mut buf, 2, 4),
368            Err(ParseErr::Incomplete)
369        ));
370
371        let mut buf: &[u8] = &[0xAAu8; 4];
372        let slice = Shift::parse_tdi_or_tms(&mut buf, 4, 4).expect("should parse all bytes");
373        assert_eq!(&slice[..], &[0xAAu8; 4]);
374        assert!(buf.is_empty());
375    }
376
377    #[test]
378    fn shift_parse_ok() {
379        let mut buf: Vec<u8> = Vec::new();
380        buf.extend_from_slice(&12u32.to_le_bytes());
381        let tms = [0xAAu8, 0xBB];
382        let tdi = [0x11u8, 0x22];
383        buf.extend_from_slice(&tms);
384        buf.extend_from_slice(&tdi);
385
386        let mut slice: &[u8] = &buf;
387        let shift = Shift::parse(&mut slice, 4).expect("shift parse should succeed");
388        assert_eq!(shift.num_bits(), 12);
389        assert_eq!(shift.tms(), &tms);
390        assert_eq!(shift.tdi(), &tdi);
391        assert!(slice.is_empty());
392    }
393
394    #[test]
395    fn shift_parse_too_many_bytes_error() {
396        let mut buf: Vec<u8> = Vec::new();
397        buf.extend_from_slice(&16u32.to_le_bytes()); // 16 bits -> 2 bytes each
398        buf.extend_from_slice(&[0u8, 0u8]);
399        buf.extend_from_slice(&[0u8, 0u8]);
400
401        let mut slice: &[u8] = &buf;
402        assert!(matches!(
403            Shift::parse(&mut slice, 1),
404            Err(ParseErr::TooManyBytes { .. })
405        ));
406    }
407}