1use std::io::{self, Write};
13
14use byteorder::{LittleEndian, WriteBytesExt};
15
16use super::{bitpack_read, bitpack_write, bits_needed_u64};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[repr(u8)]
23pub enum CodecType {
24 Constant = 0,
25 Bitpacked = 1,
26 Linear = 2,
27 BlockwiseLinear = 3,
28}
29
30impl CodecType {
31 pub fn from_u8(v: u8) -> Option<Self> {
32 match v {
33 0 => Some(Self::Constant),
34 1 => Some(Self::Bitpacked),
35 2 => Some(Self::Linear),
36 3 => Some(Self::BlockwiseLinear),
37 _ => None,
38 }
39 }
40}
41
42pub const BLOCKWISE_LINEAR_BLOCK_SIZE: usize = 512;
44
45pub fn validate_auto(data: &[u8], expected_values: usize) -> io::Result<()> {
49 let (&codec_id, rest) = data.split_first().ok_or_else(|| {
50 io::Error::new(io::ErrorKind::UnexpectedEof, "fast field codec is missing")
51 })?;
52
53 let packed_len = |count: usize, bpv: u8| -> io::Result<usize> {
54 if bpv > 64 {
55 return Err(io::Error::new(
56 io::ErrorKind::InvalidData,
57 "fast field bit width exceeds 64",
58 ));
59 }
60 count
61 .checked_mul(bpv as usize)
62 .and_then(|bits| bits.checked_add(7))
63 .map(|bits| bits / 8)
64 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "fast field size overflow"))
65 };
66
67 match CodecType::from_u8(codec_id) {
68 Some(CodecType::Constant) => {
69 if rest.len() != 8 {
70 return Err(io::Error::new(
71 io::ErrorKind::InvalidData,
72 "invalid constant fast field length",
73 ));
74 }
75 }
76 Some(CodecType::Bitpacked) => {
77 if rest.len() < 9 {
78 return Err(io::Error::new(
79 io::ErrorKind::UnexpectedEof,
80 "bitpacked fast field header is truncated",
81 ));
82 }
83 let expected_len = 9usize
84 .checked_add(packed_len(expected_values, rest[8])?)
85 .ok_or_else(|| {
86 io::Error::new(io::ErrorKind::InvalidData, "fast field size overflow")
87 })?;
88 if rest.len() != expected_len {
89 return Err(io::Error::new(
90 io::ErrorKind::InvalidData,
91 "bitpacked fast field length is inconsistent",
92 ));
93 }
94 }
95 Some(CodecType::Linear) => {
96 if rest.len() < 29 {
97 return Err(io::Error::new(
98 io::ErrorKind::UnexpectedEof,
99 "linear fast field header is truncated",
100 ));
101 }
102 let count = u32::from_le_bytes(rest[16..20].try_into().unwrap()) as usize;
103 if count != expected_values || count < 2 {
104 return Err(io::Error::new(
105 io::ErrorKind::InvalidData,
106 "linear fast field value count is inconsistent",
107 ));
108 }
109 let expected_len = 29usize
110 .checked_add(packed_len(count, rest[28])?)
111 .ok_or_else(|| {
112 io::Error::new(io::ErrorKind::InvalidData, "fast field size overflow")
113 })?;
114 if rest.len() != expected_len {
115 return Err(io::Error::new(
116 io::ErrorKind::InvalidData,
117 "linear fast field length is inconsistent",
118 ));
119 }
120 }
121 Some(CodecType::BlockwiseLinear) => {
122 if rest.len() < 8 {
123 return Err(io::Error::new(
124 io::ErrorKind::UnexpectedEof,
125 "blockwise fast field header is truncated",
126 ));
127 }
128 let count = u32::from_le_bytes(rest[0..4].try_into().unwrap()) as usize;
129 let num_blocks = u32::from_le_bytes(rest[4..8].try_into().unwrap()) as usize;
130 if count != expected_values || num_blocks != count.div_ceil(BLOCKWISE_LINEAR_BLOCK_SIZE)
131 {
132 return Err(io::Error::new(
133 io::ErrorKind::InvalidData,
134 "blockwise fast field counts are inconsistent",
135 ));
136 }
137
138 let mut pos = 8usize;
139 for block_idx in 0..num_blocks {
140 let header_end = pos.checked_add(29).ok_or_else(|| {
141 io::Error::new(io::ErrorKind::InvalidData, "fast field offset overflow")
142 })?;
143 if header_end > rest.len() {
144 return Err(io::Error::new(
145 io::ErrorKind::UnexpectedEof,
146 "blockwise fast field block header is truncated",
147 ));
148 }
149 let bpv = rest[pos + 24];
150 let declared =
151 u32::from_le_bytes(rest[pos + 25..header_end].try_into().unwrap()) as usize;
152 let block_start = block_idx * BLOCKWISE_LINEAR_BLOCK_SIZE;
153 let block_count = (count - block_start).min(BLOCKWISE_LINEAR_BLOCK_SIZE);
154 let expected = packed_len(block_count, bpv)?;
155 if declared != expected {
156 return Err(io::Error::new(
157 io::ErrorKind::InvalidData,
158 "blockwise fast field packed length is inconsistent",
159 ));
160 }
161 pos = header_end.checked_add(declared).ok_or_else(|| {
162 io::Error::new(io::ErrorKind::InvalidData, "fast field offset overflow")
163 })?;
164 if pos > rest.len() {
165 return Err(io::Error::new(
166 io::ErrorKind::UnexpectedEof,
167 "blockwise fast field data is truncated",
168 ));
169 }
170 }
171 if pos != rest.len() {
172 return Err(io::Error::new(
173 io::ErrorKind::InvalidData,
174 "blockwise fast field contains trailing data",
175 ));
176 }
177 }
178 None => {
179 return Err(io::Error::new(
180 io::ErrorKind::InvalidData,
181 "unknown fast field codec",
182 ));
183 }
184 }
185 Ok(())
186}
187
188pub trait CodecEstimator {
195 fn collect(&mut self, value: u64);
196 fn finalize(&mut self) {}
197 fn estimate(&self) -> Option<u64>;
198 fn serialize(&self, values: &[u64], writer: &mut dyn Write) -> io::Result<u64>;
199}
200
201#[derive(Default)]
205pub struct ConstantEstimator {
206 first: Option<u64>,
207 all_same: bool,
208}
209
210impl CodecEstimator for ConstantEstimator {
211 fn collect(&mut self, value: u64) {
212 match self.first {
213 None => {
214 self.first = Some(value);
215 self.all_same = true;
216 }
217 Some(f) => {
218 if value != f {
219 self.all_same = false;
220 }
221 }
222 }
223 }
224
225 fn estimate(&self) -> Option<u64> {
226 if self.all_same {
227 Some(9)
229 } else {
230 None
231 }
232 }
233
234 fn serialize(&self, values: &[u64], writer: &mut dyn Write) -> io::Result<u64> {
235 let val = if values.is_empty() { 0 } else { values[0] };
236 writer.write_u8(CodecType::Constant as u8)?;
237 writer.write_u64::<LittleEndian>(val)?;
238 Ok(9)
239 }
240}
241
242#[derive(Default)]
246pub struct BitpackedEstimator {
247 min: u64,
248 max: u64,
249 count: usize,
250 initialized: bool,
251}
252
253impl CodecEstimator for BitpackedEstimator {
254 fn collect(&mut self, value: u64) {
255 if !self.initialized {
256 self.min = value;
257 self.max = value;
258 self.initialized = true;
259 } else {
260 self.min = self.min.min(value);
261 self.max = self.max.max(value);
262 }
263 self.count += 1;
264 }
265
266 fn estimate(&self) -> Option<u64> {
267 if self.count == 0 {
268 return Some(0);
269 }
270 let range = self.max - self.min;
271 let bpv = bits_needed_u64(range) as u64;
272 let data_bits = self.count as u64 * bpv;
274 let data_bytes = data_bits.div_ceil(8);
275 Some(1 + 8 + 1 + data_bytes)
276 }
277
278 fn serialize(&self, values: &[u64], writer: &mut dyn Write) -> io::Result<u64> {
279 let (min_value, bpv) = if values.is_empty() {
280 (0u64, 0u8)
281 } else {
282 let min_val = values.iter().copied().min().unwrap();
283 let max_val = values.iter().copied().max().unwrap();
284 (min_val, bits_needed_u64(max_val - min_val))
285 };
286
287 writer.write_u8(CodecType::Bitpacked as u8)?;
288 writer.write_u64::<LittleEndian>(min_value)?;
289 writer.write_u8(bpv)?;
290 let mut bytes_written = 10u64; if bpv > 0 && !values.is_empty() {
293 let shifted: Vec<u64> = values.iter().map(|&v| v - min_value).collect();
294 let mut packed = Vec::new();
295 bitpack_write(&shifted, bpv, &mut packed);
296 writer.write_all(&packed)?;
297 bytes_written += packed.len() as u64;
298 }
299 Ok(bytes_written)
300 }
301}
302
303#[inline]
307pub fn bitpacked_read(data: &[u8], index: usize) -> u64 {
308 let min_value = u64::from_le_bytes(data[0..8].try_into().unwrap());
309 let bpv = data[8];
310 if bpv == 0 {
311 return min_value;
312 }
313 let packed = &data[9..];
314 bitpack_read(packed, bpv, index).wrapping_add(min_value)
315}
316
317#[derive(Default)]
331pub struct LinearEstimator {
332 count: usize,
333 first: u64,
334 last: u64,
335 min_val: u64,
336 max_val: u64,
337 min_residual: i64,
338 max_residual: i64,
339 values_collected: bool,
340 overflow: bool,
342}
343
344impl CodecEstimator for LinearEstimator {
345 fn collect(&mut self, value: u64) {
346 if !self.values_collected {
347 self.first = value;
348 self.min_val = value;
349 self.max_val = value;
350 self.values_collected = true;
351 } else {
352 self.min_val = self.min_val.min(value);
353 self.max_val = self.max_val.max(value);
354 }
355 self.last = value;
356 self.count += 1;
357 }
358
359 fn finalize(&mut self) {
360 if self.count < 2 {
361 return;
362 }
363 let pred_min = self.first.min(self.last) as i128;
371 let pred_max = self.first.max(self.last) as i128;
372 let min_res = self.min_val as i128 - pred_max;
373 let max_res = self.max_val as i128 - pred_min;
374 if min_res < i64::MIN as i128 || max_res > i64::MAX as i128 {
377 self.overflow = true;
378 return;
379 }
380 self.min_residual = min_res as i64;
381 self.max_residual = max_res as i64;
382 }
383
384 fn estimate(&self) -> Option<u64> {
385 if self.count < 2 || self.overflow {
386 return None;
387 }
388 let range = (self.max_residual as i128 - self.min_residual as i128) as u64;
390 let bpv = bits_needed_u64(range) as u64;
391 let data_bits = self.count as u64 * bpv;
392 let data_bytes = data_bits.div_ceil(8);
393 Some(1 + 8 + 8 + 4 + 8 + 1 + data_bytes)
395 }
396
397 fn serialize(&self, values: &[u64], writer: &mut dyn Write) -> io::Result<u64> {
398 let n = values.len();
399 if n < 2 {
400 return Err(io::Error::new(
401 io::ErrorKind::InvalidInput,
402 "linear needs ≥ 2 values",
403 ));
404 }
405 let first = values[0];
406 let last = values[n - 1];
407
408 let mut min_residual = i128::MAX;
410 for (i, &val) in values.iter().enumerate() {
411 let predicted = interpolate(first, last, n, i);
412 let residual = val as i128 - predicted as i128;
413 min_residual = min_residual.min(residual);
414 }
415
416 if min_residual < i64::MIN as i128 || min_residual > i64::MAX as i128 {
418 return Err(io::Error::new(
419 io::ErrorKind::InvalidInput,
420 "linear codec: residual offset exceeds i64 range",
421 ));
422 }
423 let min_residual_i64 = min_residual as i64;
424
425 let shifted: Vec<u64> = values
427 .iter()
428 .enumerate()
429 .map(|(i, &val)| {
430 let predicted = interpolate(first, last, n, i);
431 let residual = val as i128 - predicted as i128;
432 (residual - min_residual) as u64
433 })
434 .collect();
435 let max_shifted = shifted.iter().copied().max().unwrap_or(0);
436 let bpv = bits_needed_u64(max_shifted);
437 writer.write_u8(CodecType::Linear as u8)?;
438 writer.write_u64::<LittleEndian>(first)?;
439 writer.write_u64::<LittleEndian>(last)?;
440 writer.write_u32::<LittleEndian>(n as u32)?;
441 writer.write_i64::<LittleEndian>(min_residual_i64)?;
442 writer.write_u8(bpv)?;
443 let mut bytes_written = 30u64; if bpv > 0 {
446 let mut packed = Vec::new();
447 bitpack_write(&shifted, bpv, &mut packed);
448 writer.write_all(&packed)?;
449 bytes_written += packed.len() as u64;
450 }
451
452 Ok(bytes_written)
453 }
454}
455
456#[inline]
458fn interpolate(first: u64, last: u64, n: usize, i: usize) -> u64 {
459 if n <= 1 {
460 return first;
461 }
462 let first = first as i128;
464 let last = last as i128;
465 let n = n as i128;
466 let i = i as i128;
467 let result = first + (last - first) * i / (n - 1);
468 result as u64
469}
470
471#[inline]
475pub fn linear_read(data: &[u8], index: usize) -> u64 {
476 let first = u64::from_le_bytes(data[0..8].try_into().unwrap());
477 let last = u64::from_le_bytes(data[8..16].try_into().unwrap());
478 let n = u32::from_le_bytes(data[16..20].try_into().unwrap()) as usize;
479 let offset = i64::from_le_bytes(data[20..28].try_into().unwrap());
480 let bpv = data[28];
481 let predicted = interpolate(first, last, n, index);
482 let residual = if bpv == 0 {
483 0u64
484 } else {
485 bitpack_read(&data[29..], bpv, index)
486 };
487 (predicted as i128 + offset as i128 + residual as i128) as u64
489}
490
491#[derive(Default)]
498pub struct BlockwiseLinearEstimator {
499 values: Vec<u64>,
500}
501
502impl CodecEstimator for BlockwiseLinearEstimator {
503 fn collect(&mut self, value: u64) {
504 self.values.push(value);
505 }
506
507 fn estimate(&self) -> Option<u64> {
508 let n = self.values.len();
509 if n < 2 * BLOCKWISE_LINEAR_BLOCK_SIZE {
510 return None;
512 }
513
514 let num_blocks = n.div_ceil(BLOCKWISE_LINEAR_BLOCK_SIZE);
515 let mut total = 9u64; for b in 0..num_blocks {
519 let start = b * BLOCKWISE_LINEAR_BLOCK_SIZE;
520 let end = (start + BLOCKWISE_LINEAR_BLOCK_SIZE).min(n);
521 let block = &self.values[start..end];
522 let block_len = block.len();
523
524 if block_len < 2 {
525 total += 29; continue;
528 }
529
530 let first = block[0];
531 let last = block[block_len - 1];
532 let mut min_res = i128::MAX;
533 let mut max_res = i128::MIN;
534 for (i, &val) in block.iter().enumerate() {
535 let pred = interpolate(first, last, block_len, i);
536 let res = val as i128 - pred as i128;
537 min_res = min_res.min(res);
538 max_res = max_res.max(res);
539 }
540 if min_res < i64::MIN as i128 || max_res > i64::MAX as i128 {
543 return None;
544 }
545 let range = (max_res - min_res) as u64;
546 let bpv = bits_needed_u64(range) as u64;
547 let data_bits = block_len as u64 * bpv;
548 let data_bytes = data_bits.div_ceil(8);
549 total += 29 + data_bytes;
550 }
551
552 Some(total)
553 }
554
555 fn serialize(&self, values: &[u64], writer: &mut dyn Write) -> io::Result<u64> {
556 let n = values.len();
557 let num_blocks = n.div_ceil(BLOCKWISE_LINEAR_BLOCK_SIZE);
558
559 writer.write_u8(CodecType::BlockwiseLinear as u8)?;
560 writer.write_u32::<LittleEndian>(n as u32)?;
561 writer.write_u32::<LittleEndian>(num_blocks as u32)?;
562 let mut bytes_written = 9u64;
563
564 for b in 0..num_blocks {
565 let start = b * BLOCKWISE_LINEAR_BLOCK_SIZE;
566 let end = (start + BLOCKWISE_LINEAR_BLOCK_SIZE).min(n);
567 let block = &values[start..end];
568 let block_len = block.len();
569
570 let first = block[0];
571 let last = if block_len > 1 {
572 block[block_len - 1]
573 } else {
574 first
575 };
576
577 let mut min_residual = i128::MAX;
579 if block_len >= 2 {
580 for (i, &val) in block.iter().enumerate() {
581 let pred = interpolate(first, last, block_len, i);
582 let res = val as i128 - pred as i128;
583 min_residual = min_residual.min(res);
584 }
585 } else {
586 min_residual = 0;
587 }
588
589 let shifted: Vec<u64> = block
590 .iter()
591 .enumerate()
592 .map(|(i, &val)| {
593 if block_len < 2 {
594 return 0;
595 }
596 let pred = interpolate(first, last, block_len, i);
597 let res = val as i128 - pred as i128;
598 (res - min_residual) as u64
599 })
600 .collect();
601 let max_shifted = shifted.iter().copied().max().unwrap_or(0);
602 let bpv = bits_needed_u64(max_shifted);
603
604 if min_residual < i64::MIN as i128 || min_residual > i64::MAX as i128 {
606 return Err(io::Error::new(
607 io::ErrorKind::InvalidInput,
608 "blockwise linear codec: per-block residual offset exceeds i64 range",
609 ));
610 }
611 let min_res_i64 = min_residual as i64;
612 writer.write_u64::<LittleEndian>(first)?;
613 writer.write_u64::<LittleEndian>(last)?;
614 writer.write_i64::<LittleEndian>(min_res_i64)?;
615 writer.write_u8(bpv)?;
616
617 let mut packed = Vec::new();
618 if bpv > 0 {
619 bitpack_write(&shifted, bpv, &mut packed);
620 }
621 writer.write_u32::<LittleEndian>(packed.len() as u32)?;
622 writer.write_all(&packed)?;
623 bytes_written += 29 + packed.len() as u64;
624 }
625
626 Ok(bytes_written)
627 }
628}
629
630pub fn blockwise_linear_read(data: &[u8], index: usize) -> u64 {
634 let _num_values = u32::from_le_bytes(data[0..4].try_into().unwrap()) as usize;
635 let num_blocks = u32::from_le_bytes(data[4..8].try_into().unwrap()) as usize;
636
637 let target_block = index / BLOCKWISE_LINEAR_BLOCK_SIZE;
638 let index_in_block = index % BLOCKWISE_LINEAR_BLOCK_SIZE;
639
640 let mut pos = 8usize;
642 for b in 0..num_blocks {
643 let first = u64::from_le_bytes(data[pos..pos + 8].try_into().unwrap());
644 let last = u64::from_le_bytes(data[pos + 8..pos + 16].try_into().unwrap());
645 let offset = i64::from_le_bytes(data[pos + 16..pos + 24].try_into().unwrap());
646 let bpv = data[pos + 24];
647 let packed_len = u32::from_le_bytes(data[pos + 25..pos + 29].try_into().unwrap()) as usize;
648
649 if b == target_block {
650 let block_start = b * BLOCKWISE_LINEAR_BLOCK_SIZE;
651 let block_end = ((b + 1) * BLOCKWISE_LINEAR_BLOCK_SIZE).min(_num_values);
652 let block_len = block_end - block_start;
653
654 let predicted = interpolate(first, last, block_len, index_in_block);
655 let residual = if bpv == 0 {
656 0u64
657 } else {
658 bitpack_read(&data[pos + 29..], bpv, index_in_block)
659 };
660 return (predicted as i128 + offset as i128 + residual as i128) as u64;
661 }
662
663 pos += 29 + packed_len;
664 }
665
666 0 }
668
669pub fn serialize_auto(values: &[u64], writer: &mut dyn Write) -> io::Result<u64> {
675 let mut constant = ConstantEstimator::default();
676 let mut bitpacked = BitpackedEstimator::default();
677 let mut linear = LinearEstimator::default();
678 let mut blockwise = BlockwiseLinearEstimator::default();
679
680 for &v in values {
682 constant.collect(v);
683 bitpacked.collect(v);
684 linear.collect(v);
685 blockwise.collect(v);
686 }
687
688 constant.finalize();
690 bitpacked.finalize();
691 linear.finalize();
692 blockwise.finalize();
693
694 let candidates: Vec<(&dyn CodecEstimator, &str)> = vec![
696 (&constant, "constant"),
697 (&bitpacked, "bitpacked"),
698 (&linear, "linear"),
699 (&blockwise, "blockwise_linear"),
700 ];
701
702 let (best, _name) = candidates
703 .into_iter()
704 .filter_map(|(est, name)| est.estimate().map(|size| (est, name, size)))
705 .min_by_key(|&(_, _, size)| size)
706 .map(|(est, name, _)| (est, name))
707 .unwrap_or((&bitpacked as &dyn CodecEstimator, "bitpacked"));
708
709 best.serialize(values, writer)
710}
711
712pub fn bitpacked_read_batch(data: &[u8], start_index: usize, out: &mut [u64]) {
719 let min_value = u64::from_le_bytes(data[0..8].try_into().unwrap());
720 let bpv = data[8];
721
722 if bpv == 0 {
723 out.iter_mut().for_each(|v| *v = min_value);
724 return;
725 }
726
727 let packed = &data[9..];
728
729 match bpv {
730 8 => {
732 for (i, v) in out.iter_mut().enumerate() {
733 let idx = start_index + i;
734 *v = (packed[idx] as u64).wrapping_add(min_value);
735 }
736 }
737 16 => {
738 for (i, v) in out.iter_mut().enumerate() {
739 let idx = start_index + i;
740 let byte_off = idx * 2;
741 let raw = u16::from_le_bytes([packed[byte_off], packed[byte_off + 1]]);
742 *v = (raw as u64).wrapping_add(min_value);
743 }
744 }
745 32 => {
746 for (i, v) in out.iter_mut().enumerate() {
747 let idx = start_index + i;
748 let byte_off = idx * 4;
749 let raw = u32::from_le_bytes(packed[byte_off..byte_off + 4].try_into().unwrap());
750 *v = (raw as u64).wrapping_add(min_value);
751 }
752 }
753 64 => {
754 for (i, v) in out.iter_mut().enumerate() {
755 let idx = start_index + i;
756 let byte_off = idx * 8;
757 let raw = u64::from_le_bytes(packed[byte_off..byte_off + 8].try_into().unwrap());
758 *v = raw.wrapping_add(min_value);
759 }
760 }
761 _ => {
763 for (i, v) in out.iter_mut().enumerate() {
764 *v = super::bitpack_read(packed, bpv, start_index + i).wrapping_add(min_value);
765 }
766 }
767 }
768}
769
770pub fn auto_read_batch(data: &[u8], start_index: usize, out: &mut [u64]) {
775 if data.is_empty() || out.is_empty() {
776 out.iter_mut().for_each(|v| *v = 0);
777 return;
778 }
779 let codec_id = data[0];
780 let rest = &data[1..];
781 match CodecType::from_u8(codec_id) {
782 Some(CodecType::Constant) => {
783 let val = u64::from_le_bytes(rest[0..8].try_into().unwrap());
784 out.iter_mut().for_each(|v| *v = val);
785 }
786 Some(CodecType::Bitpacked) => bitpacked_read_batch(rest, start_index, out),
787 Some(CodecType::Linear) => {
788 for (i, v) in out.iter_mut().enumerate() {
789 *v = linear_read(rest, start_index + i);
790 }
791 }
792 Some(CodecType::BlockwiseLinear) => {
793 for (i, v) in out.iter_mut().enumerate() {
794 *v = blockwise_linear_read(rest, start_index + i);
795 }
796 }
797 None => out.iter_mut().for_each(|v| *v = 0),
798 }
799}
800
801#[inline]
805pub fn auto_read(data: &[u8], index: usize) -> u64 {
806 if data.is_empty() {
807 return 0;
808 }
809 let codec_id = data[0];
810 let rest = &data[1..];
811 match CodecType::from_u8(codec_id) {
812 Some(CodecType::Constant) => {
813 u64::from_le_bytes(rest[0..8].try_into().unwrap())
815 }
816 Some(CodecType::Bitpacked) => bitpacked_read(rest, index),
817 Some(CodecType::Linear) => linear_read(rest, index),
818 Some(CodecType::BlockwiseLinear) => blockwise_linear_read(rest, index),
819 None => 0,
820 }
821}
822
823#[cfg(test)]
826mod tests {
827 use super::*;
828
829 fn roundtrip(values: &[u64]) -> Vec<u64> {
830 let mut buf = Vec::new();
831 serialize_auto(values, &mut buf).unwrap();
832 (0..values.len()).map(|i| auto_read(&buf, i)).collect()
833 }
834
835 #[test]
836 fn test_constant_codec() {
837 let values: Vec<u64> = vec![42; 100];
838 let mut buf = Vec::new();
839 serialize_auto(&values, &mut buf).unwrap();
840 assert_eq!(buf[0], CodecType::Constant as u8);
841 assert_eq!(buf.len(), 9);
842 assert_eq!(roundtrip(&values), values);
843 }
844
845 #[test]
846 fn test_bitpacked_codec() {
847 let values: Vec<u64> = (0..50).map(|i| 1000 + (i % 7) * 13).collect();
848 let result = roundtrip(&values);
849 assert_eq!(result, values);
850 }
851
852 #[test]
853 fn test_linear_codec_sequential() {
854 let values: Vec<u64> = (0..1000).map(|i| 100 + i * 3).collect();
856 let mut buf = Vec::new();
857 serialize_auto(&values, &mut buf).unwrap();
858 assert_eq!(roundtrip(&values), values);
860 }
861
862 #[test]
863 fn test_blockwise_linear_codec() {
864 let mut values: Vec<u64> = Vec::new();
866 for i in 0..1500 {
867 if i < 750 {
868 values.push(100 + i * 2);
869 } else {
870 values.push(5000 + (i - 750) * 5);
871 }
872 }
873 let result = roundtrip(&values);
874 assert_eq!(result, values);
875 }
876
877 #[test]
878 fn test_empty() {
879 let values: Vec<u64> = vec![];
880 let mut buf = Vec::new();
881 serialize_auto(&values, &mut buf).unwrap();
882 assert!(buf.len() <= 10);
883 }
884
885 #[test]
886 fn test_validate_rejects_truncated_and_inconsistent_payloads() {
887 assert!(validate_auto(&[], 1).is_err());
888 assert!(validate_auto(&[CodecType::Constant as u8], 1).is_err());
889
890 let mut bitpacked = vec![CodecType::Bitpacked as u8];
891 bitpacked.extend_from_slice(&0u64.to_le_bytes());
892 bitpacked.push(65);
893 assert!(validate_auto(&bitpacked, 1).is_err());
894
895 let mut valid = Vec::new();
896 serialize_auto(&[1, 2, 3, 4], &mut valid).unwrap();
897 assert!(validate_auto(&valid, 4).is_ok());
898 valid.pop();
899 assert!(validate_auto(&valid, 4).is_err());
900 }
901
902 #[test]
903 fn test_single_value() {
904 let values = vec![999u64];
905 assert_eq!(roundtrip(&values), values);
906 }
907
908 #[test]
909 fn test_two_values() {
910 let values = vec![10u64, 20];
911 assert_eq!(roundtrip(&values), values);
912 }
913
914 #[test]
915 fn test_large_range() {
916 let values = vec![0u64, u64::MAX / 2, u64::MAX];
917 assert_eq!(roundtrip(&values), values);
918 }
919
920 #[test]
921 fn test_timestamps_pick_linear_or_blockwise() {
922 let mut values: Vec<u64> = Vec::new();
924 let mut ts = 1_700_000_000u64;
925 for _ in 0..2000 {
926 values.push(ts);
927 ts += 1000 + (ts % 7); }
929 let result = roundtrip(&values);
930 assert_eq!(result, values);
931 }
932
933 fn roundtrip_batch(values: &[u64]) {
935 let mut buf = Vec::new();
936 serialize_auto(values, &mut buf).unwrap();
937
938 let mut batch_out = vec![0u64; values.len()];
940 auto_read_batch(&buf, 0, &mut batch_out);
941 assert_eq!(batch_out, values, "batch read mismatch");
942
943 if values.len() >= 10 {
945 let start = 3;
946 let count = values.len() - 6;
947 let mut sub = vec![0u64; count];
948 auto_read_batch(&buf, start, &mut sub);
949 assert_eq!(
950 sub,
951 &values[start..start + count],
952 "sub-range batch mismatch"
953 );
954 }
955 }
956
957 #[test]
958 fn test_batch_read_constant() {
959 roundtrip_batch(&vec![42u64; 100]);
960 }
961
962 #[test]
963 fn test_batch_read_bitpacked_8bit() {
964 let values: Vec<u64> = (0..200).map(|i| 1000 + (i % 200)).collect();
966 roundtrip_batch(&values);
967 }
968
969 #[test]
970 fn test_batch_read_bitpacked_16bit() {
971 let values: Vec<u64> = (0..200).map(|i| 50000 + i * 100).collect();
973 roundtrip_batch(&values);
974 }
975
976 #[test]
977 fn test_batch_read_bitpacked_arbitrary() {
978 let values: Vec<u64> = (0..100).map(|i| 999 + (i * 37) % 8000).collect();
980 roundtrip_batch(&values);
981 }
982
983 #[test]
984 fn test_batch_read_linear() {
985 let values: Vec<u64> = (0..500).map(|i| 100 + i * 3).collect();
986 roundtrip_batch(&values);
987 }
988
989 #[test]
990 fn test_batch_read_blockwise() {
991 let mut values = Vec::new();
992 for i in 0..1500u64 {
993 values.push(if i < 750 {
994 100 + i * 2
995 } else {
996 5000 + (i - 750) * 5
997 });
998 }
999 roundtrip_batch(&values);
1000 }
1001
1002 #[test]
1006 fn test_zigzag_timestamps_with_missing() {
1007 use super::super::{FAST_FIELD_MISSING, zigzag_encode};
1008
1009 let timestamps: Vec<i64> = vec![
1011 1724630400, 1724716800, 1724803200, 1700000000, 1680000000, 1724630400, ];
1018
1019 let mut values = Vec::new();
1021 for (i, &ts) in timestamps.iter().enumerate() {
1022 values.push(zigzag_encode(ts));
1023 if i % 2 == 1 {
1025 values.push(FAST_FIELD_MISSING);
1026 }
1027 }
1028
1029 let result = roundtrip(&values);
1030 assert_eq!(
1031 result, values,
1032 "zigzag timestamps + missing roundtrip failed"
1033 );
1034 }
1035
1036 #[test]
1038 fn test_codecs_individually_with_zigzag_and_missing() {
1039 use super::super::{FAST_FIELD_MISSING, zigzag_encode};
1040
1041 let values: Vec<u64> = vec![
1042 zigzag_encode(1724630400), zigzag_encode(1700000000), FAST_FIELD_MISSING,
1045 zigzag_encode(1680000000), zigzag_encode(1724716800), FAST_FIELD_MISSING,
1048 zigzag_encode(1724630400), zigzag_encode(0), ];
1051
1052 {
1054 let mut est = BitpackedEstimator::default();
1055 for &v in &values {
1056 est.collect(v);
1057 }
1058 est.finalize();
1059 if est.estimate().is_some() {
1060 let mut buf = Vec::new();
1061 est.serialize(&values, &mut buf).unwrap();
1062 for (i, &expected) in values.iter().enumerate() {
1063 let got = auto_read(&buf, i);
1064 assert_eq!(
1065 got, expected,
1066 "bitpacked: index {} expected {} got {}",
1067 i, expected, got
1068 );
1069 }
1070 }
1071 }
1072
1073 {
1075 let mut est = LinearEstimator::default();
1076 for &v in &values {
1077 est.collect(v);
1078 }
1079 est.finalize();
1080 if est.estimate().is_some() {
1081 let mut buf = Vec::new();
1082 est.serialize(&values, &mut buf).unwrap();
1083 for (i, &expected) in values.iter().enumerate() {
1084 let got = auto_read(&buf, i);
1085 assert_eq!(
1086 got, expected,
1087 "linear: index {} expected {} got {}",
1088 i, expected, got
1089 );
1090 }
1091 }
1092 }
1093
1094 let result = roundtrip(&values);
1096 assert_eq!(result, values, "auto codec roundtrip failed");
1097 }
1098
1099 #[test]
1101 fn test_specific_issued_at_roundtrip() {
1102 use super::super::{FAST_FIELD_MISSING, zigzag_encode};
1103
1104 let mut values = Vec::new();
1106 let base_ts = 1724630400i64; for i in 0..100u64 {
1108 if i % 5 == 0 {
1109 values.push(FAST_FIELD_MISSING);
1111 } else {
1112 let ts = base_ts - (i as i64 * 86400); values.push(zigzag_encode(ts));
1115 }
1116 }
1117
1118 let result = roundtrip(&values);
1119 for (i, (&expected, &got)) in values.iter().zip(result.iter()).enumerate() {
1120 assert_eq!(
1121 got,
1122 expected,
1123 "doc {}: expected {} (zigzag of {}), got {}",
1124 i,
1125 expected,
1126 if expected == FAST_FIELD_MISSING {
1127 -1 } else {
1129 super::super::zigzag_decode(expected)
1130 },
1131 got
1132 );
1133 }
1134 }
1135
1136 #[test]
1139 fn test_large_scale_timestamp_roundtrip() {
1140 use super::super::{FAST_FIELD_MISSING, zigzag_encode};
1141
1142 for num_docs in [10_000, 50_000, 100_000] {
1143 let mut values = Vec::with_capacity(num_docs);
1144 let base_ts = 1724630400i64;
1145
1146 for i in 0..num_docs {
1147 if i % 7 == 0 {
1148 values.push(FAST_FIELD_MISSING);
1149 } else {
1150 let ts = base_ts - (i as i64 * 3600) + ((i as i64 * 37) % 1000);
1152 values.push(zigzag_encode(ts));
1153 }
1154 }
1155
1156 let mut buf = Vec::new();
1158 serialize_auto(&values, &mut buf).unwrap();
1159 let codec_id = buf[0];
1160 let codec_name = match CodecType::from_u8(codec_id) {
1161 Some(CodecType::Constant) => "constant",
1162 Some(CodecType::Bitpacked) => "bitpacked",
1163 Some(CodecType::Linear) => "linear",
1164 Some(CodecType::BlockwiseLinear) => "blockwise_linear",
1165 None => "unknown",
1166 };
1167
1168 let mut failures = Vec::new();
1170 for (i, &expected) in values.iter().enumerate() {
1171 let got = auto_read(&buf, i);
1172 if got != expected {
1173 failures.push((i, expected, got));
1174 if failures.len() >= 5 {
1175 break;
1176 }
1177 }
1178 }
1179
1180 assert!(
1181 failures.is_empty(),
1182 "num_docs={}, codec={}: {} failures. First 5: {:?}",
1183 num_docs,
1184 codec_name,
1185 failures.len(),
1186 failures
1187 );
1188 }
1189 }
1190
1191 #[test]
1195 fn test_blockwise_linear_with_clustered_missing() {
1196 use super::super::{FAST_FIELD_MISSING, zigzag_encode};
1197
1198 let mut values = Vec::new();
1203
1204 for i in 0..512 {
1207 if i < 100 {
1208 values.push(FAST_FIELD_MISSING);
1209 } else {
1210 let ts = 1724630400i64 + (i as i64 * 100);
1211 values.push(zigzag_encode(ts));
1212 }
1213 }
1214
1215 for i in 512..3072 {
1217 let ts = 1724630400i64 + (i as i64 * 100);
1218 values.push(zigzag_encode(ts));
1219 }
1220
1221 let result = roundtrip(&values);
1222 let mut failures = Vec::new();
1223 for (i, (&expected, &got)) in values.iter().zip(result.iter()).enumerate() {
1224 if got != expected {
1225 failures.push((i, expected, got));
1226 }
1227 }
1228 assert!(
1229 failures.is_empty(),
1230 "blockwise linear with clustered missing: {} failures. First 5: {:?}",
1231 failures.len(),
1232 &failures[..failures.len().min(5)]
1233 );
1234 }
1235
1236 #[test]
1239 fn test_forced_codecs_with_timestamps_and_missing() {
1240 use super::super::{FAST_FIELD_MISSING, zigzag_encode};
1241
1242 let mut values = Vec::new();
1243 let base_ts = 1724630400i64;
1244 for i in 0..200 {
1245 if i % 5 == 0 {
1246 values.push(FAST_FIELD_MISSING);
1247 } else {
1248 let ts = base_ts - (i as i64 * 86400);
1249 values.push(zigzag_encode(ts));
1250 }
1251 }
1252
1253 {
1255 let est = BitpackedEstimator::default();
1256 let mut buf = Vec::new();
1257 est.serialize(&values, &mut buf).unwrap();
1258 for (i, &expected) in values.iter().enumerate() {
1259 let got = bitpacked_read(&buf[1..], i); assert_eq!(got, expected, "forced bitpacked: index {} failed", i);
1261 }
1262 }
1263
1264 {
1267 let est = LinearEstimator::default();
1268 let mut buf = Vec::new();
1269 let result = est.serialize(&values, &mut buf);
1270 assert!(
1271 result.is_err(),
1272 "linear codec should reject data with residuals exceeding i64"
1273 );
1274 }
1275
1276 {
1278 let safe_values: Vec<u64> = values
1279 .iter()
1280 .filter(|&&v| v != FAST_FIELD_MISSING)
1281 .copied()
1282 .collect();
1283 let est = LinearEstimator::default();
1284 let mut buf = Vec::new();
1285 est.serialize(&safe_values, &mut buf).unwrap();
1286 for (i, &expected) in safe_values.iter().enumerate() {
1287 let got = linear_read(&buf[1..], i);
1288 assert_eq!(got, expected, "forced linear (safe): index {} failed", i);
1289 }
1290 }
1291
1292 {
1294 let mut large_values = Vec::new();
1295 for i in 0..2000 {
1296 if i % 5 == 0 {
1297 large_values.push(FAST_FIELD_MISSING);
1298 } else {
1299 let ts = base_ts - (i as i64 * 86400);
1300 large_values.push(zigzag_encode(ts));
1301 }
1302 }
1303 let mut est = BlockwiseLinearEstimator::default();
1304 for &v in &large_values {
1305 est.collect(v);
1306 }
1307 assert!(
1308 est.estimate().is_none(),
1309 "blockwise linear should reject data with per-block residuals exceeding i64"
1310 );
1311 }
1312 }
1313}