lilliput_core/header/
seq.rs

1#[cfg(any(test, feature = "testing"))]
2use proptest::prelude::*;
3#[cfg(any(test, feature = "testing"))]
4use proptest_derive::Arbitrary;
5
6use crate::config::PackingMode;
7
8/// Header representing a sequence of values.
9#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
10#[derive(Copy, Clone, Eq, PartialEq, Debug)]
11pub enum SeqHeader {
12    /// Compact header.
13    Compact(CompactSeqHeader),
14    /// Extended header.
15    Extended(ExtendedSeqHeader),
16}
17
18impl SeqHeader {
19    /// Creates a compact header.
20    #[inline]
21    pub fn compact(len: u8) -> Self {
22        assert!(len <= Self::COMPACT_LEN_BITS);
23
24        Self::compact_unchecked(len)
25    }
26
27    /// Creates a compact header, without checking invariants.
28    #[inline]
29    pub fn compact_unchecked(len: u8) -> Self {
30        Self::Compact(CompactSeqHeader { len })
31    }
32
33    /// Creates an extended header.
34    #[inline]
35    pub fn extended(len: usize) -> Self {
36        Self::Extended(ExtendedSeqHeader { len })
37    }
38
39    /// Creates a header for a given sequence's length, for a given `packing_mode`.
40    #[inline]
41    pub fn for_len(len: usize, packing_mode: PackingMode) -> Self {
42        if let Some(len) = Self::as_compact_len(len, packing_mode) {
43            Self::compact_unchecked(len)
44        } else {
45            Self::extended(len)
46        }
47    }
48
49    /// Returns `true` if the associated value has a length of zero, otherwise `false`.
50    pub fn is_empty(&self) -> bool {
51        self.len() == 0
52    }
53
54    /// Returns the associated value's length.
55    pub fn len(&self) -> usize {
56        match self {
57            Self::Compact(compact) => compact.len().into(),
58            Self::Extended(extended) => extended.len(),
59        }
60    }
61
62    #[inline]
63    fn as_compact_len(len: usize, packing_mode: PackingMode) -> Option<u8> {
64        if packing_mode.is_optimal() && len <= (Self::COMPACT_MAX_LEN as usize) {
65            Some(len as u8)
66        } else {
67            None
68        }
69    }
70}
71
72/// Compact header representing a sequence of values.
73#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
74#[derive(Copy, Clone, Eq, PartialEq, Debug)]
75#[repr(transparent)]
76pub struct CompactSeqHeader {
77    #[cfg_attr(
78        any(test, feature = "testing"),
79        proptest(strategy = "(0..=SeqHeader::COMPACT_MAX_LEN)")
80    )]
81    pub(crate) len: u8,
82}
83
84impl CompactSeqHeader {
85    /// Returns `true` if the associated value has a length of zero, otherwise `false`.
86    pub fn is_empty(&self) -> bool {
87        self.len() == 0
88    }
89
90    /// Returns the associated value's length.
91    pub fn len(&self) -> u8 {
92        self.len
93    }
94}
95
96/// Extended header representing a sequence of values.
97#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
98#[derive(Copy, Clone, Eq, PartialEq, Debug)]
99#[repr(transparent)]
100pub struct ExtendedSeqHeader {
101    #[cfg_attr(
102        any(test, feature = "testing"),
103        proptest(strategy = "super::arbitrary_len()")
104    )]
105    pub(crate) len: usize,
106}
107
108impl ExtendedSeqHeader {
109    /// Returns `true` if the associated value has a length of zero, otherwise `false`.
110    pub fn is_empty(&self) -> bool {
111        self.len() == 0
112    }
113
114    /// Returns the associated value's length.
115    pub fn len(&self) -> usize {
116        self.len
117    }
118}
119
120impl SeqHeader {
121    pub(crate) const MASK: u8 = 0b00111111;
122    pub(crate) const TYPE_BITS: u8 = 0b00100000;
123
124    pub(crate) const COMPACT_VARIANT_BIT: u8 = 0b00010000;
125    pub(crate) const COMPACT_LEN_BITS: u8 = 0b00000111;
126    pub(crate) const EXTENDED_LEN_WIDTH_BITS: u8 = 0b00000111;
127
128    pub(crate) const COMPACT_MAX_LEN: u8 = Self::COMPACT_LEN_BITS;
129}
130#[cfg(test)]
131mod tests {
132    use proptest::prelude::*;
133    use test_log::test;
134
135    use crate::{
136        config::EncoderConfig,
137        decoder::Decoder,
138        encoder::Encoder,
139        io::{SliceReader, VecWriter},
140    };
141
142    use super::*;
143
144    proptest! {
145        #[test]
146        fn as_compact_len(len in usize::arbitrary(), packing_mode in PackingMode::arbitrary()) {
147            let compact_len = SeqHeader::as_compact_len(len, packing_mode);
148            let is_optimal = packing_mode == PackingMode::Optimal;
149            let can_be_compact = len <= (SeqHeader::COMPACT_MAX_LEN as usize);
150
151            if is_optimal && can_be_compact {
152                prop_assert_eq!(compact_len, Some(len as u8));
153            } else {
154                prop_assert_eq!(compact_len, None);
155            }
156        }
157
158        #[test]
159        fn for_len(len in usize::arbitrary(), packing_mode in PackingMode::arbitrary()) {
160            let header = SeqHeader::for_len(len, packing_mode);
161
162            match packing_mode {
163                PackingMode::None => {
164                    prop_assert!(matches!(header, SeqHeader::Extended(_)));
165                    prop_assert!(header.len() == len);
166                },
167                PackingMode::Native => {
168                    prop_assert!(matches!(header, SeqHeader::Extended(_)));
169                },
170                PackingMode::Optimal => {
171                    if len <= (SeqHeader::COMPACT_MAX_LEN as usize) {
172                        prop_assert!(matches!(header, SeqHeader::Compact(_)));
173                    } else {
174                        prop_assert!(matches!(header, SeqHeader::Extended(_)));
175                    }
176                },
177            }
178        }
179
180        #[test]
181        fn encode_decode_roundtrip(header in SeqHeader::arbitrary(), config in EncoderConfig::arbitrary()) {
182            let mut encoded: Vec<u8> = Vec::new();
183            let writer = VecWriter::new(&mut encoded);
184            let mut encoder = Encoder::new(writer, config);
185            encoder.encode_seq_header(&header).unwrap();
186
187            prop_assert!(encoded.len() <= 1 + 8);
188
189            let reader = SliceReader::new(&encoded);
190            let mut decoder = Decoder::from_reader(reader);
191            let decoded = decoder.decode_seq_header().unwrap();
192            prop_assert_eq!(&decoded, &header);
193        }
194    }
195}