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 a_column_with_nulls_round_trips_and_keeps_its_null_count() {
210 let column = vec![Some(10), None, Some(20), None, Some(30)];
211 let map = survives(&column);
212 assert_eq!(map.observed().nulls, 2);
213 assert_eq!(map.observed().rows, 3);
214 }
215
216 #[test]
217 fn a_column_of_one_key_round_trips() {
218 survives(&keys(&[42]));
219 }
220
221 #[test]
222 fn negative_keys_round_trip_because_the_base_is_an_i128() {
223 survives(&keys(&[i128::MIN + 1, i128::MIN + 9, i128::MIN + 4]));
227 }
228
229 #[test]
230 fn an_empty_key_map_round_trips_and_resolves_nothing() {
231 let built = KeyMap::build(&[]).expect("build");
232 let payload = encode(&built, INTEGER).expect("encode");
233 let (read, _) = decode(&payload.bytes).expect("decode");
234 assert!(read.is_empty());
235 assert_eq!(read.observed().min, None, "an empty map has no minimum, not a minimum of zero");
236 assert_eq!(read.lookup(0).expect("lookup"), None);
237 }
238
239 #[test]
240 fn a_non_distinct_column_carries_that_fact_through_the_round_trip() {
241 let built = KeyMap::build(&keys(&[5, 7, 5, 9])).expect("build");
245 assert!(!built.observed().distinct);
246 let payload = encode(&built, INTEGER).expect("encode");
247 let (read, _) = decode(&payload.bytes).expect("decode");
248 assert!(!read.observed().distinct);
249 assert!(!read.observed().usable_as_parent());
250 }
251
252 #[test]
253 fn a_payload_shorter_than_its_header_is_refused() {
254 let built = KeyMap::build(&keys(&[1, 2, 3])).expect("build");
255 let payload = encode(&built, INTEGER).expect("encode");
256 for cut in [0, 1, HEADER_BYTES - 1] {
257 assert!(decode(&payload.bytes[..cut]).is_err(), "a payload of {cut} bytes is refused");
258 }
259 }
260
261 #[test]
262 fn a_form_this_build_does_not_know_is_refused_rather_than_guessed() {
263 let built = KeyMap::build(&keys(&[1, 2, 3])).expect("build");
264 let mut payload = encode(&built, INTEGER).expect("encode");
265 payload.bytes[32] = 9;
266 let error = decode(&payload.bytes).expect_err("refused");
267 assert!(error.to_string().contains("form 9"), "{error}");
268 }
269
270 #[test]
271 fn a_layout_this_build_does_not_know_is_refused() {
272 let built = KeyMap::build(&keys(&[1, 2, 3])).expect("build");
273 let mut payload = encode(&built, INTEGER).expect("encode");
274 payload.bytes[36] = LAYOUT + 1;
275 let error = decode(&payload.bytes).expect_err("refused");
276 assert!(error.to_string().contains("layout"), "{error}");
277 }
278
279 #[test]
280 fn a_flag_byte_that_is_neither_zero_nor_one_is_refused() {
281 let built = KeyMap::build(&keys(&[1, 2, 3])).expect("build");
284 let mut payload = encode(&built, INTEGER).expect("encode");
285 payload.bytes[34] = 2;
286 assert!(decode(&payload.bytes).is_err(), "a torn distinct flag is refused");
287
288 let mut payload = encode(&built, INTEGER).expect("encode");
289 payload.bytes[35] = 0xff;
290 assert!(decode(&payload.bytes).is_err(), "a torn sorted flag is refused");
291 }
292
293 #[test]
294 fn a_truncated_body_is_refused_rather_than_read_past() {
295 for column in [
296 keys(&(0..2000).map(|value| value * 2).collect::<Vec<i128>>()),
297 keys(&[500, 3, 9000, 12, 7, 88, 41, 6]),
298 ] {
299 let built = KeyMap::build(&column).expect("build");
300 let payload = encode(&built, INTEGER).expect("encode");
301 let short = &payload.bytes[..payload.bytes.len() - 1];
302 assert!(decode(short).is_err(), "a truncated {:?} body is refused", built.form());
303 }
304 }
305
306 #[test]
307 fn a_body_where_the_header_expects_none_is_refused() {
308 let built = KeyMap::build(&keys(&(1..=10).collect::<Vec<i128>>())).expect("build");
311 let mut payload = encode(&built, INTEGER).expect("encode");
312 assert_eq!(payload.bytes.len(), HEADER_BYTES);
313 payload.bytes.push(0);
314 assert!(decode(&payload.bytes).is_err());
315 }
316}