Skip to content

Commit 6652b3c

Browse files
committed
add ESCAPED_LEN to AtatLen for escape-aware buffer sizing
Add ESCAPED_LEN associated constant to the AtatLen trait so commands with escape_strings=true use worst-case 3x buffer for string fields, while non-escaping commands keep the original sizing. Also fix all clippy warnings across the workspace including tests.
1 parent 9472d49 commit 6652b3c

19 files changed

Lines changed: 165 additions & 73 deletions

File tree

atat/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ log = { version = "^0.4", default-features = false, optional = true }
3838
defmt = { version = "^0.3", optional = true }
3939

4040
[dev-dependencies]
41-
embassy-time = { version = "0.5", features = ["std"] }
41+
embassy-time = { version = "0.5", features = ["std", "generic-queue-8"] }
4242
critical-section = { version = "1.1", features = ["std"] }
4343
serde_at = { path = "../serde_at", version = "^0.24.1", features = [
4444
"heapless",
@@ -48,7 +48,7 @@ static_cell = { version = "2.0.0" }
4848

4949
[features]
5050
default = ["derive", "bytes"]
51-
defmt = ["dep:defmt", "embedded-io-async/defmt-03", "heapless/defmt-03"]
51+
defmt = ["dep:defmt", "embedded-io-async/defmt-03", "heapless/defmt"]
5252
derive = ["atat_derive", "serde_at"]
5353
bytes = ["heapless-bytes", "serde_bytes"]
5454
custom-error-messages = []

atat/src/asynch/client.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ impl<'a, W: Write, const INGRESS_BUF_SIZE: usize> Client<'a, W, INGRESS_BUF_SIZE
112112

113113
impl<W: Write, const INGRESS_BUF_SIZE: usize> AtatClient for Client<'_, W, INGRESS_BUF_SIZE> {
114114
async fn send<Cmd: AtatCmd>(&mut self, cmd: &Cmd) -> Result<Cmd::Response, Error> {
115-
let len = cmd.write(&mut self.buf);
115+
let len = cmd.write(self.buf);
116116
self.send_request(len).await?;
117117
if !Cmd::EXPECTS_RESPONSE_CODE {
118118
cmd.parse(Ok(&[]))
@@ -152,6 +152,7 @@ mod tests {
152152

153153
#[derive(Clone, PartialEq, AtatEnum)]
154154
#[at_enum(u8)]
155+
#[allow(clippy::upper_case_acronyms)]
155156
pub enum Functionality {
156157
#[at_arg(value = 0)]
157158
Min,
@@ -183,8 +184,12 @@ mod tests {
183184
static mut BUF: [u8; 1000] = [0; 1000];
184185

185186
let tx_mock = crate::tx_mock::TxMock::new(TX_CHANNEL.publisher().unwrap());
186-
let client: Client<crate::tx_mock::TxMock, TEST_RX_BUF_LEN> =
187-
Client::new(tx_mock, &RES_SLOT, unsafe { BUF.as_mut() }, $config);
187+
let client: Client<crate::tx_mock::TxMock, TEST_RX_BUF_LEN> = Client::new(
188+
tx_mock,
189+
&RES_SLOT,
190+
unsafe { &mut *core::ptr::addr_of_mut!(BUF) },
191+
$config,
192+
);
188193
(client, TX_CHANNEL.subscriber().unwrap(), &RES_SLOT)
189194
}};
190195
}

atat/src/asynch/simple_client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl<'a, RW: Read + Write, D: Digester> SimpleClient<'a, RW, D> {
4848
Ok(())
4949
}
5050

51-
async fn wait_response<'guard>(&'guard mut self) -> Result<Response<256>, Error> {
51+
async fn wait_response(&mut self) -> Result<Response<256>, Error> {
5252
loop {
5353
match self.rw.read(&mut self.buf[self.pos..]).await {
5454
Ok(n) => {
@@ -143,7 +143,7 @@ impl<'a, RW: Read + Write, D: Digester> SimpleClient<'a, RW, D> {
143143

144144
impl<RW: Read + Write, D: Digester> AtatClient for SimpleClient<'_, RW, D> {
145145
async fn send<Cmd: AtatCmd>(&mut self, cmd: &Cmd) -> Result<Cmd::Response, Error> {
146-
let len = cmd.write(&mut self.buf);
146+
let len = cmd.write(self.buf);
147147

148148
self.send_request(len).await?;
149149
if !Cmd::EXPECTS_RESPONSE_CODE {

atat/src/blocking/client.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ where
106106
W: Write,
107107
{
108108
fn send<Cmd: AtatCmd>(&mut self, cmd: &Cmd) -> Result<Cmd::Response, Error> {
109-
let len = cmd.write(&mut self.buf);
109+
let len = cmd.write(self.buf);
110110
self.send_request(len)?;
111111
if !Cmd::EXPECTS_RESPONSE_CODE {
112112
cmd.parse(Ok(&[]))
@@ -132,6 +132,7 @@ mod test {
132132
const TEST_RX_BUF_LEN: usize = 256;
133133

134134
#[derive(Debug, PartialEq, Eq)]
135+
#[allow(dead_code)]
135136
pub enum InnerError {
136137
Test,
137138
}
@@ -192,6 +193,7 @@ mod test {
192193

193194
#[derive(Clone, PartialEq, AtatEnum)]
194195
#[at_enum(u8)]
196+
#[allow(clippy::upper_case_acronyms)]
195197
pub enum Functionality {
196198
#[at_arg(value = 0)]
197199
Min,
@@ -235,6 +237,7 @@ mod test {
235237
}
236238

237239
#[derive(Debug, Clone, AtatResp, PartialEq)]
240+
#[allow(dead_code)]
238241
pub struct MessageWaitingIndication {
239242
#[at_arg(position = 0)]
240243
pub status: u8,
@@ -243,6 +246,7 @@ mod test {
243246
}
244247

245248
#[derive(Debug, Clone, AtatUrc, PartialEq)]
249+
#[allow(dead_code)]
246250
pub enum Urc {
247251
#[at_urc(b"+UMWI")]
248252
MessageWaitingIndication(MessageWaitingIndication),
@@ -258,8 +262,12 @@ mod test {
258262
static mut BUF: [u8; 1000] = [0; 1000];
259263

260264
let tx_mock = crate::tx_mock::TxMock::new(TX_CHANNEL.publisher().unwrap());
261-
let client: Client<crate::tx_mock::TxMock, TEST_RX_BUF_LEN> =
262-
Client::new(tx_mock, &RES_SLOT, unsafe { BUF.as_mut() }, $config);
265+
let client: Client<crate::tx_mock::TxMock, TEST_RX_BUF_LEN> = Client::new(
266+
tx_mock,
267+
&RES_SLOT,
268+
unsafe { &mut *core::ptr::addr_of_mut!(BUF) },
269+
$config,
270+
);
263271
(client, TX_CHANNEL.subscriber().unwrap(), &RES_SLOT)
264272
}};
265273
}
@@ -272,8 +280,7 @@ mod test {
272280

273281
let sent = tokio::spawn(async move {
274282
tx.next_message_pure().await;
275-
rx.signal_response(Err(InternalError::Error).into())
276-
.unwrap();
283+
rx.signal_response(Err(InternalError::Error)).unwrap();
277284
});
278285

279286
tokio::task::spawn_blocking(move || {
@@ -296,8 +303,7 @@ mod test {
296303

297304
let sent = tokio::spawn(async move {
298305
tx.next_message_pure().await;
299-
rx.signal_response(Err(InternalError::Error).into())
300-
.unwrap();
306+
rx.signal_response(Err(InternalError::Error)).unwrap();
301307
});
302308

303309
tokio::task::spawn_blocking(move || {

atat/src/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use embassy_time::{Duration, Instant};
66
///
77
/// [`Command`]: enum.Command.html
88
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
9+
#[allow(unpredictable_function_pointer_comparisons)]
910
pub struct Config {
1011
pub(crate) cmd_cooldown: Duration,
1112
pub(crate) tx_timeout: Duration,

atat/src/derive.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,21 @@ use serde_at::HexStr;
88
/// [`atat_derive`]: https://crates.io/crates/atat_derive
99
pub trait AtatLen {
1010
const LEN: usize;
11+
const ESCAPED_LEN: usize;
1112
}
1213

1314
#[cfg(feature = "bytes")]
1415
impl<const N: usize> AtatLen for heapless_bytes::Bytes<N> {
1516
const LEN: usize = N;
17+
const ESCAPED_LEN: usize = N;
1618
}
1719

1820
macro_rules! impl_length {
1921
($type:ty, $len:expr) => {
2022
#[allow(clippy::use_self)]
2123
impl AtatLen for $type {
2224
const LEN: usize = $len;
25+
const ESCAPED_LEN: usize = $len;
2326
}
2427
};
2528
}
@@ -51,27 +54,32 @@ impl_length!(HexStr<u128>, 130);
5154

5255
impl<const T: usize> AtatLen for String<T> {
5356
const LEN: usize = 1 + T + 1;
57+
const ESCAPED_LEN: usize = 3 * T + 2;
5458
}
5559

5660
impl<T: AtatLen> AtatLen for Option<T> {
5761
const LEN: usize = T::LEN;
62+
const ESCAPED_LEN: usize = T::ESCAPED_LEN;
5863
}
5964

6065
impl<T: AtatLen> AtatLen for &T {
6166
const LEN: usize = T::LEN;
67+
const ESCAPED_LEN: usize = T::ESCAPED_LEN;
6268
}
6369

6470
impl<T, const L: usize> AtatLen for Vec<T, L>
6571
where
6672
T: AtatLen,
6773
{
6874
const LEN: usize = L * <T as AtatLen>::LEN;
75+
const ESCAPED_LEN: usize = L * <T as AtatLen>::ESCAPED_LEN;
6976
}
7077

7178
// 0x F:F:F:F
7279
// uN = (2 + (N*4) - 1) * 2 bytes
7380
impl<const L: usize> AtatLen for HexStr<[u8; L]> {
7481
const LEN: usize = (2 + L * 4 - 1) * 2;
82+
const ESCAPED_LEN: usize = (2 + L * 4 - 1) * 2;
7583
}
7684

7785
// Currently, stable rust (version at the time of writing: 1.93)
@@ -81,6 +89,7 @@ macro_rules! impl_nonzero_length {
8189
($type:ty) => {
8290
impl AtatLen for NonZero<$type> {
8391
const LEN: usize = <$type as AtatLen>::LEN;
92+
const ESCAPED_LEN: usize = <$type as AtatLen>::ESCAPED_LEN;
8493
}
8594
};
8695
}
@@ -202,8 +211,18 @@ mod tests {
202211
assert_eq!(<f32 as AtatLen>::LEN, 42);
203212
assert_eq!(<f64 as AtatLen>::LEN, 312);
204213

214+
// For non-string primitives, ESCAPED_LEN == LEN
215+
assert_eq!(<u8 as AtatLen>::ESCAPED_LEN, 3);
216+
assert_eq!(<i64 as AtatLen>::ESCAPED_LEN, 20);
217+
218+
// String<T>: LEN = 1 + T + 1 (quotes), ESCAPED_LEN = 3*T + 2
219+
assert_eq!(<String<10> as AtatLen>::LEN, 12);
220+
assert_eq!(<String<10> as AtatLen>::ESCAPED_LEN, 32);
221+
205222
assert_eq!(<SimpleEnum as AtatLen>::LEN, 3);
206223
assert_eq!(<SimpleEnumU32 as AtatLen>::LEN, 10);
224+
assert_eq!(<SimpleEnum as AtatLen>::ESCAPED_LEN, 3);
225+
assert_eq!(<SimpleEnumU32 as AtatLen>::ESCAPED_LEN, 10);
207226

208227
assert_eq!(<HexStr<u8> as AtatLen>::LEN, 10);
209228
assert_eq!(<HexStr<u16> as AtatLen>::LEN, 18);
@@ -214,18 +233,31 @@ mod tests {
214233
#[cfg(feature = "hex_str_arrays")]
215234
{
216235
assert_eq!(<HexStr<[u8; 16]> as AtatLen>::LEN, 130);
236+
assert_eq!(<HexStr<[u8; 16]> as AtatLen>::ESCAPED_LEN, 130);
217237
}
218238

219239
// (fields) + (n_fields - 1)
220-
// (3 + (1 + 128 + 1) + 2 + (1 + 150 + 1) + 3 + 10 + 3 + (10*5)) + 7
240+
// (3 + (1 + 128 + 1) + 2 + (1 + 150 + 1) + 3 + 10 + 3) + 6
221241
assert_eq!(
222242
<LengthTester<'_> as AtatLen>::LEN,
223243
(3 + (1 + 128 + 1) + 2 + (1 + 150 + 1) + 3 + 10 + 3) + 6
224244
);
245+
// ESCAPED_LEN: String<128> -> 3*128+2=386, &str len=150 -> 3*150+2=452
246+
// (3 + 386 + 2 + 452 + 3 + 10 + 3) + 6
247+
assert_eq!(
248+
<LengthTester<'_> as AtatLen>::ESCAPED_LEN,
249+
(3 + (3 * 128 + 2) + 2 + (3 * 150 + 2) + 3 + 10 + 3) + 6
250+
);
225251
assert_eq!(
226252
<MixedEnum<'_> as AtatLen>::LEN,
227253
(3 + 3 + (1 + 10 + 1) + 20 + 10) + 4
228254
);
255+
// ESCAPED_LEN: String<10> -> 3*10+2=32
256+
// max variant AdvancedTuple: (3 + 32 + 20 + 10) + 4 separators = 69
257+
assert_eq!(
258+
<MixedEnum<'_> as AtatLen>::ESCAPED_LEN,
259+
3 + (3 + 32 + 20 + 10) + 4
260+
);
229261
}
230262

231263
#[test]

atat/src/digest.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ pub mod parser {
244244
}
245245
}
246246

247-
pub fn error_response(buf: &[u8]) -> IResult<&[u8], (DigestResult, usize)> {
247+
pub fn error_response(buf: &[u8]) -> IResult<&[u8], (DigestResult<'_>, usize)> {
248248
alt((
249249
// Matches the equivalent of regex: "\r\n\+CME ERROR:\s*(\d+)\r\n"
250250
map(numeric_error("\r\n+CME ERROR:"), |(error_code, len)| {
@@ -310,8 +310,8 @@ pub mod parser {
310310
))(buf)
311311
}
312312

313-
pub fn prompt_response(buf: &[u8]) -> IResult<&[u8], (DigestResult, usize)> {
314-
for prompt in &[b'>', b'@'] {
313+
pub fn prompt_response(buf: &[u8]) -> IResult<&[u8], (DigestResult<'_>, usize)> {
314+
for prompt in b">@" {
315315
if let Ok((buf, ((prefix, p), ws, _))) = tuple((
316316
take_until_including::<_, _, nom::error::Error<_>>(&[*prompt][..]),
317317
complete::multispace0,
@@ -333,7 +333,7 @@ pub mod parser {
333333
)))
334334
}
335335

336-
pub fn success_response(buf: &[u8]) -> IResult<&[u8], (DigestResult, usize)> {
336+
pub fn success_response(buf: &[u8]) -> IResult<&[u8], (DigestResult<'_>, usize)> {
337337
let (i, ((data, tag), ws)) = alt((
338338
tuple((
339339
take_until_including("\r\nOK\r\n"),
@@ -499,10 +499,7 @@ mod test {
499499

500500
use super::parser::{echo, urc_helper};
501501
use super::*;
502-
use crate::{
503-
error::{CmeError, CmsError, ConnectionError},
504-
helpers::LossyStr,
505-
};
502+
use crate::{error::CmeError, helpers::LossyStr};
506503

507504
const TEST_RX_BUF_LEN: usize = 256;
508505

atat/src/ingress.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ mod tests {
476476
}
477477
impl embedded_io_async::Read for Reader {
478478
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
479-
assert!(buf.len() > 0);
479+
assert!(!buf.is_empty());
480480
if self.pos >= self.data.len() {
481481
// Simulate waiting on more data.
482482
loop {

atat/src/response.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,9 @@ impl<'a, const N: usize> From<&'a Response<N>> for Result<&'a [u8], InternalErro
6969
Response::AbortedError => Err(InternalError::Aborted),
7070
Response::ParseError => Err(InternalError::Parse),
7171
Response::OtherError => Err(InternalError::Error),
72-
Response::CmeError(e) => Err(InternalError::CmeError((*e).try_into().unwrap())),
73-
Response::CmsError(e) => Err(InternalError::CmsError((*e).try_into().unwrap())),
74-
Response::ConnectionError(e) => {
75-
Err(InternalError::ConnectionError((*e).try_into().unwrap()))
76-
}
72+
Response::CmeError(e) => Err(InternalError::CmeError((*e).into())),
73+
Response::CmsError(e) => Err(InternalError::CmsError((*e).into())),
74+
Response::ConnectionError(e) => Err(InternalError::ConnectionError((*e).into())),
7775
Response::CustomError(e) => Err(InternalError::Custom(e)),
7876
}
7977
}

atat/src/response_slot.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ pub type ResponseSlotGuard<'a, const N: usize> =
1919
#[derive(Debug)]
2020
pub struct SlotInUseError;
2121

22+
impl<const N: usize> Default for ResponseSlot<N> {
23+
fn default() -> Self {
24+
Self::new()
25+
}
26+
}
27+
2228
impl<const N: usize> ResponseSlot<N> {
2329
pub const fn new() -> Self {
2430
Self(

0 commit comments

Comments
 (0)