Skip to content

Commit 2055c3a

Browse files
committed
Minimize copy for MqttString and MqttBinary.
1 parent fe389da commit 2055c3a

File tree

9 files changed

+208
-51
lines changed

9 files changed

+208
-51
lines changed

src/mqtt/packet/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
// SOFTWARE.
2222
mod mqtt_string;
23-
pub use self::mqtt_string::MqttString;
23+
pub use self::mqtt_string::{IntoMqttString, MqttString};
2424
mod mqtt_binary;
25-
pub use self::mqtt_binary::MqttBinary;
25+
pub use self::mqtt_binary::{IntoMqttBinary, MqttBinary};
2626

2727
mod enum_packet;
2828
mod enum_store_packet;

src/mqtt/packet/mqtt_binary.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,47 @@ impl core::fmt::Debug for MqttBinary {
300300
}
301301
}
302302

303+
/// Trait for types that can be converted into `MqttBinary`
304+
pub trait IntoMqttBinary {
305+
fn into_mqtt_binary(self) -> Result<MqttBinary, MqttError>;
306+
}
307+
308+
impl IntoMqttBinary for &[u8] {
309+
fn into_mqtt_binary(self) -> Result<MqttBinary, MqttError> {
310+
MqttBinary::new(self)
311+
}
312+
}
313+
314+
impl<const N: usize> IntoMqttBinary for &[u8; N] {
315+
fn into_mqtt_binary(self) -> Result<MqttBinary, MqttError> {
316+
MqttBinary::new(&self[..])
317+
}
318+
}
319+
320+
impl IntoMqttBinary for &str {
321+
fn into_mqtt_binary(self) -> Result<MqttBinary, MqttError> {
322+
MqttBinary::new(self.as_bytes())
323+
}
324+
}
325+
326+
impl IntoMqttBinary for Vec<u8> {
327+
fn into_mqtt_binary(self) -> Result<MqttBinary, MqttError> {
328+
MqttBinary::new(&self)
329+
}
330+
}
331+
332+
impl IntoMqttBinary for &Vec<u8> {
333+
fn into_mqtt_binary(self) -> Result<MqttBinary, MqttError> {
334+
MqttBinary::new(self)
335+
}
336+
}
337+
338+
impl IntoMqttBinary for MqttBinary {
339+
fn into_mqtt_binary(self) -> Result<MqttBinary, MqttError> {
340+
Ok(self)
341+
}
342+
}
343+
303344
#[cfg(test)]
304345
mod tests {
305346
use super::*;

src/mqtt/packet/mqtt_string.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,59 @@ impl TryFrom<String> for MqttString {
681681
}
682682
}
683683

