xvc_protocol/
codec.rs

1/// Read and write implementations for the protocol messages
2use std::io::{self, BufRead, BufReader, Read, Write};
3
4use crate::{
5    error::ReadError,
6    protocol::{Message, Version, XvcInfo},
7};
8
9const XVC_INFO_PREFIX: &[u8] = b"xvcServer";
10
11impl XvcInfo {
12    pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()> {
13        writeln!(
14            writer,
15            "xvcServer_v{}:{}",
16            self.version(),
17            self.max_vector_len()
18        )
19    }
20
21    pub fn from_reader(reader: &mut impl Read) -> Result<XvcInfo, ReadError> {
22        let mut buf_reader = BufReader::with_capacity(32, reader);
23        let mut line = Vec::with_capacity(32);
24        let _ = buf_reader.read_until(b'\n', &mut line)?;
25
26        // Remove trailing newline
27        let mut line = line.trim_ascii_end();
28
29        // Parse format: "xvcServer_v{version}:{max_vector_len_bytes}"
30        if !line.starts_with(XVC_INFO_PREFIX) {
31            return Err(ReadError::InvalidFormat(
32                "Invalid prefix in info message".to_string(),
33            ));
34        }
35
36        line = &line[XVC_INFO_PREFIX.len()..];
37        if line[0] != b'_' {
38            return Err(ReadError::InvalidFormat(
39                "Missing '_' separator".to_string(),
40            ));
41        }
42
43        line = &line[1..];
44        if line[0] != b'v' {
45            return Err(ReadError::InvalidFormat(
46                "Version must start with 'v".to_string(),
47            ));
48        }
49
50        line = &line[1..];
51        let colon_index = line.iter().position(|l| *l == b':').ok_or_else(|| {
52            ReadError::InvalidFormat("Missing ':' separator in info message".to_string())
53        })?;
54
55        let (version_part, rest) = line.split_at(colon_index);
56
57        let version = match version_part {
58            b"1.0" => Version::V1_0,
59            _ => {
60                return Err(ReadError::UnsupportedVersion(
61                    String::from_utf8_lossy(version_part).to_string(),
62                ));
63            }
64        };
65
66        let max_vector_len = str::from_utf8(&rest[1..])?.parse::<u32>()?;
67
68        Ok(XvcInfo::new(version, max_vector_len))
69    }
70}
71
72#[test]
73fn write_server_info() {
74    let mut out = Vec::new();
75    XvcInfo::default().write_to(&mut out).unwrap();
76    assert_eq!(out, b"xvcServer_v1.0:10485760\n".to_vec());
77}
78
79#[test]
80fn read_server_info() {
81    let data = b"xvcServer_v1.0:32\n";
82    let mut cursor = std::io::Cursor::new(data);
83    let info = XvcInfo::from_reader(&mut cursor).unwrap();
84    assert_eq!(info.version(), Version::V1_0);
85    assert_eq!(info.max_vector_len(), 32)
86}
87
88impl Message {
89    const CMD_NAME_GET_INFO: &[u8; 7] = b"getinfo";
90    const CMD_NAME_SET_TCK: &[u8; 6] = b"settck";
91    const CMD_NAME_SHIFT: &[u8; 5] = b"shift";
92    const CMD_DELIMITER: u8 = b':';
93
94    pub fn from_reader(
95        reader: &mut impl Read,
96        max_shift_bytes: usize,
97    ) -> Result<Message, ReadError> {
98        // Buffer must accommodate: "shift:" (5) + num_bits (4) = 13 bytes minimum
99        let mut buf = [0u8; 16];
100        // read 2 bytes into the buffer
101        reader.read_exact(&mut buf[..2])?;
102        match &buf[..2] {
103            b"ge" => {
104                reader.read_exact(&mut buf[2..Self::CMD_NAME_GET_INFO.len() + 1])?;
105                if &buf[..Self::CMD_NAME_GET_INFO.len()] != Self::CMD_NAME_GET_INFO
106                    || buf[Self::CMD_NAME_GET_INFO.len()] != Self::CMD_DELIMITER
107                {
108                    return Err(ReadError::InvalidCommand(
109                        String::from_utf8_lossy(&buf).to_string(),
110                    ));
111                }
112                Ok(Message::GetInfo)
113            }
114            b"se" => {
115                reader.read_exact(&mut buf[2..Self::CMD_NAME_SET_TCK.len() + 1 + 4])?;
116                if &buf[..Self::CMD_NAME_SET_TCK.len()] != Self::CMD_NAME_SET_TCK
117                    || buf[Self::CMD_NAME_SET_TCK.len()] != Self::CMD_DELIMITER
118                {
119                    return Err(ReadError::InvalidCommand(
120                        String::from_utf8_lossy(&buf).to_string(),
121                    ));
122                }
123                let period = u32::from_le_bytes(
124                    buf[Self::CMD_NAME_SET_TCK.len() + 1..Self::CMD_NAME_SET_TCK.len() + 5]
125                        .try_into()
126                        .unwrap(),
127                );
128                Ok(Message::SetTck { period_ns: period })
129            }
130            b"sh" => {
131                reader.read_exact(&mut buf[2..Self::CMD_NAME_SHIFT.len() + 1 + 4])?;
132                if &buf[..Self::CMD_NAME_SHIFT.len()] != Self::CMD_NAME_SHIFT
133                    || buf[Self::CMD_NAME_SHIFT.len()] != Self::CMD_DELIMITER
134                {
135                    return Err(ReadError::InvalidCommand(
136                        String::from_utf8_lossy(&buf).to_string(),
137                    ));
138                }
139                let num_bits = u32::from_le_bytes(
140                    buf[Self::CMD_NAME_SHIFT.len() + 1..Self::CMD_NAME_SHIFT.len() + 5]
141                        .try_into()
142                        .unwrap(),
143                );
144                let num_bytes = num_bits.div_ceil(8_u32) as usize;
145                if num_bytes > max_shift_bytes {
146                    return Err(ReadError::TooManyBytes {
147                        max: max_shift_bytes,
148                        got: num_bytes,
149                    });
150                }
151                let mut tms_vector = vec![0_u8; num_bytes].into_boxed_slice();
152                reader.read_exact(&mut tms_vector[..])?;
153                let mut tdi_vector = vec![0_u8; num_bytes].into_boxed_slice();
154                reader.read_exact(&mut tdi_vector[..])?;
155                Ok(Message::Shift {
156                    num_bits,
157                    tms: tms_vector,
158                    tdi: tdi_vector,
159                })
160            }
161            _ => Err(ReadError::InvalidCommandPrefix(
162                String::from_utf8_lossy(&buf[..2]).to_string(),
163            )),
164        }
165    }
166
167    pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()> {
168        match self {
169            Message::GetInfo => {
170                writer.write_all(Self::CMD_NAME_GET_INFO)?;
171                writer.write_all(&[Self::CMD_DELIMITER])
172            }
173            Message::SetTck {
174                period_ns: period_in_ns,
175            } => {
176                writer.write_all(Self::CMD_NAME_SET_TCK)?;
177                writer.write_all(&[Self::CMD_DELIMITER])?;
178                writer.write_all(&period_in_ns.to_le_bytes())
179            }
180            Message::Shift {
181                num_bits,
182                tms: tms_vector,
183                tdi: tdi_vector,
184            } => {
185                writer.write_all(Self::CMD_NAME_SHIFT)?;
186                writer.write_all(&[Self::CMD_DELIMITER])?;
187                writer.write_all(&num_bits.to_le_bytes())?;
188                writer.write_all(tms_vector)?;
189                writer.write_all(tdi_vector)
190            }
191        }
192    }
193}
194
195#[cfg(test)]
196mod test {
197    use crate::error::ReadError;
198    use crate::protocol::Message;
199    use std::io::Cursor;
200
201    const DEFAULT_MAX_SHIFT_BYTES: usize = 1024;
202
203    #[test]
204    fn read_getinfo() {
205        let data = b"getinfo:".to_vec();
206        let mut cursor = Cursor::new(data);
207        match Message::from_reader(&mut cursor, DEFAULT_MAX_SHIFT_BYTES).unwrap() {
208            Message::GetInfo => {}
209            _ => panic!("expected GetInfo"),
210        }
211    }
212
213    #[test]
214    fn write_getinfo() {
215        let mut out = Vec::new();
216        Message::GetInfo.write_to(&mut out).unwrap();
217        assert_eq!(out, b"getinfo:".to_vec());
218    }
219
220    #[test]
221    fn read_settck() {
222        let period: u32 = 0x1234_5678;
223        let mut data = b"settck:".to_vec();
224        data.extend_from_slice(&period.to_le_bytes());
225        let mut cursor = Cursor::new(data);
226        match Message::from_reader(&mut cursor, DEFAULT_MAX_SHIFT_BYTES).unwrap() {
227            Message::SetTck {
228                period_ns: period_in_ns,
229            } => assert_eq!(period_in_ns, period),
230            _ => panic!("expected SetTck"),
231        }
232    }
233
234    #[test]
235    fn write_settck() {
236        let period: u32 = 0x1234_5678;
237        let mut out = Vec::new();
238        Message::SetTck { period_ns: period }
239            .write_to(&mut out)
240            .unwrap();
241        let mut expected = b"settck:".to_vec();
242        expected.extend_from_slice(&period.to_le_bytes());
243        assert_eq!(out, expected);
244    }
245
246    #[test]
247    fn read_shift() {
248        let num_bits: u32 = 13; // 2 bytes
249        let num_bytes = ((num_bits + 7) / 8) as usize;
250        let tms = vec![0xAAu8; num_bytes];
251        let tdi = vec![0x55u8; num_bytes];
252
253        let mut data = b"shift:".to_vec();
254        data.extend_from_slice(&num_bits.to_le_bytes());
255        data.extend_from_slice(&tms);
256        data.extend_from_slice(&tdi);
257
258        let mut cursor = Cursor::new(data);
259        match Message::from_reader(&mut cursor, DEFAULT_MAX_SHIFT_BYTES).unwrap() {
260            Message::Shift {
261                num_bits: nb,
262                tms: tms_vector,
263                tdi: tdi_vector,
264            } => {
265                assert_eq!(nb, num_bits);
266                assert_eq!(&*tms_vector, &tms[..]);
267                assert_eq!(&*tdi_vector, &tdi[..]);
268            }
269            _ => panic!("expected Shift"),
270        }
271    }
272
273    #[test]
274    fn write_shift() {
275        let num_bits: u32 = 13; // 2 bytes
276        let num_bytes = ((num_bits + 7) / 8) as usize;
277        let tms = vec![0xAAu8; num_bytes].into_boxed_slice();
278        let tdi = vec![0x55u8; num_bytes].into_boxed_slice();
279
280        let cmd = Message::Shift {
281            num_bits,
282            tms: tms.clone(),
283            tdi: tdi.clone(),
284        };
285        let mut out = Vec::new();
286        cmd.write_to(&mut out).unwrap();
287
288        let mut expected = b"shift:".to_vec();
289        expected.extend_from_slice(&num_bits.to_le_bytes());
290        expected.extend_from_slice(&*tms);
291        expected.extend_from_slice(&*tdi);
292
293        assert_eq!(out, expected);
294    }
295
296    #[test]
297    fn invalid_prefix() {
298        let data = b"xx".to_vec();
299        let mut cursor = Cursor::new(data);
300        match Message::from_reader(&mut cursor, DEFAULT_MAX_SHIFT_BYTES) {
301            Err(ReadError::InvalidCommandPrefix(p)) => assert_eq!(p, "xx"),
302            other => panic!("expected InvalidCommandPrefix, got {:?}", other),
303        }
304    }
305
306    #[test]
307    fn too_many_bytes_shift() {
308        // force number of bytes to exceed MAX_SHIFT_BYTES
309        let num_bytes_exceed = 1024 + 1;
310        let num_bits = (num_bytes_exceed * 8) as u32;
311        let mut data = b"shift:".to_vec();
312        data.extend_from_slice(&num_bits.to_le_bytes());
313        let mut cursor = Cursor::new(data);
314        match Message::from_reader(&mut cursor, 1024) {
315            Err(ReadError::TooManyBytes { max, got }) => {
316                assert_eq!(max, 1024);
317                assert_eq!(got, num_bytes_exceed);
318            }
319            other => panic!("expected TooManyBytes, got {:?}", other),
320        }
321    }
322}