1use crate::coding::{
17 bits_needed, checked_count, checked_decode_count, pack_bits, unpack_bits, varint_decode,
18 varint_encode, zigzag_decode, zigzag_encode,
19};
20use crate::error::{EncodingError, Result};
21
22#[derive(Debug, Clone, Copy)]
24pub struct IntegerEncoder;
25
26#[derive(Debug, Clone, Copy)]
28pub struct IntegerDecoder;
29
30impl IntegerEncoder {
31 pub fn encode_i64(values: &[i64]) -> Result<Vec<u8>> {
37 if values.is_empty() {
38 return Err(EncodingError::EmptyInput {
39 context: "integer encoder (i64)",
40 });
41 }
42
43 let mut zigzag_deltas = Vec::with_capacity(values.len().saturating_sub(1));
45 let mut max_zz: u64 = 0;
46
47 for i in 1..values.len() {
48 let delta = values[i].wrapping_sub(values[i - 1]);
49 let zz = zigzag_encode(delta);
50 max_zz = max_zz.max(zz);
51 zigzag_deltas.push(zz);
52 }
53
54 let bit_width = bits_needed(max_zz);
55
56 let count = checked_count(values.len())?;
58 let header_size = 4 + 8 + 1;
59 let data_bits = zigzag_deltas.len() as u64 * u64::from(bit_width);
60 let data_bytes = data_bits.div_ceil(8) as usize;
61 let mut buf = Vec::with_capacity(header_size + data_bytes);
62
63 buf.extend_from_slice(&count.to_le_bytes());
65 buf.extend_from_slice(&values[0].to_le_bytes());
67 buf.push(bit_width);
69
70 if bit_width == 0 {
71 return Ok(buf);
73 }
74
75 pack_bits(&zigzag_deltas, bit_width, &mut buf);
77
78 Ok(buf)
79 }
80
81 pub fn encode_u64(values: &[u64]) -> Result<Vec<u8>> {
87 if values.is_empty() {
88 return Err(EncodingError::EmptyInput {
89 context: "integer encoder (u64)",
90 });
91 }
92
93 let i64_values: Vec<i64> = values.iter().map(|&v| v as i64).collect();
95 Self::encode_i64(&i64_values)
96 }
97}
98
99impl IntegerDecoder {
100 pub fn decode_i64(data: &[u8]) -> Result<Vec<i64>> {
106 if data.len() < 13 {
107 return Err(EncodingError::CorruptData {
108 detail: "integer data too short for header".to_string(),
109 });
110 }
111
112 let count = checked_decode_count(
113 u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize,
114 "integer",
115 )?;
116 let first_value = i64::from_le_bytes([
117 data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11],
118 ]);
119 let bit_width = data[12];
120
121 if bit_width > 64 {
122 return Err(EncodingError::CorruptData {
123 detail: format!("invalid bit_width {bit_width}, max is 64"),
124 });
125 }
126
127 if count == 0 {
128 return Ok(Vec::new());
129 }
130
131 let mut result = Vec::with_capacity(count);
132 result.push(first_value);
133
134 if count == 1 || bit_width == 0 {
135 for _ in 1..count {
137 result.push(first_value);
138 }
139 return Ok(result);
140 }
141
142 let packed_data = &data[13..];
144 let deltas = unpack_bits(packed_data, count - 1, bit_width)?;
145
146 let mut decoded_deltas = Vec::new();
148 crate::simd::batch_zigzag_decode(&deltas, &mut decoded_deltas);
149 crate::simd::batch_prefix_sum_i64(first_value, &decoded_deltas, &mut result);
150
151 Ok(result)
152 }
153
154 pub fn decode_u64(data: &[u8]) -> Result<Vec<u64>> {
160 let i64_values = Self::decode_i64(data)?;
161 Ok(i64_values.into_iter().map(|v| v as u64).collect())
162 }
163}
164
165#[derive(Debug, Clone, Copy)]
183pub struct VarintEncoder;
184
185#[derive(Debug, Clone, Copy)]
187pub struct VarintDecoder;
188
189impl VarintEncoder {
190 pub fn encode_i64(values: &[i64]) -> Result<Vec<u8>> {
196 if values.is_empty() {
197 return Err(EncodingError::EmptyInput {
198 context: "varint encoder (i64)",
199 });
200 }
201
202 let count = checked_count(values.len())?;
203
204 let mut buf = Vec::with_capacity(12 + values.len() * 2);
206 buf.extend_from_slice(&count.to_le_bytes());
207 buf.extend_from_slice(&values[0].to_le_bytes());
208
209 for i in 1..values.len() {
210 let delta = values[i].wrapping_sub(values[i - 1]);
211 let zz = zigzag_encode(delta);
212 varint_encode(zz, &mut buf);
213 }
214
215 Ok(buf)
216 }
217
218 pub fn encode_u64(values: &[u64]) -> Result<Vec<u8>> {
224 if values.is_empty() {
225 return Err(EncodingError::EmptyInput {
226 context: "varint encoder (u64)",
227 });
228 }
229 let i64_values: Vec<i64> = values.iter().map(|&v| v as i64).collect();
230 Self::encode_i64(&i64_values)
231 }
232}
233
234impl VarintDecoder {
235 pub fn decode_i64(data: &[u8]) -> Result<Vec<i64>> {
241 if data.len() < 12 {
242 return Err(EncodingError::CorruptData {
243 detail: "varint data too short for header".to_string(),
244 });
245 }
246
247 let count = checked_decode_count(
248 u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize,
249 "integer",
250 )?;
251 let first_value = i64::from_le_bytes([
252 data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11],
253 ]);
254
255 if count == 0 {
256 return Ok(Vec::new());
257 }
258
259 let mut result = Vec::with_capacity(count);
260 result.push(first_value);
261
262 let mut pos = 12;
263 for _ in 1..count {
264 if pos >= data.len() {
265 return Err(EncodingError::CorruptData {
266 detail: "unexpected end of varint-encoded data".to_string(),
267 });
268 }
269 let (zz, consumed) = varint_decode(&data[pos..])?;
270 pos += consumed;
271 let delta = zigzag_decode(zz);
272 let prev = result[result.len() - 1];
273 result.push(prev.wrapping_add(delta));
274 }
275
276 Ok(result)
277 }
278
279 pub fn decode_u64(data: &[u8]) -> Result<Vec<u64>> {
285 let i64_values = Self::decode_i64(data)?;
286 Ok(i64_values.into_iter().map(|v| v as u64).collect())
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn roundtrip_monotonic_i64() {
296 let values: Vec<i64> = (0..1000).collect();
297 let encoded = IntegerEncoder::encode_i64(&values).unwrap();
298 let decoded = IntegerDecoder::decode_i64(&encoded).unwrap();
299 assert_eq!(values, decoded);
300 }
301
302 #[test]
303 fn roundtrip_constant_i64() {
304 let values = vec![42_i64; 100];
305 let encoded = IntegerEncoder::encode_i64(&values).unwrap();
306 let decoded = IntegerDecoder::decode_i64(&encoded).unwrap();
307 assert_eq!(values, decoded);
308 assert_eq!(encoded.len(), 13);
310 }
311
312 #[test]
313 fn roundtrip_negative_i64() {
314 let values = vec![-100, -50, -10, 0, 10, 50, 100];
315 let encoded = IntegerEncoder::encode_i64(&values).unwrap();
316 let decoded = IntegerDecoder::decode_i64(&encoded).unwrap();
317 assert_eq!(values, decoded);
318 }
319
320 #[test]
321 fn roundtrip_edge_i64() {
322 let values = vec![i64::MIN, 0, i64::MAX];
323 let encoded = IntegerEncoder::encode_i64(&values).unwrap();
324 let decoded = IntegerDecoder::decode_i64(&encoded).unwrap();
325 assert_eq!(values, decoded);
326 }
327
328 #[test]
329 fn roundtrip_single_i64() {
330 let values = vec![42_i64];
331 let encoded = IntegerEncoder::encode_i64(&values).unwrap();
332 let decoded = IntegerDecoder::decode_i64(&encoded).unwrap();
333 assert_eq!(values, decoded);
334 }
335
336 #[test]
337 fn roundtrip_u64() {
338 let values: Vec<u64> = vec![0, 1, 100, u64::MAX / 2, u64::MAX];
339 let encoded = IntegerEncoder::encode_u64(&values).unwrap();
340 let decoded = IntegerDecoder::decode_u64(&encoded).unwrap();
341 assert_eq!(values, decoded);
342 }
343
344 #[test]
345 fn empty_error() {
346 assert!(IntegerEncoder::encode_i64(&[]).is_err());
347 assert!(IntegerEncoder::encode_u64(&[]).is_err());
348 }
349
350 #[test]
351 fn compression_ratio_monotonic() {
352 let values: Vec<i64> = (0..10_000).collect();
353 let raw_size = values.len() * 8;
354 let encoded = IntegerEncoder::encode_i64(&values).unwrap();
355 #[allow(clippy::cast_precision_loss)]
356 let ratio = raw_size as f64 / encoded.len() as f64;
357 assert!(
358 ratio > 5.0,
359 "Expected ratio > 5x on monotonic data, got {ratio:.1}x"
360 );
361 }
362
363 #[test]
364 fn corrupt_bit_width() {
365 let mut data = vec![0u8; 13];
367 data[0..4].copy_from_slice(&2u32.to_le_bytes()); data[4..12].copy_from_slice(&42i64.to_le_bytes()); data[12] = 255; assert!(IntegerDecoder::decode_i64(&data).is_err());
371 }
372
373 #[test]
376 fn varint_roundtrip_monotonic_i64() {
377 let values: Vec<i64> = (0..1000).collect();
378 let encoded = VarintEncoder::encode_i64(&values).unwrap();
379 let decoded = VarintDecoder::decode_i64(&encoded).unwrap();
380 assert_eq!(values, decoded);
381 }
382
383 #[test]
384 fn varint_roundtrip_constant_i64() {
385 let values = vec![42_i64; 100];
386 let encoded = VarintEncoder::encode_i64(&values).unwrap();
387 let decoded = VarintDecoder::decode_i64(&encoded).unwrap();
388 assert_eq!(values, decoded);
389 assert_eq!(encoded.len(), 12 + 99);
392 }
393
394 #[test]
395 fn varint_roundtrip_negative_i64() {
396 let values = vec![-100, -50, -10, 0, 10, 50, 100];
397 let encoded = VarintEncoder::encode_i64(&values).unwrap();
398 let decoded = VarintDecoder::decode_i64(&encoded).unwrap();
399 assert_eq!(values, decoded);
400 }
401
402 #[test]
403 fn varint_roundtrip_edge_i64() {
404 let values = vec![i64::MIN, 0, i64::MAX];
405 let encoded = VarintEncoder::encode_i64(&values).unwrap();
406 let decoded = VarintDecoder::decode_i64(&encoded).unwrap();
407 assert_eq!(values, decoded);
408 }
409
410 #[test]
411 fn varint_roundtrip_single_i64() {
412 let values = vec![42_i64];
413 let encoded = VarintEncoder::encode_i64(&values).unwrap();
414 let decoded = VarintDecoder::decode_i64(&encoded).unwrap();
415 assert_eq!(values, decoded);
416 }
417
418 #[test]
419 fn varint_roundtrip_u64() {
420 let values: Vec<u64> = vec![0, 1, 100, u64::MAX / 2, u64::MAX];
421 let encoded = VarintEncoder::encode_u64(&values).unwrap();
422 let decoded = VarintDecoder::decode_u64(&encoded).unwrap();
423 assert_eq!(values, decoded);
424 }
425
426 #[test]
427 fn varint_empty_error() {
428 assert!(VarintEncoder::encode_i64(&[]).is_err());
429 assert!(VarintEncoder::encode_u64(&[]).is_err());
430 }
431
432 #[test]
433 fn varint_better_for_sparse_data() {
434 let mut values = Vec::new();
438 for i in 0..1000 {
439 if i % 100 == 0 {
440 values.push(i * 1_000_000); } else {
442 values.push(values.last().copied().unwrap_or(0) + 1); }
444 }
445 let fixed = IntegerEncoder::encode_i64(&values).unwrap();
446 let varint = VarintEncoder::encode_i64(&values).unwrap();
447 assert!(
448 varint.len() <= fixed.len(),
449 "varint ({}) should be <= fixed-width ({}) for sparse data",
450 varint.len(),
451 fixed.len(),
452 );
453 }
454}
455
456#[cfg(test)]
457mod proptests {
458 use super::*;
459 use proptest::prelude::*;
460
461 proptest! {
462 #[test]
463 fn roundtrip_arbitrary_i64(values in proptest::collection::vec(any::<i64>(), 1..500)) {
464 let encoded = IntegerEncoder::encode_i64(&values).unwrap();
465 let decoded = IntegerDecoder::decode_i64(&encoded).unwrap();
466 prop_assert_eq!(&values, &decoded);
467 }
468
469 #[test]
470 fn roundtrip_arbitrary_u64(values in proptest::collection::vec(any::<u64>(), 1..500)) {
471 let encoded = IntegerEncoder::encode_u64(&values).unwrap();
472 let decoded = IntegerDecoder::decode_u64(&encoded).unwrap();
473 prop_assert_eq!(&values, &decoded);
474 }
475
476 #[test]
477 fn varint_roundtrip_arbitrary_i64(values in proptest::collection::vec(any::<i64>(), 1..500)) {
478 let encoded = VarintEncoder::encode_i64(&values).unwrap();
479 let decoded = VarintDecoder::decode_i64(&encoded).unwrap();
480 prop_assert_eq!(&values, &decoded);
481 }
482
483 #[test]
484 fn varint_roundtrip_arbitrary_u64(values in proptest::collection::vec(any::<u64>(), 1..500)) {
485 let encoded = VarintEncoder::encode_u64(&values).unwrap();
486 let decoded = VarintDecoder::decode_u64(&encoded).unwrap();
487 prop_assert_eq!(&values, &decoded);
488 }
489 }
490}