sqry_core/graph/unified/storage/
segment.rs1use serde::{Deserialize, Serialize};
18
19use crate::graph::unified::file::id::FileId;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23pub struct FileSegment {
24 pub start_slot: u32,
26 pub slot_count: u32,
28}
29
30impl FileSegment {
31 #[inline]
33 #[must_use]
34 pub fn end_slot(&self) -> u32 {
35 self.start_slot.saturating_add(self.slot_count)
36 }
37
38 #[inline]
40 #[must_use]
41 pub fn is_empty(&self) -> bool {
42 self.slot_count == 0
43 }
44}
45
46#[derive(Debug, Clone, Default, Serialize, Deserialize)]
52pub struct FileSegmentTable {
53 segments: Vec<Option<FileSegment>>,
56}
57
58impl FileSegmentTable {
59 #[must_use]
61 pub fn new() -> Self {
62 Self {
63 segments: Vec::new(),
64 }
65 }
66
67 #[must_use]
69 pub fn with_capacity(file_count: usize) -> Self {
70 Self {
71 segments: vec![None; file_count],
72 }
73 }
74
75 pub fn record_range(&mut self, file_id: FileId, start_slot: u32, slot_count: u32) {
77 let idx = file_id.index() as usize;
78 if idx >= self.segments.len() {
79 self.segments.resize(idx + 1, None);
80 }
81 self.segments[idx] = Some(FileSegment {
82 start_slot,
83 slot_count,
84 });
85 }
86
87 #[inline]
89 #[must_use]
90 pub fn get(&self, file_id: FileId) -> Option<&FileSegment> {
91 let idx = file_id.index() as usize;
92 self.segments.get(idx).and_then(|opt| opt.as_ref())
93 }
94
95 pub fn remove(&mut self, file_id: FileId) {
98 let idx = file_id.index() as usize;
99 if idx < self.segments.len() {
100 self.segments[idx] = None;
101 }
102 }
103
104 #[must_use]
106 pub fn segment_count(&self) -> usize {
107 self.segments.iter().filter(|s| s.is_some()).count()
108 }
109
110 #[must_use]
112 pub fn total_slots(&self) -> u64 {
113 self.segments
114 .iter()
115 .filter_map(|s| s.as_ref())
116 .map(|s| u64::from(s.slot_count))
117 .sum()
118 }
119
120 pub fn iter(&self) -> impl Iterator<Item = (FileId, &FileSegment)> + '_ {
122 self.segments
123 .iter()
124 .enumerate()
125 .filter_map(|(idx, opt)| opt.as_ref().map(|seg| (FileId::new(idx as u32), seg)))
126 }
127
128 #[must_use]
130 pub fn capacity(&self) -> usize {
131 self.segments.len()
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn record_and_retrieve() {
141 let mut table = FileSegmentTable::new();
142 table.record_range(FileId::new(0), 0, 10);
143 table.record_range(FileId::new(5), 10, 20);
144
145 let seg0 = table.get(FileId::new(0)).unwrap();
146 assert_eq!(seg0.start_slot, 0);
147 assert_eq!(seg0.slot_count, 10);
148 assert_eq!(seg0.end_slot(), 10);
149
150 let seg5 = table.get(FileId::new(5)).unwrap();
151 assert_eq!(seg5.start_slot, 10);
152 assert_eq!(seg5.slot_count, 20);
153
154 assert!(table.get(FileId::new(3)).is_none());
155 assert_eq!(table.segment_count(), 2);
156 assert_eq!(table.total_slots(), 30);
157 }
158
159 #[test]
160 fn remove_segment() {
161 let mut table = FileSegmentTable::new();
162 table.record_range(FileId::new(1), 0, 5);
163 assert!(table.get(FileId::new(1)).is_some());
164
165 table.remove(FileId::new(1));
166 assert!(table.get(FileId::new(1)).is_none());
167 assert_eq!(table.segment_count(), 0);
168 }
169
170 #[test]
171 fn overwrite_segment() {
172 let mut table = FileSegmentTable::new();
173 table.record_range(FileId::new(1), 0, 5);
174 table.record_range(FileId::new(1), 100, 15);
175
176 let seg = table.get(FileId::new(1)).unwrap();
177 assert_eq!(seg.start_slot, 100);
178 assert_eq!(seg.slot_count, 15);
179 }
180
181 #[test]
182 fn iter_over_segments() {
183 let mut table = FileSegmentTable::new();
184 table.record_range(FileId::new(0), 0, 10);
185 table.record_range(FileId::new(2), 10, 5);
186
187 let entries: Vec<_> = table.iter().collect();
188 assert_eq!(entries.len(), 2);
189 assert_eq!(entries[0].0, FileId::new(0));
190 assert_eq!(entries[1].0, FileId::new(2));
191 }
192
193 #[test]
194 fn empty_segment() {
195 let seg = FileSegment {
196 start_slot: 0,
197 slot_count: 0,
198 };
199 assert!(seg.is_empty());
200 assert_eq!(seg.end_slot(), 0);
201 }
202}