use std::os::raw::c_void;
use openzl_sys::*;
fn zl_is_error(report: ZL_Report) -> bool {
unsafe { report._code != ZL_ErrorCode_no_error }
}
fn zl_valid_result(report: ZL_Report) -> usize {
unsafe { report._value._value }
}
unsafe extern "C" fn struct_compression_graph(compressor: *mut ZL_Compressor) -> ZL_GraphID {
let result = ZL_Compressor_setParameter(
compressor,
ZL_CParam_formatVersion,
ZL_MAX_FORMAT_VERSION as i32,
);
if zl_is_error(result) {
return ZL_GraphID {
gid: ZL_StandardGraphID_illegal,
};
}
let header_size: usize = 28;
let nodes = [
ZL_NodeID {
nid: ZL_StandardNodeID_convert_struct_to_num_le,
},
ZL_NodeID {
nid: ZL_StandardNodeID_delta_int,
},
];
let sra0 = ZL_Compressor_registerStaticGraph_fromPipelineNodes1o(
compressor,
nodes.as_ptr(),
nodes.len(),
ZL_GraphID {
gid: ZL_StandardGraphID_field_lz,
},
);
let sdec0 = ZL_Compressor_registerStaticGraph_fromNode1o(
compressor,
ZL_NodeID {
nid: ZL_StandardNodeID_transpose_split,
},
ZL_GraphID {
gid: ZL_StandardGraphID_zstd,
},
);
let token_compress = ZL_Compressor_registerTokenizeGraph(
compressor,
ZL_Type_struct,
false,
ZL_GraphID {
gid: ZL_StandardGraphID_field_lz,
},
ZL_GraphID {
gid: ZL_StandardGraphID_field_lz,
},
);
let huffman_inner = ZL_Compressor_registerTokenizeGraph(
compressor,
ZL_Type_numeric,
false,
ZL_GraphID {
gid: ZL_StandardGraphID_huffman,
},
ZL_GraphID {
gid: ZL_StandardGraphID_huffman,
},
);
let num_huffman = ZL_Compressor_registerStaticGraph_fromNode1o(
compressor,
ZL_NodeID {
nid: ZL_StandardNodeID_convert_struct_to_num_le,
},
huffman_inner,
);
let is = num_huffman;
let mag = num_huffman;
let xrpm = token_compress;
let xdpm = token_compress;
let field_sizes = [8usize, 8, 2, 2, 4, 4];
let field_graphs = [sra0, sdec0, is, mag, xrpm, xdpm];
let split_structure = ZL_Compressor_registerSplitByStructGraph(
compressor,
field_sizes.as_ptr(),
field_graphs.as_ptr(),
6,
);
let sizes = [header_size, 0]; let graphs = [
ZL_GraphID {
gid: ZL_StandardGraphID_store,
},
split_structure,
];
ZL_Compressor_registerSplitGraph(
compressor,
ZL_Type_serial,
sizes.as_ptr(),
graphs.as_ptr(),
2,
)
}
unsafe extern "C" fn graph_with_parameters(compressor: *mut ZL_Compressor) -> ZL_GraphID {
let graph_id = struct_compression_graph(compressor);
let illegal = ZL_GraphID {
gid: ZL_StandardGraphID_illegal,
};
if graph_id.gid == illegal.gid {
return illegal;
}
let result = ZL_Compressor_setParameter(compressor, ZL_CParam_compressionLevel, 3);
if zl_is_error(result) {
return illegal;
}
graph_id
}
fn compress(src: &[u8]) -> Result<Vec<u8>, String> {
unsafe {
let src_size = src.len();
let dst_capacity = ZL_compressBound(src_size);
let mut dst = vec![0u8; dst_capacity];
let report = ZL_compress_usingGraphFn(
dst.as_mut_ptr() as *mut c_void,
dst_capacity,
src.as_ptr() as *const c_void,
src_size,
Some(graph_with_parameters),
);
if zl_is_error(report) {
return Err(format!(
"Compression failed with error code: {:?}",
report._code
));
}
let compressed_size = zl_valid_result(report);
dst.truncate(compressed_size);
Ok(dst)
}
}
fn decompress(compressed: &[u8]) -> Result<Vec<u8>, String> {
unsafe {
let size_report =
ZL_getDecompressedSize(compressed.as_ptr() as *const c_void, compressed.len());
if zl_is_error(size_report) {
return Err(format!(
"Failed to get decompressed size, error code: {:?}",
size_report._code
));
}
let decompressed_size = zl_valid_result(size_report);
let mut dst = vec![0u8; decompressed_size];
let report = ZL_decompress(
dst.as_mut_ptr() as *mut c_void,
decompressed_size,
compressed.as_ptr() as *const c_void,
compressed.len(),
);
if zl_is_error(report) {
return Err(format!(
"Decompression failed with error code: {:?}",
report._code
));
}
let actual_size = zl_valid_result(report);
assert_eq!(actual_size, decompressed_size);
Ok(dst)
}
}
#[test]
fn test_compress_decompress_roundtrip() {
const STRUCT_SIZE: usize = 28;
const NUM_STRUCTS: usize = 100;
const HEADER_SIZE: usize = 28;
let mut input = Vec::new();
input.extend_from_slice(&[0u8; HEADER_SIZE]);
for i in 0..NUM_STRUCTS {
let mut structure = [0u8; STRUCT_SIZE];
structure[0..8].copy_from_slice(&(i as u64).to_le_bytes());
structure[8..16].copy_from_slice(&((i * 2) as u64).to_le_bytes());
structure[16] = (i % 256) as u8;
structure[17] = ((i + 1) % 256) as u8;
structure[18..22].copy_from_slice(&((i as f32) * 0.5).to_le_bytes());
structure[22..26].copy_from_slice(&((i as f32) * 0.25).to_le_bytes());
structure[26] = ((i * 3) % 256) as u8;
structure[27] = ((i * 5) % 256) as u8;
input.extend_from_slice(&structure);
}
let input_size = input.len();
println!("Input size: {} bytes", input_size);
println!("Compress bound: {} bytes", unsafe {
ZL_compressBound(input_size)
});
let compressed = compress(&input).expect("Compression should succeed");
let compressed_size = compressed.len();
println!("Compressed size: {} bytes", compressed_size);
assert!(
compressed_size < input_size,
"Compression should reduce size"
);
let decompressed = decompress(&compressed).expect("Decompression should succeed");
assert_eq!(
decompressed.len(),
input_size,
"Decompressed size should match original"
);
assert_eq!(
decompressed, input,
"Decompressed data should match original"
);
println!("Round-trip test passed!");
println!(
"Compression ratio: {:.2}%",
(compressed_size as f64 / input_size as f64) * 100.0
);
}