lilliput_core/header/
seq.rs1#[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#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
10#[derive(Copy, Clone, Eq, PartialEq, Debug)]
11pub enum SeqHeader {
12 Compact(CompactSeqHeader),
14 Extended(ExtendedSeqHeader),
16}
17
18impl SeqHeader {
19 #[inline]
21 pub fn compact(len: u8) -> Self {
22 assert!(len <= Self::COMPACT_LEN_BITS);
23
24 Self::compact_unchecked(len)
25 }
26
27 #[inline]
29 pub fn compact_unchecked(len: u8) -> Self {
30 Self::Compact(CompactSeqHeader { len })
31 }
32
33 #[inline]
35 pub fn extended(len: usize) -> Self {
36 Self::Extended(ExtendedSeqHeader { len })
37 }
38
39 #[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 pub fn is_empty(&self) -> bool {
51 self.len() == 0
52 }
53
54 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#[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 pub fn is_empty(&self) -> bool {
87 self.len() == 0
88 }
89
90 pub fn len(&self) -> u8 {
92 self.len
93 }
94}
95
96#[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 pub fn is_empty(&self) -> bool {
111 self.len() == 0
112 }
113
114 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}