1use std::mem::size_of;
6
7use crate::bounds::{checked_add, checked_capacity, checked_mul, decoded_len, encode_input_len};
8use crate::error::CodecError;
9
10use super::block::{decode_block, encode_block};
11use super::header::{GLOBAL_HEADER_SIZE, expected_block_count, parse_header};
12
13pub fn encode(values: &[i64]) -> Result<Vec<u8>, CodecError> {
15 let total_bytes = checked_mul(values.len(), size_of::<i64>(), "FastLanes input bytes")?;
16 decoded_len(total_bytes, "FastLanes input")?;
17 let total_count = encode_input_len(values.len(), "FastLanes value count")?;
18 let block_count = values.len().div_ceil(super::header::BLOCK_SIZE);
19 let block_count = u16::try_from(block_count).map_err(|_| CodecError::ResourceLimit {
20 resource: "FastLanes block count".into(),
21 requested: block_count,
22 limit: u16::MAX as usize,
23 })?;
24 let estimated = checked_add(
25 GLOBAL_HEADER_SIZE,
26 checked_mul(values.len(), 5, "FastLanes output estimate")?,
27 "FastLanes output estimate",
28 )?;
29 let mut out = Vec::with_capacity(estimated);
30 out.extend_from_slice(&total_count.to_le_bytes());
31 out.extend_from_slice(&block_count.to_le_bytes());
32 for chunk in values.chunks(super::header::BLOCK_SIZE) {
33 encode_block(chunk, &mut out)?;
34 }
35 Ok(out)
36}
37
38pub fn decode(data: &[u8]) -> Result<Vec<i64>, CodecError> {
40 if data.len() < GLOBAL_HEADER_SIZE {
41 return Err(CodecError::Truncated {
42 expected: GLOBAL_HEADER_SIZE,
43 actual: data.len(),
44 });
45 }
46
47 let (total_count, block_count) = parse_header(data)?;
48 if total_count == 0 {
49 if data.len() != GLOBAL_HEADER_SIZE {
50 return Err(CodecError::Corrupt {
51 detail: "trailing bytes after empty FastLanes frame".into(),
52 });
53 }
54 return Ok(Vec::new());
55 }
56
57 let value_capacity = checked_capacity(total_count, size_of::<i64>(), "FastLanes values")?;
58 let mut values = Vec::with_capacity(value_capacity);
59 let mut offset = GLOBAL_HEADER_SIZE;
60 for block_idx in 0..block_count {
61 offset = decode_block(
62 data,
63 offset,
64 &mut values,
65 block_idx,
66 expected_block_count(total_count, block_idx)?,
67 )?;
68 }
69 if offset != data.len() {
70 return Err(CodecError::Corrupt {
71 detail: "trailing bytes after FastLanes frame".into(),
72 });
73 }
74 if values.len() != total_count {
75 return Err(CodecError::Corrupt {
76 detail: format!(
77 "value count mismatch: header says {total_count}, decoded {}",
78 values.len()
79 ),
80 });
81 }
82
83 Ok(values)
84}
85
86#[cfg(test)]
87mod tests {
88 use super::super::bits;
89 use super::super::block;
90 use super::super::iterator::BlockIterator;
91 use super::super::range::{
92 block_byte_offsets, block_count, decode_block_range, decode_single_block,
93 };
94 use super::*;
95 use crate::fastlanes::bit_width_for_range;
96
97 fn encode(values: &[i64]) -> Vec<u8> {
98 super::encode(values).expect("test FastLanes encode")
99 }
100
101 #[test]
102 fn empty_roundtrip() {
103 let encoded = encode(&[]);
104 let decoded = decode(&encoded).unwrap();
105 assert!(decoded.is_empty());
106 }
107
108 #[test]
109 fn single_value() {
110 let encoded = encode(&[42i64]);
111 let decoded = decode(&encoded).unwrap();
112 assert_eq!(decoded, vec![42i64]);
113 }
114
115 #[test]
116 fn identical_values_zero_bits() {
117 let values = vec![999i64; 1024];
118 let encoded = encode(&values);
119 let decoded = decode(&encoded).unwrap();
120 assert_eq!(decoded, values);
121
122 assert_eq!(encoded.len(), 17);
125 }
126
127 #[test]
128 fn small_range_values() {
129 let values: Vec<i64> = (0..1024).map(|i| 100 + (i % 8)).collect();
131 let encoded = encode(&values);
132 let decoded = decode(&encoded).unwrap();
133 assert_eq!(decoded, values);
134
135 let expected_packed = (1024usize * 3).div_ceil(8); let expected_total = GLOBAL_HEADER_SIZE + block::BLOCK_HEADER_SIZE + expected_packed;
138 assert_eq!(encoded.len(), expected_total);
139 }
140
141 #[test]
142 fn constant_rate_timestamps() {
143 let values: Vec<i64> = (0..10_000)
144 .map(|i| 1_700_000_000_000 + i * 10_000)
145 .collect();
146 let encoded = encode(&values);
147 let decoded = decode(&encoded).unwrap();
148 assert_eq!(decoded, values);
149
150 let bytes_per_sample = encoded.len() as f64 / values.len() as f64;
151 assert!(
152 bytes_per_sample < 4.0,
153 "timestamps should pack to <4 bytes/sample, got {bytes_per_sample:.2}"
154 );
155 }
156
157 #[test]
158 fn pre_delta_timestamps() {
159 let deltas: Vec<i64> = vec![10_000i64; 10_000];
160 let encoded = encode(&deltas);
161 let decoded = decode(&encoded).unwrap();
162 assert_eq!(decoded, deltas);
163
164 let bytes_per_sample = encoded.len() as f64 / deltas.len() as f64;
165 assert!(
166 bytes_per_sample < 0.2,
167 "constant deltas should pack to near-zero, got {bytes_per_sample:.2}"
168 );
169 }
170
171 #[test]
172 fn pre_delta_timestamps_with_jitter() {
173 let mut deltas = Vec::with_capacity(10_000);
174 let mut rng: u64 = 42;
175 for _ in 0..10_000 {
176 rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
177 let jitter = ((rng >> 33) as i64 % 101) - 50;
178 deltas.push(10_000 + jitter);
179 }
180 let encoded = encode(&deltas);
181 let decoded = decode(&encoded).unwrap();
182 assert_eq!(decoded, deltas);
183
184 let bytes_per_sample = encoded.len() as f64 / deltas.len() as f64;
185 assert!(
186 bytes_per_sample < 1.5,
187 "jittered deltas should pack to <1.5 bytes/sample, got {bytes_per_sample:.2}"
188 );
189 }
190
191 #[test]
192 fn negative_values() {
193 let values: Vec<i64> = (-500..500).collect();
194 let encoded = encode(&values);
195 let decoded = decode(&encoded).unwrap();
196 assert_eq!(decoded, values);
197 }
198
199 #[test]
200 fn boundary_values() {
201 let values = vec![i64::MIN, 0, i64::MAX];
202 let encoded = encode(&values);
203 let decoded = decode(&encoded).unwrap();
204 assert_eq!(decoded, values);
205 }
206
207 #[test]
208 fn multiple_blocks() {
209 let values: Vec<i64> = (0..3000).map(|i| i * 7 + 100).collect();
210 let encoded = encode(&values);
211 let decoded = decode(&encoded).unwrap();
212 assert_eq!(decoded, values);
213 }
214
215 #[test]
216 fn partial_last_block() {
217 let values: Vec<i64> = (0..1025).collect();
218 let encoded = encode(&values);
219 let decoded = decode(&encoded).unwrap();
220 assert_eq!(decoded, values);
221 }
222
223 #[test]
224 fn compression_vs_raw() {
225 let values: Vec<i64> = (0..10_000)
226 .map(|i| 1_700_000_000_000 + i * 10_000)
227 .collect();
228 let encoded = encode(&values);
229 let raw_size = values.len() * 8;
230 let ratio = raw_size as f64 / encoded.len() as f64;
231 assert!(ratio > 2.0, "expected >2x compression, got {ratio:.1}x");
232 }
233
234 #[test]
235 fn bit_width_calculation() {
236 assert_eq!(bit_width_for_range(0, 0), 0);
237 assert_eq!(bit_width_for_range(100, 100), 0);
238 assert_eq!(bit_width_for_range(0, 1), 1);
239 assert_eq!(bit_width_for_range(0, 7), 3);
240 assert_eq!(bit_width_for_range(0, 8), 4);
241 assert_eq!(bit_width_for_range(0, 255), 8);
242 assert_eq!(bit_width_for_range(0, 256), 9);
243 assert_eq!(bit_width_for_range(i64::MIN, i64::MAX), 64);
244 }
245
246 #[test]
247 fn pack_unpack_roundtrip() {
248 for bw in 1..=64u8 {
249 let max_val: u64 = if bw == 64 { u64::MAX } else { (1u64 << bw) - 1 };
250 let test_vals = [0u64, 1, max_val / 2, max_val];
251 for &val in &test_vals {
252 let mut packed = vec![0u8; 16];
253 bits::pack_bits(&mut packed, 0, val, bw);
254 let unpacked = bits::unpack_bits(&packed, 0, bw);
255 let mask = if bw == 64 { u64::MAX } else { (1u64 << bw) - 1 };
256 assert_eq!(
257 unpacked & mask,
258 val & mask,
259 "pack/unpack failed for bw={bw}, val={val}"
260 );
261 }
262 }
263 }
264
265 #[test]
266 fn pack_unpack_at_offsets() {
267 let mut packed = vec![0u8; 32];
268 bits::pack_bits(&mut packed, 0, 0b101, 3);
269 bits::pack_bits(&mut packed, 3, 0b110, 3);
270 bits::pack_bits(&mut packed, 6, 0b011, 3);
271
272 assert_eq!(bits::unpack_bits(&packed, 0, 3), 0b101);
273 assert_eq!(bits::unpack_bits(&packed, 3, 3), 0b110);
274 assert_eq!(bits::unpack_bits(&packed, 6, 3), 0b011);
275 }
276
277 #[test]
278 fn truncated_input_errors() {
279 assert!(decode(&[]).is_err());
280 assert!(decode(&[1, 0, 0, 0, 1, 0]).is_err()); }
282
283 #[test]
284 fn large_dataset_roundtrip() {
285 let mut values = Vec::with_capacity(100_000);
286 let mut rng: u64 = 12345;
287 for _ in 0..100_000 {
288 rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
289 values.push((rng >> 1) as i64);
290 }
291 let encoded = encode(&values);
292 let decoded = decode(&encoded).unwrap();
293 assert_eq!(decoded, values);
294 }
295
296 #[test]
297 fn decode_single_block_correctness() {
298 let values: Vec<i64> = (0..3000).collect();
299 let encoded = encode(&values);
300 assert_eq!(block_count(&encoded).unwrap(), 3);
301
302 let b0 = decode_single_block(&encoded, 0).unwrap();
303 assert_eq!(b0.len(), 1024);
304 assert_eq!(b0, &values[..1024]);
305
306 let b1 = decode_single_block(&encoded, 1).unwrap();
307 assert_eq!(b1.len(), 1024);
308 assert_eq!(b1, &values[1024..2048]);
309
310 let b2 = decode_single_block(&encoded, 2).unwrap();
311 assert_eq!(b2.len(), 952);
312 assert_eq!(b2, &values[2048..]);
313 }
314
315 #[test]
316 fn block_iterator_matches_full_decode() {
317 let values: Vec<i64> = (0..5000).map(|i| i * 7 - 2000).collect();
318 let encoded = encode(&values);
319
320 let mut all = Vec::new();
321 let iter = BlockIterator::new(&encoded).unwrap();
322 for blk in iter {
323 all.extend(blk.unwrap());
324 }
325 assert_eq!(all, values);
326 }
327
328 #[test]
329 fn block_iterator_skip() {
330 let values: Vec<i64> = (0..3000).collect();
331 let encoded = encode(&values);
332
333 let mut iter = BlockIterator::new(&encoded).unwrap();
334 iter.skip_block().unwrap(); let b1 = iter.next().unwrap().unwrap();
336 assert_eq!(b1, &values[1024..2048]);
337 }
338
339 #[test]
340 fn hostile_counts_and_block_shapes_fail_before_allocation_or_looping() {
341 let mut huge = vec![0; GLOBAL_HEADER_SIZE];
342 huge[..4].copy_from_slice(&u32::MAX.to_le_bytes());
343 huge[4..].copy_from_slice(&u16::MAX.to_le_bytes());
344 assert!(matches!(
345 decode(&huge),
346 Err(CodecError::ResourceLimit { .. })
347 ));
348
349 let mut mismatched = vec![0; GLOBAL_HEADER_SIZE];
350 mismatched[..4].copy_from_slice(&1u32.to_le_bytes());
351 assert!(matches!(
352 decode(&mismatched),
353 Err(CodecError::Corrupt { .. })
354 ));
355
356 let mut bad_block = encode(&[1, 2]);
357 bad_block[GLOBAL_HEADER_SIZE..GLOBAL_HEADER_SIZE + 2].copy_from_slice(&1u16.to_le_bytes());
358 assert!(matches!(
359 decode(&bad_block),
360 Err(CodecError::Corrupt { .. })
361 ));
362 }
363
364 #[test]
365 fn nonzero_final_padding_is_rejected_by_all_offset_scans() {
366 let mut encoded = encode(&[1, 2]);
367 *encoded.last_mut().expect("packed byte") |= 0x80;
368 assert!(matches!(decode(&encoded), Err(CodecError::Corrupt { .. })));
369 assert!(matches!(
370 block_byte_offsets(&encoded),
371 Err(CodecError::Corrupt { .. })
372 ));
373 assert!(matches!(
374 decode_block_range(&encoded, 0, 1),
375 Err(CodecError::Corrupt { .. })
376 ));
377 assert!(matches!(
378 decode_single_block(&encoded, 0),
379 Err(CodecError::Corrupt { .. })
380 ));
381 assert!(matches!(
382 BlockIterator::new(&encoded),
383 Err(CodecError::Corrupt { .. })
384 ));
385 }
386
387 #[test]
388 fn truncated_packed_block_is_rejected_by_all_offset_scans() {
389 let mut encoded = encode(&[1, 2]);
390 encoded.pop();
391 assert!(matches!(
392 decode(&encoded),
393 Err(CodecError::Truncated { .. })
394 ));
395 assert!(matches!(
396 block_byte_offsets(&encoded),
397 Err(CodecError::Truncated { .. })
398 ));
399 assert!(matches!(
400 decode_single_block(&encoded, 0),
401 Err(CodecError::Truncated { .. })
402 ));
403 }
404}