modbius_core/
util.rs

1use crate::ModbusSerializationError;
2
3/// Reads an u16 from the given modbus data. The data is considered to be big endian with msB first
4/// 
5/// # Errors 
6/// If the data slice contains less than 2 bytes a [ModbusSerializationError::UnexpectedEOF] will be returned 
7/// with its expected field set to 2 and its got field set to data.len().
8pub fn read_u16(data: &[u8]) -> Result<(u16, &[u8]), crate::ModbusSerializationError> {
9    if let [Some(dhi), Some(dlo)] = [data.get(0), data.get(1)] {
10        Ok((u16::from_be_bytes([*dhi, *dlo]), &data[2..]))
11    } else {
12        Err(ModbusSerializationError::UnexpectedEOF {
13            expected: 2,
14            got: data.len(),
15        })
16    }
17}
18
19/// Reads an u16 from the given modbus data without performing bounds checks. The data is considered to be big endian with msB first.
20pub unsafe fn read_u16_unchecked(data: &[u8]) -> (u16, &[u8]) {
21    let word = u16::from_be_bytes([*data.get_unchecked(0), *data.get_unchecked(1)]);
22
23    //This is safe if data.len() > 2 as get_unchecked only invokes undefined behavior
24    //if get would return none (idx > len)
25    (word, data.get_unchecked(2..))
26}
27
28#[cfg(test)]
29mod read_u16_test {
30    use super::read_u16;
31
32    #[test]
33    fn read8() {
34        let (w8, tail) = read_u16(&[0, 8]).unwrap();
35        assert_eq!(w8, 8);
36        assert_eq!(tail, []);
37    }
38
39    #[test]
40    fn read0() {
41        let (w8, tail) = read_u16(&[0, 0, 100]).unwrap();
42        assert_eq!(w8, 0);
43        assert_eq!(tail, &[100]);
44    }
45
46    #[test]
47    fn read256() {
48        let (w8, tail) = read_u16(&[1, 0, 100, 200, 2, 3, 4, 5]).unwrap();
49        assert_eq!(w8, 256);
50        assert_eq!(tail, &[100, 200, 2, 3, 4, 5]);
51    }
52
53    #[test]
54    #[should_panic(expected = "called `Result::unwrap()` on an `Err` value")]
55    fn read_error1() {
56        let (_w8, _tail) = read_u16(&[1]).unwrap();
57    }
58
59    #[test]
60    #[should_panic(expected = "called `Result::unwrap()` on an `Err` value")]
61    fn read_error0() {
62        let (_w8, _tail) = read_u16(&[]).unwrap();
63    }
64}
65/* 
66/// Interprets the data as an address and a quantity. They are returned in this order.
67/// The data left in the slice is returned too. 
68/// 
69/// # Errors 
70/// If the data slice contains less than 4 bytes a [ModbusSerializationError::UnexpectedEOF] will be returned 
71/// with its expected field set to 4 and its got field set to data.len().
72pub fn read_addr_quantity(data: &[u8]) -> Result<(u16, u16, &[u8]), ModbusSerializationError>  {
73    if data.len() < 4 {
74        return Err(ModbusSerializationError::UnexpectedEOF {expected: 4, got: data.len()})
75    }
76
77    Ok(unsafe { read_addr_quantity_unchecked(data) })
78}
79
80pub unsafe fn read_addr_quantity_unchecked(data: &[u8]) -> (u16, u16, &[u8]) {
81    let (addr, data) = read_u16_unchecked(data);
82    let (quantity, data) = read_u16_unchecked(data);
83
84    (addr, quantity, data)
85}
86
87pub fn write_addr_quantity(
88    addr: u16,
89    quantity: u16,
90    out: &mut [u8],
91) -> Result<(), ModbusSerializationError> {
92    if out.len() < 4 {
93        return Err(ModbusSerializationError::InsufficientBuffer {
94            expected: 4,
95            got: out.len(),
96        });
97    }
98
99    unsafe {
100        write_addr_quantity_unchecked(addr, quantity, out);
101    }
102
103    Ok(())
104}
105
106pub unsafe fn write_addr_quantity_unchecked(addr: u16, quantity: u16, out: &mut [u8]) {
107    let addr_bytes = addr.to_be_bytes();
108    *out.get_unchecked_mut(0) = addr_bytes[0];
109    *out.get_unchecked_mut(1) = addr_bytes[1];
110
111    let quantity_bytes = quantity.to_be_bytes();
112    *out.get_unchecked_mut(2) = quantity_bytes[0];
113    *out.get_unchecked_mut(3) = quantity_bytes[1];
114} */