From 7b67467054f0ead27b2979b6cd3e5a1239b866bc Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sun, 23 Jul 2023 15:53:28 +0200 Subject: [PATCH] Response::Custom: Return payload as Bytes instead of Vec --- CHANGELOG.md | 6 ++++-- src/codec/mod.rs | 34 +++++++++++++++++++++++----------- src/codec/rtu.rs | 16 ++++++++++------ src/codec/tcp.rs | 15 +++++++++------ src/frame/mod.rs | 4 +++- src/lib.rs | 7 +++++++ 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8310af83..bb6c371b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/codec/mod.rs b/src/codec/mod.rs index 01b12575..3fd561ea 100644 --- a/src/codec/mod.rs +++ b/src/codec/mod.rs @@ -1,19 +1,24 @@ // SPDX-FileCopyrightText: Copyright (c) 2017-2023 slowtec GmbH // 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 @@ -328,7 +333,10 @@ impl TryFrom for Response { } ReadWriteMultipleRegisters(data) } - _ => Custom(fn_code, bytes[1..].into()), + _ => { + let mut bytes = bytes; + Custom(fn_code, bytes.split_off(1)) + } }; Ok(rsp) } @@ -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] @@ -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); @@ -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])) + ); } } } diff --git a/src/codec/rtu.rs b/src/codec/rtu.rs index 6675f53e..10e8a0b3 100644 --- a/src/codec/rtu.rs +++ b/src/codec/rtu.rs @@ -1,16 +1,20 @@ // SPDX-FileCopyrightText: Copyright (c) 2017-2023 slowtec GmbH // 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; @@ -364,7 +368,7 @@ impl Encoder for ServerCodec { #[cfg(test)] mod tests { use super::*; - use bytes::Bytes; + use crate::bytes::Bytes; #[test] fn test_calc_crc() { diff --git a/src/codec/tcp.rs b/src/codec/tcp.rs index c5995c21..e3ee35ba 100644 --- a/src/codec/tcp.rs +++ b/src/codec/tcp.rs @@ -1,15 +1,18 @@ // SPDX-FileCopyrightText: Copyright (c) 2017-2023 slowtec GmbH // 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 @@ -174,7 +177,7 @@ impl Encoder for ServerCodec { #[cfg(test)] mod tests { use super::*; - use bytes::Bytes; + use crate::bytes::Bytes; mod client { diff --git a/src/frame/mod.rs b/src/frame/mod.rs index c42d41f3..fb516058 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -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; @@ -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), + Custom(FunctionCode, Bytes), } /// A server (slave) exception. diff --git a/src/lib.rs b/src/lib.rs index d84a3748..0672b02b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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;