1use std::mem::size_of;
33
34use crate::bounds::{
35 checked_add, checked_capacity, checked_mul, decoded_len, encode_input_len, u32_to_usize,
36};
37use crate::error::CodecError;
38
39#[derive(Debug)]
44pub(crate) struct BitWriter {
45 buf: Vec<u8>,
46 bit_pos: usize,
47}
48
49impl BitWriter {
50 pub(crate) fn new() -> Self {
51 Self {
52 buf: Vec::with_capacity(1024),
53 bit_pos: 0,
54 }
55 }
56
57 pub(crate) fn write_bit(&mut self, bit: bool) {
58 let byte_idx = self.bit_pos / 8;
59 let bit_idx = 7 - (self.bit_pos % 8);
60 if byte_idx >= self.buf.len() {
61 self.buf.push(0);
62 }
63 if bit {
64 self.buf[byte_idx] |= 1 << bit_idx;
65 }
66 self.bit_pos += 1;
67 }
68
69 pub(crate) fn write_bits(&mut self, value: u64, num_bits: usize) {
70 for i in (0..num_bits).rev() {
71 self.write_bit((value >> i) & 1 == 1);
72 }
73 }
74
75 fn try_write_bit(&mut self, bit: bool) -> Result<(), CodecError> {
76 checked_add(self.bit_pos, 1, "DoubleDelta bit offset")?;
77 self.write_bit(bit);
78 Ok(())
79 }
80
81 fn try_write_bits(&mut self, value: u64, num_bits: usize) -> Result<(), CodecError> {
82 checked_add(self.bit_pos, num_bits, "DoubleDelta bit offset")?;
83 for i in (0..num_bits).rev() {
84 self.write_bit((value >> i) & 1 == 1);
85 }
86 Ok(())
87 }
88
89 pub(crate) fn as_bytes(&self) -> &[u8] {
90 &self.buf
91 }
92
93 pub(crate) fn bit_len(&self) -> usize {
94 self.bit_pos
95 }
96}
97
98pub(crate) struct BitReader<'a> {
99 buf: &'a [u8],
100 bit_pos: usize,
101}
102
103impl<'a> BitReader<'a> {
104 pub(crate) fn new(buf: &'a [u8]) -> Self {
105 Self { buf, bit_pos: 0 }
106 }
107
108 pub(crate) fn read_bit(&mut self) -> Result<bool, CodecError> {
109 let byte_idx = self.bit_pos / 8;
110 if byte_idx >= self.buf.len() {
111 return Err(CodecError::Truncated {
112 expected: checked_add(byte_idx, 1, "DoubleDelta bit byte index")?,
113 actual: self.buf.len(),
114 });
115 }
116 let bit_idx = 7 - (self.bit_pos % 8);
117 let bit = (self.buf[byte_idx] >> bit_idx) & 1 == 1;
118 self.bit_pos = checked_add(self.bit_pos, 1, "DoubleDelta bit offset")?;
119 Ok(bit)
120 }
121
122 pub(crate) fn read_bits(&mut self, num_bits: usize) -> Result<u64, CodecError> {
123 let mut value = 0u64;
124 for _ in 0..num_bits {
125 value = (value << 1) | u64::from(self.read_bit()?);
126 }
127 Ok(value)
128 }
129}
130
131pub fn encode(values: &[i64]) -> Result<Vec<u8>, CodecError> {
137 let count = encode_value_count(values.len(), "DoubleDelta")?;
138 let estimated = checked_add(20, values.len() / 4, "DoubleDelta output estimate")?;
139 let mut out = Vec::with_capacity(estimated);
140
141 out.extend_from_slice(&count.to_le_bytes());
142
143 if values.is_empty() {
144 return Ok(out);
145 }
146
147 out.extend_from_slice(&values[0].to_le_bytes());
148
149 if values.len() == 1 {
150 return Ok(out);
151 }
152
153 let first_delta = values[1].wrapping_sub(values[0]);
154 out.extend_from_slice(&first_delta.to_le_bytes());
155
156 if values.len() == 2 {
157 return Ok(out);
158 }
159
160 let mut bs = BitWriter::new();
161 let mut prev_delta = first_delta;
162
163 for i in 2..values.len() {
164 let delta = values[i].wrapping_sub(values[i - 1]);
165 let dod = delta.wrapping_sub(prev_delta);
166 encode_dod(&mut bs, dod)?;
167 prev_delta = delta;
168 }
169
170 out.extend_from_slice(bs.as_bytes());
171 Ok(out)
172}
173
174pub fn decode(data: &[u8]) -> Result<Vec<i64>, CodecError> {
176 if data.len() < 4 {
177 return Err(CodecError::Truncated {
178 expected: 4,
179 actual: data.len(),
180 });
181 }
182
183 let count = u32_to_usize(
184 u32::from_le_bytes(data[0..4].try_into().map_err(|_| CodecError::Corrupt {
185 detail: "invalid header".into(),
186 })?),
187 "DoubleDelta value count",
188 )?;
189 validate_value_count(count, "DoubleDelta")?;
190
191 if count == 0 {
192 if data.len() != 4 {
193 return Err(CodecError::Corrupt {
194 detail: "trailing bytes after empty DoubleDelta frame".into(),
195 });
196 }
197 return Ok(Vec::new());
198 }
199
200 if data.len() < 12 {
201 return Err(CodecError::Truncated {
202 expected: 12,
203 actual: data.len(),
204 });
205 }
206
207 let first_value =
208 i64::from_le_bytes(data[4..12].try_into().map_err(|_| CodecError::Corrupt {
209 detail: "invalid first value".into(),
210 })?);
211
212 let value_capacity = checked_capacity(count, size_of::<i64>(), "DoubleDelta values")?;
213 let mut values = Vec::with_capacity(value_capacity);
214 values.push(first_value);
215
216 if count == 1 {
217 if data.len() != 12 {
218 return Err(CodecError::Corrupt {
219 detail: "trailing bytes after single-value DoubleDelta frame".into(),
220 });
221 }
222 return Ok(values);
223 }
224
225 if data.len() < 20 {
226 return Err(CodecError::Truncated {
227 expected: 20,
228 actual: data.len(),
229 });
230 }
231
232 let first_delta =
233 i64::from_le_bytes(data[12..20].try_into().map_err(|_| CodecError::Corrupt {
234 detail: "invalid first delta".into(),
235 })?);
236 values.push(first_value.wrapping_add(first_delta));
237
238 if count == 2 {
239 if data.len() != 20 {
240 return Err(CodecError::Corrupt {
241 detail: "trailing bytes after two-value DoubleDelta frame".into(),
242 });
243 }
244 return Ok(values);
245 }
246
247 let mut reader = BitReader::new(&data[20..]);
248 let mut prev_delta = first_delta;
249
250 for _ in 2..count {
251 let dod = decode_dod(&mut reader)?;
252 let delta = prev_delta.wrapping_add(dod);
253 let value = values[values.len() - 1].wrapping_add(delta);
254 values.push(value);
255 prev_delta = delta;
256 }
257 let used_bytes = reader.bit_pos.div_ceil(8);
258 if used_bytes != reader.buf.len() {
259 return Err(CodecError::Corrupt {
260 detail: "trailing bytes after DoubleDelta frame".into(),
261 });
262 }
263 if !reader.bit_pos.is_multiple_of(8) {
264 let unused_bits = 8 - reader.bit_pos % 8;
265 let padding_mask = (1u8 << unused_bits) - 1;
266 if reader.buf[used_bytes - 1] & padding_mask != 0 {
267 return Err(CodecError::Corrupt {
268 detail: "nonzero DoubleDelta padding bits".into(),
269 });
270 }
271 }
272
273 Ok(values)
274}
275
276fn encode_dod(bs: &mut BitWriter, dod: i64) -> Result<(), CodecError> {
281 if dod == 0 {
282 bs.try_write_bit(false)
283 } else if (-64..=63).contains(&dod) {
284 bs.try_write_bits(0b10, 2)?;
285 bs.try_write_bits((dod as u64) & 0x7F, 7)
286 } else if (-256..=255).contains(&dod) {
287 bs.try_write_bits(0b110, 3)?;
288 bs.try_write_bits((dod as u64) & 0x1FF, 9)
289 } else if (-2048..=2047).contains(&dod) {
290 bs.try_write_bits(0b1110, 4)?;
291 bs.try_write_bits((dod as u64) & 0xFFF, 12)
292 } else {
293 bs.try_write_bits(0b1111, 4)?;
294 bs.try_write_bits(dod as u64, 64)
295 }
296}
297
298fn decode_dod(reader: &mut BitReader<'_>) -> Result<i64, CodecError> {
299 let bit = reader.read_bit()?;
300 if !bit {
301 return Ok(0);
302 }
303
304 let bit2 = reader.read_bit()?;
305 if !bit2 {
306 let raw = reader.read_bits(7)? as i64;
307 return Ok(sign_extend(raw, 7));
308 }
309
310 let bit3 = reader.read_bit()?;
311 if !bit3 {
312 let raw = reader.read_bits(9)? as i64;
313 return Ok(sign_extend(raw, 9));
314 }
315
316 let bit4 = reader.read_bit()?;
317 if !bit4 {
318 let raw = reader.read_bits(12)? as i64;
319 return Ok(sign_extend(raw, 12));
320 }
321
322 let raw = reader.read_bits(64)?;
323 Ok(raw as i64)
324}
325
326fn sign_extend(value: i64, bits: u32) -> i64 {
327 let shift = 64 - bits;
328 (value << shift) >> shift
329}
330
331fn encode_value_count(len: usize, codec: &str) -> Result<u32, CodecError> {
332 validate_value_count(len, codec)?;
333 encode_input_len(len, codec)
334}
335
336fn validate_value_count(count: usize, codec: &str) -> Result<(), CodecError> {
337 let bytes = checked_mul(count, size_of::<i64>(), "integer decoded bytes")?;
338 decoded_len(bytes, codec)?;
339 Ok(())
340}
341
342pub struct DoubleDeltaEncoder {
349 values: Vec<i64>,
350}
351
352impl DoubleDeltaEncoder {
353 pub fn new() -> Self {
354 Self {
355 values: Vec::with_capacity(4096),
356 }
357 }
358
359 pub fn push(&mut self, value: i64) -> Result<(), CodecError> {
361 let next = checked_add(self.values.len(), 1, "DoubleDelta streaming count")?;
362 validate_value_count(next, "DoubleDelta streaming input")?;
363 self.values.push(value);
364 Ok(())
365 }
366
367 pub fn push_batch(&mut self, values: &[i64]) -> Result<(), CodecError> {
369 let next = checked_add(
370 self.values.len(),
371 values.len(),
372 "DoubleDelta streaming count",
373 )?;
374 validate_value_count(next, "DoubleDelta streaming input")?;
375 self.values.extend_from_slice(values);
376 Ok(())
377 }
378
379 pub fn count(&self) -> usize {
381 self.values.len()
382 }
383
384 pub fn finish(self) -> Result<Vec<u8>, CodecError> {
386 encode(&self.values)
387 }
388}
389
390impl Default for DoubleDeltaEncoder {
391 fn default() -> Self {
392 Self::new()
393 }
394}
395
396pub struct DoubleDeltaDecoder {
398 values: Vec<i64>,
399 pos: usize,
400}
401
402impl DoubleDeltaDecoder {
403 pub fn new(data: &[u8]) -> Result<Self, CodecError> {
405 let values = decode(data)?;
406 Ok(Self { values, pos: 0 })
407 }
408
409 pub fn decode_all(data: &[u8]) -> Result<Vec<i64>, CodecError> {
411 decode(data)
412 }
413
414 pub fn next_value(&mut self) -> Option<i64> {
416 if self.pos < self.values.len() {
417 let v = self.values[self.pos];
418 self.pos += 1;
419 Some(v)
420 } else {
421 None
422 }
423 }
424
425 pub fn remaining(&self) -> usize {
427 self.values.len() - self.pos
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use super::*;
434
435 fn encode(values: &[i64]) -> Vec<u8> {
436 super::encode(values).expect("test DoubleDelta encode")
437 }
438
439 #[test]
440 fn empty_roundtrip() {
441 let encoded = encode(&[]);
442 let decoded = decode(&encoded).unwrap();
443 assert!(decoded.is_empty());
444 }
445
446 #[test]
447 fn single_value() {
448 let encoded = encode(&[1_700_000_000_000i64]);
449 let decoded = decode(&encoded).unwrap();
450 assert_eq!(decoded, vec![1_700_000_000_000i64]);
451 assert_eq!(encoded.len(), 12);
452 }
453
454 #[test]
455 fn two_values() {
456 let values = vec![1000i64, 2000];
457 let encoded = encode(&values);
458 let decoded = decode(&encoded).unwrap();
459 assert_eq!(decoded, values);
460 assert_eq!(encoded.len(), 20);
461 }
462
463 #[test]
464 fn constant_rate_timestamps() {
465 let values: Vec<i64> = (0..10_000)
466 .map(|i| 1_700_000_000_000 + i * 10_000)
467 .collect();
468 let encoded = encode(&values);
469 let decoded = decode(&encoded).unwrap();
470 assert_eq!(decoded, values);
471
472 let bits_per_sample = (encoded.len() as f64 * 8.0) / values.len() as f64;
473 assert!(
474 bits_per_sample < 2.0,
475 "constant-rate should compress to ~1 bit/sample, got {bits_per_sample:.1}"
476 );
477 }
478
479 #[test]
480 fn monotonic_with_jitter() {
481 let mut values = Vec::with_capacity(10_000);
482 let mut ts = 1_700_000_000_000i64;
483 let mut rng: u64 = 42;
484 for _ in 0..10_000 {
485 values.push(ts);
486 rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
487 let jitter = ((rng >> 33) as i64 % 101) - 50;
488 ts += 10_000 + jitter;
489 }
490 let encoded = encode(&values);
491 let decoded = decode(&encoded).unwrap();
492 assert_eq!(decoded, values);
493
494 let bytes_per_sample = encoded.len() as f64 / values.len() as f64;
495 assert!(
496 bytes_per_sample < 2.0,
497 "jittered timestamps should compress to <2 bytes/sample, got {bytes_per_sample:.2}"
498 );
499 }
500
501 #[test]
502 fn non_monotonic_values() {
503 let values: Vec<i64> = vec![100, 50, 200, 10, 300, 5, 1000, -500, 0, 42];
504 let encoded = encode(&values);
505 let decoded = decode(&encoded).unwrap();
506 assert_eq!(decoded, values);
507 }
508
509 #[test]
510 fn negative_values() {
511 let values: Vec<i64> = vec![-1000, -999, -998, -997, -996];
512 let encoded = encode(&values);
513 let decoded = decode(&encoded).unwrap();
514 assert_eq!(decoded, values);
515 }
516
517 #[test]
518 fn large_deltas() {
519 let values: Vec<i64> = vec![0, i64::MAX / 2, i64::MIN / 2, i64::MAX / 4, 0];
520 let encoded = encode(&values);
521 let decoded = decode(&encoded).unwrap();
522 assert_eq!(decoded, values);
523 }
524
525 #[test]
526 fn boundary_values() {
527 let values: Vec<i64> = vec![i64::MIN, 0, i64::MAX, 0, i64::MIN];
528 let encoded = encode(&values);
529 let decoded = decode(&encoded).unwrap();
530 assert_eq!(decoded, values);
531 }
532
533 #[test]
534 fn compression_better_than_raw_for_constant_rate() {
535 let values: Vec<i64> = (0..100_000)
536 .map(|i| 1_700_000_000_000 + i * 10_000)
537 .collect();
538 let encoded = encode(&values);
539 let raw_size = values.len() * 8;
540 let ratio = raw_size as f64 / encoded.len() as f64;
541 assert!(
542 ratio > 5.0,
543 "expected >5x compression for constant-rate, got {ratio:.1}x"
544 );
545 }
546
547 #[test]
548 fn streaming_encoder_matches_batch() {
549 let values: Vec<i64> = (0..1000).map(|i| 1_000_000 + i * 100).collect();
550 let batch_encoded = encode(&values);
551
552 let mut enc = DoubleDeltaEncoder::new();
553 for &v in &values {
554 enc.push(v).expect("push");
555 }
556 let stream_encoded = enc.finish().expect("finish");
557
558 assert_eq!(batch_encoded, stream_encoded);
559 }
560
561 #[test]
562 fn streaming_decoder() {
563 let values: Vec<i64> = (0..100).map(|i| 5000 + i * 10).collect();
564 let encoded = encode(&values);
565 let mut dec = DoubleDeltaDecoder::new(&encoded).unwrap();
566
567 for &expected in &values {
568 assert_eq!(dec.next_value(), Some(expected));
569 }
570 assert_eq!(dec.next_value(), None);
571 assert_eq!(dec.remaining(), 0);
572 }
573
574 #[test]
575 fn huge_count_and_noncanonical_tail_are_rejected_before_decode_work() {
576 let mut huge = u32::MAX.to_le_bytes().to_vec();
577 huge.extend_from_slice(&[0; 16]);
578 assert!(matches!(
579 decode(&huge),
580 Err(CodecError::ResourceLimit { .. })
581 ));
582
583 let mut one = encode(&[7]);
584 one.push(1);
585 assert!(matches!(decode(&one), Err(CodecError::Corrupt { .. })));
586
587 let mut padded = encode(&[0, 1, 2]);
588 let last = padded.len() - 1;
589 padded[last] |= 1;
590 assert!(matches!(decode(&padded), Err(CodecError::Corrupt { .. })));
591 }
592
593 #[test]
594 fn truncated_input_errors() {
595 assert!(decode(&[]).is_err());
596 assert!(decode(&[1, 0, 0, 0]).is_err());
597 assert!(decode(&[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).is_err());
598 }
599}