1use std::mem::size_of;
22
23use crate::bounds::{
24 checked_add, checked_capacity, checked_mul, decoded_len, encode_input_len, u32_to_usize,
25};
26use crate::error::CodecError;
27
28#[inline]
37fn zigzag_encode(v: i64) -> u64 {
38 ((v << 1) ^ (v >> 63)) as u64
39}
40
41#[inline]
43fn zigzag_decode(v: u64) -> i64 {
44 ((v >> 1) as i64) ^ -((v & 1) as i64)
45}
46
47fn write_varint(buf: &mut Vec<u8>, mut value: u64) {
49 loop {
50 let mut byte = (value & 0x7F) as u8;
51 value >>= 7;
52 if value != 0 {
53 byte |= 0x80;
54 }
55 buf.push(byte);
56 if value == 0 {
57 break;
58 }
59 }
60}
61
62fn read_varint(data: &[u8]) -> Result<(u64, usize), CodecError> {
66 let mut value = 0u64;
67 for (index, &byte) in data.iter().enumerate() {
68 if index >= 10 {
69 return Err(CodecError::Corrupt {
70 detail: "varint exceeds ten bytes".into(),
71 });
72 }
73 let payload = u64::from(byte & 0x7f);
74 if index == 9 && (byte & 0x80 != 0 || payload > 1) {
75 return Err(CodecError::Corrupt {
76 detail: "varint overflows u64".into(),
77 });
78 }
79 value |= payload << (index * 7);
80 if byte & 0x80 == 0 {
81 if index > 0 && payload == 0 {
82 return Err(CodecError::Corrupt {
83 detail: "varint is noncanonical".into(),
84 });
85 }
86 return Ok((value, index + 1));
87 }
88 }
89
90 Err(CodecError::Truncated {
91 expected: checked_add(data.len(), 1, "Delta varint length")?,
92 actual: data.len(),
93 })
94}
95
96pub fn encode(values: &[i64]) -> Result<Vec<u8>, CodecError> {
102 let count = encode_value_count(values.len(), "Delta")?;
103 let estimated = checked_add(
104 12,
105 checked_mul(values.len(), 2, "Delta output estimate")?,
106 "Delta output estimate",
107 )?;
108 let mut out = Vec::with_capacity(estimated);
109
110 out.extend_from_slice(&count.to_le_bytes());
111
112 if values.is_empty() {
113 return Ok(out);
114 }
115
116 out.extend_from_slice(&values[0].to_le_bytes());
117
118 for i in 1..values.len() {
119 let delta = values[i].wrapping_sub(values[i - 1]);
120 write_varint(&mut out, zigzag_encode(delta));
121 }
122
123 Ok(out)
124}
125
126pub fn decode(data: &[u8]) -> Result<Vec<i64>, CodecError> {
128 if data.len() < 4 {
129 return Err(CodecError::Truncated {
130 expected: 4,
131 actual: data.len(),
132 });
133 }
134
135 let count = u32_to_usize(
136 u32::from_le_bytes(data[0..4].try_into().map_err(|_| CodecError::Corrupt {
137 detail: "invalid header".into(),
138 })?),
139 "Delta value count",
140 )?;
141 validate_value_count(count, "Delta")?;
142
143 if count == 0 {
144 if data.len() != 4 {
145 return Err(CodecError::Corrupt {
146 detail: "trailing bytes after empty Delta frame".into(),
147 });
148 }
149 return Ok(Vec::new());
150 }
151
152 if data.len() < 12 {
153 return Err(CodecError::Truncated {
154 expected: 12,
155 actual: data.len(),
156 });
157 }
158
159 let first_value =
160 i64::from_le_bytes(data[4..12].try_into().map_err(|_| CodecError::Corrupt {
161 detail: "invalid first value".into(),
162 })?);
163
164 let value_capacity = checked_capacity(count, size_of::<i64>(), "Delta values")?;
165 let mut values = Vec::with_capacity(value_capacity);
166 values.push(first_value);
167
168 let mut offset = 12;
169 for _ in 1..count {
170 if offset >= data.len() {
171 return Err(CodecError::Truncated {
172 expected: checked_add(offset, 1, "Delta varint offset")?,
173 actual: data.len(),
174 });
175 }
176 let tail = data.get(offset..).ok_or(CodecError::Truncated {
177 expected: checked_add(offset, 1, "Delta varint offset")?,
178 actual: data.len(),
179 })?;
180 let (encoded_delta, consumed) = read_varint(tail)?;
181 let delta = zigzag_decode(encoded_delta);
182 let value = values[values.len() - 1].wrapping_add(delta);
183 values.push(value);
184 offset = checked_add(offset, consumed, "Delta varint offset")?;
185 }
186 if offset != data.len() {
187 return Err(CodecError::Corrupt {
188 detail: "trailing bytes after Delta frame".into(),
189 });
190 }
191
192 Ok(values)
193}
194
195fn encode_value_count(len: usize, codec: &str) -> Result<u32, CodecError> {
196 validate_value_count(len, codec)?;
197 encode_input_len(len, codec)
198}
199
200fn validate_value_count(count: usize, codec: &str) -> Result<(), CodecError> {
201 let bytes = checked_mul(count, size_of::<i64>(), "integer decoded bytes")?;
202 decoded_len(bytes, codec)?;
203 Ok(())
204}
205
206pub struct DeltaEncoder {
213 values: Vec<i64>,
214}
215
216impl DeltaEncoder {
217 pub fn new() -> Self {
218 Self {
219 values: Vec::with_capacity(4096),
220 }
221 }
222
223 pub fn push(&mut self, value: i64) -> Result<(), CodecError> {
224 let next = checked_add(self.values.len(), 1, "Delta streaming count")?;
225 validate_value_count(next, "Delta streaming input")?;
226 self.values.push(value);
227 Ok(())
228 }
229
230 pub fn push_batch(&mut self, values: &[i64]) -> Result<(), CodecError> {
231 let next = checked_add(self.values.len(), values.len(), "Delta streaming count")?;
232 validate_value_count(next, "Delta streaming input")?;
233 self.values.extend_from_slice(values);
234 Ok(())
235 }
236
237 pub fn count(&self) -> usize {
238 self.values.len()
239 }
240
241 pub fn finish(self) -> Result<Vec<u8>, CodecError> {
242 encode(&self.values)
243 }
244}
245
246impl Default for DeltaEncoder {
247 fn default() -> Self {
248 Self::new()
249 }
250}
251
252pub struct DeltaDecoder {
254 values: Vec<i64>,
255 pos: usize,
256}
257
258impl DeltaDecoder {
259 pub fn new(data: &[u8]) -> Result<Self, CodecError> {
260 let values = decode(data)?;
261 Ok(Self { values, pos: 0 })
262 }
263
264 pub fn decode_all(data: &[u8]) -> Result<Vec<i64>, CodecError> {
265 decode(data)
266 }
267
268 pub fn next_value(&mut self) -> Option<i64> {
269 if self.pos < self.values.len() {
270 let v = self.values[self.pos];
271 self.pos += 1;
272 Some(v)
273 } else {
274 None
275 }
276 }
277
278 pub fn remaining(&self) -> usize {
279 self.values.len() - self.pos
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 fn encode(values: &[i64]) -> Vec<u8> {
288 super::encode(values).expect("test Delta encode")
289 }
290
291 #[test]
292 fn zigzag_roundtrip() {
293 for v in [0i64, 1, -1, 2, -2, 63, -63, 127, -128, i64::MAX, i64::MIN] {
294 assert_eq!(zigzag_decode(zigzag_encode(v)), v, "zigzag failed for {v}");
295 }
296 }
297
298 #[test]
299 fn varint_roundtrip() {
300 for v in [0u64, 1, 127, 128, 255, 16383, 16384, u64::MAX / 2, u64::MAX] {
301 let mut buf = Vec::new();
302 write_varint(&mut buf, v);
303 let (decoded, consumed) = read_varint(&buf).unwrap();
304 assert_eq!(decoded, v, "varint failed for {v}");
305 assert_eq!(consumed, buf.len());
306 }
307 }
308
309 #[test]
310 fn empty_roundtrip() {
311 let encoded = encode(&[]);
312 let decoded = decode(&encoded).unwrap();
313 assert!(decoded.is_empty());
314 }
315
316 #[test]
317 fn single_value() {
318 let encoded = encode(&[42i64]);
319 let decoded = decode(&encoded).unwrap();
320 assert_eq!(decoded, vec![42i64]);
321 assert_eq!(encoded.len(), 12); }
323
324 #[test]
325 fn monotonic_counter() {
326 let values: Vec<i64> = (0..10_000).map(|i| i * 1000).collect();
328 let encoded = encode(&values);
329 let decoded = decode(&encoded).unwrap();
330 assert_eq!(decoded, values);
331
332 let bytes_per_sample = encoded.len() as f64 / values.len() as f64;
334 assert!(
335 bytes_per_sample < 3.0,
336 "monotonic counter should compress to <3 bytes/sample, got {bytes_per_sample:.2}"
337 );
338 }
339
340 #[test]
341 fn counter_with_small_increments() {
342 let values: Vec<i64> = (0..10_000).collect();
344 let encoded = encode(&values);
345 let decoded = decode(&encoded).unwrap();
346 assert_eq!(decoded, values);
347
348 let bytes_per_sample = encoded.len() as f64 / values.len() as f64;
350 assert!(
351 bytes_per_sample < 2.0,
352 "unit-increment counter should compress to <2 bytes/sample, got {bytes_per_sample:.2}"
353 );
354 }
355
356 #[test]
357 fn counter_reset() {
358 let mut values: Vec<i64> = (0..500).map(|i| i * 100).collect();
360 values.push(0); values.extend((1..500).map(|i| i * 100));
362
363 let encoded = encode(&values);
364 let decoded = decode(&encoded).unwrap();
365 assert_eq!(decoded, values);
366 }
367
368 #[test]
369 fn non_monotonic_gauge() {
370 let mut values = Vec::with_capacity(10_000);
372 let mut val = 50i64;
373 let mut rng: u64 = 12345;
374 for _ in 0..10_000 {
375 values.push(val);
376 rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
377 let delta = ((rng >> 33) as i64 % 11) - 5; val += delta;
379 }
380 let encoded = encode(&values);
381 let decoded = decode(&encoded).unwrap();
382 assert_eq!(decoded, values);
383
384 let bytes_per_sample = encoded.len() as f64 / values.len() as f64;
386 assert!(
387 bytes_per_sample < 3.0,
388 "small-delta gauge should compress to <3 bytes/sample, got {bytes_per_sample:.2}"
389 );
390 }
391
392 #[test]
393 fn negative_values() {
394 let values: Vec<i64> = vec![-1000, -999, -998, -997, -996];
395 let encoded = encode(&values);
396 let decoded = decode(&encoded).unwrap();
397 assert_eq!(decoded, values);
398 }
399
400 #[test]
401 fn large_values() {
402 let values: Vec<i64> = vec![i64::MAX, i64::MAX - 1, i64::MAX - 2];
403 let encoded = encode(&values);
404 let decoded = decode(&encoded).unwrap();
405 assert_eq!(decoded, values);
406 }
407
408 #[test]
409 fn boundary_values() {
410 let values: Vec<i64> = vec![i64::MIN, 0, i64::MAX];
411 let encoded = encode(&values);
412 let decoded = decode(&encoded).unwrap();
413 assert_eq!(decoded, values);
414 }
415
416 #[test]
417 fn streaming_encoder_matches_batch() {
418 let values: Vec<i64> = (0..1000).map(|i| i * 7).collect();
419 let batch = encode(&values);
420
421 let mut enc = DeltaEncoder::new();
422 for &v in &values {
423 enc.push(v).expect("push");
424 }
425 assert_eq!(enc.finish().expect("finish"), batch);
426 }
427
428 #[test]
429 fn streaming_decoder() {
430 let values: Vec<i64> = (0..100).map(|i| i * 10).collect();
431 let encoded = encode(&values);
432 let mut dec = DeltaDecoder::new(&encoded).unwrap();
433
434 for &expected in &values {
435 assert_eq!(dec.next_value(), Some(expected));
436 }
437 assert_eq!(dec.next_value(), None);
438 }
439
440 #[test]
441 fn truncated_input_errors() {
442 assert!(decode(&[]).is_err());
443 assert!(decode(&[1, 0, 0, 0]).is_err()); }
445
446 #[test]
447 fn rejects_overflowed_noncanonical_and_huge_input_before_allocation() {
448 assert!(matches!(
449 read_varint(&[0x80, 0x00]),
450 Err(CodecError::Corrupt { .. })
451 ));
452 assert!(matches!(
453 read_varint(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02]),
454 Err(CodecError::Corrupt { .. })
455 ));
456 assert!(matches!(
457 read_varint(&[0x80; 10]),
458 Err(CodecError::Corrupt { .. })
459 ));
460 assert!(matches!(
461 read_varint(&[0x80]),
462 Err(CodecError::Truncated { .. })
463 ));
464 let mut huge = u32::MAX.to_le_bytes().to_vec();
465 huge.extend_from_slice(&[0; 8]);
466 assert!(matches!(
467 decode(&huge),
468 Err(CodecError::ResourceLimit { .. })
469 ));
470 }
471
472 #[test]
473 fn compression_vs_raw() {
474 let values: Vec<i64> = (0..100_000).map(|i| i * 1000).collect();
475 let encoded = encode(&values);
476 let raw_size = values.len() * 8;
477 let ratio = raw_size as f64 / encoded.len() as f64;
478 assert!(
479 ratio > 3.0,
480 "expected >3x compression for monotonic counter, got {ratio:.1}x"
481 );
482 }
483}