Skip to content

Commit

Permalink
Response::Custom: Return payload as Bytes instead of Vec
Browse files Browse the repository at this point in the history
  • Loading branch information
uklotzde committed Jul 25, 2023
1 parent cc02319 commit 9bc168c
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 26 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@

## v0.9.0 (Unreleased)

- Avoid allocations when writing multiple coils/registers
- Optimization: Avoid allocations when writing multiple coils/registers.
- Optimization: Avoid allocations when receiving custom responses.

### Breaking Changes

- `Request` requires a lifetime and stores multiple coils/registers wrapped into `Cow`.
- `Request`: Requires a lifetime and stores multiple coils/registers wrapped into `Cow` to avoid allocations.
- `Response::Custom`: The payload is returned as `Bytes` instead of `Vec` to avoid allocations.

## v0.8.2 (2023-06-15)

Expand Down
34 changes: 23 additions & 11 deletions src/codec/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
// SPDX-FileCopyrightText: Copyright (c) 2017-2023 slowtec GmbH <post@slowtec.de>
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::{
convert::TryFrom,
io::{self, Cursor, Error, ErrorKind},
};

use byteorder::{BigEndian, ReadBytesExt as _};

use crate::{
bytes::{BufMut, Bytes, BytesMut},
frame::*,
};

#[cfg(feature = "rtu")]
pub(crate) mod rtu;

#[cfg(feature = "tcp")]
pub(crate) mod tcp;

use crate::frame::*;

use byteorder::{BigEndian, ReadBytesExt as _};
use bytes::{BufMut, Bytes, BytesMut};
use std::convert::TryFrom;
use std::io::{self, Cursor, Error, ErrorKind};

#[allow(clippy::cast_possible_truncation)]
fn u16_len(len: usize) -> u16 {
// This type conversion should always be safe, because either
Expand Down Expand Up @@ -328,7 +333,10 @@ impl TryFrom<Bytes> for Response {
}
ReadWriteMultipleRegisters(data)
}
_ => Custom(fn_code, bytes[1..].into()),
_ => {
let mut bytes = bytes;
Custom(fn_code, bytes.split_off(1))
}
};
Ok(rsp)
}
Expand Down Expand Up @@ -581,7 +589,7 @@ mod tests {
assert_eq!(rsp_to_fn_code(&WriteMultipleRegisters(0, 0)), 0x10);
assert_eq!(rsp_to_fn_code(&MaskWriteRegister(0, 0, 0)), 0x16);
assert_eq!(rsp_to_fn_code(&ReadWriteMultipleRegisters(vec![])), 0x17);
assert_eq!(rsp_to_fn_code(&Custom(99, vec![])), 99);
assert_eq!(rsp_to_fn_code(&Custom(99, Bytes::from_static(&[]))), 99);
}

#[test]
Expand Down Expand Up @@ -1064,7 +1072,8 @@ mod tests {

#[test]
fn custom() {
let bytes: Bytes = Response::Custom(0x55, vec![0xCC, 0x88, 0xAA, 0xFF]).into();
let bytes: Bytes =
Response::Custom(0x55, Bytes::from_static(&[0xCC, 0x88, 0xAA, 0xFF])).into();
assert_eq!(bytes[0], 0x55);
assert_eq!(bytes[1], 0xCC);
assert_eq!(bytes[2], 0x88);
Expand Down Expand Up @@ -1186,7 +1195,10 @@ mod tests {
fn custom() {
let bytes = Bytes::from(vec![0x55, 0xCC, 0x88, 0xAA, 0xFF]);
let rsp = Response::try_from(bytes).unwrap();
assert_eq!(rsp, Response::Custom(0x55, vec![0xCC, 0x88, 0xAA, 0xFF]));
assert_eq!(
rsp,
Response::Custom(0x55, Bytes::from_static(&[0xCC, 0x88, 0xAA, 0xFF]))
);
}
}
}
16 changes: 10 additions & 6 deletions src/codec/rtu.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// SPDX-FileCopyrightText: Copyright (c) 2017-2023 slowtec GmbH <post@slowtec.de>
// SPDX-License-Identifier: MIT OR Apache-2.0

use super::*;

use crate::{frame::rtu::*, slave::SlaveId};
use std::io::{Cursor, Error, ErrorKind, Result};

use byteorder::BigEndian;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use smallvec::SmallVec;
use std::io::{Cursor, Error, ErrorKind, Result};
use tokio_util::codec::{Decoder, Encoder};

use crate::{
bytes::{Buf, BufMut, Bytes, BytesMut},
frame::rtu::*,
slave::SlaveId,
};

use super::*;

// [Modbus over Serial Line Specification and Implementation Guide V1.02](http://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf), page 13
// "The maximum size of a Modbus RTU frame is 256 bytes."
const MAX_FRAME_LEN: usize = 256;
Expand Down Expand Up @@ -364,7 +368,7 @@ impl Encoder<ResponseAdu> for ServerCodec {
#[cfg(test)]
mod tests {
use super::*;
use bytes::Bytes;
use crate::bytes::Bytes;

#[test]
fn test_calc_crc() {
Expand Down
15 changes: 9 additions & 6 deletions src/codec/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
// SPDX-FileCopyrightText: Copyright (c) 2017-2023 slowtec GmbH <post@slowtec.de>
// SPDX-License-Identifier: MIT OR Apache-2.0

use super::*;

use crate::frame::tcp::*;
use std::io::{Error, ErrorKind, Result};

use byteorder::{BigEndian, ByteOrder};
use bytes::{BufMut, Bytes, BytesMut};
use std::io::{Error, ErrorKind, Result};
use tokio_util::codec::{Decoder, Encoder};

use crate::{
bytes::{BufMut, Bytes, BytesMut},
frame::tcp::*,
};

use super::*;

const HEADER_LEN: usize = 7;

const PROTOCOL_ID: u16 = 0x0000; // TCP
Expand Down Expand Up @@ -174,7 +177,7 @@ impl Encoder<ResponseAdu> for ServerCodec {
#[cfg(test)]
mod tests {
use super::*;
use bytes::Bytes;
use crate::bytes::Bytes;

mod client {

Expand Down
4 changes: 3 additions & 1 deletion src/frame/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub(crate) mod tcp;

use std::{borrow::Cow, error, fmt};

use crate::bytes::Bytes;

/// A Modbus function code is represented by an unsigned 8 bit integer.
pub type FunctionCode = u8;

Expand Down Expand Up @@ -220,7 +222,7 @@ pub enum Response {
/// Response to a raw Modbus request
/// The first parameter contains the returned Modbus function code
/// The second parameter contains the bytes read following the function code
Custom(FunctionCode, Vec<u8>),
Custom(FunctionCode, Bytes),
}

/// A server (slave) exception.
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
#![warn(rustdoc::broken_intra_doc_links)]
#![doc = include_str!("../README.md")]

/// Re-export the `bytes` crate
///
/// Needed to prevent version conflicts with types that are exposed by the public API.
///
/// Used by [`Response::Custom`].
pub use bytes;

pub mod prelude;

pub mod client;
Expand Down

0 comments on commit 9bc168c

Please sign in to comment.