use std::sync::Arc;
pub(crate) struct FlatNeighborStore {
data: FlatArray,
offsets: FlatArray,
starts: Vec<u32>,
}
#[allow(dead_code)]
enum FlatArray {
Owned(Vec<u32>),
#[cfg(target_endian = "little")]
ZeroCopy {
_backing: Arc<rkyv::util::AlignedVec>,
ptr: *const u32,
len: usize,
},
}
unsafe impl Send for FlatArray {}
unsafe impl Sync for FlatArray {}
impl std::ops::Deref for FlatArray {
type Target = [u32];
fn deref(&self) -> &[u32] {
match self {
Self::Owned(v) => v,
#[cfg(target_endian = "little")]
Self::ZeroCopy { ptr, len, .. } => unsafe { std::slice::from_raw_parts(*ptr, *len) },
}
}
}
impl FlatNeighborStore {
pub fn from_nested(neighbors: &[Vec<Vec<u32>>]) -> Self {
let num_nodes = neighbors.len();
let mut starts = Vec::with_capacity(num_nodes + 1);
let mut offsets = Vec::new();
let mut data = Vec::new();
for node_neighbors in neighbors {
starts.push(offsets.len() as u32);
let mut pos = data.len() as u32;
for layer_neighbors in node_neighbors {
offsets.push(pos);
data.extend_from_slice(layer_neighbors);
pos = data.len() as u32;
}
offsets.push(pos);
}
starts.push(offsets.len() as u32);
Self {
data: FlatArray::Owned(data),
offsets: FlatArray::Owned(offsets),
starts,
}
}
#[cfg(target_endian = "little")]
#[allow(dead_code)]
pub unsafe fn from_rkyv(
backing: Arc<rkyv::util::AlignedVec>,
data_ptr: *const u32,
data_len: usize,
offsets_ptr: *const u32,
offsets_len: usize,
starts: Vec<u32>,
) -> Self {
Self {
data: FlatArray::ZeroCopy {
_backing: backing.clone(),
ptr: data_ptr,
len: data_len,
},
offsets: FlatArray::ZeroCopy {
_backing: backing,
ptr: offsets_ptr,
len: offsets_len,
},
starts,
}
}
pub fn num_layers(&self, node_id: u32) -> usize {
let s = self.starts[node_id as usize] as usize;
let e = self.starts[node_id as usize + 1] as usize;
if e > s { e - s - 1 } else { 0 }
}
pub fn neighbors_at(&self, node_id: u32, layer: usize) -> &[u32] {
let layer_base = self.starts[node_id as usize] as usize;
let num_layers = self.num_layers(node_id);
if layer >= num_layers {
return &[];
}
let start = self.offsets[layer_base + layer] as usize;
let end = self.offsets[layer_base + layer + 1] as usize;
&self.data[start..end]
}
pub fn to_nested(&self, num_nodes: usize) -> Vec<Vec<Vec<u32>>> {
let mut result = Vec::with_capacity(num_nodes);
for i in 0..num_nodes {
let nl = self.num_layers(i as u32);
let mut layers = Vec::with_capacity(nl);
for l in 0..nl {
layers.push(self.neighbors_at(i as u32, l).to_vec());
}
result.push(layers);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_empty() {
let neighbors: Vec<Vec<Vec<u32>>> = vec![vec![], vec![]];
let flat = FlatNeighborStore::from_nested(&neighbors);
assert_eq!(flat.num_layers(0), 0);
assert_eq!(flat.num_layers(1), 0);
let nested = flat.to_nested(2);
assert_eq!(nested, neighbors);
}
#[test]
fn roundtrip_basic() {
let neighbors = vec![
vec![vec![1, 2, 3], vec![1]], vec![vec![0, 2]], vec![vec![0, 1], vec![0]], ];
let flat = FlatNeighborStore::from_nested(&neighbors);
assert_eq!(flat.num_layers(0), 2);
assert_eq!(flat.num_layers(1), 1);
assert_eq!(flat.num_layers(2), 2);
assert_eq!(flat.neighbors_at(0, 0), &[1, 2, 3]);
assert_eq!(flat.neighbors_at(0, 1), &[1]);
assert_eq!(flat.neighbors_at(1, 0), &[0, 2]);
assert_eq!(flat.neighbors_at(2, 0), &[0, 1]);
assert_eq!(flat.neighbors_at(2, 1), &[0]);
assert_eq!(flat.neighbors_at(1, 5), &[] as &[u32]);
let nested = flat.to_nested(3);
assert_eq!(nested, neighbors);
}
}