use super::super::{csr_shared::OverflowTable, ColdStamps, HotNbr, Nbr};
#[derive(Debug, Clone, Default)]
pub struct OverflowChunk {
hot: Vec<HotNbr>,
cold: Vec<ColdStamps>,
}
impl OverflowChunk {
pub fn with_capacity(cap: usize) -> Self {
Self {
hot: Vec::with_capacity(cap),
cold: Vec::with_capacity(cap),
}
}
#[inline]
pub fn len(&self) -> usize {
self.hot.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.hot.is_empty()
}
#[inline]
pub fn capacity(&self) -> usize {
self.hot.capacity()
}
#[inline]
pub fn slot_at(&self, index: usize) -> Option<Nbr> {
Some(Nbr::from_parts(
*self.hot.get(index)?,
*self.cold.get(index)?,
))
}
#[inline]
pub fn hot_at(&self, index: usize) -> Option<HotNbr> {
self.hot.get(index).copied()
}
#[inline]
pub fn cold_at(&self, index: usize) -> Option<ColdStamps> {
self.cold.get(index).copied()
}
#[inline]
pub fn hot_slice(&self) -> &[HotNbr] {
&self.hot
}
#[inline]
pub fn cold_slice(&self) -> &[ColdStamps] {
&self.cold
}
#[inline]
pub fn cold_at_mut(&mut self, index: usize) -> Option<&mut ColdStamps> {
self.cold.get_mut(index)
}
#[inline]
pub fn push(&mut self, nbr: Nbr) {
self.hot.push(nbr.hot());
self.cold.push(nbr.cold());
debug_assert_eq!(self.hot.len(), self.cold.len());
}
#[inline]
pub fn remove(&mut self, index: usize) -> Nbr {
let hot = self.hot.swap_remove(index);
let cold = self.cold.swap_remove(index);
Nbr::from_parts(hot, cold)
}
pub fn extend_from_slice(&mut self, slots: &[Nbr]) {
self.hot.reserve(slots.len());
self.cold.reserve(slots.len());
for nbr in slots {
self.hot.push(nbr.hot());
self.cold.push(nbr.cold());
}
}
pub fn consolidated(slots: &[Nbr]) -> Self {
let mut chunk = OverflowChunk::default();
chunk.extend_from_slice(slots);
chunk
}
pub fn consolidated_with_reserve(slots: &[Nbr], spare: usize) -> Self {
let mut chunk = OverflowChunk::default();
chunk.hot.reserve(slots.len().saturating_add(spare));
chunk.cold.reserve(slots.len().saturating_add(spare));
chunk.extend_from_slice(slots);
chunk
}
}
pub(crate) const OVERFLOW_REPACK_CHUNKS_PER_VERTEX: usize = 8;
impl super::super::csr_shared::OverflowChunkSpec for OverflowChunk {
type Slot = Nbr;
fn with_capacity(cap: usize) -> Self {
OverflowChunk::with_capacity(cap)
}
#[inline]
fn len(&self) -> usize {
self.len()
}
#[inline]
fn capacity(&self) -> usize {
self.capacity()
}
#[inline]
fn push_slot(&mut self, slot: Nbr) {
self.push(slot);
}
}
pub type OverflowStorage = OverflowTable<OverflowChunk>;
#[cfg(test)]
mod tests {
use super::super::super::csr_shared::SEGMENT_SIZE;
use super::*;
use linkrs_core::types::EdgeId;
fn chunks_with_first_endpoint(endpoint: u32) -> Vec<OverflowChunk> {
let mut chunk = OverflowChunk::default();
chunk.push(Nbr::new(endpoint, 0, EdgeId::new(1)));
vec![chunk]
}
#[test]
fn empty_vertices_cost_only_pointers() {
let mut storage = OverflowStorage::new();
storage.ensure_capacity(1_000_000);
assert!(storage.is_empty());
assert!(storage.index_bytes() < 1_000_000);
assert!(storage.get(999_999).is_none());
}
#[test]
fn routes_across_segment_boundary() {
let mut storage = OverflowStorage::new();
let lo = SEGMENT_SIZE as u32 - 1;
let hi = SEGMENT_SIZE as u32;
let far = 2 * SEGMENT_SIZE as u32 + 5;
storage.insert(lo, chunks_with_first_endpoint(10));
storage.insert(hi, chunks_with_first_endpoint(20));
storage.insert(far, chunks_with_first_endpoint(30));
assert_eq!(storage.len(), 3);
for (vid, endpoint) in [(lo, 10), (hi, 20), (far, 30)] {
let chunks = storage.get(vid).expect("row present");
assert_eq!(chunks[0].hot_at(0).expect("slot").endpoint, endpoint);
}
let taken = storage.remove(hi).expect("row removed");
assert_eq!(taken[0].hot_at(0).expect("slot").endpoint, 20);
assert_eq!(storage.len(), 2);
assert!(storage.get(hi).is_none());
}
#[test]
fn iter_walks_rows_in_vertex_order() {
let mut storage = OverflowStorage::new();
let far = 3 * SEGMENT_SIZE as u32 + 7;
let near = 5u32;
storage.insert(far, chunks_with_first_endpoint(30));
storage.insert(near, chunks_with_first_endpoint(10));
storage.insert(SEGMENT_SIZE as u32, chunks_with_first_endpoint(20));
let order: Vec<u32> = storage.iter().map(|(vid, _)| vid).collect();
assert_eq!(order, vec![near, SEGMENT_SIZE as u32, far]);
}
#[test]
fn clear_empties_allocated_segments() {
let mut storage = OverflowStorage::new();
storage.insert(SEGMENT_SIZE as u32 + 1, chunks_with_first_endpoint(1));
storage.clear();
assert!(storage.is_empty());
assert_eq!(storage.len(), 0);
assert!(storage.get(SEGMENT_SIZE as u32 + 1).is_none());
storage.insert(3, chunks_with_first_endpoint(2));
assert_eq!(storage.len(), 1);
}
}