1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! An allocation-free codec for BACnet MS/TP frames (Clause 9): the standard
//! frames and the two extended data frames.
//!
//! The crate covers the wire format and nothing above it: frame types, the
//! header and data CRCs, the COBS encoding and CRC-32K of extended frames,
//! one-shot encode and decode, and an incremental decoder for a serial byte
//! stream. Token passing, timers and line control belong to the layer that
//! uses it.
//!
//! The default build never allocates. [`Frame`] borrows its data,
//! [`Frame::encode_into`] writes into caller storage, and [`StreamDecoder`]
//! keeps its receive state in a buffer the caller lends it. Only the optional
//! `alloc` and `bytes` sinks grow on the heap.
//!
//! # Extended frames
//!
//! Types 32 and 33 ([`FrameType::BACNET_EXTENDED_DATA_EXPECTING_REPLY`] and
//! [`FrameType::BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY`]) carry NPDUs too long
//! for a standard frame (Clauses 9.3.9, 9.3.10).
//! On the wire their data is COBS-encoded with a CRC-32K, so no preamble can
//! appear inside them (Clause 9.10), and the header's Length counts the two
//! encoded fields less two, which lets a receiver without COBS skip them by
//! count (Clause 9.3). [`Frame::data`] is always the decoded data: encoding
//! takes it plain, and decoding hands it back plain. Decoding an extended
//! frame writes its data over its encoded form, so it takes writable input:
//! [`decode_frame_in_place`], or a [`StreamDecoder`], which does it in its
//! own storage. [`decode_frame`] borrows read-only input and refuses an
//! extended frame with [`FrameError::ExtendedFrame`] as soon as its header
//! checks out.
//!
//! The other COBS-encoded types, 34-127, are reserved: both directions refuse
//! them with [`FrameError::UnsupportedCobsFrame`], after the header's other
//! checks. The exception is a [`StreamDecoder`] built
//! [`with_reserved_cobs`](StreamDecoder::with_reserved_cobs), for a receiver
//! that supports COBS: it checks their CRC-32K as an extended frame's and
//! yields a good one as a frame of a type it doesn't know. Test_Request and
//! Test_Response stay standard frames.
//!
//! # Validation
//!
//! Decoding is a raw, link-level check. [`decode_frame`] verifies the
//! preamble, the header CRC, the header fields Clause 9.5.8 checks, then the
//! frame's data. The header checks run in this order: the source, which may
//! not be 255, the broadcast address no station may send from (Clause 9.3);
//! the Length, at most [`MAX_STANDARD_MPDU_DATA`] on a standard type and
//! within [`MIN_COBS_LENGTH`]..=[`MAX_COBS_LENGTH`] on a COBS-encoded one;
//! and last whether the codec supports the type. Decoding accepts every other
//! source, including the slave-only range 128-254: whether a station may use
//! its address in its role is for the receiver to judge. It also accepts data
//! on a frame type that never carries any, and a proprietary frame too short
//! for its vendor identifier, so a receiver can see what arrived.
//!
//! Encoding is strict. [`Frame::encode_into`] and [`Frame::encode_to_sink`]
//! apply the same header checks in the same order, then refuse data on the
//! four frame types defined without any (see [`FrameType::allows_data`]) and
//! a proprietary frame (128-255) with less than its 2-octet vendor identifier
//! (Clause 9.3.11). The reserved types 8-31 get the same CRC, source and
//! length checks as any frame in both directions, but the codec doesn't
//! police their content. An extended frame needs data, since an empty one
//! would fall below [`MIN_COBS_LENGTH`]; the BACnet range of 502 to
//! [`MAX_EXTENDED_MPDU_DATA`] octets for it is the sending layer's to keep.
//!
//! # Storage
//!
//! Each frame class has a constant for its longest frame on the wire, which
//! is the [`StreamDecoder`] storage it takes (plus room for the slices pushed
//! into it):
//!
//! - [`MAX_STANDARD_FRAME_LENGTH`] (511): every standard frame.
//! - [`MAX_EXTENDED_FRAME_LENGTH`] (1516): every extended frame that carries
//! a BACnet NPDU, at most [`MAX_EXTENDED_MPDU_DATA`] octets.
//! - [`MAX_COBS_FRAME_LENGTH`] (2053): every frame whose header passes, a
//! COBS-encoded one at the largest Length included.
//!
//! A frame longer than the storage is reported once and skipped; see
//! [`StreamDecoder`].
//!
//! # Features
//!
//! No feature needs `std`. Most add a [`FrameSink`] implementation:
//!
//! - `alloc`: for `alloc::vec::Vec<u8>`.
//! - `bytes`: for `bytes::BytesMut`; implies `alloc`.
//! - `heapless`: for `heapless::Vec<u8, N>`.
//! - `crc32k-table`: works out the CRC-32K with a 256-entry table, about
//! 1 KiB more flash than the default 16-entry one and half the lookups (see
//! [`crc::crc32k`]).
//!
//! # Example
//!
//! ```
//! use bacnet_mstp_codec::{decode_frame, Frame, FrameType, MAX_STANDARD_FRAME_LENGTH};
//!
//! let frame = Frame::new(FrameType::BACNET_DATA_NOT_EXPECTING_REPLY, 0x10, 0x05, &[0x01, 0x00]);
//! let mut wire = [0u8; MAX_STANDARD_FRAME_LENGTH];
//! let length = frame.encode_into(&mut wire)?;
//!
//! let (decoded, consumed) = decode_frame(&wire[..length])?;
//! assert_eq!(decoded, frame);
//! assert_eq!(consumed, length);
//! # Ok::<(), bacnet_mstp_codec::FrameError>(())
//! ```
extern crate alloc;
pub use ;
pub use FrameError;
pub use ;
pub use ;
pub use ;
/// The two preamble octets that start every frame.
pub const PREAMBLE: = ;
/// Header length after the preamble: type, destination, source, the two
/// length octets and the header CRC.
pub const HEADER_LENGTH: usize = 6;
/// Largest data field a standard (non-COBS) frame can carry.
pub const MAX_STANDARD_MPDU_DATA: usize = 501;
/// Highest station address a master may use. Slaves may also use 128-254.
pub const MAX_MASTER: u8 = 127;
/// The broadcast address. It is valid only as a destination.
pub const BROADCAST_MAC: u8 = 0xFF;
/// Longest standard frame on the wire: preamble, header, the largest data
/// field and its CRC.
pub const MAX_STANDARD_FRAME_LENGTH: usize =
PREAMBLE.len + HEADER_LENGTH + MAX_STANDARD_MPDU_DATA + 2;
/// Smallest Length a COBS-encoded frame's header may declare: one data
/// octet takes two encoded, the CRC field always takes five, and the Length
/// leaves out two (Clause 9.5.3, Nmin_COBS_length).
pub const MIN_COBS_LENGTH: usize = 5;
/// Largest Length a COBS-encoded frame's header may declare (Clause 9.5.3,
/// Nmax_COBS_length).
pub const MAX_COBS_LENGTH: usize = 2043;
/// Largest NPDU an extended frame carries (Clauses 9.3.9, 9.3.10).
pub const MAX_EXTENDED_MPDU_DATA: usize = 1497;
/// Longest extended frame carrying a BACnet NPDU: preamble, header, the
/// largest NPDU with COBS's worst-case overhead of one octet per 254, and the
/// Encoded CRC-32K (Clause 9.5.2 sizes a receive buffer the same way).
pub const MAX_EXTENDED_FRAME_LENGTH: usize = PREAMBLE.len
+ HEADER_LENGTH
+ MAX_EXTENDED_MPDU_DATA
+ MAX_EXTENDED_MPDU_DATA.div_ceil
+ ENCODED_CRC_LENGTH;
/// Longest frame whose header passes validation: a COBS-encoded frame of
/// the largest Length.
pub const MAX_COBS_FRAME_LENGTH: usize =
PREAMBLE.len + HEADER_LENGTH + MAX_COBS_LENGTH + LENGTH_ADJUSTMENT;
/// The README's example, compiled and run as a doctest.
;