Skip to content

Commit

Permalink
Merge pull request #29 from MathiasKoch/wip/alloc-feature
Browse files Browse the repository at this point in the history
Make alloc optional, defaulting to slice based API
  • Loading branch information
00imvj00 authored Nov 30, 2020
2 parents be13f97 + c3cdf17 commit 159c8e6
Show file tree
Hide file tree
Showing 12 changed files with 776 additions and 469 deletions.
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ default = ["std"]

# Implements serde::{Serialize,Deserialize} on mqttrs::Pid.
derive = ["serde"]
std = ["bytes/std", "serde/std"]
std = ["bytes", "bytes/std", "serde/std"]

[dependencies]
bytes = { version = "0.5", default-features = false }
bytes = { version = "0.5", default-features = false, optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
heapless = "0.5.5"

[dev-dependencies]
proptest = "0.9.4"
proptest = "0.10.0"
14 changes: 6 additions & 8 deletions src/codec_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ use crate::*;
use bytes::BytesMut;
use proptest::{bool, collection::vec, num::*, prelude::*};
use core::convert::TryFrom;
use alloc::string::String;
use alloc::format;

// Proptest strategies to generate packet elements
prop_compose! {
Expand Down Expand Up @@ -172,19 +170,19 @@ macro_rules! impl_proptests {
// modified to accept other buffer types.

// Check that encoding into a small buffer fails cleanly
//buf.clear();
//buf.split_off(encoded.len());
//prop_assert!(encoded.len() == buf.remaining_mut() && buf.is_empty(),
// buf.clear();
// buf.split_off(encoded.len());
// prop_assert!(encoded.len() == buf.remaining_mut() && buf.is_empty(),
// "Wrong buffer init1 {}/{}/{}", encoded.len(), buf.remaining_mut(), buf.is_empty());
//prop_assert!(encode(&pkt, &mut buf).is_ok(), "exact buffer capacity {}", buf.capacity());
//for l in (0..encoded.len()).rev() {
// prop_assert!(encode(&pkt, &mut buf).is_ok(), "exact buffer capacity {}", buf.capacity());
// for l in (0..encoded.len()).rev() {
// buf.clear();
// buf.split_to(1);
// prop_assert!(l == buf.remaining_mut() && buf.is_empty(),
// "Wrong buffer init2 {}/{}/{}", l, buf.remaining_mut(), buf.is_empty());
// prop_assert_eq!(Err(Error::WriteZero), encode(&pkt, &mut buf),
// "small buffer capacity {}/{}", buf.capacity(), encoded.len());
//}
// }
}
}
};
Expand Down
113 changes: 63 additions & 50 deletions src/connect.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use crate::{decoder::*, encoder::*, *};
use alloc::{string::String, vec::Vec};
use bytes::{Buf, BufMut, BytesMut};

