#![cfg(c_ffi)]
use sparsemap::SparseMap;
use std::collections::BTreeSet;
use std::os::raw::c_void;
#[allow(non_camel_case_types)]
type sm_t = c_void;
extern "C" {
fn sm_create(size: usize) -> *mut sm_t;
fn sm_free(map: *mut sm_t);
fn sm_add(map: *mut sm_t, idx: u64) -> u64;
fn sm_contains(map: *mut sm_t, idx: u64) -> bool;
fn sm_cardinality(map: *mut sm_t) -> usize;
fn sm_serialized_size(map: *const sm_t) -> usize;
fn sm_serialize(map: *const sm_t, out: *mut u8, out_size: usize) -> usize;
fn sm_deserialize(input: *const u8, n: usize) -> *mut sm_t;
}
fn c_serialize(bits: &BTreeSet<u64>) -> Vec<u8> {
unsafe {
let map = sm_create(4096);
assert!(!map.is_null());
for &b in bits {
let rc = sm_add(map, b);
assert_ne!(rc, u64::MAX, "C sm_add ENOSPC; enlarge test buffer");
}
assert_eq!(sm_cardinality(map), bits.len());
let sz = sm_serialized_size(map);
let mut buf = vec![0u8; sz];
let wrote = sm_serialize(map, buf.as_mut_ptr(), sz);
assert_eq!(wrote, sz);
sm_free(map);
buf
}
}
fn c_deserialize_bits(bytes: &[u8], probe: &BTreeSet<u64>) -> (bool, Vec<u64>) {
unsafe {
let map = sm_deserialize(bytes.as_ptr(), bytes.len());
if map.is_null() {
return (false, Vec::new());
}
let present: Vec<u64> = probe
.iter()
.copied()
.filter(|&b| sm_contains(map, b))
.collect();
sm_free(map);
(true, present)
}
}
fn sample_sets() -> Vec<BTreeSet<u64>> {
vec![
BTreeSet::new(),
[42u64].into_iter().collect(),
[1, 2, 3, 2047, 2048, 4096, 100_000].into_iter().collect(),
(0..5000u64).collect(), (0..2048u64 * 4).collect(), (0..100u64).chain(10_000..10_050).collect(),
(1000..1000u64 + 6000).collect(), ]
}
#[test]
fn c_writes_rust_reads() {
for bits in sample_sets() {
let c_bytes = c_serialize(&bits);
let m = SparseMap::from_bytes(&c_bytes).expect("Rust must deserialize C output");
let got: BTreeSet<u64> = m.iter().collect();
assert_eq!(got, bits, "Rust read of C bytes diverged");
}
}
#[test]
fn rust_writes_c_reads() {
for bits in sample_sets() {
let m: SparseMap = bits.iter().copied().collect();
let rust_bytes = m.to_bytes().unwrap();
let mut probe = bits.clone();
for &b in &bits {
probe.insert(b + 1);
probe.insert(b.saturating_sub(1));
}
let (ok, present) = c_deserialize_bits(&rust_bytes, &probe);
if bits.is_empty() {
assert!(present.is_empty());
continue;
}
assert!(ok, "C must deserialize Rust output");
let got: BTreeSet<u64> = present.into_iter().collect();
assert_eq!(got, bits, "C read of Rust bytes diverged");
}
}