lilliput_core/header/
map.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 MapHeader {
12 Compact(CompactMapHeader),
14 Extended(ExtendedMapHeader),
16}
17
18impl MapHeader {
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(CompactMapHeader { len })
31 }
32
33 #[inline]
35 pub fn extended(len: usize) -> Self {
36 Self::Extended(ExtendedMapHeader { 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 CompactMapHeader {
77 #[cfg_attr(
78 any(test, feature = "testing"),
79 proptest(strategy = "(0..=MapHeader::COMPACT_MAX_LEN)")
80 )]
81 pub(crate) len: u8,
82}
83
84impl CompactMapHeader {
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 ExtendedMapHeader {
101 #[cfg_attr(
102 any(test, feature = "testing"),
103 proptest(strategy = "super::arbitrary_len()")
104 )]
105 pub(crate) len: usize,
106}
107
108impl ExtendedMapHeader {
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 MapHeader {
121 pub(crate) const MASK: u8 = 0b00011111;
122 pub(crate) const TYPE_BITS: u8 = 0b00010000;
123
124 pub(crate) const COMPACT_VARIANT_BIT: u8 = 0b00001000;
125 pub(crate) const COMPACT_LEN_BITS: u8 = 0b00000111;
126
127 pub(crate) const EXTENDED_LEN_WIDTH_BITS: u8 = 0b00000111;
128
129 pub(crate) const COMPACT_MAX_LEN: u8 = Self::COMPACT_LEN_BITS;
130}
131
132#[cfg(test)]
133mod tests {
134 use proptest::prelude::*;
135 use test_log::test;
136
137 use crate::{
138 config::EncoderConfig,
139 decoder::Decoder,
140 encoder::Encoder,
141 io::{SliceReader, VecWriter},
142 };
143
144 use super::*;
145
146 proptest! {
147 #[test]
148 fn as_compact_len(len in usize::arbitrary(), packing_mode in PackingMode::arbitrary()) {
149 let compact_len = MapHeader::as_compact_len(len, packing_mode);
150
151 if packing_mode.is_optimal() && len <= (MapHeader::COMPACT_MAX_LEN as usize) {
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 = MapHeader::for_len(len, packing_mode);
161
162 match packing_mode {
163 PackingMode::None => {
164 prop_assert!(matches!(header, MapHeader::Extended(_)));
165 prop_assert!(header.len() == len);
166 },
167 PackingMode::Native => {
168 prop_assert!(matches!(header, MapHeader::Extended(_)));
169 },
170 PackingMode::Optimal => {
171 if len <= (MapHeader::COMPACT_MAX_LEN as usize) {
172 prop_assert!(matches!(header, MapHeader::Compact(_)));
173 } else {
174 prop_assert!(matches!(header, MapHeader::Extended(_)));
175 }
176 },
177 }
178 }
179
180 #[test]
181 fn encode_decode_roundtrip(header in MapHeader::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_map_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_map_header().unwrap();
192 prop_assert_eq!(&decoded, &header);
193 }
194 }
195}