/// Protocol version.
///
Expand Down Expand Up @@ -28,18 +26,29 @@ impl Protocol {
_ => Err(Error::InvalidProtocol(name.into(), level)),
}
}
pub(crate) fn to_buffer(&self, buf: &mut impl BufMut) -> Result<usize, Error> {
pub(crate) fn from_buffer<'a>(buf: &'a [u8], offset: &mut usize) -> Result<Self, Error> {
let protocol_name = read_str(buf, offset)?;
let protocol_level = buf[*offset];
*offset += 1;

Protocol::new(protocol_name, protocol_level)
}
pub(crate) fn to_buffer(&self, buf: &mut [u8], offset: &mut usize) -> Result<usize, Error> {
match self {
Protocol::MQTT311 => {
let slice = &[0u8, 4, 'M' as u8, 'Q' as u8, 'T' as u8, 'T' as u8, 4];
buf.put_slice(slice);
for &byte in slice {
write_u8(buf, offset, byte)?;
}
Ok(slice.len())
}
Protocol::MQIsdp => {
let slice = &[
0u8, 4, 'M' as u8, 'Q' as u8, 'i' as u8, 's' as u8, 'd' as u8, 'p' as u8, 4,
];
buf.put_slice(slice);
for &byte in slice {
write_u8(buf, offset, byte)?;
}
Ok(slice.len())
}
}
Expand All @@ -53,9 +62,9 @@ impl Protocol {
/// [Connect]: struct.Connect.html
/// [MQTT 3.1.3.3]: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718031
#[derive(Debug, Clone, PartialEq)]
pub struct LastWill {
pub topic: String,
pub message: Vec<u8>,
pub struct LastWill<'a> {
pub topic: &'a str,
pub message: &'a [u8],
pub qos: QoS,
pub retain: bool,
}
Expand Down Expand Up @@ -103,14 +112,14 @@ impl ConnectReturnCode {
///
/// [MQTT 3.1]: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028
#[derive(Debug, Clone, PartialEq)]
pub struct Connect {
pub struct Connect<'a> {
pub protocol: Protocol,
pub keep_alive: u16,
pub client_id: String,
pub client_id: &'a str,
pub clean_session: bool,
pub last_will: Option<LastWill>,
pub username: Option<String>,
pub password: Option<Vec<u8>>,
pub last_will: Option<LastWill<'a>>,
pub username: Option<&'a str>,
pub password: Option<&'a [u8]>,
}

/// Connack packet ([MQTT 3.2]).
Expand All @@ -122,20 +131,19 @@ pub struct Connack {
pub code: ConnectReturnCode,
}

impl Connect {
pub(crate) fn from_buffer(buf: &mut BytesMut) -> Result<Self, Error> {
let protocol_name = read_string(buf)?;
let protocol_level = buf.get_u8();
let protocol = Protocol::new(&protocol_name, protocol_level)?;
impl<'a> Connect<'a> {
pub(crate) fn from_buffer(buf: &'a [u8], offset: &mut usize) -> Result<Self, Error> {
let protocol = Protocol::from_buffer(buf, offset)?;

let connect_flags = buf.get_u8();
let keep_alive = buf.get_u16();
let connect_flags = buf[*offset];
let keep_alive = ((buf[*offset + 1] as u16) << 8) | buf[*offset + 2] as u16;
*offset += 3;

let client_id = read_string(buf)?;
let client_id = read_str(buf, offset)?;

let last_will = if connect_flags & 0b100 != 0 {
let will_topic = read_string(buf)?;
let will_message = read_bytes(buf)?;
let will_topic = read_str(buf, offset)?;
let will_message = read_bytes(buf, offset)?;
let will_qod = QoS::from_u8((connect_flags & 0b11000) >> 3)?;
Some(LastWill {
topic: will_topic,
Expand All @@ -148,13 +156,13 @@ impl Connect {
};

let username = if connect_flags & 0b10000000 != 0 {
Some(read_string(buf)?)
Some(read_str(buf, offset)?)
} else {
None
};

let password = if connect_flags & 0b01000000 != 0 {
Some(read_bytes(buf)?)
Some(read_bytes(buf, offset)?)
} else {
None
};
Expand All @@ -171,7 +179,8 @@ impl Connect {
clean_session,
})
}
pub(crate) fn to_buffer(&self, buf: &mut impl BufMut) -> Result<usize, Error> {

pub(crate) fn to_buffer(&self, buf: &mut [u8], offset: &mut usize) -> Result<usize, Error> {
let header: u8 = 0b00010000;
let mut length: usize = 6 + 1 + 1; // NOTE: protocol_name(6) + protocol_level(1) + flags(1);
let mut connect_flags: u8 = 0b00000000;
Expand All @@ -180,12 +189,12 @@ impl Connect {
};
length += 2 + self.client_id.len();
length += 2; // keep alive
if let Some(username) = &self.username {
if let Some(username) = self.username {
connect_flags |= 0b10000000;
length += username.len();
length += 2;
};
if let Some(password) = &self.password {
if let Some(password) = self.password {
connect_flags |= 0b01000000;
length += password.len();
length += 2;
Expand All @@ -200,54 +209,58 @@ impl Connect {
length += last_will.topic.len();
length += 4;
};
check_remaining(buf, length + 1)?;
check_remaining(buf, offset, length + 1)?;

// NOTE: putting data into buffer.
buf.put_u8(header);
let write_len = write_length(length, buf)? + 1;
self.protocol.to_buffer(buf)?;
buf.put_u8(connect_flags);
buf.put_u16(self.keep_alive);
write_string(self.client_id.as_ref(), buf)?;
write_u8(buf, offset, header)?;

let write_len = write_length(buf, offset, length)? + 1;
self.protocol.to_buffer(buf, offset)?;

write_u8(buf, offset, connect_flags)?;
write_u16(buf, offset, self.keep_alive)?;

write_string(buf, offset, self.client_id)?;

if let Some(last_will) = &self.last_will {
write_string(last_will.topic.as_ref(), buf)?;
write_bytes(&last_will.message, buf)?;
write_string(buf, offset, last_will.topic)?;
write_bytes(buf, offset, &last_will.message)?;
};

if let Some(username) = &self.username {
write_string(username.as_ref(), buf)?;
if let Some(username) = self.username {
write_string(buf, offset, username)?;
};
if let Some(password) = &self.password {
write_bytes(password, buf)?;
if let Some(password) = self.password {
write_bytes(buf, offset, password)?;
};
// NOTE: END
Ok(write_len)
}
}

impl Connack {
pub(crate) fn from_buffer(buf: &mut impl Buf) -> Result<Self, Error> {
let flags = buf.get_u8();
let return_code = buf.get_u8();
pub(crate) fn from_buffer<'a>(buf: &'a [u8], offset: &mut usize) -> Result<Self, Error> {
let flags = buf[*offset];
let return_code = buf[*offset + 1];
*offset += 2;
Ok(Connack {
session_present: (flags & 0b1 == 1),
code: ConnectReturnCode::from_u8(return_code)?,
})
}
pub(crate) fn to_buffer(&self, buf: &mut impl BufMut) -> Result<usize, Error> {
check_remaining(buf, 4)?;
pub(crate) fn to_buffer(&self, buf: &mut [u8], offset: &mut usize) -> Result<usize, Error> {
check_remaining(buf, offset, 4)?;
let header: u8 = 0b00100000;
let length: u8 = 2;
let mut flags: u8 = 0b00000000;
if self.session_present {
flags |= 0b1;
};
let rc = self.code.to_u8();
buf.put_u8(header);
buf.put_u8(length);
buf.put_u8(flags);
buf.put_u8(rc);
write_u8(buf, offset, header)?;
write_u8(buf, offset, length)?;
write_u8(buf, offset, flags)?;
write_u8(buf, offset, rc)?;
Ok(4)
}
}
Loading

0 comments on commit 159c8e6

Please sign in to comment.