1use crate::coding::{checked_count, checked_decode_count};
37use crate::error::{EncodingError, Result};
38use crate::unified::{ColumnDecoder, ColumnEncoder, DecodedColumn, EncodedBlock, EncodingType};
39
40pub(crate) const FORM_NARROW: u8 = 0;
42pub(crate) const FORM_WIDE: u8 = 1;
44
45#[inline]
47fn zigzag_encode_i128(n: i128) -> u128 {
48 ((n << 1) ^ (n >> 127)) as u128
49}
50
51#[inline]
53fn zigzag_decode_i128(n: u128) -> i128 {
54 ((n >> 1) as i128) ^ -((n & 1) as i128)
55}
56
57#[inline]
59fn varint_encode_u128(mut value: u128, out: &mut Vec<u8>) {
60 while value >= 0x80 {
61 out.push((value as u8) | 0x80);
62 value >>= 7;
63 }
64 out.push(value as u8);
65}
66
67fn varint_decode_u128(bytes: &[u8]) -> Result<(u128, usize)> {
69 let mut value: u128 = 0;
70 let mut shift = 0u32;
71 for (i, &byte) in bytes.iter().enumerate() {
72 if shift > 126 {
75 return Err(EncodingError::CorruptData {
76 detail: "decimal varint exceeds 128 bits".to_string(),
77 });
78 }
79 value |= u128::from(byte & 0x7f) << shift;
80 if byte & 0x80 == 0 {
81 return Ok((value, i + 1));
82 }
83 shift += 7;
84 }
85 Err(EncodingError::CorruptData {
86 detail: "decimal varint truncated".to_string(),
87 })
88}
89
90#[derive(Debug, Clone, Copy)]
92pub struct DecimalEncoder;
93
94impl DecimalEncoder {
95 pub fn encode(values: &[i128]) -> Result<Vec<u8>> {
102 if values.is_empty() {
103 return Err(EncodingError::EmptyInput {
104 context: "decimal encoder",
105 });
106 }
107
108 let narrow = values.iter().all(|&v| i64::try_from(v).is_ok());
111
112 if narrow {
113 let as_i64: Vec<i64> = values.iter().map(|&v| v as i64).collect();
114 let inner = ColumnEncoder::encode_i64(&as_i64)?;
115 let mut out = Vec::with_capacity(2 + inner.payload.len());
116 out.push(FORM_NARROW);
117 out.push(inner.encoding.tag());
118 out.extend_from_slice(&inner.payload);
119 return Ok(out);
120 }
121
122 let count = checked_count(values.len())?;
123 let mut out = Vec::with_capacity(1 + 4 + 16 + values.len() * 4);
124 out.push(FORM_WIDE);
125 out.extend_from_slice(&count.to_le_bytes());
126 out.extend_from_slice(&values[0].to_le_bytes());
127 for window in values.windows(2) {
128 let delta = window[1].wrapping_sub(window[0]);
131 varint_encode_u128(zigzag_encode_i128(delta), &mut out);
132 }
133 Ok(out)
134 }
135}
136
137#[derive(Debug, Clone, Copy)]
139pub struct DecimalDecoder;
140
141impl DecimalDecoder {
142 pub fn decode(payload: &[u8]) -> Result<Vec<i128>> {
150 let (&form, rest) = payload
151 .split_first()
152 .ok_or_else(|| EncodingError::CorruptData {
153 detail: "empty decimal block".to_string(),
154 })?;
155
156 match form {
157 FORM_NARROW => Self::decode_narrow(rest),
158 FORM_WIDE => Self::decode_wide(rest),
159 other => Err(EncodingError::CorruptData {
160 detail: format!("unknown decimal form: {other}"),
161 }),
162 }
163 }
164
165 fn decode_narrow(rest: &[u8]) -> Result<Vec<i128>> {
168 let (&tag, inner) = rest
169 .split_first()
170 .ok_or_else(|| EncodingError::CorruptData {
171 detail: "narrow decimal block has no inner encoding tag".to_string(),
172 })?;
173 let encoding = EncodingType::from_tag(tag)?;
174 if encoding == EncodingType::DecimalI128 {
178 return Err(EncodingError::CorruptData {
179 detail: "narrow decimal block nests a decimal block".to_string(),
180 });
181 }
182 let block = EncodedBlock {
183 encoding,
184 payload: inner.to_vec(),
185 };
186 match ColumnDecoder::decode(&block)? {
187 DecodedColumn::I64(values) => Ok(values.into_iter().map(i128::from).collect()),
188 other => Err(EncodingError::CorruptData {
189 detail: format!(
190 "narrow decimal block decoded to {} values, expected i64",
191 match other {
192 DecodedColumn::F64(_) => "f64",
193 DecodedColumn::U64(_) => "u64",
194 DecodedColumn::Bool(_) => "bool",
195 DecodedColumn::String(_) => "string",
196 _ => "nullable or wide",
197 }
198 ),
199 }),
200 }
201 }
202
203 fn decode_wide(rest: &[u8]) -> Result<Vec<i128>> {
205 if rest.len() < 4 + 16 {
206 return Err(EncodingError::CorruptData {
207 detail: "wide decimal block truncated".to_string(),
208 });
209 }
210 let count = checked_decode_count(
211 u32::from_le_bytes([rest[0], rest[1], rest[2], rest[3]]) as usize,
212 "decimal",
213 )?;
214 if count == 0 {
215 return Err(EncodingError::CorruptData {
216 detail: "wide decimal block declares zero values".to_string(),
217 });
218 }
219 let mut first_bytes = [0u8; 16];
220 first_bytes.copy_from_slice(&rest[4..20]);
221 let mut current = i128::from_le_bytes(first_bytes);
222
223 let mut values = Vec::with_capacity(count);
224 values.push(current);
225 let mut cursor = 20;
226 for _ in 1..count {
227 let (zz, used) = varint_decode_u128(&rest[cursor..])?;
228 cursor += used;
229 current = current.wrapping_add(zigzag_decode_i128(zz));
230 values.push(current);
231 }
232 Ok(values)
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 fn round_trip(values: &[i128]) {
241 let encoded = DecimalEncoder::encode(values).unwrap();
242 let decoded = DecimalDecoder::decode(&encoded).unwrap();
243 assert_eq!(decoded, values, "round trip");
244 }
245
246 #[test]
247 fn narrow_round_trip() {
248 round_trip(&[23_145]);
249 round_trip(&[0, 1, 2, 3, 4, 5]);
250 round_trip(&[-5, -4, -3, 0, 3, 4, 5]);
251 round_trip(&[i64::MAX as i128, i64::MIN as i128]);
252 }
253
254 #[test]
255 fn wide_round_trip() {
256 round_trip(&[i128::from(i64::MAX) + 1]);
257 round_trip(&[
258 99_999_999_999_999_999_999_999_999_999_999_999_999,
259 -99_999_999_999_999_999_999_999_999_999_999_999_999,
260 0,
261 ]);
262 }
263
264 #[test]
265 fn form_is_narrow_when_every_mantissa_fits_i64() {
266 let encoded = DecimalEncoder::encode(&[1, 2, 3]).unwrap();
267 assert_eq!(encoded[0], FORM_NARROW);
268 let encoded = DecimalEncoder::encode(&[i128::from(i64::MAX) + 1]).unwrap();
269 assert_eq!(encoded[0], FORM_WIDE);
270 }
271
272 #[test]
273 fn a_monotone_register_compresses() {
274 let values: Vec<i128> = (0..4096).map(|i| 10_000_000_000 + i * 2_500_000).collect();
278 let encoded = DecimalEncoder::encode(&values).unwrap();
279 let raw = values.len() * 16;
280 assert!(
281 encoded.len() * 8 < raw,
282 "expected >8x on a monotone register, got {} vs {raw}",
283 encoded.len()
284 );
285 assert_eq!(DecimalDecoder::decode(&encoded).unwrap(), values);
286 }
287
288 #[test]
289 fn a_constant_register_compresses_hard() {
290 let values = vec![42_0000i128; 4096];
291 let encoded = DecimalEncoder::encode(&values).unwrap();
292 assert!(
293 encoded.len() < 128,
294 "constant column: {} bytes",
295 encoded.len()
296 );
297 assert_eq!(DecimalDecoder::decode(&encoded).unwrap(), values);
298 }
299
300 #[test]
301 fn empty_input_is_an_error() {
302 assert!(matches!(
303 DecimalEncoder::encode(&[]),
304 Err(EncodingError::EmptyInput { .. })
305 ));
306 }
307
308 #[test]
309 fn corrupt_form_byte_is_an_error() {
310 assert!(DecimalDecoder::decode(&[]).is_err());
311 assert!(DecimalDecoder::decode(&[7]).is_err());
312 }
313
314 #[test]
315 fn a_nested_decimal_tag_does_not_recurse() {
316 let nested = [FORM_NARROW, EncodingType::DecimalI128.tag(), 0, 0];
319 assert!(DecimalDecoder::decode(&nested).is_err());
320 }
321
322 #[test]
323 fn truncated_wide_block_is_an_error() {
324 let encoded = DecimalEncoder::encode(&[i128::from(i64::MAX) + 1, 5, 9]).unwrap();
325 for cut in 1..encoded.len() {
326 let _ = DecimalDecoder::decode(&encoded[..cut]);
329 }
330 }
331
332 #[test]
333 fn a_corrupt_count_cannot_allocate_the_machine_away() {
334 let mut encoded = DecimalEncoder::encode(&[i128::from(i64::MAX) + 1]).unwrap();
335 encoded[1..5].copy_from_slice(&u32::MAX.to_le_bytes());
336 assert!(DecimalDecoder::decode(&encoded).is_err());
337 }
338
339 #[test]
340 fn varint_round_trip_at_the_boundaries() {
341 for value in [0u128, 1, 127, 128, u128::from(u64::MAX), u128::MAX] {
342 let mut buf = Vec::new();
343 varint_encode_u128(value, &mut buf);
344 let (decoded, used) = varint_decode_u128(&buf).unwrap();
345 assert_eq!(decoded, value);
346 assert_eq!(used, buf.len());
347 }
348 }
349
350 #[test]
351 fn zigzag_round_trip_at_the_boundaries() {
352 for value in [0i128, 1, -1, i128::MAX, i128::MIN, i128::from(i64::MIN)] {
353 assert_eq!(zigzag_decode_i128(zigzag_encode_i128(value)), value);
354 }
355 }
356
357 use proptest::prelude::*;
367
368 proptest! {
369 #[test]
375 fn roundtrip_arbitrary_mantissas(
376 values in prop::collection::vec(any::<i128>(), 1..200),
377 ) {
378 let encoded = DecimalEncoder::encode(&values).unwrap();
379 let decoded = DecimalDecoder::decode(&encoded).unwrap();
380 prop_assert_eq!(decoded, values);
381 }
382
383 #[test]
387 fn roundtrip_narrow_mantissas(
388 values in prop::collection::vec(any::<i64>(), 1..200),
389 ) {
390 let wide: Vec<i128> = values.iter().map(|&v| i128::from(v)).collect();
391 let encoded = DecimalEncoder::encode(&wide).unwrap();
392 prop_assert_eq!(encoded[0], FORM_NARROW);
393 let decoded = DecimalDecoder::decode(&encoded).unwrap();
394 prop_assert_eq!(decoded, wide);
395 }
396
397 #[test]
400 fn roundtrip_wide_deltas_that_may_wrap(
401 base in any::<i128>(),
402 steps in prop::collection::vec(any::<i64>(), 1..200),
403 ) {
404 let mut values = vec![base];
405 for step in steps {
406 let prev = *values.last().unwrap();
407 values.push(prev.wrapping_add(i128::from(step)));
408 }
409 let encoded = DecimalEncoder::encode(&values).unwrap();
410 let decoded = DecimalDecoder::decode(&encoded).unwrap();
411 prop_assert_eq!(decoded, values);
412 }
413
414 #[test]
416 fn decode_never_panics(bytes in prop::collection::vec(any::<u8>(), 0..512)) {
417 let _ = DecimalDecoder::decode(&bytes);
418 }
419
420 #[test]
423 fn varint_roundtrips_every_u128(value in any::<u128>()) {
424 let mut buf = Vec::new();
425 varint_encode_u128(value, &mut buf);
426 let (decoded, used) = varint_decode_u128(&buf).unwrap();
427 prop_assert_eq!(decoded, value);
428 prop_assert_eq!(used, buf.len());
429 }
430
431 #[test]
433 fn zigzag_roundtrips_every_i128(value in any::<i128>()) {
434 prop_assert_eq!(zigzag_decode_i128(zigzag_encode_i128(value)), value);
435 }
436 }
437}