1use crate::coding::{checked_count, checked_decode_count, zigzag_decode, zigzag_encode};
49use crate::error::{EncodingError, Result};
50
51#[derive(Debug, Clone, Copy)]
53pub struct DeltaOfDeltaEncoder;
54
55#[derive(Debug, Clone, Copy)]
57pub struct DeltaOfDeltaDecoder;
58
59impl DeltaOfDeltaEncoder {
60 pub fn encode(timestamps: &[i64]) -> Result<Vec<u8>> {
75 if timestamps.is_empty() {
76 return Err(EncodingError::EmptyInput {
77 context: "delta-of-delta encoder",
78 });
79 }
80
81 let mut writer = BitWriter::with_capacity(8 + timestamps.len());
83
84 let count = checked_count(timestamps.len())?;
86 writer.write_bits(u64::from(count), 32);
87
88 writer.write_bits(timestamps[0] as u64, 64);
90
91 if timestamps.len() == 1 {
92 return Ok(writer.finish());
93 }
94
95 let first_delta = timestamps[1].wrapping_sub(timestamps[0]);
97 writer.write_bits(first_delta as u64, 64);
98
99 let mut prev_delta = first_delta;
100
101 for i in 2..timestamps.len() {
102 let delta = timestamps[i].wrapping_sub(timestamps[i - 1]);
105 let dod = delta.wrapping_sub(prev_delta);
106 encode_dod(&mut writer, dod);
107 prev_delta = delta;
108 }
109
110 Ok(writer.finish())
111 }
112}
113
114fn encode_dod(writer: &mut BitWriter, dod: i64) {
115 match dod {
116 0 => {
117 writer.write_bit(false);
118 }
119 -64..=63 => {
120 writer.write_bits(0b10, 2);
121 writer.write_bits(zigzag_encode(dod), 7);
124 }
125 -256..=255 => {
126 writer.write_bits(0b110, 3);
127 writer.write_bits(zigzag_encode(dod), 9);
129 }
130 -2048..=2047 => {
131 writer.write_bits(0b1110, 4);
132 writer.write_bits(zigzag_encode(dod), 12);
134 }
135 _ => {
136 writer.write_bits(0b1111, 4);
137 writer.write_bits(dod as u64, 64);
138 }
139 }
140}
141
142impl DeltaOfDeltaDecoder {
143 pub fn decode(data: &[u8]) -> Result<Vec<i64>> {
156 if data.len() < 4 {
157 return Err(EncodingError::CorruptData {
158 detail: "delta-of-delta data too short for header".to_string(),
159 });
160 }
161
162 let mut reader = BitReader::new(data);
163
164 let count = checked_decode_count(reader.read_bits(32)? as u32 as usize, "delta")?;
165 if count == 0 {
166 return Ok(Vec::new());
167 }
168
169 let mut result = Vec::with_capacity(count);
170
171 let first = reader.read_bits(64)? as i64;
173 result.push(first);
174
175 if count == 1 {
176 return Ok(result);
177 }
178
179 let first_delta = reader.read_bits(64)? as i64;
181 result.push(first.wrapping_add(first_delta));
182
183 let mut prev_delta = first_delta;
184
185 for _ in 2..count {
186 let dod = decode_dod(&mut reader)?;
187 let delta = prev_delta.wrapping_add(dod);
189 let value = result[result.len() - 1].wrapping_add(delta);
190 result.push(value);
191 prev_delta = delta;
192 }
193
194 Ok(result)
195 }
196}
197
198fn decode_dod(reader: &mut BitReader) -> Result<i64> {
199 if !reader.read_bit()? {
200 return Ok(0);
202 }
203 if !reader.read_bit()? {
204 let z = reader.read_bits(7)?;
206 return Ok(zigzag_decode(z));
207 }
208 if !reader.read_bit()? {
209 let z = reader.read_bits(9)?;
211 return Ok(zigzag_decode(z));
212 }
213 if !reader.read_bit()? {
214 let z = reader.read_bits(12)?;
216 return Ok(zigzag_decode(z));
217 }
218 let v = reader.read_bits(64)? as i64;
220 Ok(v)
221}
222
223pub(crate) struct BitWriter {
235 buf: Vec<u8>,
236 accumulator: u64,
238 bits_in_acc: u8,
240}
241
242impl BitWriter {
243 pub(crate) fn with_capacity(bytes: usize) -> Self {
245 Self {
246 buf: Vec::with_capacity(bytes),
247 accumulator: 0,
248 bits_in_acc: 0,
249 }
250 }
251
252 #[inline]
254 fn flush_accumulator(&mut self) {
255 self.buf.extend_from_slice(&self.accumulator.to_be_bytes());
256 self.accumulator = 0;
257 self.bits_in_acc = 0;
258 }
259
260 #[inline]
262 pub(crate) fn write_bit(&mut self, bit: bool) {
263 if bit {
264 self.accumulator |= 1u64 << (63 - self.bits_in_acc);
265 }
266 self.bits_in_acc += 1;
267 if self.bits_in_acc == 64 {
268 self.flush_accumulator();
269 }
270 }
271
272 #[inline]
277 pub(crate) fn write_bits(&mut self, value: u64, count: u8) {
278 debug_assert!(count <= 64);
279 if count == 0 {
280 return;
281 }
282
283 let masked = if count >= 64 {
285 value
286 } else {
287 value & ((1u64 << count) - 1)
288 };
289
290 let space = 64 - self.bits_in_acc;
291 if count <= space {
292 self.accumulator |= masked << (space - count);
294 self.bits_in_acc += count;
295 if self.bits_in_acc == 64 {
296 self.flush_accumulator();
297 }
298 } else {
299 self.accumulator |= masked >> (count - space);
301 self.flush_accumulator();
302 let remaining = count - space;
303 self.accumulator = masked << (64 - remaining);
304 self.bits_in_acc = remaining;
305 }
306 }
307
308 pub(crate) fn finish(mut self) -> Vec<u8> {
310 if self.bits_in_acc > 0 {
311 let bytes_needed = self.bits_in_acc.div_ceil(8) as usize;
312 let word_bytes = self.accumulator.to_be_bytes();
313 self.buf.extend_from_slice(&word_bytes[..bytes_needed]);
314 }
315 self.buf
316 }
317}
318
319pub(crate) struct BitReader<'a> {
325 data: &'a [u8],
326 byte_pos: usize,
327 bit_pos: u8, }
329
330impl<'a> BitReader<'a> {
331 pub(crate) fn new(data: &'a [u8]) -> Self {
333 Self {
334 data,
335 byte_pos: 0,
336 bit_pos: 0,
337 }
338 }
339
340 #[inline]
342 pub(crate) fn read_bit(&mut self) -> Result<bool> {
343 if self.byte_pos >= self.data.len() {
344 return Err(EncodingError::CorruptData {
345 detail: "unexpected end of bit stream".to_string(),
346 });
347 }
348 let bit = (self.data[self.byte_pos] >> (7 - self.bit_pos)) & 1 == 1;
349 self.bit_pos += 1;
350 if self.bit_pos == 8 {
351 self.byte_pos += 1;
352 self.bit_pos = 0;
353 }
354 Ok(bit)
355 }
356
357 #[inline]
362 pub(crate) fn read_bits(&mut self, count: u8) -> Result<u64> {
363 debug_assert!(count <= 64);
364 if count == 0 {
365 return Ok(0);
366 }
367
368 let mut value: u64 = 0;
369 let mut remaining = count;
370
371 while remaining > 0 {
372 if self.byte_pos >= self.data.len() {
373 return Err(EncodingError::CorruptData {
374 detail: "unexpected end of bit stream".to_string(),
375 });
376 }
377
378 let bits_left_in_byte = 8 - self.bit_pos;
379 let to_read = remaining.min(bits_left_in_byte);
380
381 let shift = bits_left_in_byte - to_read;
383 let mask: u8 = if to_read >= 8 {
384 0xFF
385 } else {
386 (1u8 << to_read) - 1
387 };
388 let bits = (self.data[self.byte_pos] >> shift) & mask;
389
390 value = (value << to_read) | u64::from(bits);
391 remaining -= to_read;
392 self.bit_pos += to_read;
393
394 if self.bit_pos >= 8 {
395 self.byte_pos += 1;
396 self.bit_pos = 0;
397 }
398 }
399
400 Ok(value)
401 }
402}
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407
408 #[test]
409 fn roundtrip_monotonic_1s() {
410 let ts: Vec<i64> = (0..1000)
411 .map(|i| 1_000_000_000 + i * 1_000_000_000)
412 .collect();
413 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
414 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
415 assert_eq!(ts, decoded);
416 assert!(encoded.len() < ts.len()); }
419
420 #[test]
421 fn roundtrip_single_value() {
422 let ts = vec![42_i64];
423 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
424 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
425 assert_eq!(ts, decoded);
426 }
427
428 #[test]
429 fn roundtrip_two_values() {
430 let ts = vec![100_i64, 200];
431 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
432 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
433 assert_eq!(ts, decoded);
434 }
435
436 #[test]
437 fn roundtrip_constant_timestamps() {
438 let ts = vec![42_i64; 100];
439 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
440 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
441 assert_eq!(ts, decoded);
442 }
443
444 #[test]
445 fn roundtrip_irregular_intervals() {
446 let ts = vec![0, 1, 5, 10, 50, 100, 500, 1000, 2000];
447 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
448 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
449 assert_eq!(ts, decoded);
450 }
451
452 #[test]
453 fn roundtrip_negative_timestamps() {
454 let ts = vec![-1000, -500, -200, -100, 0, 100, 200];
455 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
456 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
457 assert_eq!(ts, decoded);
458 }
459
460 #[test]
461 fn roundtrip_edge_i64() {
462 let ts = vec![i64::MIN, i64::MIN / 2, 0, i64::MAX / 2, i64::MAX];
463 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
464 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
465 assert_eq!(ts, decoded);
466 }
467
468 #[test]
469 fn empty_input_error() {
470 let result = DeltaOfDeltaEncoder::encode(&[]);
471 assert!(result.is_err());
472 }
473
474 #[test]
475 fn compression_ratio_regular() {
476 let ts: Vec<i64> = (0..10_000).map(|i| i * 1_000_000_000).collect();
478 let raw_size = ts.len() * 8;
479 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
480 #[allow(clippy::cast_precision_loss)]
481 let ratio = raw_size as f64 / encoded.len() as f64;
482 assert!(
484 ratio > 10.0,
485 "Expected ratio > 10x, got {ratio:.1}x ({} raw, {} encoded)",
486 raw_size,
487 encoded.len()
488 );
489 }
490
491 #[test]
496 fn roundtrip_dod_boundary_values() {
497 let ts_64 = vec![0_i64, 0, 64];
499 let encoded = DeltaOfDeltaEncoder::encode(&ts_64).unwrap();
500 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
501 assert_eq!(ts_64, decoded, "DoD=64 boundary failed");
502
503 let ts_neg64 = vec![0_i64, 64, 0];
505 let encoded = DeltaOfDeltaEncoder::encode(&ts_neg64).unwrap();
506 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
507 assert_eq!(ts_neg64, decoded, "DoD=-64 boundary failed");
508
509 let ts_63 = vec![0_i64, 0, 63];
511 let encoded = DeltaOfDeltaEncoder::encode(&ts_63).unwrap();
512 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
513 assert_eq!(ts_63, decoded, "DoD=63 boundary failed");
514
515 let ts_256 = vec![0_i64, 0, 256];
517 let encoded = DeltaOfDeltaEncoder::encode(&ts_256).unwrap();
518 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
519 assert_eq!(ts_256, decoded, "DoD=256 boundary failed");
520
521 let ts_neg256 = vec![0_i64, 256, 0];
523 let encoded = DeltaOfDeltaEncoder::encode(&ts_neg256).unwrap();
524 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
525 assert_eq!(ts_neg256, decoded, "DoD=-256 boundary failed");
526
527 let ts_255 = vec![0_i64, 0, 255];
529 let encoded = DeltaOfDeltaEncoder::encode(&ts_255).unwrap();
530 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
531 assert_eq!(ts_255, decoded, "DoD=255 boundary failed");
532
533 let ts_2048 = vec![0_i64, 0, 2048];
535 let encoded = DeltaOfDeltaEncoder::encode(&ts_2048).unwrap();
536 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
537 assert_eq!(ts_2048, decoded, "DoD=2048 boundary failed");
538
539 let ts_neg2048 = vec![0_i64, 2048, 0];
541 let encoded = DeltaOfDeltaEncoder::encode(&ts_neg2048).unwrap();
542 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
543 assert_eq!(ts_neg2048, decoded, "DoD=-2048 boundary failed");
544
545 let ts_2047 = vec![0_i64, 0, 2047];
547 let encoded = DeltaOfDeltaEncoder::encode(&ts_2047).unwrap();
548 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
549 assert_eq!(ts_2047, decoded, "DoD=2047 boundary failed");
550 }
551
552 #[test]
555 fn roundtrip_dod_just_outside_boundaries() {
556 let ts = vec![0_i64, 0, 65];
558 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
559 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
560 assert_eq!(ts, decoded);
561
562 let ts = vec![0_i64, 0, 257];
564 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
565 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
566 assert_eq!(ts, decoded);
567
568 let ts = vec![0_i64, 0, 2049];
570 let encoded = DeltaOfDeltaEncoder::encode(&ts).unwrap();
571 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
572 assert_eq!(ts, decoded);
573 }
574}
575
576#[cfg(test)]
577mod proptests {
578 use super::*;
579 use proptest::prelude::*;
580
581 proptest! {
582 #[test]
583 fn roundtrip_arbitrary_sorted(mut values in proptest::collection::vec(any::<i64>(), 1..500)) {
584 values.sort_unstable();
585 let encoded = DeltaOfDeltaEncoder::encode(&values).unwrap();
586 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
587 prop_assert_eq!(&values, &decoded);
588 }
589
590 #[test]
591 fn roundtrip_arbitrary_unsorted(values in proptest::collection::vec(any::<i64>(), 1..500)) {
592 let encoded = DeltaOfDeltaEncoder::encode(&values).unwrap();
594 let decoded = DeltaOfDeltaDecoder::decode(&encoded).unwrap();
595 prop_assert_eq!(&values, &decoded);
596 }
597 }
598}