use serde::{Deserialize, Serialize};
use crate::graph::unified::file::id::FileId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct FileSegment {
pub start_slot: u32,
pub slot_count: u32,
}
impl FileSegment {
#[inline]
#[must_use]
pub fn end_slot(&self) -> u32 {
self.start_slot.saturating_add(self.slot_count)
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.slot_count == 0
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FileSegmentTable {
segments: Vec<Option<FileSegment>>,
}
impl FileSegmentTable {
#[must_use]
pub fn new() -> Self {
Self {
segments: Vec::new(),
}
}
#[must_use]
pub fn with_capacity(file_count: usize) -> Self {
Self {
segments: vec![None; file_count],
}
}
pub fn record_range(&mut self, file_id: FileId, start_slot: u32, slot_count: u32) {
let idx = file_id.index() as usize;
if idx >= self.segments.len() {
self.segments.resize(idx + 1, None);
}
self.segments[idx] = Some(FileSegment {
start_slot,
slot_count,
});
}
#[inline]
#[must_use]
pub fn get(&self, file_id: FileId) -> Option<&FileSegment> {
let idx = file_id.index() as usize;
self.segments.get(idx).and_then(|opt| opt.as_ref())
}
pub fn remove(&mut self, file_id: FileId) {
let idx = file_id.index() as usize;
if idx < self.segments.len() {
self.segments[idx] = None;
}
}
#[must_use]
pub fn segment_count(&self) -> usize {
self.segments.iter().filter(|s| s.is_some()).count()
}
#[must_use]
pub fn total_slots(&self) -> u64 {
self.segments
.iter()
.filter_map(|s| s.as_ref())
.map(|s| u64::from(s.slot_count))
.sum()
}
pub fn iter(&self) -> impl Iterator<Item = (FileId, &FileSegment)> + '_ {
self.segments
.iter()
.enumerate()
.filter_map(|(idx, opt)| opt.as_ref().map(|seg| (FileId::new(idx as u32), seg)))
}
#[must_use]
pub fn capacity(&self) -> usize {
self.segments.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_and_retrieve() {
let mut table = FileSegmentTable::new();
table.record_range(FileId::new(0), 0, 10);
table.record_range(FileId::new(5), 10, 20);
let seg0 = table.get(FileId::new(0)).unwrap();
assert_eq!(seg0.start_slot, 0);
assert_eq!(seg0.slot_count, 10);
assert_eq!(seg0.end_slot(), 10);
let seg5 = table.get(FileId::new(5)).unwrap();
assert_eq!(seg5.start_slot, 10);
assert_eq!(seg5.slot_count, 20);
assert!(table.get(FileId::new(3)).is_none());
assert_eq!(table.segment_count(), 2);
assert_eq!(table.total_slots(), 30);
}
#[test]
fn remove_segment() {
let mut table = FileSegmentTable::new();
table.record_range(FileId::new(1), 0, 5);
assert!(table.get(FileId::new(1)).is_some());
table.remove(FileId::new(1));
assert!(table.get(FileId::new(1)).is_none());
assert_eq!(table.segment_count(), 0);
}
#[test]
fn overwrite_segment() {
let mut table = FileSegmentTable::new();
table.record_range(FileId::new(1), 0, 5);
table.record_range(FileId::new(1), 100, 15);
let seg = table.get(FileId::new(1)).unwrap();
assert_eq!(seg.start_slot, 100);
assert_eq!(seg.slot_count, 15);
}
#[test]
fn iter_over_segments() {
let mut table = FileSegmentTable::new();
table.record_range(FileId::new(0), 0, 10);
table.record_range(FileId::new(2), 10, 5);
let entries: Vec<_> = table.iter().collect();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].0, FileId::new(0));
assert_eq!(entries[1].0, FileId::new(2));
}
#[test]
fn empty_segment() {
let seg = FileSegment {
start_slot: 0,
slot_count: 0,
};
assert!(seg.is_empty());
assert_eq!(seg.end_slot(), 0);
}
}