Skip to content

Commit

Permalink
Merge pull request #3 from dbrgn/no-alloc
Browse files Browse the repository at this point in the history
Remove dependency on alloc
  • Loading branch information
marius-meissner authored Oct 24, 2022
2 parents 7af2280 + 5d04af1 commit 7beaa05
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 7 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ fugit = "0.3.6"
fugit-timer = "0.1.3"
heapless = "0.7.16"
bbqueue = { version = "0.5.0", optional=true }
numtoa = "0.2"
base16 = { version = "0.2", default-features = false }

[dev-dependencies]
mockall = "0.11.2"
Expand All @@ -36,4 +38,4 @@ log = ['atat/log']
thumbv6 = ['bbqueue/thumbv6']

# Contains mocks for doc examples and may be disabled for production.
examples = []
examples = []
104 changes: 99 additions & 5 deletions src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use core::fmt::Write;

use crate::responses::LocalAddressResponse;
use crate::responses::NoResponse;
use crate::stack::Error as StackError;
use crate::wifi::{AddressErrors, JoinError};
use alloc::string::ToString;
use atat::atat_derive::AtatCmd;
use atat::heapless::{String, Vec};
use atat::{AtatCmd, Error as AtError, InternalError};
use embedded_nal::{SocketAddrV4, SocketAddrV6};
use embedded_nal::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use numtoa::NumToA;

const MAX_IP_LENGTH: usize = 39; // IPv4: 15, IPv6: 39

/// Trait for mapping command errors
pub trait CommandErrorHandler {
Expand Down Expand Up @@ -156,19 +160,58 @@ pub struct ConnectCommand {
connection_type: String<5>,

/// Remote IPv4 or IPV6 address
remote_host: String<39>,
remote_host: String<MAX_IP_LENGTH>,

/// Remote port
port: u16,
}

/// Convert a `IPv4Addr` to a heapless `String`
fn ipv4_to_string(ip: &Ipv4Addr) -> String<MAX_IP_LENGTH> {
let mut ip_string = String::new();
let mut num_buf = [0u8; 3];
for (i, octet) in ip.octets().iter().enumerate() {
ip_string.write_str(octet.numtoa_str(10, &mut num_buf)).unwrap();
if i != 3 {
ip_string.write_char('.').unwrap();
}
}
ip_string
}

/// Convert a `SocketAddrV6` IP to a heapless `String`
fn ipv6_to_string(ip: &Ipv6Addr) -> String<MAX_IP_LENGTH> {
let mut ip_string = String::new();
let mut hex_buf = [0u8; 4];
for (i, segment) in ip.segments().iter().enumerate() {
// Write segment (hexadectet)
if segment == &0 {
// All-zero-segments can be shortened
ip_string.write_str("0").unwrap()
} else {
// Hex-encode IPv6 segment
base16::encode_config_slice(&segment.to_be_bytes(), base16::EncodeLower, &mut hex_buf);
ip_string
// Safety: The result from hex-encoding will always be valid UTF-8
.write_str(unsafe { core::str::from_utf8_unchecked(&hex_buf) })
.unwrap();
}

// Write separator
if i != 7 {
ip_string.write_char(':').unwrap();
}
}
ip_string
}

impl ConnectCommand {
/// Establishes a IPv4 TCP connection
pub fn tcp_v4(link_id: usize, remote: SocketAddrV4) -> Self {
Self {
link_id,
connection_type: String::from("TCP"),
remote_host: String::from(remote.ip().to_string().as_str()),
remote_host: ipv4_to_string(remote.ip()),
port: remote.port(),
}
}
Expand All @@ -178,7 +221,7 @@ impl ConnectCommand {
Self {
link_id,
connection_type: String::from("TCPv6"),
remote_host: String::from(remote.ip().to_string().as_str()),
remote_host: ipv6_to_string(remote.ip()),
port: remote.port(),
}
}
Expand Down Expand Up @@ -302,3 +345,54 @@ impl CommandErrorHandler for CloseSocketCommand {
StackError::CloseError(error)
}
}

#[cfg(test)]
mod tests {
use embedded_nal::{Ipv4Addr, Ipv6Addr};
use heapless::String;

use super::MAX_IP_LENGTH;

macro_rules! test_v4 {
($a:expr, $b:expr, $c:expr, $d:expr, $string:literal) => {{
assert_eq!(
super::ipv4_to_string(&Ipv4Addr::new($a, $b, $c, $d)),
String::<MAX_IP_LENGTH>::from($string)
);
}};
}

macro_rules! test_v6 {
($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $string:literal) => {{
assert_eq!(
super::ipv6_to_string(&Ipv6Addr::new($a, $b, $c, $d, $e, $f, $g, $h)),
String::<MAX_IP_LENGTH>::from($string)
);
}};
}

#[test]
fn test_ipv4_to_string() {
test_v4!(127, 0, 0, 1, "127.0.0.1");
test_v4!(0, 0, 0, 0, "0.0.0.0");
test_v4!(255, 255, 255, 0, "255.255.255.0");
test_v4!(255, 255, 255, 255, "255.255.255.255");
test_v4!(1, 2, 3, 4, "1.2.3.4");
}

#[test]
fn test_ipv6_to_string() {
test_v6!(0, 0, 0, 0, 0, 0, 0, 1, "0:0:0:0:0:0:0:0001"); // ::1
test_v6!(
0x0102,
0xaabb,
0xffff,
0x4242,
0x0000,
0x1111,
0x2222,
0x3333,
"0102:aabb:ffff:4242:0:1111:2222:3333"
);
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#![cfg_attr(not(test), no_std)]
#![cfg_attr(feature = "strict", deny(warnings))]

#[cfg(test)]
extern crate alloc;

pub(crate) mod commands;
Expand Down
2 changes: 1 addition & 1 deletion src/tests/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ fn test_connect_correct_commands_ipv6() {
assert_eq!(3, commands.len());
assert_eq!("AT+CIPRECVMODE=1\r\n".to_string(), commands[1]);
assert_eq!(
"AT+CIPSTART=0,\"TCPv6\",\"2001:db8::1\",8080\r\n".to_string(),
"AT+CIPSTART=0,\"TCPv6\",\"2001:0db8:0:0:0:0:0:0001\",8080\r\n".to_string(),
commands[2]
);
}
Expand Down

0 comments on commit 7beaa05

Please sign in to comment.