Skip to main content

mediadecode/
packet.rs

1//! Compressed `Packet` types and `PacketFlags`.
2//!
3//! The Packet types proper land in later tasks; this module starts
4//! with `PacketFlags` so dependent types can use it.
5
6use bitflags::bitflags;
7
8bitflags! {
9  /// Per-packet flags.
10  ///
11  /// Bit values are the public API:
12  /// - `KEY = 0b001` — packet starts a keyframe (FFmpeg `AV_PKT_FLAG_KEY`,
13  ///   WebCodecs `'key'`, ProRes RAW absence of
14  ///   `kCMSampleAttachmentKey_NotSync`).
15  /// - `CORRUPT = 0b010` — packet is known-corrupt (FFmpeg
16  ///   `AV_PKT_FLAG_CORRUPT`).
17  /// - `DISCARD = 0b100` — packet should be skipped during reconstruction
18  ///   (FFmpeg `AV_PKT_FLAG_DISCARD`).
19  ///
20  /// # Text form
21  ///
22  /// This type deliberately has **no** `Display` / `FromStr`, and its
23  /// serde shape is the raw [`bits`](Self::bits) as a number. A
24  /// vocabulary of *names* takes a text form; a bit *set* takes a
25  /// number. A flag-set grammar (`"key|discard"`) would need two shapes
26  /// rather than one, because a bit this build has no constant for can
27  /// only be printed as a bare literal — and there are such bits today:
28  /// FFmpeg carries `AV_PKT_FLAG_TRUSTED` (`0b0_1000`) and
29  /// `AV_PKT_FLAG_DISPOSABLE` (`0b1_0000`), which this set does not
30  /// name. Human-readable names live in `Debug` and in whatever
31  /// consumer surface wants them. This is `mediaframe::TrackDisposition`'s
32  /// stance, for the same reason.
33  #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
34  pub struct PacketFlags: u8 {
35    /// Keyframe / sync sample.
36    const KEY     = 0b001;
37    /// Bitstream-level corruption known.
38    const CORRUPT = 0b010;
39    /// Demuxer hint: skip this packet.
40    const DISCARD = 0b100;
41  }
42}
43
44use crate::Timestamp;
45
46/// A compressed video packet.
47///
48/// Generic over the [`VideoAdapter`] (which contributes
49/// `A::PacketExtra`) and the buffer type `B: AsRef<[u8]>`.
50///
51/// `pts` / `dts` / `duration` are `Option<Timestamp>` because not
52/// every backend supplies all three (WebCodecs `EncodedVideoChunk`
53/// has no DTS; vendor RAW SDKs that produce packets at all derive
54/// timestamps from frame index × fps).
55pub struct VideoPacket<E, D> {
56  pts: Option<Timestamp>,
57  dts: Option<Timestamp>,
58  duration: Option<Timestamp>,
59  flags: PacketFlags,
60  data: D,
61  extra: E,
62}
63
64impl<E, D> VideoPacket<E, D> {
65  /// Constructs a `VideoPacket` from `data` and `extra`. All
66  /// timestamps default to `None` and flags to empty.
67  #[cfg_attr(not(tarpaulin), inline(always))]
68  pub const fn new(data: D, extra: E) -> Self {
69    Self {
70      pts: None,
71      dts: None,
72      duration: None,
73      flags: PacketFlags::empty(),
74      data,
75      extra,
76    }
77  }
78
79  /// Returns the presentation timestamp.
80  #[cfg_attr(not(tarpaulin), inline(always))]
81  pub const fn pts(&self) -> Option<Timestamp> {
82    self.pts
83  }
84  /// Returns the decompression timestamp.
85  #[cfg_attr(not(tarpaulin), inline(always))]
86  pub const fn dts(&self) -> Option<Timestamp> {
87    self.dts
88  }
89  /// Returns the packet duration.
90  #[cfg_attr(not(tarpaulin), inline(always))]
91  pub const fn duration(&self) -> Option<Timestamp> {
92    self.duration
93  }
94  /// Returns the packet flags.
95  #[cfg_attr(not(tarpaulin), inline(always))]
96  pub const fn flags(&self) -> PacketFlags {
97    self.flags
98  }
99  /// Returns the compressed data buffer.
100  #[cfg_attr(not(tarpaulin), inline(always))]
101  pub const fn data(&self) -> &D {
102    &self.data
103  }
104  /// Returns the backend-specific extras.
105  #[cfg_attr(not(tarpaulin), inline(always))]
106  pub const fn extra(&self) -> &E {
107    &self.extra
108  }
109  /// Returns a mutable reference to the backend-specific extras.
110  #[cfg_attr(not(tarpaulin), inline(always))]
111  pub fn extra_mut(&mut self) -> &mut E {
112    &mut self.extra
113  }
114  /// Consumes the packet and returns the buffer.
115  #[cfg_attr(not(tarpaulin), inline(always))]
116  pub fn into_data(self) -> D {
117    self.data
118  }
119  /// Consumes the packet and returns `(buffer, extras)`.
120  #[cfg_attr(not(tarpaulin), inline(always))]
121  pub fn into_parts(self) -> (D, E) {
122    (self.data, self.extra)
123  }
124
125  /// Sets the PTS (consuming builder).
126  #[cfg_attr(not(tarpaulin), inline(always))]
127  #[must_use]
128  pub const fn with_pts(mut self, v: Option<Timestamp>) -> Self {
129    self.pts = v;
130    self
131  }
132  /// Sets the DTS (consuming builder).
133  #[cfg_attr(not(tarpaulin), inline(always))]
134  #[must_use]
135  pub const fn with_dts(mut self, v: Option<Timestamp>) -> Self {
136    self.dts = v;
137    self
138  }
139  /// Sets the duration (consuming builder).
140  #[cfg_attr(not(tarpaulin), inline(always))]
141  #[must_use]
142  pub const fn with_duration(mut self, v: Option<Timestamp>) -> Self {
143    self.duration = v;
144    self
145  }
146  /// Sets the flags (consuming builder).
147  #[cfg_attr(not(tarpaulin), inline(always))]
148  #[must_use]
149  pub const fn with_flags(mut self, v: PacketFlags) -> Self {
150    self.flags = v;
151    self
152  }
153
154  /// Sets the PTS in place.
155  #[cfg_attr(not(tarpaulin), inline(always))]
156  pub const fn set_pts(&mut self, v: Option<Timestamp>) -> &mut Self {
157    self.pts = v;
158    self
159  }
160  /// Sets the DTS in place.
161  #[cfg_attr(not(tarpaulin), inline(always))]
162  pub const fn set_dts(&mut self, v: Option<Timestamp>) -> &mut Self {
163    self.dts = v;
164    self
165  }
166  /// Sets the duration in place.
167  #[cfg_attr(not(tarpaulin), inline(always))]
168  pub const fn set_duration(&mut self, v: Option<Timestamp>) -> &mut Self {
169    self.duration = v;
170    self
171  }
172  /// Sets the flags in place.
173  #[cfg_attr(not(tarpaulin), inline(always))]
174  pub const fn set_flags(&mut self, v: PacketFlags) -> &mut Self {
175    self.flags = v;
176    self
177  }
178}
179
180/// A compressed audio packet.
181pub struct AudioPacket<E, D> {
182  pts: Option<Timestamp>,
183  dts: Option<Timestamp>,
184  duration: Option<Timestamp>,
185  flags: PacketFlags,
186  data: D,
187  extra: E,
188}
189
190impl<E, D> AudioPacket<E, D> {
191  /// Constructs an `AudioPacket` from `data` and `extra`.
192  #[cfg_attr(not(tarpaulin), inline(always))]
193  pub const fn new(data: D, extra: E) -> Self {
194    Self {
195      pts: None,
196      dts: None,
197      duration: None,
198      flags: PacketFlags::empty(),
199      data,
200      extra,
201    }
202  }
203
204  /// Returns the presentation timestamp.
205  #[cfg_attr(not(tarpaulin), inline(always))]
206  pub const fn pts(&self) -> Option<Timestamp> {
207    self.pts
208  }
209  /// Returns the decompression timestamp.
210  #[cfg_attr(not(tarpaulin), inline(always))]
211  pub const fn dts(&self) -> Option<Timestamp> {
212    self.dts
213  }
214  /// Returns the duration.
215  #[cfg_attr(not(tarpaulin), inline(always))]
216  pub const fn duration(&self) -> Option<Timestamp> {
217    self.duration
218  }
219  /// Returns the flags.
220  #[cfg_attr(not(tarpaulin), inline(always))]
221  pub const fn flags(&self) -> PacketFlags {
222    self.flags
223  }
224  /// Returns the compressed audio data.
225  #[cfg_attr(not(tarpaulin), inline(always))]
226  pub const fn data(&self) -> &D {
227    &self.data
228  }
229  /// Returns the backend extras.
230  #[cfg_attr(not(tarpaulin), inline(always))]
231  pub const fn extra(&self) -> &E {
232    &self.extra
233  }
234  /// Returns a mutable reference to the backend extras.
235  #[cfg_attr(not(tarpaulin), inline(always))]
236  pub fn extra_mut(&mut self) -> &mut E {
237    &mut self.extra
238  }
239  /// Consumes the packet and returns the buffer.
240  #[cfg_attr(not(tarpaulin), inline(always))]
241  pub fn into_data(self) -> D {
242    self.data
243  }
244  /// Consumes the packet and returns `(buffer, extras)`.
245  #[cfg_attr(not(tarpaulin), inline(always))]
246  pub fn into_parts(self) -> (D, E) {
247    (self.data, self.extra)
248  }
249
250  /// Sets the PTS (consuming builder).
251  #[cfg_attr(not(tarpaulin), inline(always))]
252  #[must_use]
253  pub const fn with_pts(mut self, v: Option<Timestamp>) -> Self {
254    self.pts = v;
255    self
256  }
257  /// Sets the DTS (consuming builder).
258  #[cfg_attr(not(tarpaulin), inline(always))]
259  #[must_use]
260  pub const fn with_dts(mut self, v: Option<Timestamp>) -> Self {
261    self.dts = v;
262    self
263  }
264  /// Sets the duration (consuming builder).
265  #[cfg_attr(not(tarpaulin), inline(always))]
266  #[must_use]
267  pub const fn with_duration(mut self, v: Option<Timestamp>) -> Self {
268    self.duration = v;
269    self
270  }
271  /// Sets the flags (consuming builder).
272  #[cfg_attr(not(tarpaulin), inline(always))]
273  #[must_use]
274  pub const fn with_flags(mut self, v: PacketFlags) -> Self {
275    self.flags = v;
276    self
277  }
278
279  /// Sets the PTS in place.
280  #[cfg_attr(not(tarpaulin), inline(always))]
281  pub const fn set_pts(&mut self, v: Option<Timestamp>) -> &mut Self {
282    self.pts = v;
283    self
284  }
285  /// Sets the DTS in place.
286  #[cfg_attr(not(tarpaulin), inline(always))]
287  pub const fn set_dts(&mut self, v: Option<Timestamp>) -> &mut Self {
288    self.dts = v;
289    self
290  }
291  /// Sets the duration in place.
292  #[cfg_attr(not(tarpaulin), inline(always))]
293  pub const fn set_duration(&mut self, v: Option<Timestamp>) -> &mut Self {
294    self.duration = v;
295    self
296  }
297  /// Sets the flags in place.
298  #[cfg_attr(not(tarpaulin), inline(always))]
299  pub const fn set_flags(&mut self, v: PacketFlags) -> &mut Self {
300    self.flags = v;
301    self
302  }
303}
304
305/// A compressed subtitle packet.
306pub struct SubtitlePacket<E, D> {
307  pts: Option<Timestamp>,
308  duration: Option<Timestamp>,
309  flags: PacketFlags,
310  data: D,
311  extra: E,
312}
313
314impl<E, D> SubtitlePacket<E, D> {
315  /// Constructs a `SubtitlePacket` from `data` and `extra`.
316  #[cfg_attr(not(tarpaulin), inline(always))]
317  pub const fn new(data: D, extra: E) -> Self {
318    Self {
319      pts: None,
320      duration: None,
321      flags: PacketFlags::empty(),
322      data,
323      extra,
324    }
325  }
326
327  /// Returns the presentation timestamp.
328  #[cfg_attr(not(tarpaulin), inline(always))]
329  pub const fn pts(&self) -> Option<Timestamp> {
330    self.pts
331  }
332  /// Returns the duration.
333  #[cfg_attr(not(tarpaulin), inline(always))]
334  pub const fn duration(&self) -> Option<Timestamp> {
335    self.duration
336  }
337  /// Returns the flags.
338  #[cfg_attr(not(tarpaulin), inline(always))]
339  pub const fn flags(&self) -> PacketFlags {
340    self.flags
341  }
342  /// Returns the compressed subtitle data.
343  #[cfg_attr(not(tarpaulin), inline(always))]
344  pub const fn data(&self) -> &D {
345    &self.data
346  }
347  /// Returns the backend extras.
348  #[cfg_attr(not(tarpaulin), inline(always))]
349  pub const fn extra(&self) -> &E {
350    &self.extra
351  }
352  /// Returns a mutable reference to the backend extras.
353  #[cfg_attr(not(tarpaulin), inline(always))]
354  pub fn extra_mut(&mut self) -> &mut E {
355    &mut self.extra
356  }
357  /// Consumes the packet and returns the buffer.
358  #[cfg_attr(not(tarpaulin), inline(always))]
359  pub fn into_data(self) -> D {
360    self.data
361  }
362  /// Consumes the packet and returns `(buffer, extras)`.
363  #[cfg_attr(not(tarpaulin), inline(always))]
364  pub fn into_parts(self) -> (D, E) {
365    (self.data, self.extra)
366  }
367
368  /// Sets the PTS (consuming builder).
369  #[cfg_attr(not(tarpaulin), inline(always))]
370  #[must_use]
371  pub const fn with_pts(mut self, v: Option<Timestamp>) -> Self {
372    self.pts = v;
373    self
374  }
375  /// Sets the duration (consuming builder).
376  #[cfg_attr(not(tarpaulin), inline(always))]
377  #[must_use]
378  pub const fn with_duration(mut self, v: Option<Timestamp>) -> Self {
379    self.duration = v;
380    self
381  }
382  /// Sets the flags (consuming builder).
383  #[cfg_attr(not(tarpaulin), inline(always))]
384  #[must_use]
385  pub const fn with_flags(mut self, v: PacketFlags) -> Self {
386    self.flags = v;
387    self
388  }
389
390  /// Sets the PTS in place.
391  #[cfg_attr(not(tarpaulin), inline(always))]
392  pub const fn set_pts(&mut self, v: Option<Timestamp>) -> &mut Self {
393    self.pts = v;
394    self
395  }
396  /// Sets the duration in place.
397  #[cfg_attr(not(tarpaulin), inline(always))]
398  pub const fn set_duration(&mut self, v: Option<Timestamp>) -> &mut Self {
399    self.duration = v;
400    self
401  }
402  /// Sets the flags in place.
403  #[cfg_attr(not(tarpaulin), inline(always))]
404  pub const fn set_flags(&mut self, v: PacketFlags) -> &mut Self {
405    self.flags = v;
406    self
407  }
408}
409
410// ---------------------------------------------------------------------------
411//  Optional trait matrices (`serde` / `arbitrary` / `quickcheck`) for
412//  `PacketFlags`. The packet types themselves are generic over a caller's
413//  buffer and extras and are not a wire vocabulary; the flag set is.
414// ---------------------------------------------------------------------------
415
416#[cfg(feature = "serde")]
417#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
418mod serde_impls {
419  //! The bit set travels as its number.
420  //!
421  //! This is the opposite of the choice `channel`'s two vocabularies
422  //! make, and for the opposite reason. There, a `u32` wire would let an
423  //! unrecognised code decode to `Unknown` — inventing a value — so the
424  //! name is the only faithful shape. Here every bit pattern *is* a
425  //! value, including the ones this build has no constant for
426  //! (`AV_PKT_FLAG_TRUSTED`, `AV_PKT_FLAG_DISPOSABLE`), so the number is
427  //! the only shape that carries them all. `from_bits_retain` is what
428  //! keeps that round trip lossless; `from_bits` would reject the very
429  //! bits the wire exists to preserve.
430  //!
431  //! `mediaframe::TrackDisposition` sits on this same wire, so a
432  //! consumer that stores both sees one convention.
433
434  use serde::{Deserialize, Deserializer, Serialize, Serializer};
435
436  use super::PacketFlags;
437
438  impl Serialize for PacketFlags {
439    #[cfg_attr(not(tarpaulin), inline(always))]
440    fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
441      ser.serialize_u8(self.bits())
442    }
443  }
444
445  impl<'de> Deserialize<'de> for PacketFlags {
446    #[cfg_attr(not(tarpaulin), inline(always))]
447    fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
448      u8::deserialize(de).map(Self::from_bits_retain)
449    }
450  }
451}
452
453#[cfg(feature = "arbitrary")]
454#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
455mod arbitrary_impls {
456  //! Uniform over `u8`, decoded with `from_bits_retain`.
457  //!
458  //! Again the opposite of `channel`'s roster draw, and again because a
459  //! bit set has no fallback variant to collapse into: every one of the
460  //! 256 patterns is a distinct value, each named bit is set in half of
461  //! them, and the unnamed bits — the ones a real FFmpeg packet does
462  //! carry — appear at the same rate. Choosing from a roster of the
463  //! three named flags would generate exactly the inputs that cannot go
464  //! wrong.
465
466  use arbitrary::{Arbitrary, Result, Unstructured};
467
468  use super::PacketFlags;
469
470  impl<'a> Arbitrary<'a> for PacketFlags {
471    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
472      Ok(Self::from_bits_retain(u8::arbitrary(u)?))
473    }
474  }
475}
476
477#[cfg(feature = "quickcheck")]
478#[cfg_attr(docsrs, doc(cfg(feature = "quickcheck")))]
479mod quickcheck_impls {
480  //! The `quickcheck` half of what `arbitrary_impls` gives, drawn the
481  //! same way and for the same reason.
482
483  use quickcheck::{Arbitrary, Gen};
484
485  use super::PacketFlags;
486
487  impl Arbitrary for PacketFlags {
488    fn arbitrary(g: &mut Gen) -> Self {
489      Self::from_bits_retain(u8::arbitrary(g))
490    }
491  }
492}
493
494#[cfg(test)]
495mod tests {
496  use super::*;
497
498  #[test]
499  fn flag_bits_are_stable() {
500    assert_eq!(PacketFlags::KEY.bits(), 0b001);
501    assert_eq!(PacketFlags::CORRUPT.bits(), 0b010);
502    assert_eq!(PacketFlags::DISCARD.bits(), 0b100);
503  }
504
505  #[test]
506  fn flags_combine() {
507    let f = PacketFlags::KEY | PacketFlags::CORRUPT;
508    assert!(f.contains(PacketFlags::KEY));
509    assert!(f.contains(PacketFlags::CORRUPT));
510    assert!(!f.contains(PacketFlags::DISCARD));
511  }
512
513  #[test]
514  fn empty_default() {
515    assert_eq!(PacketFlags::default(), PacketFlags::empty());
516  }
517
518  use crate::Timebase;
519  use core::num::NonZeroI32;
520
521  fn ms_tb() -> Timebase {
522    Timebase::new(1, NonZeroI32::new(1000).unwrap())
523  }
524
525  #[test]
526  fn video_packet_construct_and_access() {
527    let data: &[u8] = &[1, 2, 3];
528    let p: VideoPacket<_, &[u8]> = VideoPacket::new(data, ());
529    assert_eq!(p.pts(), None);
530    assert_eq!(p.flags(), PacketFlags::empty());
531    assert_eq!(*p.data(), data);
532  }
533
534  #[test]
535  fn video_packet_builders_chain() {
536    let pts = crate::Timestamp::new(1500, ms_tb());
537    let p: VideoPacket<_, &[u8]> = VideoPacket::new(&[][..], ())
538      .with_pts(Some(pts))
539      .with_flags(PacketFlags::KEY);
540    assert_eq!(p.pts(), Some(pts));
541    assert!(p.flags().contains(PacketFlags::KEY));
542  }
543
544  #[test]
545  fn video_packet_into_parts() {
546    let p: VideoPacket<_, &[u8]> = VideoPacket::new(&[1u8, 2][..], ());
547    let (data, _extra) = p.into_parts();
548    assert_eq!(data, &[1, 2]);
549  }
550
551  #[test]
552  fn audio_packet_round_trip() {
553    let data: &[u8] = &[7, 8, 9];
554    let p: AudioPacket<_, &[u8]> = AudioPacket::new(data, ()).with_flags(PacketFlags::KEY);
555    assert_eq!(*p.data(), data);
556    assert!(p.flags().contains(PacketFlags::KEY));
557    let (recovered, _) = p.into_parts();
558    assert_eq!(recovered, data);
559  }
560
561  #[test]
562  fn subtitle_packet_round_trip() {
563    let data: &[u8] = b"hi";
564    let p: SubtitlePacket<_, &[u8]> = SubtitlePacket::new(data, ());
565    assert_eq!(*p.data(), data);
566  }
567
568  // -------------------------------------------------------------------
569  //  Optional matrices (`serde` / `arbitrary` / `quickcheck`)
570  // -------------------------------------------------------------------
571
572  // The wire assertions need a real self-describing format, which needs
573  // an allocator; the impls themselves compile at every tier.
574  #[cfg(all(feature = "serde", any(feature = "alloc", feature = "std")))]
575  mod serde_tests {
576    // The crate root links `alloc` under the name `std` (the workspace's
577    // alloc-as-std alias), so `alloc` has to be named here to reach
578    // `ToString` — which the std prelude would otherwise have supplied.
579    // Both this and the `use` are valid under `std` too, hence no `cfg`:
580    // the enclosing module is already gated on `alloc` or `std`.
581    extern crate alloc;
582
583    use alloc::string::ToString;
584
585    use super::*;
586
587    #[test]
588    fn the_wire_is_a_number_not_a_flag_grammar() {
589      // The ruling this pins: a bit set reaches the wire as its bits.
590      // `bitflags`' own serde would have written `"KEY | CORRUPT"` here
591      // for a human-readable format, which is why that sub-feature is
592      // not the mechanism.
593      let flags = PacketFlags::KEY | PacketFlags::CORRUPT;
594      assert_eq!(
595        serde_json::to_string(&flags).expect("flags always serialize"),
596        "3"
597      );
598      assert_eq!(
599        serde_json::to_string(&PacketFlags::empty()).expect("flags always serialize"),
600        "0"
601      );
602    }
603
604    #[test]
605    fn a_name_is_not_a_number() {
606      assert!(serde_json::from_str::<PacketFlags>(r#""KEY""#).is_err());
607      assert!(serde_json::from_str::<PacketFlags>(r#""key|corrupt""#).is_err());
608    }
609
610    #[test]
611    fn every_bit_pattern_round_trips_including_the_unnamed_ones() {
612      // 0b0000_1000 and 0b0001_0000 are FFmpeg's TRUSTED / DISPOSABLE,
613      // which this set does not name. They still have to survive, which
614      // is what `from_bits_retain` buys and what `from_bits` would lose.
615      for bits in 0..=u8::MAX {
616        let flags = PacketFlags::from_bits_retain(bits);
617        let json = serde_json::to_string(&flags).expect("flags always serialize");
618        assert_eq!(json, bits.to_string());
619        assert_eq!(
620          serde_json::from_str::<PacketFlags>(&json).expect("its own output parses"),
621          flags,
622          "round-trip failed for {bits:#010b}"
623        );
624      }
625    }
626
627    #[test]
628    fn a_value_no_u8_can_hold_is_refused() {
629      assert!(serde_json::from_str::<PacketFlags>("256").is_err());
630      assert!(serde_json::from_str::<PacketFlags>("-1").is_err());
631    }
632  }
633
634  #[cfg(feature = "arbitrary")]
635  mod arbitrary_tests {
636    use arbitrary::{Arbitrary, Unstructured};
637
638    use super::*;
639
640    #[test]
641    fn every_bit_pattern_is_reachable() {
642      let mut seen = [false; 256];
643      for byte in 0..=u8::MAX {
644        let data = [byte];
645        let mut u = Unstructured::new(&data);
646        let flags = PacketFlags::arbitrary(&mut u).expect("the generator is total");
647        seen[flags.bits() as usize] = true;
648      }
649      assert!(
650        seen.iter().all(|&s| s),
651        "a bit pattern the generator never produces"
652      );
653    }
654  }
655
656  #[cfg(feature = "quickcheck")]
657  mod quickcheck_tests {
658    use quickcheck::{Arbitrary, Gen};
659
660    use super::*;
661
662    #[test]
663    fn the_named_flags_and_the_unnamed_bits_are_both_reachable() {
664      // 4000 draws over 256 patterns: missing a named flag here means
665      // the generator is skewed, not unlucky.
666      let mut g = Gen::new(16);
667      let mut union = PacketFlags::empty();
668      let mut saw_unnamed = false;
669      for _ in 0..4000 {
670        let flags = PacketFlags::arbitrary(&mut g);
671        union |= flags;
672        saw_unnamed |= !PacketFlags::all().contains(flags);
673      }
674      assert_eq!(
675        union,
676        PacketFlags::from_bits_retain(u8::MAX),
677        "a bit the generator never sets"
678      );
679      assert!(saw_unnamed, "an unnamed bit is never produced");
680    }
681  }
682}