1use crate::ColumnCodec;
17use crate::error::CodecError;
18
19pub fn encode_i64_pipeline(values: &[i64], codec: ColumnCodec) -> Result<Vec<u8>, CodecError> {
28 match codec {
29 ColumnCodec::DeltaFastLanesLz4 => encode_delta_fastlanes_lz4(values),
31 ColumnCodec::FastLanesLz4 => encode_fastlanes_lz4_i64(values),
32 ColumnCodec::PcodecLz4 => {
33 let pco_bytes = crate::pcodec::encode_i64(values)?;
34 crate::lz4::encode(&pco_bytes)
35 }
36 ColumnCodec::DeltaFastLanesRans => encode_delta_fastlanes_rans(values),
38 ColumnCodec::DoubleDelta => crate::double_delta::encode(values),
40 ColumnCodec::Delta => crate::delta::encode(values),
41 ColumnCodec::Gorilla => Ok(crate::gorilla::encode_timestamps(values)),
42 ColumnCodec::Raw => {
43 let raw: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
44 Ok(crate::raw::encode(&raw))
45 }
46 ColumnCodec::Lz4 => {
47 let raw: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
48 crate::lz4::encode(&raw)
49 }
50 ColumnCodec::Zstd => {
51 let raw: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
52 crate::zstd_codec::encode(&raw)
53 }
54 _ => crate::delta::encode(values),
55 }
56}
57
58pub fn encode_f64_pipeline(values: &[f64], codec: ColumnCodec) -> Result<Vec<u8>, CodecError> {
60 match codec {
61 ColumnCodec::AlpFastLanesLz4 => encode_alp_fastlanes_lz4(values),
63 ColumnCodec::AlpRdLz4 => {
64 let alp_rd_bytes = crate::alp_rd::encode(values)?;
65 crate::lz4::encode(&alp_rd_bytes)
66 }
67 ColumnCodec::PcodecLz4 => {
68 let pco_bytes = crate::pcodec::encode_f64(values)?;
69 crate::lz4::encode(&pco_bytes)
70 }
71 ColumnCodec::AlpFastLanesRans => encode_alp_fastlanes_rans(values),
73 ColumnCodec::Gorilla => Ok(crate::gorilla::encode_f64(values)),
75 ColumnCodec::Raw => {
76 let raw: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
77 Ok(crate::raw::encode(&raw))
78 }
79 ColumnCodec::Lz4 => {
80 let raw: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
81 crate::lz4::encode(&raw)
82 }
83 ColumnCodec::Zstd => {
84 let raw: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
85 crate::zstd_codec::encode(&raw)
86 }
87 _ => Ok(crate::gorilla::encode_f64(values)),
88 }
89}
90
91pub fn encode_bytes_pipeline(raw: &[u8], codec: ColumnCodec) -> Result<Vec<u8>, CodecError> {
93 match codec {
94 ColumnCodec::FsstLz4 => {
95 let fsst_bytes = crate::fsst::encode(&[raw])?;
97 crate::lz4::encode(&fsst_bytes)
98 }
99 ColumnCodec::FsstRans => {
100 let fsst_bytes = crate::fsst::encode(&[raw])?;
101 crate::rans::encode(&fsst_bytes)
102 }
103 ColumnCodec::Raw => Ok(crate::raw::encode(raw)),
104 ColumnCodec::Lz4 => crate::lz4::encode(raw),
105 ColumnCodec::Zstd => crate::zstd_codec::encode(raw),
106 ColumnCodec::FastLanesLz4 => {
107 if raw.len().is_multiple_of(4) {
109 let i64_vals: Vec<i64> = raw
110 .as_chunks::<4>()
111 .0
112 .iter()
113 .map(|c| u32::from_le_bytes(*c) as i64)
114 .collect();
115 encode_fastlanes_lz4_i64(&i64_vals)
116 } else {
117 Ok(crate::raw::encode(raw))
118 }
119 }
120 _ => Ok(crate::raw::encode(raw)),
121 }
122}
123
124pub fn decode_i64_pipeline(data: &[u8], codec: ColumnCodec) -> Result<Vec<i64>, CodecError> {
130 match codec {
131 ColumnCodec::DeltaFastLanesLz4 => decode_delta_fastlanes_lz4(data),
133 ColumnCodec::FastLanesLz4 => decode_fastlanes_lz4_i64(data),
134 ColumnCodec::PcodecLz4 => {
135 let pco_bytes = crate::lz4::decode(data)?;
136 crate::pcodec::decode_i64(&pco_bytes)
137 }
138 ColumnCodec::DeltaFastLanesRans => decode_delta_fastlanes_rans(data),
140 ColumnCodec::DoubleDelta => crate::double_delta::decode(data),
142 ColumnCodec::Delta => crate::delta::decode(data),
143 ColumnCodec::Gorilla => crate::gorilla::decode_timestamps(data),
144 ColumnCodec::Raw => {
145 let raw = crate::raw::decode(data)?;
146 raw_to_i64(&raw)
147 }
148 ColumnCodec::Lz4 => {
149 let raw = crate::lz4::decode(data)?;
150 raw_to_i64(&raw)
151 }
152 ColumnCodec::Zstd => {
153 let raw = crate::zstd_codec::decode(data)?;
154 raw_to_i64(&raw)
155 }
156 _ => crate::delta::decode(data),
157 }
158}
159
160pub fn decode_f64_pipeline(data: &[u8], codec: ColumnCodec) -> Result<Vec<f64>, CodecError> {
162 match codec {
163 ColumnCodec::AlpFastLanesLz4 => decode_alp_fastlanes_lz4(data),
165 ColumnCodec::AlpRdLz4 => {
166 let alp_rd_bytes = crate::lz4::decode(data)?;
167 crate::alp_rd::decode(&alp_rd_bytes)
168 }
169 ColumnCodec::PcodecLz4 => {
170 let pco_bytes = crate::lz4::decode(data)?;
171 crate::pcodec::decode_f64(&pco_bytes)
172 }
173 ColumnCodec::AlpFastLanesRans => decode_alp_fastlanes_rans(data),
175 ColumnCodec::Gorilla => crate::gorilla::decode_f64(data),
177 ColumnCodec::Raw => {
178 let raw = crate::raw::decode(data)?;
179 raw_to_f64(&raw)
180 }
181 ColumnCodec::Lz4 => {
182 let raw = crate::lz4::decode(data)?;
183 raw_to_f64(&raw)
184 }
185 ColumnCodec::Zstd => {
186 let raw = crate::zstd_codec::decode(data)?;
187 raw_to_f64(&raw)
188 }
189 _ => crate::gorilla::decode_f64(data),
190 }
191}
192
193pub fn decode_bytes_pipeline(data: &[u8], codec: ColumnCodec) -> Result<Vec<u8>, CodecError> {
195 match codec {
196 ColumnCodec::FsstLz4 => {
197 let fsst_bytes = crate::lz4::decode(data)?;
198 let strings = crate::fsst::decode(&fsst_bytes)?;
199 strings.into_iter().next().ok_or(CodecError::Corrupt {
200 detail: "FSST decode produced no output".into(),
201 })
202 }
203 ColumnCodec::FsstRans => {
204 let fsst_bytes = crate::rans::decode(data)?;
205 let strings = crate::fsst::decode(&fsst_bytes)?;
206 strings.into_iter().next().ok_or(CodecError::Corrupt {
207 detail: "FSST decode produced no output".into(),
208 })
209 }
210 ColumnCodec::Raw => crate::raw::decode(data),
211 ColumnCodec::Lz4 => crate::lz4::decode(data),
212 ColumnCodec::Zstd => crate::zstd_codec::decode(data),
213 ColumnCodec::FastLanesLz4 => {
214 let i64_vals = decode_fastlanes_lz4_i64(data)?;
215 Ok(i64_vals
216 .iter()
217 .flat_map(|&v| (v as u32).to_le_bytes())
218 .collect())
219 }
220 _ => crate::raw::decode(data).or_else(|_| Ok(data.to_vec())),
221 }
222}
223
224fn encode_alp_fastlanes_lz4(values: &[f64]) -> Result<Vec<u8>, CodecError> {
230 let alp_encoded = crate::alp::encode(values)?;
232 crate::lz4::encode(&alp_encoded)
234}
235
236fn decode_alp_fastlanes_lz4(data: &[u8]) -> Result<Vec<f64>, CodecError> {
237 let alp_bytes = crate::lz4::decode(data)?;
239 crate::alp::decode(&alp_bytes)
240}
241
242fn encode_delta_fastlanes_lz4(values: &[i64]) -> Result<Vec<u8>, CodecError> {
244 if values.is_empty() {
245 return crate::lz4::encode(&crate::fastlanes::encode(&[])?);
246 }
247
248 let mut deltas = Vec::with_capacity(values.len());
250 deltas.push(values[0]); for i in 1..values.len() {
252 deltas.push(values[i].wrapping_sub(values[i - 1]));
253 }
254
255 let packed = crate::fastlanes::encode(&deltas)?;
257
258 crate::lz4::encode(&packed)
260}
261
262fn decode_delta_fastlanes_lz4(data: &[u8]) -> Result<Vec<i64>, CodecError> {
263 let packed = crate::lz4::decode(data)?;
265 let deltas = crate::fastlanes::decode(&packed)?;
266
267 if deltas.is_empty() {
268 return Ok(Vec::new());
269 }
270
271 let mut values = Vec::with_capacity(deltas.len());
273 values.push(deltas[0]); for &d in &deltas[1..] {
275 let prev = values[values.len() - 1];
276 values.push(prev.wrapping_add(d));
277 }
278
279 Ok(values)
280}
281
282fn encode_fastlanes_lz4_i64(values: &[i64]) -> Result<Vec<u8>, CodecError> {
284 let packed = crate::fastlanes::encode(values)?;
285 crate::lz4::encode(&packed)
286}
287
288fn decode_fastlanes_lz4_i64(data: &[u8]) -> Result<Vec<i64>, CodecError> {
289 let packed = crate::lz4::decode(data)?;
290 crate::fastlanes::decode(&packed)
291}
292
293fn encode_alp_fastlanes_rans(values: &[f64]) -> Result<Vec<u8>, CodecError> {
295 let alp_encoded = crate::alp::encode(values)?;
296 crate::rans::encode(&alp_encoded)
297}
298
299fn decode_alp_fastlanes_rans(data: &[u8]) -> Result<Vec<f64>, CodecError> {
300 let alp_bytes = crate::rans::decode(data)?;
301 crate::alp::decode(&alp_bytes)
302}
303
304fn encode_delta_fastlanes_rans(values: &[i64]) -> Result<Vec<u8>, CodecError> {
306 if values.is_empty() {
307 return crate::rans::encode(&crate::fastlanes::encode(&[])?);
308 }
309
310 let mut deltas = Vec::with_capacity(values.len());
311 deltas.push(values[0]);
312 for i in 1..values.len() {
313 deltas.push(values[i].wrapping_sub(values[i - 1]));
314 }
315
316 let packed = crate::fastlanes::encode(&deltas)?;
317 crate::rans::encode(&packed)
318}
319
320fn decode_delta_fastlanes_rans(data: &[u8]) -> Result<Vec<i64>, CodecError> {
321 let packed = crate::rans::decode(data)?;
322 let deltas = crate::fastlanes::decode(&packed)?;
323
324 if deltas.is_empty() {
325 return Ok(Vec::new());
326 }
327
328 let mut values = Vec::with_capacity(deltas.len());
329 values.push(deltas[0]);
330 for &d in &deltas[1..] {
331 let prev = values[values.len() - 1];
332 values.push(prev.wrapping_add(d));
333 }
334
335 Ok(values)
336}
337
338fn raw_to_i64(data: &[u8]) -> Result<Vec<i64>, CodecError> {
343 if !data.len().is_multiple_of(8) {
344 return Err(CodecError::Corrupt {
345 detail: "i64 data not aligned to 8 bytes".into(),
346 });
347 }
348 Ok(data
349 .as_chunks::<8>()
350 .0
351 .iter()
352 .map(|c| i64::from_le_bytes(*c))
353 .collect())
354}
355
356fn raw_to_f64(data: &[u8]) -> Result<Vec<f64>, CodecError> {
357 if !data.len().is_multiple_of(8) {
358 return Err(CodecError::Corrupt {
359 detail: "f64 data not aligned to 8 bytes".into(),
360 });
361 }
362 Ok(data
363 .as_chunks::<8>()
364 .0
365 .iter()
366 .map(|c| f64::from_le_bytes(*c))
367 .collect())
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 #[test]
375 fn alp_fastlanes_lz4_decimal_metrics() {
376 let values: Vec<f64> = (0..10_000).map(|i| i as f64 * 0.1).collect();
377 let encoded = encode_f64_pipeline(&values, ColumnCodec::AlpFastLanesLz4).unwrap();
378 let decoded = decode_f64_pipeline(&encoded, ColumnCodec::AlpFastLanesLz4).unwrap();
379
380 for (i, (a, b)) in values.iter().zip(decoded.iter()).enumerate() {
381 assert_eq!(a.to_bits(), b.to_bits(), "mismatch at {i}");
382 }
383
384 let raw_size = values.len() * 8;
385 let ratio = raw_size as f64 / encoded.len() as f64;
386 assert!(
387 ratio > 3.0,
388 "ALP+FL+LZ4 should compress decimals >3x, got {ratio:.1}x"
389 );
390 }
391
392 #[test]
393 fn alp_beats_gorilla_on_decimals() {
394 let mut values = Vec::with_capacity(10_000);
395 let mut rng: u64 = 42;
396 for _ in 0..10_000 {
397 rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
398 let cpu = ((rng >> 33) as f64 / (u32::MAX as f64)) * 100.0;
399 values.push((cpu * 10.0).round() / 10.0);
400 }
401
402 let alp_size = encode_f64_pipeline(&values, ColumnCodec::AlpFastLanesLz4)
403 .unwrap()
404 .len();
405 let gorilla_size = encode_f64_pipeline(&values, ColumnCodec::Gorilla)
406 .unwrap()
407 .len();
408
409 assert!(
410 alp_size < gorilla_size,
411 "ALP ({alp_size}) should beat Gorilla ({gorilla_size}) on decimal metrics"
412 );
413 }
414
415 #[test]
416 fn delta_fastlanes_lz4_timestamps() {
417 let values: Vec<i64> = (0..10_000)
418 .map(|i| 1_700_000_000_000 + i * 10_000)
419 .collect();
420 let encoded = encode_i64_pipeline(&values, ColumnCodec::DeltaFastLanesLz4).unwrap();
421 let decoded = decode_i64_pipeline(&encoded, ColumnCodec::DeltaFastLanesLz4).unwrap();
422 assert_eq!(decoded, values);
423
424 let raw_size = values.len() * 8;
425 let ratio = raw_size as f64 / encoded.len() as f64;
426 assert!(
427 ratio > 5.0,
428 "Delta+FL+LZ4 should compress timestamps >5x, got {ratio:.1}x"
429 );
430 }
431
432 #[test]
433 fn delta_fastlanes_lz4_jittered_timestamps() {
434 let mut values = Vec::with_capacity(10_000);
435 let mut ts = 1_700_000_000_000i64;
436 let mut rng: u64 = 42;
437 for _ in 0..10_000 {
438 values.push(ts);
439 rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
440 let jitter = ((rng >> 33) as i64 % 101) - 50;
441 ts += 10_000 + jitter;
442 }
443 let encoded = encode_i64_pipeline(&values, ColumnCodec::DeltaFastLanesLz4).unwrap();
444 let decoded = decode_i64_pipeline(&encoded, ColumnCodec::DeltaFastLanesLz4).unwrap();
445 assert_eq!(decoded, values);
446 }
447
448 #[test]
449 fn delta_fastlanes_lz4_counters() {
450 let values: Vec<i64> = (0..10_000).map(|i| i * 1000).collect();
451 let encoded = encode_i64_pipeline(&values, ColumnCodec::DeltaFastLanesLz4).unwrap();
452 let decoded = decode_i64_pipeline(&encoded, ColumnCodec::DeltaFastLanesLz4).unwrap();
453 assert_eq!(decoded, values);
454 }
455
456 #[test]
457 fn fastlanes_lz4_symbol_ids() {
458 let values: Vec<i64> = (0..5000).map(|i| i % 150).collect();
459 let encoded = encode_i64_pipeline(&values, ColumnCodec::FastLanesLz4).unwrap();
460 let decoded = decode_i64_pipeline(&encoded, ColumnCodec::FastLanesLz4).unwrap();
461 assert_eq!(decoded, values);
462 }
463
464 #[test]
465 fn legacy_codecs_still_work() {
466 let i64_vals: Vec<i64> = (0..1000).collect();
467 for codec in [
468 ColumnCodec::DoubleDelta,
469 ColumnCodec::Delta,
470 ColumnCodec::Gorilla,
471 ] {
472 let encoded = encode_i64_pipeline(&i64_vals, codec).unwrap();
473 let decoded = decode_i64_pipeline(&encoded, codec).unwrap();
474 assert_eq!(decoded, i64_vals, "legacy i64 codec {codec} failed");
475 }
476
477 let f64_vals: Vec<f64> = (0..1000).map(|i| i as f64 * 0.5).collect();
478 let encoded = encode_f64_pipeline(&f64_vals, ColumnCodec::Gorilla).unwrap();
479 let decoded = decode_f64_pipeline(&encoded, ColumnCodec::Gorilla).unwrap();
480 for (a, b) in f64_vals.iter().zip(decoded.iter()) {
481 assert_eq!(a.to_bits(), b.to_bits());
482 }
483 }
484
485 #[test]
486 fn empty_values() {
487 let empty_i64: Vec<i64> = vec![];
488 let empty_f64: Vec<f64> = vec![];
489
490 for codec in [
491 ColumnCodec::DeltaFastLanesLz4,
492 ColumnCodec::FastLanesLz4,
493 ColumnCodec::AlpFastLanesLz4,
494 ] {
495 if matches!(codec, ColumnCodec::AlpFastLanesLz4) {
496 let enc = encode_f64_pipeline(&empty_f64, codec).unwrap();
497 let dec = decode_f64_pipeline(&enc, codec).unwrap();
498 assert!(dec.is_empty());
499 } else {
500 let enc = encode_i64_pipeline(&empty_i64, codec).unwrap();
501 let dec = decode_i64_pipeline(&enc, codec).unwrap();
502 assert!(dec.is_empty());
503 }
504 }
505 }
506
507 #[test]
508 fn bytes_pipeline_roundtrip() {
509 let raw: Vec<u8> = (0..1000u32).flat_map(|i| i.to_le_bytes()).collect();
510 for codec in [ColumnCodec::Raw, ColumnCodec::Lz4] {
511 let encoded = encode_bytes_pipeline(&raw, codec).unwrap();
512 let decoded = decode_bytes_pipeline(&encoded, codec).unwrap();
513 assert_eq!(decoded, raw, "bytes pipeline {codec} failed");
514 }
515 }
516}