Skip to content

Commit 10fee81

Browse files
committed
Minimize copy for MqttString and MqttBinary.
1 parent fe389da commit 10fee81

File tree

8 files changed

+152
-41
lines changed

8 files changed

+152
-41
lines changed

src/mqtt/packet/mqtt_binary.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,40 @@ impl TryFrom<&str> for MqttBinary {
285285
}
286286
}
287287

288+
/// Implementation of `TryFrom<&[u8; N]>` for `MqttBinary`
289+
///
290+
/// Converts a fixed-size array reference to `MqttBinary`. This allows array literals
291+
/// like `b"data"` to be used with generic code that accepts `TryInto<MqttBinary>`.
292+
impl<const N: usize> TryFrom<&[u8; N]> for MqttBinary {
293+
type Error = MqttError;
294+
295+
fn try_from(arr: &[u8; N]) -> Result<Self, Self::Error> {
296+
Self::new(&arr[..])
297+
}
298+
}
299+
300+
/// Implementation of `TryFrom<Vec<u8>>` for `MqttBinary`
301+
///
302+
/// Converts an owned `Vec<u8>` to `MqttBinary`.
303+
impl TryFrom<Vec<u8>> for MqttBinary {
304+
type Error = MqttError;
305+
306+
fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
307+
Self::new(&v)
308+
}
309+
}
310+
311+
/// Implementation of `TryFrom<&Vec<u8>>` for `MqttBinary`
312+
///
313+
/// Converts a `Vec<u8>` reference to `MqttBinary`.
314+
impl TryFrom<&Vec<u8>> for MqttBinary {
315+
type Error = MqttError;
316+
317+
fn try_from(v: &Vec<u8>) -> Result<Self, Self::Error> {
318+
Self::new(v.as_slice())
319+
}
320+
}
321+
288322
/// Implementation of `Default` for `MqttBinary`
289323
impl Default for MqttBinary {
290324
fn default() -> Self {

src/mqtt/packet/mqtt_string.rs

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

684+
/// Implementation of `TryFrom<&String>` for `MqttString`
685+
///
686+
/// Converts a `String` reference to `MqttString`.
687+
impl TryFrom<&String> for MqttString {
688+
type Error = MqttError;
689+
690+
fn try_from(s: &String) -> Result<Self, Self::Error> {
691+
MqttString::new(s.as_str())
692+
}
693+
}
694+
684695
#[cfg(test)]
685696
mod tests {
686697
use super::*;

src/mqtt/packet/property.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::mqtt::packet::DecodeResult;
2727
use crate::mqtt::packet::VariableByteInteger;
2828
use crate::mqtt::result_code::MqttError;
2929
use alloc::{string::String, vec::Vec};
30-
use core::convert::TryFrom;
30+
use core::convert::{TryFrom, TryInto};
3131
use core::fmt;
3232
use num_enum::TryFromPrimitive;
3333
use serde::ser::SerializeStruct;
@@ -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: TryInto<MqttBinary, Error = MqttError>,
376376
{
377-
let binary = MqttBinary::new(v)?;
377+
let binary = v.try_into()?;
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: TryInto<MqttString, Error = MqttError>,
557557
{
558-
let value = MqttString::new(s)?;
558+
let value = s.try_into()?;
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: TryInto<MqttString, Error = MqttError>,
720+
V: TryInto<MqttString, Error = MqttError>,
721721
{
722-
let key_mqtt = MqttString::new(key)?;
723-
let val_mqtt = MqttString::new(val)?;
722+
let key_mqtt = key.try_into()?;
723+
let val_mqtt = val.try_into()?;
724724

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

src/mqtt/packet/sub_entry.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::mqtt::packet::RetainHandling;
2626
use crate::mqtt::result_code::MqttError;
2727
use alloc::string::ToString;
2828
use alloc::{string::String, vec::Vec};
29+
use core::convert::TryInto;
2930
use core::fmt;
3031
use serde::ser::{SerializeStruct, Serializer};
3132
use serde::Serialize;
@@ -564,8 +565,11 @@ impl SubEntry {
564565
/// let opts = mqtt::packet::SubOpts::new().set_qos(mqtt::packet::Qos::AtLeastOnce);
565566
/// let entry = mqtt::packet::SubEntry::new("home/+/status", opts).unwrap();
566567
/// ```
567-
pub fn new(topic_filter: impl AsRef<str>, sub_opts: SubOpts) -> Result<Self, MqttError> {
568-
let topic_filter = MqttString::new(topic_filter)?;
568+
pub fn new<T>(topic_filter: T, sub_opts: SubOpts) -> Result<Self, MqttError>
569+
where
570+
T: TryInto<MqttString, Error = MqttError>,
571+
{
572+
let topic_filter = topic_filter.try_into()?;
569573
Ok(Self {
570574
topic_filter,
571575
sub_opts,

src/mqtt/packet/v3_1_1/connect.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use crate::mqtt::packet::variable_byte_integer::VariableByteInteger;
4040
use crate::mqtt::packet::GenericPacketDisplay;
4141
use crate::mqtt::packet::GenericPacketTrait;
4242
use crate::mqtt::result_code::MqttError;
43+
use core::convert::TryInto;
4344

4445
/// MQTT v3.1.1 CONNECT packet representation
4546
///
@@ -679,8 +680,11 @@ impl ConnectBuilder {
679680
/// .client_id("device-001")
680681
/// .unwrap();
681682
/// ```
682-
pub fn client_id(mut self, id: impl AsRef<str>) -> Result<Self, MqttError> {
683-
let mqtt_str = MqttString::new(id.as_ref())?;
683+
pub fn client_id<T>(mut self, id: T) -> Result<Self, MqttError>
684+
where
685+
T: TryInto<MqttString, Error = MqttError>,
686+
{
687+
let mqtt_str = id.try_into()?;
684688
self.client_id_buf = Some(mqtt_str);
685689
Ok(self)
686690
}
@@ -766,15 +770,19 @@ impl ConnectBuilder {
766770
/// .build()
767771
/// .unwrap();
768772
/// ```
769-
pub fn will_message(
773+
pub fn will_message<T, B>(
770774
mut self,
771-
topic: impl AsRef<str>,
772-
payload: impl AsRef<[u8]>,
775+
topic: T,
776+
payload: B,
773777
qos: Qos,
774778
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())?;
779+
) -> Result<Self, MqttError>
780+
where
781+
T: TryInto<MqttString, Error = MqttError>,
782+
B: TryInto<MqttBinary, Error = MqttError>,
783+
{
784+
let will_topic = topic.try_into()?;
785+
let will_payload = payload.try_into()?;
778786

779787
self.will_topic_buf = Some(will_topic);
780788
self.will_payload_buf = Some(will_payload);
@@ -815,8 +823,11 @@ impl ConnectBuilder {
815823
/// .build()
816824
/// .unwrap();
817825
/// ```
818-
pub fn user_name(mut self, name: impl AsRef<str>) -> Result<Self, MqttError> {
819-
let mqtt_str = MqttString::new(name.as_ref())?;
826+
pub fn user_name<T>(mut self, name: T) -> Result<Self, MqttError>
827+
where
828+
T: TryInto<MqttString, Error = MqttError>,
829+
{
830+
let mqtt_str = name.try_into()?;
820831
self.user_name_buf = Some(mqtt_str);
821832

822833
let mut flags = self.connect_flags_buf.unwrap_or([0b0000_0010])[0];
@@ -862,8 +873,11 @@ impl ConnectBuilder {
862873
/// .build()
863874
/// .unwrap();
864875
/// ```
865-
pub fn password(mut self, pwd: impl AsRef<[u8]>) -> Result<Self, MqttError> {
866-
let mqtt_bin = MqttBinary::new(pwd.as_ref().to_vec())?;
876+
pub fn password<B>(mut self, pwd: B) -> Result<Self, MqttError>
877+
where
878+
B: TryInto<MqttBinary, Error = MqttError>,
879+
{
880+
let mqtt_bin = pwd.try_into()?;
867881
self.password_buf = Some(mqtt_bin);
868882

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

src/mqtt/packet/v3_1_1/publish.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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: TryInto<MqttString, Error = MqttError>,
740+
{
741+
let mqtt_str = topic.try_into()?;
725742
if mqtt_str.as_str().contains('#') || mqtt_str.as_str().contains('+') {
726743
return Err(MqttError::MalformedPacket);
727744
}

src/mqtt/packet/v5_0/connect.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use crate::mqtt::packet::mqtt_string::MqttString;
3737
use crate::mqtt::packet::packet_type::{FixedHeader, PacketType};
3838
use crate::mqtt::packet::GenericPacketDisplay;
3939
use crate::mqtt::packet::GenericPacketTrait;
40+
use core::convert::TryInto;
4041

4142
use crate::mqtt::packet::property::PropertiesToContinuousBuffer;
4243
use crate::mqtt::packet::qos::Qos;
@@ -734,8 +735,11 @@ impl ConnectBuilder {
734735
/// .build()
735736
/// .unwrap();
736737
/// ```
737-
pub fn client_id(mut self, id: impl AsRef<str>) -> Result<Self, MqttError> {
738-
let mqtt_str = MqttString::new(id.as_ref())?;
738+
pub fn client_id<T>(mut self, id: T) -> Result<Self, MqttError>
739+
where
740+
T: TryInto<MqttString, Error = MqttError>,
741+
{
742+
let mqtt_str = id.try_into()?;
739743
self.client_id_buf = Some(mqtt_str);
740744
Ok(self)
741745
}
@@ -814,15 +818,19 @@ impl ConnectBuilder {
814818
/// .build()
815819
/// .unwrap();
816820
/// ```
817-
pub fn will_message(
821+
pub fn will_message<T, B>(
818822
mut self,
819-
topic: impl AsRef<str>,
820-
payload: impl AsRef<[u8]>,
823+
topic: T,
824+
payload: B,
821825
qos: Qos,
822826
retain: bool,
823-
) -> Result<Self, MqttError> {
824-
let will_topic = MqttString::new(topic.as_ref())?;
825-
let will_payload = MqttBinary::new(payload.as_ref().to_vec())?;
827+
) -> Result<Self, MqttError>
828+
where
829+
T: TryInto<MqttString, Error = MqttError>,
830+
B: TryInto<MqttBinary, Error = MqttError>,
831+
{
832+
let will_topic = topic.try_into()?;
833+
let will_payload = payload.try_into()?;
826834

827835
self.will_topic_buf = Some(will_topic);
828836
self.will_payload_buf = Some(will_payload);
@@ -863,8 +871,11 @@ impl ConnectBuilder {
863871
/// .build()
864872
/// .unwrap();
865873
/// ```
866-
pub fn user_name(mut self, name: impl AsRef<str>) -> Result<Self, MqttError> {
867-
let mqtt_str = MqttString::new(name.as_ref())?;
874+
pub fn user_name<T>(mut self, name: T) -> Result<Self, MqttError>
875+
where
876+
T: TryInto<MqttString, Error = MqttError>,
877+
{
878+
let mqtt_str = name.try_into()?;
868879
self.user_name_buf = Some(mqtt_str);
869880

870881
let mut flags = self.connect_flags_buf.unwrap_or([0b0000_0010])[0];
@@ -900,8 +911,11 @@ impl ConnectBuilder {
900911
/// .build()
901912
/// .unwrap();
902913
/// ```
903-
pub fn password(mut self, pwd: impl AsRef<[u8]>) -> Result<Self, MqttError> {
904-
let mqtt_bin = MqttBinary::new(pwd.as_ref().to_vec())?;
914+
pub fn password<B>(mut self, pwd: B) -> Result<Self, MqttError>
915+
where
916+
B: TryInto<MqttBinary, Error = MqttError>,
917+
{
918+
let mqtt_bin = pwd.try_into()?;
905919
self.password_buf = Some(mqtt_bin);
906920

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

src/mqtt/packet/v5_0/publish.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,9 +1004,16 @@ where
10041004
/// is published. Topic names cannot contain wildcard characters (+ or #)
10051005
/// in PUBLISH packets, as these are only allowed in SUBSCRIBE packets.
10061006
///
1007+
/// This method accepts both string types (&str, String) and pre-constructed
1008+
/// `MqttString` instances. When passing a pre-constructed `MqttString`, no
1009+
/// additional heap allocation occurs, making it efficient for cross-thread
1010+
/// message passing scenarios.
1011+
///
10071012
/// # Parameters
10081013
///
1009-
/// - `topic`: The topic name as any type that can be referenced as a string
1014+
/// - `topic`: The topic name. Can be:
1015+
/// - `&str` or `String` (will be converted to `MqttString`)
1016+
/// - `MqttString` (passed by value without additional allocation)
10101017
///
10111018
/// # Returns
10121019
///
@@ -1023,12 +1030,22 @@ where
10231030
/// ```ignore
10241031
/// use mqtt_protocol_core::mqtt;
10251032
///
1033+
/// // From &str (backward compatible)
10261034
/// let builder = mqtt::packet::v5_0::Publish::builder()
10271035
/// .topic_name("sensors/temperature/room1")
10281036
/// .unwrap();
1037+
///
1038+
/// // From pre-constructed MqttString (no additional allocation)
1039+
/// let topic = mqtt::packet::MqttString::new("sensors/temperature/room1").unwrap();
1040+
/// let builder = mqtt::packet::v5_0::Publish::builder()
1041+
/// .topic_name(topic)
1042+
/// .unwrap();
10291043
/// ```
1030-
pub fn topic_name<T: AsRef<str>>(mut self, topic: T) -> Result<Self, MqttError> {
1031-
let mqtt_str = MqttString::new(topic)?;
1044+
pub fn topic_name<T>(mut self, topic: T) -> Result<Self, MqttError>
1045+
where
1046+
T: TryInto<MqttString, Error = MqttError>,
1047+
{
1048+
let mqtt_str = topic.try_into()?;
10321049
if mqtt_str.as_str().contains('#') || mqtt_str.as_str().contains('+') {
10331050
return Err(MqttError::MalformedPacket);
10341051
}

0 commit comments

Comments
 (0)