684+
/// Trait for types that can be converted into `MqttString`
685+
///
686+
/// This trait provides a flexible way to create `MqttString` instances from various string types.
687+
/// It allows you to pass pre-constructed `MqttString` objects directly without additional
688+
/// heap allocations, which is particularly useful in multi-threaded scenarios.
689+
///
690+
/// # Examples
691+
///
692+
/// ```ignore
693+
/// use mqtt_protocol_core::mqtt::packet::{IntoMqttString, MqttString};
694+
///
695+
/// // From &str
696+
/// let s1 = "test".into_mqtt_string().unwrap();
697+
/// assert_eq!(s1.as_str(), "test");
698+
///
699+
/// // From String
700+
/// let s2 = String::from("test").into_mqtt_string().unwrap();
701+
/// assert_eq!(s2.as_str(), "test");
702+
///
703+
/// // From MqttString (no additional allocation)
704+
/// let mqtt_str = MqttString::new("test").unwrap();
705+
/// let s3 = mqtt_str.into_mqtt_string().unwrap();
706+
/// assert_eq!(s3.as_str(), "test");
707+
/// ```
708+
pub trait IntoMqttString {
709+
/// Convert self into an MqttString
710+
fn into_mqtt_string(self) -> Result<MqttString, MqttError>;
711+
}
712+
713+
impl IntoMqttString for &str {
714+
fn into_mqtt_string(self) -> Result<MqttString, MqttError> {
715+
MqttString::new(self)
716+
}
717+
}
718+
719+
impl IntoMqttString for String {
720+
fn into_mqtt_string(self) -> Result<MqttString, MqttError> {
721+
MqttString::new(self)
722+
}
723+
}
724+
725+
impl IntoMqttString for &String {
726+
fn into_mqtt_string(self) -> Result<MqttString, MqttError> {
727+
MqttString::new(self)
728+
}
729+
}
730+
731+
impl IntoMqttString for MqttString {
732+
fn into_mqtt_string(self) -> Result<MqttString, MqttError> {
733+
Ok(self)
734+
}
735+
}
736+
684737
#[cfg(test)]
685738
mod tests {
686739
use super::*;

src/mqtt/packet/property.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
// SOFTWARE.
2222

2323
use crate::mqtt::packet::escape_binary_json_string;
24-
use crate::mqtt::packet::mqtt_binary::MqttBinary;
25-
use crate::mqtt::packet::mqtt_string::MqttString;
24+
use crate::mqtt::packet::mqtt_binary::{IntoMqttBinary, MqttBinary};
25+
use crate::mqtt::packet::mqtt_string::{IntoMqttString, MqttString};
2626
use crate::mqtt::packet::DecodeResult;
2727
use crate::mqtt::packet::VariableByteInteger;
2828
use crate::mqtt::result_code::MqttError;
@@ -372,9 +372,9 @@ macro_rules! mqtt_property_binary {
372372
/// ```
373373
pub fn new<T>(v: T) -> Result<Self, MqttError>
374374
where
375-
T: AsRef<[u8]>,
375+
T: IntoMqttBinary,
376376
{
377-
let binary = MqttBinary::new(v)?;
377+
let binary = v.into_mqtt_binary()?;
378378

379379
Ok(Self {
380380
id_bytes: [$id as u8],
@@ -553,9 +553,9 @@ macro_rules! mqtt_property_string {
553553
/// ```
554554
pub fn new<T>(s: T) -> Result<Self, MqttError>
555555
where
556-
T: AsRef<str>,
556+
T: IntoMqttString,
557557
{
558-
let value = MqttString::new(s)?;
558+
let value = s.into_mqtt_string()?;
559559

560560
Ok(Self {
561561
id_bytes: [$id as u8],
@@ -716,11 +716,11 @@ macro_rules! mqtt_property_string_pair {
716716
/// ```
717717
pub fn new<K, V>(key: K, val: V) -> Result<Self, MqttError>
718718
where
719-
K: AsRef<str>,
720-
V: AsRef<str>,
719+
K: IntoMqttString,
720+
V: IntoMqttString,
721721
{
722-
let key_mqtt = MqttString::new(key)?;
723-
let val_mqtt = MqttString::new(val)?;
722+
let key_mqtt = key.into_mqtt_string()?;
723+
let val_mqtt = val.into_mqtt_string()?;
724724

725725
Ok(Self {
726726
id_bytes: [$id as u8],

src/mqtt/packet/sub_entry.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
// SOFTWARE.
2222

23-
use crate::mqtt::packet::MqttString;
2423
use crate::mqtt::packet::Qos;
2524
use crate::mqtt::packet::RetainHandling;
25+
use crate::mqtt::packet::{IntoMqttString, MqttString};
2626
use crate::mqtt::result_code::MqttError;
2727
use alloc::string::ToString;
2828
use alloc::{string::String, vec::Vec};
@@ -564,8 +564,11 @@ impl SubEntry {
564564
/// let opts = mqtt::packet::SubOpts::new().set_qos(mqtt::packet::Qos::AtLeastOnce);
565565
/// let entry = mqtt::packet::SubEntry::new("home/+/status", opts).unwrap();
566566
/// ```
567-
pub fn new(topic_filter: impl AsRef<str>, sub_opts: SubOpts) -> Result<Self, MqttError> {
568-
let topic_filter = MqttString::new(topic_filter)?;
567+
pub fn new<T>(topic_filter: T, sub_opts: SubOpts) -> Result<Self, MqttError>
568+
where
569+
T: IntoMqttString,
570+
{
571+
let topic_filter = topic_filter.into_mqtt_string()?;
569572
Ok(Self {
570573
topic_filter,
571574
sub_opts,

src/mqtt/packet/v3_1_1/connect.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ use serde::Serialize;
3232
use getset::{CopyGetters, Getters};
3333

3434
use crate::mqtt::packet::json_bin_encode::escape_binary_json_string;
35-
use crate::mqtt::packet::mqtt_binary::MqttBinary;
36-
use crate::mqtt::packet::mqtt_string::MqttString;
35+
use crate::mqtt::packet::mqtt_binary::{IntoMqttBinary, MqttBinary};
36+
use crate::mqtt::packet::mqtt_string::{IntoMqttString, MqttString};
3737
use crate::mqtt::packet::packet_type::{FixedHeader, PacketType};
3838
use crate::mqtt::packet::qos::Qos;
3939
use crate::mqtt::packet::variable_byte_integer::VariableByteInteger;
@@ -679,8 +679,11 @@ impl ConnectBuilder {
679679
/// .client_id("device-001")
680680
/// .unwrap();
681681
/// ```
682-
pub fn client_id(mut self, id: impl AsRef<str>) -> Result<Self, MqttError> {
683-
let mqtt_str = MqttString::new(id.as_ref())?;
682+
pub fn client_id<T>(mut self, id: T) -> Result<Self, MqttError>
683+
where
684+
T: IntoMqttString,
685+
{
686+
let mqtt_str = id.into_mqtt_string()?;
684687
self.client_id_buf = Some(mqtt_str);
685688
Ok(self)
686689
}
@@ -766,15 +769,19 @@ impl ConnectBuilder {
766769
/// .build()
767770
/// .unwrap();
768771
/// ```
769-
pub fn will_message(
772+
pub fn will_message<T, B>(
770773
mut self,
771-
topic: impl AsRef<str>,
772-
payload: impl AsRef<[u8]>,
774+
topic: T,
775+
payload: B,
773776
qos: Qos,
774777
retain: bool,
775-
) -> Result<Self, MqttError> {
776-
let will_topic = MqttString::new(topic.as_ref())?;
777-
let will_payload = MqttBinary::new(payload.as_ref().to_vec())?;
778+
) -> Result<Self, MqttError>
779+
where
780+
T: IntoMqttString,
781+
B: IntoMqttBinary,
782+
{
783+
let will_topic = topic.into_mqtt_string()?;
784+
let will_payload = payload.into_mqtt_binary()?;
778785

779786
self.will_topic_buf = Some(will_topic);
780787
self.will_payload_buf = Some(will_payload);
@@ -815,8 +822,11 @@ impl ConnectBuilder {
815822
/// .build()
816823
/// .unwrap();
817824
/// ```
818-
pub fn user_name(mut self, name: impl AsRef<str>) -> Result<Self, MqttError> {
819-
let mqtt_str = MqttString::new(name.as_ref())?;
825+
pub fn user_name<T>(mut self, name: T) -> Result<Self, MqttError>
826+
where
827+
T: IntoMqttString,
828+
{
829+
let mqtt_str = name.into_mqtt_string()?;
820830
self.user_name_buf = Some(mqtt_str);
821831

822832
let mut flags = self.connect_flags_buf.unwrap_or([0b0000_0010])[0];
@@ -862,8 +872,11 @@ impl ConnectBuilder {
862872
/// .build()
863873
/// .unwrap();
864874
/// ```
865-
pub fn password(mut self, pwd: impl AsRef<[u8]>) -> Result<Self, MqttError> {
866-
let mqtt_bin = MqttBinary::new(pwd.as_ref().to_vec())?;
875+
pub fn password<B>(mut self, pwd: B) -> Result<Self, MqttError>
876+
where
877+
B: IntoMqttBinary,
878+
{
879+
let mqtt_bin = pwd.into_mqtt_binary()?;
867880
self.password_buf = Some(mqtt_bin);
868881

869882
let mut flags = self.connect_flags_buf.unwrap_or([0b0000_0010])[0];

src/mqtt/packet/v3_1_1/publish.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ use crate::mqtt::packet::qos::Qos;
3939
use crate::mqtt::packet::variable_byte_integer::VariableByteInteger;
4040
use crate::mqtt::packet::GenericPacketDisplay;
4141
use crate::mqtt::packet::GenericPacketTrait;
42-
use crate::mqtt::packet::{IntoPacketId, IsPacketId};
42+
use crate::mqtt::packet::{IntoMqttString, IntoPacketId, IsPacketId};
4343
use crate::mqtt::result_code::MqttError;
4444
use crate::mqtt::{Arc, ArcPayload, IntoPayload};
4545

@@ -690,9 +690,16 @@ where
690690
/// is published. Topic names must be valid UTF-8 strings and cannot contain
691691
/// wildcard characters (+ or #) which are reserved for subscription filters.
692692
///
693+
/// This method accepts both string types (&str, String) and pre-constructed
694+
/// `MqttString` instances. When passing a pre-constructed `MqttString`, no
695+
/// additional heap allocation occurs, making it efficient for cross-thread
696+
/// message passing scenarios.
697+
///
693698
/// # Parameters
694699
///
695-
/// * `topic` - The topic name as a string reference
700+
/// * `topic` - The topic name. Can be:
701+
/// - `&str` or `String` (will be converted to `MqttString`)
702+
/// - `MqttString` (passed by value without additional allocation)
696703
///
697704
/// # Returns
698705
///
@@ -711,17 +718,27 @@ where
711718
/// ```ignore
712719
/// use mqtt_protocol_core::mqtt;
713720
///
721+
/// // From &str (backward compatible)
714722
/// let builder = mqtt::packet::v3_1_1::Publish::builder()
715723
/// .topic_name("sensors/temperature/room1")
716724
/// .unwrap();
717725
///
726+
/// // From pre-constructed MqttString (no additional allocation)
727+
/// let topic = mqtt::packet::MqttString::new("sensors/temperature/room1").unwrap();
728+
/// let builder = mqtt::packet::v3_1_1::Publish::builder()
729+
/// .topic_name(topic)
730+
/// .unwrap();
731+
///
718732
/// // This would fail due to wildcard
719733
/// // let invalid = mqtt::packet::v3_1_1::Publish::builder()
720734
/// // .topic_name("sensors/+/temperature")
721735
/// // .unwrap();
722736
/// ```
723-
pub fn topic_name<T: AsRef<str>>(mut self, topic: T) -> Result<Self, MqttError> {
724-
let mqtt_str = MqttString::new(topic)?;
737+
pub fn topic_name<T>(mut self, topic: T) -> Result<Self, MqttError>
738+
where
739+
T: IntoMqttString,
740+
{
741+
let mqtt_str = topic.into_mqtt_string()?;
725742
if mqtt_str.as_str().contains('#') || mqtt_str.as_str().contains('+') {
726743
return Err(MqttError::MalformedPacket);
727744
}

0 commit comments

Comments
 (0)