1use rudb_common::{Error, Result};
23
24use crate::keymap::{Form, KeyMap, Observed};
25
26pub const HEADER_BYTES: usize = 40;
30
31const LAYOUT: u8 = 1;
38
39#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Payload {
42 pub flags: u32,
47 pub header_bytes: u32,
49 pub bytes: Vec<u8>,
51}
52
53pub fn encode(map: &KeyMap, type_tag: u8) -> Result<Payload> {
66 let observed = *map.observed();
67 let mut bytes = Vec::with_capacity(HEADER_BYTES + map.bytes());
68 bytes.extend_from_slice(&map.base().to_le_bytes());
69 bytes.extend_from_slice(&observed.rows.to_le_bytes());
70 bytes.extend_from_slice(&observed.nulls.to_le_bytes());
71 bytes.push(map.form().tag());
72 bytes.push(type_tag);
73 bytes.push(u8::from(observed.distinct));
74 bytes.push(u8::from(observed.sorted));
75 bytes.push(LAYOUT);
76 bytes.extend_from_slice(&[0; 3]);
77 debug_assert_eq!(bytes.len(), HEADER_BYTES, "the key map header is forty bytes");
78 map.write_body(&mut bytes)?;
79 Ok(Payload {
80 flags: u32::from(map.form().tag()),
81 header_bytes: u32::try_from(HEADER_BYTES).map_err(|_| malformed("header overflow"))?,
82 bytes,
83 })
84}
85
86pub fn decode(bytes: &[u8]) -> Result<(KeyMap, u8)> {
95 if bytes.len() < HEADER_BYTES {
96 return Err(malformed("a key map payload is shorter than its header"));
97 }
98 let base = i128::from_le_bytes(bytes[0..16].try_into().map_err(|_| torn())?);
99 let rows = u64::from_le_bytes(bytes[16..24].try_into().map_err(|_| torn())?);
100 let nulls = u64::from_le_bytes(bytes[24..32].try_into().map_err(|_| torn())?);
101 let form = Form::from_tag(bytes[32])?;
102 let type_tag = bytes[33];
103 let distinct = flag(bytes[34])?;
104 let sorted = flag(bytes[35])?;
105 if bytes[36] != LAYOUT {
106 return Err(malformed(format!("key map layout {} is not one this build knows", bytes[36])));
107 }
108 let observed = Observed {
109 rows,
110 nulls,
111 distinct,
112 sorted,
113 min: (rows > 0).then_some(base),
114 max: None,
117 };
118 let map = KeyMap::read_body(form, base, observed, &bytes[HEADER_BYTES..])?;
119 Ok((map, type_tag))
120}
121
122fn flag(byte: u8) -> Result<bool> {
128 match byte {
129 0 => Ok(false),
130 1 => Ok(true),
131 _ => Err(malformed("a flag byte in a key map header is neither zero nor one")),
132 }
133}
134
135fn torn() -> Error {
136 malformed("a key map header is torn")
137}
138
139fn malformed(message: impl Into<String>) -> Error {
140 Error::invalid_input(format!("invalid rudb key map payload: {}", message.into()))
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 const INTEGER: u8 = 4;
150
151 fn keys(values: &[i128]) -> Vec<Option<i128>> {
152 values.iter().copied().map(Some).collect()
153 }
154
155 fn survives(column: &[Option<i128>]) -> KeyMap {
161 let built = KeyMap::build(column).expect("build");
162 let payload = encode(&built, INTEGER).expect("encode");
163 assert_eq!(payload.header_bytes as usize, HEADER_BYTES);
164 assert_eq!(payload.flags, u32::from(built.form().tag()));
165 let (read, type_tag) = decode(&payload.bytes).expect("decode");
166 assert_eq!(type_tag, INTEGER, "the type tag is carried, not interpreted");
167 assert_eq!(read.form(), built.form(), "the form survives");
168 assert_eq!(read.observed(), built.observed(), "the observed facts survive");
169 for (rid, key) in column.iter().enumerate() {
170 let Some(key) = *key else { continue };
171 assert_eq!(
172 read.lookup(key).expect("lookup"),
173 Some(rid as u64),
174 "key {key} did not survive the round trip"
175 );
176 }
177 read
178 }
179
180 #[test]
181 fn the_identity_form_round_trips_and_is_header_only() {
182 let map = survives(&keys(&(1..=1000).collect::<Vec<i128>>()));
183 assert_eq!(map.form(), Form::Identity);
184 let payload = encode(&map, INTEGER).expect("encode");
185 assert_eq!(payload.bytes.len(), HEADER_BYTES, "section 3.3: no extents beyond the header");
186 }
187
188 #[test]
189 fn the_dense_form_round_trips_with_its_rank_index() {
190 let map = survives(&keys(&(0..20_000).map(|value| value * 2).collect::<Vec<i128>>()));
194 assert_eq!(map.form(), Form::Dense);
195 let payload = encode(&map, INTEGER).expect("encode");
196 let bitmap = 40_000 / 8;
197 let body = payload.bytes.len() - HEADER_BYTES;
198 assert!(body > bitmap, "the body is {body} bytes and the bitmap alone is {bitmap}");
199 assert!(body < bitmap * 5 / 4, "the index costs about an eighth, not {body} over {bitmap}");
200 }
201
202 #[test]
203 fn the_sorted_form_round_trips_with_both_of_its_bit_packed_arrays() {
204 let map = survives(&keys(&[500, 3, 9000, 12, 7, 88, 41, 6]));
205 assert_eq!(map.form(), Form::Sorted);
206 }
207
208 #[test]
209 fn the_permuted_form_round_trips_with_its_bitmap_index_and_rids() {
210 let column: Vec<Option<i128>> =
213 (0..5_000_i128).map(|at| Some((at * 7_919 % 5_000) * 3)).collect();
214 let map = survives(&column);
215 assert_eq!(map.form(), Form::Permuted);
216 assert_eq!(map.lookup(1).expect("lookup"), None);
217 }
218
219 #[test]
220 fn a_column_with_nulls_round_trips_and_keeps_its_null_count() {
221 let column = vec![Some(10), None, Some(20), None, Some(30)];
222 let map = survives(&column);
223 assert_eq!(map.observed().nulls, 2);
224 assert_eq!(map.observed().rows, 3);
225 }
226
227 #[test]
228 fn a_column_of_one_key_round_trips() {
229 survives(&keys(&[42]));
230 }
231
232 #[test]
233 fn negative_keys_round_trip_because_the_base_is_an_i128() {
234 survives(&keys(&[i128::MIN + 1, i128::MIN + 9, i128::MIN + 4]));
238 }
239
240 #[test]
241 fn an_empty_key_map_round_trips_and_resolves_nothing() {
242 let built = KeyMap::build(&[]).expect("build");
243 let payload = encode(&built, INTEGER).expect("encode");
244 let (read, _) = decode(&payload.bytes).expect("decode");
245 assert!(read.is_empty());
246 assert_eq!(read.observed().min, None, "an empty map has no minimum, not a minimum of zero");
247 assert_eq!(read.lookup(0).expect("lookup"), None);
248 }
249
250 #[test]
251 fn a_non_distinct_column_carries_that_fact_through_the_round_trip() {
252 let built = KeyMap::build(&keys(&[5, 7, 5, 9])).expect("build");
256 assert!(!built.observed().distinct);
257 let payload = encode(&built, INTEGER).expect("encode");
258 let (read, _) = decode(&payload.bytes).expect("decode");
259 assert!(!read.observed().distinct);
260 assert!(!read.observed().usable_as_parent());
261 }
262
263 #[test]
264 fn a_payload_shorter_than_its_header_is_refused() {
265 let built = KeyMap::build(&keys(&[1, 2, 3])).expect("build");
266 let payload = encode(&built, INTEGER).expect("encode");
267 for cut in [0, 1, HEADER_BYTES - 1] {
268 assert!(decode(&payload.bytes[..cut]).is_err(), "a payload of {cut} bytes is refused");
269 }
270 }
271
272 #[test]
273 fn a_form_this_build_does_not_know_is_refused_rather_than_guessed() {
274 let built = KeyMap::build(&keys(&[1, 2, 3])).expect("build");
275 let mut payload = encode(&built, INTEGER).expect("encode");
276 payload.bytes[32] = 9;
277 let error = decode(&payload.bytes).expect_err("refused");
278 assert!(error.to_string().contains("form 9"), "{error}");
279 }
280
281 #[test]
282 fn a_layout_this_build_does_not_know_is_refused() {
283 let built = KeyMap::build(&keys(&[1, 2, 3])).expect("build");
284 let mut payload = encode(&built, INTEGER).expect("encode");
285 payload.bytes[36] = LAYOUT + 1;
286 let error = decode(&payload.bytes).expect_err("refused");
287 assert!(error.to_string().contains("layout"), "{error}");
288 }
289
290 #[test]
291 fn a_flag_byte_that_is_neither_zero_nor_one_is_refused() {
292 let built = KeyMap::build(&keys(&[1, 2, 3])).expect("build");
295 let mut payload = encode(&built, INTEGER).expect("encode");
296 payload.bytes[34] = 2;
297 assert!(decode(&payload.bytes).is_err(), "a torn distinct flag is refused");
298
299 let mut payload = encode(&built, INTEGER).expect("encode");
300 payload.bytes[35] = 0xff;
301 assert!(decode(&payload.bytes).is_err(), "a torn sorted flag is refused");
302 }
303
304 #[test]
305 fn a_truncated_body_is_refused_rather_than_read_past() {
306 for column in [
307 keys(&(0..2000).map(|value| value * 2).collect::<Vec<i128>>()),
308 keys(&[500, 3, 9000, 12, 7, 88, 41, 6]),
309 ] {
310 let built = KeyMap::build(&column).expect("build");
311 let payload = encode(&built, INTEGER).expect("encode");
312 let short = &payload.bytes[..payload.bytes.len() - 1];
313 assert!(decode(short).is_err(), "a truncated {:?} body is refused", built.form());
314 }
315 }
316
317 #[test]
318 fn a_body_where_the_header_expects_none_is_refused() {
319 let built = KeyMap::build(&keys(&(1..=10).collect::<Vec<i128>>())).expect("build");
322 let mut payload = encode(&built, INTEGER).expect("encode");
323 assert_eq!(payload.bytes.len(), HEADER_BYTES);
324 payload.bytes.push(0);
325 assert!(decode(&payload.bytes).is_err());
326 }
327}