space_packet/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![forbid(unsafe_code)]
4//! Generic implementation of the CCSDS 133.0-B-2 Space Packet Protocol (SPP). That is, this crate
5//! concerns itself only with parsing and construction of CCSDS Space Packets, as that is
6//! independent of the precise implementation. Endpoint functionality, i.e., actually consuming and
7//! responding to the packet contents is implementation specific, and hence out of scope.
8//!
9//! Readers of the code are advised to start with the `PacketAssembly`, `PacketTransfer`,
10//! `PacketReception` and `PacketExtraction` traits. These describe the interfaces that application
11//! processes supporting the Space Packet Protocol are expected to expose.
12//!
13//! Tested and formally-verified implementations of the underlying parsing and semantic checking
14//! functionality needed to handle Space Packets is found in the actual `SpacePacket`
15//! implementation. This functionality is included in the hope that it helps write simple and
16//! robust SPP implementations.
17
18use thiserror::Error;
19use zerocopy::byteorder::network_endian;
20use zerocopy::{ByteEq, CastError, FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
21
22/// The `PacketAssembly` trait describes the "Packet Assembly" function from the CCSDS 133.0-B-2
23/// Space Packet Protocol recommended standard. This function concerns the ability of some protocol
24/// entity to build Space Packets from octet strings (packet data fields). It is the sending
25/// counterpart of the `PacketExtraction` trait.
26///
27/// We deviate slightly from the strict "Packet Assembly" function definition in permitting
28/// population of the octet string only after assembly of a packet with given packet data field
29/// length. This is useful, because it means that no copy is needed to prepend the Space Packet
30/// header to the data field, which saves a `memcpy`.
31pub trait PacketAssembly {
32    /// Generates Space Packets from octet strings. See CCSDS 133.0-B-2 Section 4.2.2 "Packet
33    /// Assembly Function". The Packet Assembly function shall itself keep track of the source
34    /// sequence count of packets for a given packet identification (version, packet type, and
35    /// APID), but all other elements must be provided by the service user. On top, since the
36    /// Packet Assembly function is used in tandem with the Octet String service (which does not
37    /// permit segmentation), the packet sequence flags shall be 0b11 ("Unsegmented").
38    ///
39    /// An error shall be returned if an empty packet data field is requested, since that indicates
40    /// an error on the user input side. In all other cases, no error may be returned - though
41    /// there may be cases where the packet is lost.
42    fn assemble<'a>(
43        &mut self,
44        packet_type: PacketType,
45        apid: Apid,
46        secondary_header_flag: SecondaryHeaderFlag,
47        buffer: &'a mut [u8],
48    ) -> Result<&'a mut SpacePacket, PacketAssemblyError> {
49        let sequence_count = self.packet_sequence_count(packet_type, apid);
50        SpacePacket::assemble(
51            buffer,
52            packet_type,
53            secondary_header_flag,
54            apid,
55            SequenceFlag::Unsegmented,
56            sequence_count,
57        )
58    }
59
60    /// In practice, the primary reason that the `PacketAssembly` function exists is that it is a
61    /// centralized manner to determine the packet sequence count of any newly-created packet. To
62    /// make life easier, we require this as separate method based on which we provide a default
63    /// implementation of `assemble()`.
64    ///
65    /// Implementations of this function shall also result in an appropriate update of the packet
66    /// sequence count.
67    fn packet_sequence_count(&mut self, packet_type: PacketType, apid: Apid)
68    -> PacketSequenceCount;
69}
70
71/// The `PacketTransfer` trait describes the "Packet Transfer" function from the CCSDS 133.0-B-2
72/// Space Packet Protocol recommended standard. It concerns the ability of some protocol entity to
73/// transfer packets towards the appropriate managed data path. It is the sending counterpart of
74/// the `PacketReception` trait.
75pub trait PacketTransfer {
76    /// Inspects an incoming or newly-created Space Packet (its APID, in particular) to determine
77    /// the target packet service entity at the receiving end. Routes this packet towards the
78    /// appropriate managed data path using a service of the underlying OSI reference model layers.
79    fn transfer(&mut self, packet: &SpacePacket);
80}
81
82/// The `PacketReception` trait describes the "Packet Reception" function from the CCSDS 133.0-B-2
83/// Space Packet Protocol recommended standard. It concerns the ability to receive new Space
84/// Packets from some underlying subnetwork layer.
85pub trait PacketReception {
86    /// Polls the message bus to see if new Space Packets have been received for a given APID. If
87    /// so, returns a reference to it. Need not perform any checking, such as data loss checks:
88    /// that may be done by the receiving party.
89    ///
90    /// After reception, the Space Packet shall be removed from the packet receptor: on future
91    /// polls (for the same `self`), it shall no longer be returned.
92    fn receive(&mut self) -> Option<&SpacePacket>;
93}
94
95/// The `PacketExtraction` trait describes the "Packet Extraction" function from the CCSDS 133.0-B-2
96/// Space Packet Protocol recommended standard. It concerns the ability to unpack Space Packets
97/// that have been received from some underlying subnetwork into the transmitted octet strings.
98pub trait PacketExtraction {
99    /// Value that may optionally be returned when extracting a packet to indicate whether (and
100    /// potentially to what degree) the packet sequence count suggests data loss to have occurred.
101    type DataLossIndicator;
102
103    /// Unpacks the given Space Packet into its underlying packet data field. Shall also return
104    /// whether there was a mismatch between the expected and actual Space Packet sequence
105    /// counters: if so, returns an appropriate data loss indicator. Finally, the secondary header
106    /// flag as contained in the primary header may also be returned.
107    fn extract<'a>(
108        &mut self,
109        packet: &'a SpacePacket,
110    ) -> (&'a [u8], SecondaryHeaderFlag, Self::DataLossIndicator) {
111        let packet_type = packet.packet_type();
112        let apid = packet.apid();
113        let secondary_header_flag = packet.secondary_header_flag();
114        let packet_sequence_count = packet.packet_sequence_count();
115        let data_loss_indicator =
116            self.data_loss_indicator(packet_type, apid, packet_sequence_count);
117        let packet_data_field = packet.packet_data_field();
118        (
119            packet_data_field,
120            secondary_header_flag,
121            data_loss_indicator,
122        )
123    }
124
125    /// Given some message ID (packet type and APID) and the sequence count found in the packet,
126    /// determines whether data loss has likely occurred. Updates the packet extractor with
127    /// this new packet sequence count to permit future data loss detection.
128    ///
129    /// This is the "meat" of the Packet Extraction function: the actual extraction of the packet
130    /// itself is otherwise quite trivial. Hence, we separately define this function, with the
131    /// `extract` function derived based on it.
132    fn data_loss_indicator(
133        &mut self,
134        packet_type: PacketType,
135        apid: Apid,
136        packet_sequence_count: PacketSequenceCount,
137    ) -> Self::DataLossIndicator;
138}
139
140/// Space packets are implemented as dynamically-sized structs that contain the primary header as
141/// their first field, followed by the packet data as pure byte array. In this manner,
142/// deserialization can be reduced to a simple byte cast followed by interpretation of the primary
143/// header - without any data copies needed. This is useful for high-throughput applications, and
144/// ensures that no allocation or significant additional memory is needed to consume Space Packets.
145///
146/// This does also mean that Space Packets may only be handled by reference. In the context of this
147/// crate that helps enforce that no spurious copies can be made of the user data (which may be
148/// rather large and would incur additional allocations), albeit at the cost of some convenience.
149///
150/// Any means of constructing a SpacePacket in this crate shall perform a consistency check on any
151/// received bytes. Hence, any SpacePacket object may be assumed to be a valid Space Packet.
152#[repr(C, packed)]
153#[derive(ByteEq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
154pub struct SpacePacket {
155    primary_header: SpacePacketPrimaryHeader,
156    data_field: [u8],
157}
158
159impl SpacePacket {
160    /// Attempts to parse a Space Packet from a given byte slice. If this fails, a reason is
161    /// given for this failure. Shall never panic: rather, an error enum is returned explaining why
162    /// the given octet string is not a valid Space Packet.
163    ///
164    /// This deserialization is fully zero-copy. The `&SpacePacket` returned on success directly
165    /// references the input slice `bytes`, but is merely validated to be a valid Space Packet.
166    pub fn parse(bytes: &[u8]) -> Result<&SpacePacket, InvalidSpacePacket> {
167        // First, we simply cast the packet into a header and check that the byte buffer permits
168        // this: i.e., if it is large enough to contain a header.
169        let primary_header = match SpacePacket::ref_from_bytes(bytes) {
170            Ok(primary_header) => primary_header,
171            Err(CastError::Size(_)) => {
172                return Err(InvalidSpacePacket::SliceTooSmallForSpacePacketHeader {
173                    length: bytes.len(),
174                });
175            }
176            Err(CastError::Alignment(_)) => unreachable!(),
177        };
178
179        // Then, we verify that the resulting packet contents semantically form a valid space
180        // packet.
181        primary_header.validate()?;
182
183        // Finally, we truncate the passed byte slice to exactly accommodate the specified space
184        // packet and construct a Space Packet that consists of only this memory region.
185        let packet_size = primary_header.packet_data_length() + Self::primary_header_size();
186        let packet_bytes = &bytes[..packet_size];
187        let packet = match SpacePacket::ref_from_bytes(packet_bytes) {
188            Ok(primary_header) => primary_header,
189            Err(_) => unreachable!(),
190        };
191
192        Ok(packet)
193    }
194
195    /// Assembles a Space Packet in-place on a given buffer. Computes the required packet data
196    /// length from the passed buffer size. It is assumed that the caller has reserved the first
197    /// six bytes of the buffer for the packet header. All other bytes are assumed to form the
198    /// packet data field.
199    pub fn assemble(
200        buffer: &mut [u8],
201        packet_type: PacketType,
202        secondary_header_flag: SecondaryHeaderFlag,
203        apid: Apid,
204        sequence_flag: SequenceFlag,
205        sequence_count: PacketSequenceCount,
206    ) -> Result<&mut SpacePacket, PacketAssemblyError> {
207        if buffer.len() < 6 {
208            Err(PacketAssemblyError::BufferTooSmall {
209                buffer_length: buffer.len(),
210                packet_length: 6,
211            })
212        } else {
213            Self::construct(
214                buffer,
215                packet_type,
216                secondary_header_flag,
217                apid,
218                sequence_flag,
219                sequence_count,
220                buffer.len() as u16 - 6,
221            )
222        }
223    }
224
225    /// Constructs a Space Packet in-place on a given buffer. May return a
226    /// `SpacePacketConstructionError` if this is not possible for whatever reason. Note that the
227    /// data field is only "allocated" on the buffer, but never further populated. That may be done
228    /// after the SpacePacket is otherwise fully constructed (or before: it is not touched during
229    /// construction).
230    pub fn construct(
231        buffer: &mut [u8],
232        packet_type: PacketType,
233        secondary_header_flag: SecondaryHeaderFlag,
234        apid: Apid,
235        sequence_flag: SequenceFlag,
236        sequence_count: PacketSequenceCount,
237        packet_data_length: u16,
238    ) -> Result<&mut SpacePacket, PacketAssemblyError> {
239        // As per the CCSDS Space Packet Protocol standard, we must reject requests for data field
240        // lengths of zero.
241        if packet_data_length == 0 {
242            return Err(PacketAssemblyError::EmptyDataFieldRequested);
243        }
244
245        // Verify that the packet length as requested may actually fit on the supplied buffer.
246        let packet_length = SpacePacket::primary_header_size() + packet_data_length as usize;
247        let buffer_length = buffer.len();
248        if packet_length > buffer_length {
249            return Err(PacketAssemblyError::BufferTooSmall {
250                buffer_length,
251                packet_length,
252            });
253        }
254
255        // Afterwards, we truncate the buffer to use only the bytes that actually belong to the
256        // packet. With the length check done, the `SpacePacket::mut_from_bytes()` call is known
257        // to be infallible, so we simply unwrap.
258        let packet_bytes = &mut buffer[..packet_length];
259        let packet = SpacePacket::mut_from_bytes(packet_bytes).unwrap();
260
261        // Initialize header bytes to valid values.
262        packet.primary_header.set_apid(apid);
263        packet.primary_header.initialize_packet_version();
264        packet.primary_header.set_packet_type(packet_type);
265        packet
266            .primary_header
267            .set_secondary_header_flag(secondary_header_flag);
268        packet.primary_header.set_sequence_flag(sequence_flag);
269        packet
270            .primary_header
271            .set_packet_sequence_count(sequence_count);
272        packet
273            .primary_header
274            .set_packet_data_length(packet_data_length)?;
275
276        Ok(packet)
277    }
278
279    /// Validates that the Space Packet is valid, in that its fields are coherent. In particular,
280    /// it is verified that the version number is that of a supported Space Packet, and that the
281    /// packet size as stored in the header is not larger than the packet size as permitted by the
282    /// actual memory span of which the packet consists.
283    ///
284    /// Note that this concerns semantic validity. The implementation shall not depend on this for
285    /// memory safety.
286    fn validate(&self) -> Result<(), InvalidSpacePacket> {
287        // First, we check that the primary header is valid and consistent.
288        self.primary_header.validate()?;
289
290        // The packet header contains an indication of the actual amount of bytes stored in the packet.
291        // If this is larger than the size of the actual memory contents, only a partial packet was
292        // received.
293        let packet_size = self.packet_data_length() + Self::primary_header_size();
294        let buffer_size = self.packet_length();
295        if packet_size > buffer_size {
296            return Err(InvalidSpacePacket::PartialPacket {
297                packet_size,
298                buffer_size,
299            });
300        }
301
302        Ok(())
303    }
304
305    /// Returns the size of a Space Packet primary header, in bytes. In the version that is
306    /// presently implemented, that is always 6 bytes.
307    pub const fn primary_header_size() -> usize {
308        6
309    }
310
311    /// Since the Space Packet protocol may technically support alternative packet structures in
312    /// future versions, the 3-bit packet version field may not actually contain a "correct" value.
313    pub fn packet_version(&self) -> PacketVersionNumber {
314        self.primary_header.packet_version()
315    }
316
317    /// The packet type denotes whether a packet is a telecommand (request) or telemetry (report)
318    /// packet. Note that the exact definition of telecommand and telemetry may differ per system,
319    /// and indeed the "correct" value here may differ per project.
320    pub fn packet_type(&self) -> PacketType {
321        self.primary_header.packet_type()
322    }
323
324    /// Sets the packet type to the given value.
325    pub fn set_packet_type(&mut self, packet_type: PacketType) {
326        self.primary_header.set_packet_type(packet_type)
327    }
328
329    /// Denotes whether the packet contains a secondary header. If no user field is present, the
330    /// secondary header is mandatory (presumably, to ensure that some data is always transferred,
331    /// considering the Space Packet header itself contains no meaningful data).
332    pub fn secondary_header_flag(&self) -> SecondaryHeaderFlag {
333        self.primary_header.secondary_header_flag()
334    }
335
336    /// Updates the value of the secondary header flag with the provided value.
337    pub fn set_secondary_header_flag(&mut self, secondary_header_flag: SecondaryHeaderFlag) {
338        self.primary_header
339            .set_secondary_header_flag(secondary_header_flag)
340    }
341
342    /// Returns the application process ID stored in the packet. The actual meaning of this APID
343    /// field may differ per implementation: technically, it only represents "some" data path.
344    /// In practice, it will often be a identifier for a data channel, the packet source, or the
345    /// packet destination.
346    pub fn apid(&self) -> Apid {
347        self.primary_header.apid()
348    }
349
350    /// Sets the APID used to route the packet to the given value.
351    pub fn set_apid(&mut self, apid: Apid) {
352        self.primary_header.set_apid(apid)
353    }
354
355    /// Sequence flags may be used to indicate that the data contained in a packet is only part of
356    /// a larger set of application data.
357    pub fn sequence_flag(&self) -> SequenceFlag {
358        self.primary_header.sequence_flag()
359    }
360
361    /// Sets the sequence flag to the provided value.
362    pub fn set_sequence_flag(&mut self, sequence_flag: SequenceFlag) {
363        self.primary_header.set_sequence_flag(sequence_flag)
364    }
365
366    /// The packet sequence count is unique per APID and denotes the sequential binary count of
367    /// each Space Packet (generated per APID). For telecommands (i.e., with packet type 1) this
368    /// may also be a "packet name" that identifies the telecommand packet within its
369    /// communications session.
370    pub fn packet_sequence_count(&self) -> PacketSequenceCount {
371        self.primary_header.packet_sequence_count()
372    }
373
374    /// Sets the packet sequence count to the provided value. This value must be provided by an
375    /// external counter and is not provided at a Space Packet type level because it might differ
376    /// between packet streams.
377    pub fn set_packet_sequence_count(&mut self, sequence_count: PacketSequenceCount) {
378        self.primary_header
379            .set_packet_sequence_count(sequence_count)
380    }
381
382    /// The packet data length field represents the length of the associated packet data field.
383    /// However, it is not stored directly: rather, the "length count" is stored, which is the
384    /// packet data length minus one.
385    pub fn packet_data_length(&self) -> usize {
386        self.primary_header.packet_data_length()
387    }
388
389    /// Sets the packet data length field to the provided value. Note that the given value is not
390    /// stored directly, but rather decremented by one first. Accordingly, and as per the CCSDS
391    /// Space Packet Protocol standard, packet data lengths of 0 are not allowed.
392    pub fn set_packet_data_length(
393        &mut self,
394        packet_data_length: u16,
395    ) -> Result<(), InvalidPacketDataLength> {
396        if packet_data_length == 0 {
397            return Err(InvalidPacketDataLength::EmptyDataField);
398        }
399
400        let buffer_length = self.data_field.len();
401        if packet_data_length as usize > buffer_length {
402            return Err(InvalidPacketDataLength::LargerThanPacketDataBuffer {
403                packet_data_length,
404                buffer_length,
405            });
406        }
407
408        let stored_data_field_length = packet_data_length - 1;
409        self.primary_header
410            .data_length
411            .set(stored_data_field_length);
412        Ok(())
413    }
414
415    /// Returns the total length of the packet in bytes. Note the distinction from the packet data
416    /// length, which refers only to the length of the data field of the packet.
417    pub fn packet_length(&self) -> usize {
418        self.as_bytes().len()
419    }
420
421    /// Returns a reference to the packet data field contained in this Space Packet.
422    pub fn packet_data_field(&self) -> &[u8] {
423        &self.data_field
424    }
425
426    /// Returns a mutable reference to the packet data field contained in this Space Packet.
427    pub fn packet_data_field_mut(&mut self) -> &mut [u8] {
428        &mut self.data_field
429    }
430}
431
432impl core::hash::Hash for SpacePacket {
433    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
434        self.primary_header.hash(state);
435        self.data_field.hash(state);
436    }
437}
438
439impl core::fmt::Debug for SpacePacket {
440    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
441        f.debug_struct("SpacePacket")
442            .field("primary_header", &self.primary_header)
443            .field("data_field", &&self.data_field)
444            .finish()
445    }
446}
447
448/// Representation of only the fixed-size primary header part of a space packet. Used to construct
449/// generic space packets, but mostly useful in permitting composition of derived packet types,
450/// like PUS packets; otherwise, the dynamically-sized data field member would get in the way of
451/// including the primary header directly in derived packets.
452#[repr(C)]
453#[derive(
454    Copy, Clone, Debug, ByteEq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Hash,
455)]
456pub struct SpacePacketPrimaryHeader {
457    packet_identification: network_endian::U16,
458    packet_sequence_control: network_endian::U16,
459    data_length: network_endian::U16,
460}
461
462impl SpacePacketPrimaryHeader {
463    /// Validates that the Space Packet primary header is valid, in that its fields are coherent.
464    /// In particular, it is verified that the version number is that of a supported Space Packet.
465    ///
466    /// Note that this concerns semantic validity. The implementation shall not depend on this for
467    /// memory safety.
468    fn validate(&self) -> Result<(), InvalidSpacePacket> {
469        // We verify that the packet version found in the packet header is a version that is
470        // supported by this library.
471        let version = self.packet_version();
472        if !version.is_supported() {
473            return Err(InvalidSpacePacket::UnsupportedPacketVersion { version });
474        }
475
476        // Idle packets may not contain a secondary header field. If we do find that the secondary
477        // header flag is set, we must reject the packet.
478        if self.apid().is_idle() && self.secondary_header_flag() == SecondaryHeaderFlag::Present {
479            return Err(InvalidSpacePacket::IdlePacketWithSecondaryHeader);
480        }
481
482        Ok(())
483    }
484
485    /// Returns the size of a Space Packet primary header, in bytes. In the version that is
486    /// presently implemented, that is always 6 bytes.
487    pub const fn primary_header_size() -> usize {
488        6
489    }
490
491    /// Since the Space Packet protocol may technically support alternative packet structures in
492    /// future versions, the 3-bit packet version field may not actually contain a "correct" value.
493    pub fn packet_version(&self) -> PacketVersionNumber {
494        use core::ops::Shr;
495        PacketVersionNumber(self.packet_identification.as_bytes()[0].shr(5))
496    }
497
498    /// Initializes the packet version to the proper value. Must be a fixed value, so this function
499    /// takes no arguments.
500    pub fn initialize_packet_version(&mut self) {
501        self.packet_identification.as_mut_bytes()[0] &= 0b0001_1111;
502        self.packet_identification.as_mut_bytes()[0] |=
503            PacketVersionNumber::version1_ccsds_packet().0 << 5;
504    }
505
506    /// The packet type denotes whether a packet is a telecommand (request) or telemetry (report)
507    /// packet. Note that the exact definition of telecommand and telemetry may differ per system,
508    /// and indeed the "correct" value here may differ per project.
509    pub fn packet_type(&self) -> PacketType {
510        match (self.packet_identification.as_bytes()[0] & 0x10) == 0x10 {
511            true => PacketType::Telecommand,
512            false => PacketType::Telemetry,
513        }
514    }
515
516    /// Sets the packet type to the given value.
517    pub fn set_packet_type(&mut self, packet_type: PacketType) {
518        self.packet_identification.as_mut_bytes()[0] &= 0b1110_1111;
519        self.packet_identification.as_mut_bytes()[0] |= (packet_type as u8) << 4;
520    }
521
522    /// Denotes whether the packet contains a secondary header. If no user field is present, the
523    /// secondary header is mandatory (presumably, to ensure that some data is always transferred,
524    /// considering the Space Packet header itself contains no meaningful data).
525    pub fn secondary_header_flag(&self) -> SecondaryHeaderFlag {
526        match (self.packet_identification.as_bytes()[0] & 0x08) == 0x08 {
527            true => SecondaryHeaderFlag::Present,
528            false => SecondaryHeaderFlag::Absent,
529        }
530    }
531
532    /// Updates the value of the secondary header flag with the provided value.
533    pub fn set_secondary_header_flag(&mut self, secondary_header_flag: SecondaryHeaderFlag) {
534        self.packet_identification.as_mut_bytes()[0] &= 0b1111_0111;
535        self.packet_identification.as_mut_bytes()[0] |= (secondary_header_flag as u8) << 3;
536    }
537
538    /// Returns the application process ID stored in the packet. The actual meaning of this APID
539    /// field may differ per implementation: technically, it only represents "some" data path.
540    /// In practice, it will often be a identifier for a data channel, the packet source, or the
541    /// packet destination.
542    pub fn apid(&self) -> Apid {
543        Apid(self.packet_identification.get() & 0b0000_0111_1111_1111)
544    }
545
546    /// Sets the APID used to route the packet to the given value.
547    pub fn set_apid(&mut self, apid: Apid) {
548        let apid = apid.0.to_be_bytes();
549        self.packet_identification.as_mut_bytes()[0] &= 0b1111_1000;
550        self.packet_identification.as_mut_bytes()[0] |= apid[0] & 0b0000_0111;
551        self.packet_identification.as_mut_bytes()[1] = apid[1];
552    }
553
554    /// Sequence flags may be used to indicate that the data contained in a packet is only part of
555    /// a larger set of application data.
556    pub fn sequence_flag(&self) -> SequenceFlag {
557        use core::ops::Shr;
558        match self.packet_sequence_control.as_bytes()[0].shr(6i32) {
559            0b00 => SequenceFlag::Continuation,
560            0b01 => SequenceFlag::First,
561            0b10 => SequenceFlag::Last,
562            0b11 => SequenceFlag::Unsegmented,
563            _ => unreachable!("Internal error: Reached unreachable code segment"),
564        }
565    }
566
567    /// Sets the sequence flag to the provided value.
568    pub fn set_sequence_flag(&mut self, sequence_flag: SequenceFlag) {
569        self.packet_sequence_control.as_mut_bytes()[0] &= 0b0011_1111;
570        self.packet_sequence_control.as_mut_bytes()[0] |= (sequence_flag as u8) << 6;
571    }
572
573    /// The packet sequence count is unique per APID and denotes the sequential binary count of
574    /// each Space Packet (generated per APID). For telecommands (i.e., with packet type 1) this
575    /// may also be a "packet name" that identifies the telecommand packet within its
576    /// communications session.
577    pub fn packet_sequence_count(&self) -> PacketSequenceCount {
578        PacketSequenceCount(self.packet_sequence_control.get() & 0b0011_1111_1111_1111)
579    }
580
581    /// Sets the packet sequence count to the provided value. This value must be provided by an
582    /// external counter and is not provided at a Space Packet type level because it might differ
583    /// between packet streams.
584    pub fn set_packet_sequence_count(&mut self, sequence_count: PacketSequenceCount) {
585        self.packet_sequence_control.as_mut_bytes()[0] &= 0b1100_0000;
586        self.packet_sequence_control.as_mut_bytes()[0] |=
587            sequence_count.0.to_be_bytes()[0] & 0b0011_1111;
588        self.packet_sequence_control.as_mut_bytes()[1] = sequence_count.0.to_be_bytes()[1];
589    }
590
591    /// The packet data length field represents the length of the associated packet data field.
592    /// However, it is not stored directly: rather, the "length count" is stored, which is the
593    /// packet data length minus one.
594    pub fn packet_data_length(&self) -> usize {
595        self.data_length.get() as usize + 1
596    }
597
598    /// Sets the packet data length field to the provided value. Note that the given value is not
599    /// stored directly, but rather decremented by one first. Accordingly, and as per the CCSDS
600    /// Space Packet Protocol standard, packet data lengths of 0 are not allowed.
601    pub fn set_packet_data_length(
602        &mut self,
603        packet_data_length: u16,
604    ) -> Result<(), InvalidPacketDataLength> {
605        if packet_data_length == 0 {
606            return Err(InvalidPacketDataLength::EmptyDataField);
607        }
608
609        let stored_data_field_length = packet_data_length - 1;
610        self.data_length.set(stored_data_field_length);
611        Ok(())
612    }
613}
614
615/// Representation of the set of errors that may be encountered while deserializing a Space Packet.
616/// Marked as non-exhaustive to permit extension with additional semantic errors in the future
617/// without breaking API.
618#[non_exhaustive]
619#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Error)]
620pub enum InvalidSpacePacket {
621    /// Returned when a byte slice is too small to contain any Space Packet (i.e., is smaller than
622    /// a header with a single-byte user data field).
623    #[error(
624        "buffer too small for space packet header (has {length} bytes, at least 6 are required)"
625    )]
626    SliceTooSmallForSpacePacketHeader { length: usize },
627    /// Returned when a slice does not have a known and supported packet version. For convenience,
628    /// the packet version that is stored at the "conventional" (CCSDS packet version 0) is also
629    /// returned, though it does not need to be meaningful in other packet versions.
630    #[error("unsupported CCSDS Space Packet version: {version:?}")]
631    UnsupportedPacketVersion { version: PacketVersionNumber },
632    /// Returned when the decoded packet is not fully contained in the passed buffer.
633    #[error("detected partial packet (buffer is {buffer_size} bytes, packet {packet_size})")]
634    PartialPacket {
635        packet_size: usize,
636        buffer_size: usize,
637    },
638    /// Returned when the Space Packet is idle (has an 'all ones' APID) but also contains a
639    /// secondary header. This is forbidden by CCSDS 133.0-B-2.
640    #[error("idle packet contains a secondary header, this is forbidden")]
641    IdlePacketWithSecondaryHeader,
642}
643
644/// Representation of the set of errors that may be encountered while constructing a Space Packet.
645/// Marked as non-exhaustive to permit extension with additional semantic errors in the future
646/// without breaking API.
647#[non_exhaustive]
648#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Error)]
649pub enum PacketAssemblyError {
650    /// Returned when the underlying buffer does not have sufficient bytes to contain a given space
651    /// packet.
652    #[error(
653        "buffer too small for space packet (has {buffer_length} bytes, packet requires at least {packet_length})"
654    )]
655    BufferTooSmall {
656        buffer_length: usize,
657        packet_length: usize,
658    },
659    /// As per the CCSDS standard, Space Packets shall have at least one byte in their data field.
660    /// Hence, requests for an empty data field must be rejected.
661    #[error("empty data field requested, this is forbidden")]
662    EmptyDataFieldRequested,
663}
664
665/// This error may be returned when setting the data field of some newly-constructed Space Packet
666/// if the requested packet data length is 0 (which is generally illegal) or if the requested
667/// packet data length does not fit in the buffer on which the packet must be stored.
668#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Error)]
669pub enum InvalidPacketDataLength {
670    #[error("empty data field requested, this is forbidden")]
671    EmptyDataField,
672    #[error(
673        "requested packet data length ({packet_data_length} bytes) is too large for buffer ({buffer_length} bytes)"
674    )]
675    LargerThanPacketDataBuffer {
676        packet_data_length: u16,
677        buffer_length: usize,
678    },
679}
680
681impl From<InvalidPacketDataLength> for PacketAssemblyError {
682    fn from(value: InvalidPacketDataLength) -> Self {
683        match value {
684            InvalidPacketDataLength::EmptyDataField => PacketAssemblyError::EmptyDataFieldRequested,
685            InvalidPacketDataLength::LargerThanPacketDataBuffer {
686                packet_data_length,
687                buffer_length,
688            } => PacketAssemblyError::BufferTooSmall {
689                buffer_length: buffer_length + SpacePacket::primary_header_size(),
690                packet_length: packet_data_length as usize + SpacePacket::primary_header_size(),
691            },
692        }
693    }
694}
695
696/// The packet version number represents the version of the Space Packet protocol that is used. In
697/// the version presently implemented, this is defined to be zeroes.
698#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
699#[repr(transparent)]
700pub struct PacketVersionNumber(u8);
701
702impl PacketVersionNumber {
703    /// The Space Packet protocol version presently implemented in this crate is based on issue 2
704    /// of the CCSDS SPP blue book, which encompasses only the Version 1 CCSDS Packet, indicated by
705    /// a version number of 0. Other packet structures may be added in the future.
706    pub fn is_supported(&self) -> bool {
707        matches!(self.0, 0b0000_0000u8)
708    }
709
710    /// Returns the packet version number corresponding with the Version 1 CCSDS Packet.
711    pub fn version1_ccsds_packet() -> Self {
712        Self(0)
713    }
714}
715
716/// The packet type denotes whether a packet is a telecommand (request) or telemetry (report)
717/// packet. Note that the exact definition of telecommand and telemetry may differ per system,
718/// and indeed the "correct" value here may differ per project.
719#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
720#[cfg_attr(kani, derive(kani::Arbitrary))]
721#[repr(u8)]
722pub enum PacketType {
723    Telemetry = 0,
724    Telecommand = 1,
725}
726
727/// Denotes whether the packet contains a secondary header. If no user field is present, the
728/// secondary header is mandatory (presumably, to ensure that some data is always transferred,
729/// considering the Space Packet header itself contains no meaningful data).
730#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
731#[cfg_attr(kani, derive(kani::Arbitrary))]
732#[repr(u8)]
733pub enum SecondaryHeaderFlag {
734    Absent = 0,
735    Present = 1,
736}
737
738/// Returns the application process ID stored in the packet. The actual meaning of this APID
739/// field may differ per implementation: technically, it only represents "some" data path.
740/// In practice, it will often be a identifier for: a data channel, the packet source, or the
741/// packet destination.
742#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
743#[cfg_attr(kani, derive(kani::Arbitrary))]
744#[repr(transparent)]
745pub struct Apid(u16);
746
747impl Apid {
748    const MAX: u16 = 0b0000_0111_1111_1111u16;
749
750    pub fn new(id: u16) -> Self {
751        assert!(
752            id <= Self::MAX,
753            "APIDs may not exceed 2047 (due to maximum of 13 bits in representation)"
754        );
755        Self(id)
756    }
757
758    /// Helper functions used during formal verification to create an APID that is actually within
759    /// the stated bounds, since we cannot use the type system to express this range.
760    #[cfg(kani)]
761    fn any_apid() -> Self {
762        match kani::any() {
763            any @ 0..=Self::MAX => Self(any),
764            _ => Self(42),
765        }
766    }
767
768    /// A special APID value (0x7ff) is reserved for idle Space Packets, i.e., packets that do not
769    /// carry any actual data.
770    pub fn is_idle(&self) -> bool {
771        self.0 == 0x7ff
772    }
773
774    /// Returns the APID as a regular 16-bit unsigned integer.
775    pub fn as_u16(&self) -> u16 {
776        self.0
777    }
778}
779
780impl From<Apid> for u16 {
781    fn from(value: Apid) -> Self {
782        value.0
783    }
784}
785
786/// Sequence flags may be used to indicate that the data contained in a packet is only part of
787/// a larger set of application data.
788#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
789#[cfg_attr(kani, derive(kani::Arbitrary))]
790#[repr(u8)]
791pub enum SequenceFlag {
792    Continuation = 0b00,
793    First = 0b01,
794    Last = 0b10,
795    #[default]
796    Unsegmented = 0b11,
797}
798
799/// The packet sequence count is unique per APID and denotes the sequential binary count of
800/// each Space Packet (generated per APID). For telecommands (i.e., with packet type 1) this
801/// may also be a "packet name" that identifies the telecommand packet within its
802/// communications session.
803#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Debug, Default)]
804#[cfg_attr(kani, derive(kani::Arbitrary))]
805pub struct PacketSequenceCount(u16);
806
807impl PacketSequenceCount {
808    const MAX: u16 = 0b0011_1111_1111_1111u16;
809
810    /// The packet sequence count is initialized to zero by default.
811    pub fn new() -> Self {
812        Self(0)
813    }
814
815    /// Helper functions used during formal verification to create a packet sequence count that is
816    /// actually within the stated bounds, since we cannot use the type system to express this
817    /// range.
818    #[cfg(kani)]
819    fn any_packet_sequence_count() -> Self {
820        match kani::any() {
821            any @ 0..=Self::MAX => Self(any),
822            _ => Self(42),
823        }
824    }
825
826    /// A good default behaviour is for the packet sequence count to increment by one every time
827    /// a new packet is sent. This method permits a simple wrapping increment to be performed, to
828    /// make this easier.
829    pub fn increment(&mut self) {
830        self.0 += 1;
831        if self.0 > Self::MAX {
832            self.0 = 0;
833        }
834    }
835}
836
837/// Test harness for formal verification.
838#[cfg(kani)]
839mod kani_harness {
840    use super::*;
841    use ::kani;
842
843    /// This test verifies that all possible primary headers may be parsed for all packets up to
844    /// u16::MAX in size, without panics. Note that the packet data field is assumed to always be
845    /// zero here. This is needed to restrict the search space for kani, and is a valid assumption
846    /// because the parsing implementation never touches the packet data field contents.
847    #[kani::proof]
848    fn header_parsing() {
849        let mut bytes = [0u8; u16::MAX as usize];
850        bytes[0] = kani::any();
851        bytes[1] = kani::any();
852        bytes[2] = kani::any();
853        bytes[3] = kani::any();
854        bytes[4] = kani::any();
855        bytes[5] = kani::any();
856        bytes[6] = kani::any();
857
858        let packet = SpacePacket::parse(&bytes);
859        if let Ok(packet) = packet {
860            assert!(packet.packet_length() <= bytes.len());
861            assert_eq!(
862                packet.packet_data_field().len(),
863                packet.packet_data_length()
864            );
865            assert!(packet.apid().0 <= 0b0000_0111_1111_1111);
866        }
867    }
868
869    /// This test verifies that all (!) possible packet construction requests can be handled
870    /// without panics when working with a fixed-size buffer that does not permit all possible
871    /// packet size requests. Here, we do not touch the data field, to prevent exponential blow-up
872    /// of the proof pipeline. Since the packet constructor performs no actions on the packet data
873    /// field beyond returning a reference to it, this makes for a strong proof about the safety of
874    /// this function.
875    ///
876    /// The buffer size is rather arbitrarily chosen to be 1024. This covers a significant amount
877    /// of valid packet sizes, but also ensures that the "error path" is covered, where the
878    /// requested packet is larger than the available buffer.
879    #[kani::proof]
880    fn packet_construction() {
881        let mut bytes = [kani::any(); 1024];
882        let maximum_packet_length = bytes.len();
883        let packet_type = kani::any();
884        let secondary_header_flag = kani::any();
885        let apid = Apid::any_apid();
886        let sequence_flag = kani::any();
887        let sequence_count = PacketSequenceCount::any_packet_sequence_count();
888        let packet_data_length = kani::any();
889
890        let packet = SpacePacket::construct(
891            &mut bytes,
892            packet_type,
893            secondary_header_flag,
894            apid,
895            sequence_flag,
896            sequence_count,
897            packet_data_length,
898        );
899
900        // First, we verify that all valid requests result in a returned packet.
901        let valid_request = packet_data_length != 0
902            && (packet_data_length as usize)
903                <= (maximum_packet_length - SpacePacket::primary_header_size() as usize);
904        if valid_request {
905            assert!(packet.is_ok());
906        }
907
908        // Vice versa, any invalid requests must be rejected.
909        if !valid_request {
910            assert!(!packet.is_ok());
911        }
912
913        // These checks ensure that any returned packet is indeed consistent with the requested
914        // packet header information.
915        if let Ok(packet) = packet {
916            assert!(packet.packet_length() <= maximum_packet_length);
917            assert_eq!(
918                packet.packet_data_field().len(),
919                packet.packet_data_length()
920            );
921
922            assert_eq!(packet.packet_type(), packet_type);
923            assert_eq!(packet.secondary_header_flag(), secondary_header_flag);
924            assert_eq!(packet.apid(), apid);
925            assert_eq!(packet.sequence_flag(), sequence_flag);
926            assert_eq!(packet.packet_sequence_count(), sequence_count);
927            assert_eq!(packet.packet_data_length(), packet_data_length as usize);
928        }
929    }
930}
931
932/// Test generated for harness `kani_harness::packet_construction` after assertion failure. Test
933/// case initially failed on resizing the packet to the proper length when a larger byte buffer was
934/// passed than what was covered by the packet contents.
935#[test]
936fn kani_failure1() {
937    const BYTES: usize = 16;
938    let mut bytes = [0; BYTES];
939    let packet = SpacePacket::construct(
940        &mut bytes,
941        PacketType::Telecommand,
942        SecondaryHeaderFlag::Present,
943        Apid::new(0),
944        SequenceFlag::Unsegmented,
945        PacketSequenceCount(65535),
946        8,
947    );
948
949    if let Ok(packet) = packet {
950        assert!(packet.packet_length() <= BYTES);
951        assert_eq!(
952            packet.packet_data_field().len(),
953            packet.packet_data_length(),
954            "Packet data field length does not match packet data field as stored: {packet:?}"
955        );
956        assert!(packet.apid().0 <= 0b0000_0111_1111_1111);
957    }
958}
959
960/// Deserialization of a relatively trivial packet. Used to verify that all basic deserialization
961/// logic is correct.
962#[test]
963fn deserialize_trivial_packet() {
964    let bytes = &[
965        0b0000_1000u8,
966        0b0000_0000u8,
967        0b1100_0000u8,
968        0b0000_0000u8,
969        0b0000_0000u8,
970        0b0000_0000u8,
971        0b0000_0000u8,
972    ];
973    let packet = SpacePacket::parse(bytes).unwrap();
974
975    assert_eq!(packet.packet_length(), 7);
976    assert_eq!(
977        packet.packet_version(),
978        PacketVersionNumber::version1_ccsds_packet()
979    );
980    assert_eq!(packet.packet_type(), PacketType::Telemetry);
981    assert_eq!(packet.secondary_header_flag(), SecondaryHeaderFlag::Present);
982    assert_eq!(packet.apid(), Apid::new(0));
983    assert_eq!(packet.sequence_flag(), SequenceFlag::Unsegmented);
984    assert_eq!(packet.packet_sequence_count(), PacketSequenceCount(0));
985    assert_eq!(packet.packet_data_length(), 1);
986    assert_eq!(packet.packet_data_field(), &bytes[6..]);
987}
988
989/// Serialization of a relatively trivial packet. Used to verify that all serialization logic is
990/// correct.
991#[test]
992fn serialize_trivial_packet() {
993    let mut bytes = [0u8; 7];
994    let packet = SpacePacket::construct(
995        &mut bytes,
996        PacketType::Telemetry,
997        SecondaryHeaderFlag::Present,
998        Apid::new(0),
999        SequenceFlag::Unsegmented,
1000        PacketSequenceCount(0),
1001        1,
1002    )
1003    .unwrap();
1004
1005    assert_eq!(packet.packet_length(), 7);
1006    assert_eq!(
1007        packet.packet_version(),
1008        PacketVersionNumber::version1_ccsds_packet()
1009    );
1010    assert_eq!(packet.packet_type(), PacketType::Telemetry);
1011    assert_eq!(packet.secondary_header_flag(), SecondaryHeaderFlag::Present);
1012    assert_eq!(packet.apid(), Apid::new(0));
1013    assert_eq!(packet.sequence_flag(), SequenceFlag::Unsegmented);
1014    assert_eq!(packet.packet_sequence_count(), PacketSequenceCount(0));
1015    assert_eq!(packet.packet_data_length(), 1);
1016    assert_eq!(
1017        packet.packet_data_field(),
1018        &[
1019            0b0000_1000u8,
1020            0b0000_0000u8,
1021            0b1100_0000u8,
1022            0b0000_0000u8,
1023            0b0000_0000u8,
1024            0b0000_0000u8,
1025            0b0000_0000u8,
1026        ][6..]
1027    );
1028}
1029
1030/// Roundtrip serialization and subsequent deserialization of Space Packets shall result in exactly
1031/// identical byte slices for any valid (!) input. We test this by generating 10,000 random space
1032/// packets and seeing whether they remain identical through this transformation.
1033///
1034/// Since this test only considers valid inputs, other unit tests are needed to cover off-nominal
1035/// cases, such as when the buffer is too small or when the requested data field size is 0.
1036#[test]
1037fn roundtrip() {
1038    use rand::{RngCore, SeedableRng};
1039    // Note that we always use the same seed for reproducibility.
1040    let mut rng = rand::rngs::SmallRng::seed_from_u64(42);
1041    let mut buffer = [0u8; 16000];
1042    for _ in 0..10_000 {
1043        let packet_type = match rng.next_u32() & 1 {
1044            0 => PacketType::Telemetry,
1045            1 => PacketType::Telecommand,
1046            _ => unreachable!(),
1047        };
1048        let secondary_header_flag = match rng.next_u32() & 1 {
1049            0 => SecondaryHeaderFlag::Absent,
1050            1 => SecondaryHeaderFlag::Present,
1051            _ => unreachable!(),
1052        };
1053        let apid = Apid::new((rng.next_u32() & Apid::MAX as u32) as u16);
1054        let sequence_flag = match rng.next_u32() & 3 {
1055            0b00 => SequenceFlag::Continuation,
1056            0b01 => SequenceFlag::First,
1057            0b10 => SequenceFlag::Last,
1058            0b11 => SequenceFlag::Unsegmented,
1059            _ => unreachable!(),
1060        };
1061        let sequence_count =
1062            PacketSequenceCount((rng.next_u32() & PacketSequenceCount::MAX as u32) as u16);
1063
1064        let packet_data_length = (rng.next_u32() % (buffer.len() as u32 - 7)) as u16 + 1;
1065
1066        let space_packet = SpacePacket::construct(
1067            &mut buffer,
1068            packet_type,
1069            secondary_header_flag,
1070            apid,
1071            sequence_flag,
1072            sequence_count,
1073            packet_data_length,
1074        )
1075        .unwrap();
1076
1077        assert_eq!(
1078            packet_type,
1079            space_packet.packet_type(),
1080            "Serialized packet type ({:?}) does not match with final deserialized packet type ({:?}) for packet ({:?})",
1081            packet_type,
1082            space_packet.packet_type(),
1083            space_packet
1084        );
1085
1086        assert_eq!(
1087            secondary_header_flag,
1088            space_packet.secondary_header_flag(),
1089            "Serialized secondary header flag ({:?}) does not match with final deserialized secondary header flag ({:?}) for packet ({:?})",
1090            secondary_header_flag,
1091            space_packet.secondary_header_flag(),
1092            space_packet
1093        );
1094
1095        assert_eq!(
1096            apid,
1097            space_packet.apid(),
1098            "Serialized APID ({:?}) does not match with final deserialized APID ({:?}) for packet ({:?})",
1099            apid,
1100            space_packet.apid(),
1101            space_packet
1102        );
1103
1104        assert_eq!(
1105            sequence_flag,
1106            space_packet.sequence_flag(),
1107            "Serialized sequence flag ({:?}) does not match with final deserialized sequence flag ({:?}) for packet ({:?})",
1108            sequence_flag,
1109            space_packet.sequence_flag(),
1110            space_packet
1111        );
1112
1113        assert_eq!(
1114            sequence_count,
1115            space_packet.packet_sequence_count(),
1116            "Serialized sequence count ({:?}) does not match with final deserialized sequence count ({:?}) for packet ({:?})",
1117            sequence_count,
1118            space_packet.packet_sequence_count(),
1119            space_packet
1120        );
1121
1122        assert_eq!(
1123            packet_data_length as usize,
1124            space_packet.packet_data_length(),
1125            "Serialized packet type ({:?}) does not match with final deserialized packet type ({:?}) for packet ({:?})",
1126            packet_data_length,
1127            space_packet.packet_data_length(),
1128            space_packet
1129        );
1130    }
1131}
1132
1133/// Empty packet data fields are not permitted by CCSDS 133.0-B-2, so such requests must be
1134/// rejected.
1135#[test]
1136fn empty_packet_data_field() {
1137    let mut bytes = [0u8; 7];
1138    let result = SpacePacket::construct(
1139        &mut bytes,
1140        PacketType::Telemetry,
1141        SecondaryHeaderFlag::Present,
1142        Apid::new(0),
1143        SequenceFlag::Unsegmented,
1144        PacketSequenceCount(0),
1145        0,
1146    );
1147    assert_eq!(result, Err(PacketAssemblyError::EmptyDataFieldRequested));
1148}
1149
1150/// When the buffer to construct a Space Packet in is too small to contain a packet primary header,
1151/// this shall be caught and an error shall be returned, independent of the actual packet request.
1152#[test]
1153fn buffer_too_small_for_header_construction() {
1154    let mut buffer = [0u8; 5];
1155    let buffer_length = buffer.len();
1156    let result = SpacePacket::construct(
1157        &mut buffer,
1158        PacketType::Telemetry,
1159        SecondaryHeaderFlag::Present,
1160        Apid::new(0),
1161        SequenceFlag::Unsegmented,
1162        PacketSequenceCount(0),
1163        1,
1164    );
1165    assert_eq!(
1166        result,
1167        Err(PacketAssemblyError::BufferTooSmall {
1168            buffer_length,
1169            packet_length: 7
1170        })
1171    );
1172}
1173
1174/// When the buffer to construct a Space Packet in is too small to contain the full packet, an
1175/// error shall be returned stating as such.
1176#[test]
1177fn buffer_too_small_for_packet_construction() {
1178    use rand::{RngCore, SeedableRng};
1179    // Note that we always use the same seed for reproducibility.
1180    let mut rng = rand::rngs::SmallRng::seed_from_u64(42);
1181    let mut buffer = [0u8; 128];
1182    let buffer_length = buffer.len();
1183
1184    for _ in 0..1000 {
1185        // Generate a pseudo-random packet data length between 128 and u16::MAX.
1186        let packet_data_length = (rng.next_u32() % (u16::MAX - 128) as u32) as u16 + 128;
1187        let result = SpacePacket::construct(
1188            &mut buffer,
1189            PacketType::Telemetry,
1190            SecondaryHeaderFlag::Present,
1191            Apid::new(0),
1192            SequenceFlag::Unsegmented,
1193            PacketSequenceCount(0),
1194            packet_data_length,
1195        );
1196        assert_eq!(
1197            result,
1198            Err(PacketAssemblyError::BufferTooSmall {
1199                buffer_length,
1200                packet_length: packet_data_length as usize + SpacePacket::primary_header_size(),
1201            })
1202        );
1203    }
1204}
1205
1206/// When the buffer to parse a packet from is too small, an error shall be returned to indicate
1207/// this.
1208#[test]
1209fn buffer_too_small_for_parsed_packet() {
1210    use rand::{RngCore, SeedableRng};
1211    // Note that we always use the same seed for reproducibility.
1212    let mut rng = rand::rngs::SmallRng::seed_from_u64(42);
1213    let mut buffer = [0u8; 256];
1214
1215    for _ in 0..1000 {
1216        // Generate a pseudo-random packet data length between 128 and 250, so that the resulting
1217        // packet will fit on a 256-byte buffer.
1218        let packet_data_length = (rng.next_u32() % 128) as u16 + 122;
1219
1220        // Construct a valid Space Packet.
1221        let packet = SpacePacket::construct(
1222            &mut buffer,
1223            PacketType::Telemetry,
1224            SecondaryHeaderFlag::Present,
1225            Apid::new(0),
1226            SequenceFlag::Unsegmented,
1227            PacketSequenceCount(0),
1228            packet_data_length,
1229        )
1230        .unwrap();
1231
1232        // Subsequently, truncate the resulting byte sequence to 127 bytes, so that it will always
1233        // be invalid (the stored packet data length will always correspond with a packet larger
1234        // than 127 bytes).
1235        let bytes = &packet.as_bytes()[..127];
1236        let result = SpacePacket::parse(bytes);
1237        assert_eq!(
1238            result,
1239            Err(InvalidSpacePacket::PartialPacket {
1240                packet_size: packet_data_length as usize + SpacePacket::primary_header_size(),
1241                buffer_size: bytes.len()
1242            })
1243        );
1244    }
1245}