use crate::backend::native::v3::compression::varint::{decode_varint, encode_varint};
fn zigzag_encode(value: i64) -> u64 {
((value << 1) ^ (value >> 63)) as u64
}
fn zigzag_decode(value: u64) -> i64 {
((value >> 1) as i64) ^ -((value & 1) as i64)
}
pub fn compress_edge_ids(edge_ids: &[i64]) -> Vec<u8> {
if edge_ids.is_empty() {
return Vec::new();
}
let mut compressed = Vec::new();
let mut prev_id = 0i64;
for &edge_id in edge_ids {
let delta = edge_id - prev_id;
let zigzag = zigzag_encode(delta);
let encoded = encode_varint(zigzag);
compressed.extend_from_slice(&encoded);
prev_id = edge_id;
}
compressed
}
pub fn decompress_edge_ids(compressed: &[u8], count: usize) -> Result<Vec<i64>, String> {
if count == 0 {
return Ok(Vec::new());
}
let mut edge_ids = Vec::with_capacity(count);
let mut prev_id = 0i64;
let mut pos = 0;
for _ in 0..count {
if pos >= compressed.len() {
return Err(format!(
"Insufficient data: expected {} values, but only {} bytes available",
count,
compressed.len()
));
}
match decode_varint(&compressed[pos..]) {
Ok((zigzag, bytes_read)) => {
let delta = zigzag_decode(zigzag);
prev_id += delta;
edge_ids.push(prev_id);
pos += bytes_read;
}
Err(e) => {
return Err(format!(
"Failed to decode varint at position {}: {:?}",
pos, e
));
}
}
}
Ok(edge_ids)
}
pub fn compression_ratio(original: &[i64], compressed: &[u8]) -> f32 {
if original.is_empty() {
return 1.0;
}
let original_size = original.len() * 8; let compressed_size = compressed.len();
compressed_size as f32 / original_size as f32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zigzag_encode_decode() {
let test_cases = vec![
(0, 0),
(1, 2),
(-1, 1),
(2, 4),
(-2, 3),
(100, 200),
(-100, 199),
];
for (original, encoded) in test_cases {
assert_eq!(zigzag_encode(original), encoded);
assert_eq!(zigzag_decode(encoded), original);
}
}
#[test]
fn test_compress_decompress_sequential_ids() {
let ids = vec![1, 2, 3, 4, 5];
let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
}
#[test]
fn test_compress_decompress_sparse_ids() {
let ids = vec![1, 5, 10, 100, 1000];
let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
}
#[test]
fn test_compress_empty_slice() {
let ids: Vec<i64> = vec![];
let compressed = compress_edge_ids(&ids);
assert!(compressed.is_empty());
}
#[test]
fn test_decompress_empty() {
let compressed = vec![];
let result = decompress_edge_ids(&compressed, 0).unwrap();
assert!(result.is_empty());
}
#[test]
fn test_compression_ratio_sequential() {
let ids: Vec<i64> = (1..=1000).collect();
let compressed = compress_edge_ids(&ids);
let ratio = compression_ratio(&ids, &compressed);
assert!(
ratio < 0.5,
"Compression ratio {} should be < 0.5 for sequential IDs",
ratio
);
}
#[test]
fn test_compression_ratio_sparse() {
let ids: Vec<i64> = (1..=1000).filter(|x| x % 10 == 0).collect();
let compressed = compress_edge_ids(&ids);
let ratio = compression_ratio(&ids, &compressed);
assert!(ratio < 1.0, "Compression ratio {} should be < 1.0", ratio);
}
#[test]
fn test_large_delta_values() {
let ids = vec![1, 1000, 1000000, 1000000000];
let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
}
#[test]
fn test_negative_deltas() {
let ids = vec![100, 50, 25, 10]; let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
}
#[test]
fn test_alternating_sequence() {
let ids = vec![10, 20, 15, 25, 30, 20]; let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
}
#[test]
fn test_decompress_insufficient_data() {
let compressed = vec![0x80]; let result = decompress_edge_ids(&compressed, 1);
assert!(result.is_err(), "Should fail with insufficient data");
}
#[test]
fn test_single_id() {
let ids = vec![42];
let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
assert_eq!(compressed.len(), 1);
}
#[test]
fn test_zero_start() {
let ids = vec![0, 1, 2, 3];
let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
}
#[test]
fn test_negative_ids() {
let ids = vec![-100, -50, 0, 50, 100];
let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
}
#[test]
fn test_large_negative_deltas() {
let ids = vec![1000000000, -1000000000, 0, 1000000000];
let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
}
#[test]
fn test_compression_efficiency_sequential() {
let ids: Vec<i64> = (1..=100).collect();
let compressed = compress_edge_ids(&ids);
assert!(
compressed.len() < 150,
"Sequential IDs should compress to < 150 bytes, got {}",
compressed.len()
);
}
#[test]
fn test_extreme_values() {
let ids = vec![i64::MIN, i64::MIN + 1, 0, i64::MAX - 1, i64::MAX];
let compressed = compress_edge_ids(&ids);
let decompressed = decompress_edge_ids(&compressed, ids.len()).unwrap();
assert_eq!(decompressed, ids);
}
}