linkrs_storage/edge/csr_with_properties/
mapping.rs1use super::{CsrWithProperties, RowVisibility, UNMAPPED_ROW};
2use linkrs_core::types::{EdgeId, Timestamp, INVALID_EDGE_ID};
3use linkrs_core::{StorageError, StorageResult, Value};
4use std::sync::Arc;
5
6impl CsrWithProperties {
7 fn segment_of(slot: usize) -> (usize, usize) {
8 debug_assert_eq!(Self::EDGE_MAP_SEGMENT_ROWS, 1024);
9 (
10 slot / Self::EDGE_MAP_SEGMENT_ROWS,
11 slot % Self::EDGE_MAP_SEGMENT_ROWS,
12 )
13 }
14
15 pub(crate) fn mapped_row(&self, edge_id: EdgeId) -> Option<usize> {
20 let (seg, off) = Self::segment_of(edge_id.0 as usize);
21 let pos = self.edge_map_segments.get(seg)?.as_ref()?[off];
22 (pos != UNMAPPED_ROW).then_some(pos as usize)
23 }
24
25 pub(crate) fn map_insert(&mut self, edge_id: EdgeId, row_idx: usize) -> StorageResult<()> {
30 if edge_id == INVALID_EDGE_ID {
31 return Err(StorageError::invalid_operation(
32 "unassignable edge id owns no property row",
33 ));
34 }
35 let slot = edge_id.0 as usize;
36 let (seg, off) = Self::segment_of(slot);
37 if seg >= self.edge_map_segments.len() {
38 self.edge_map_segments.resize_with(seg + 1, || None);
39 }
40 let segment = self.edge_map_segments[seg]
41 .get_or_insert_with(|| Box::new([UNMAPPED_ROW; Self::EDGE_MAP_SEGMENT_ROWS]));
42 if segment[off] == UNMAPPED_ROW {
43 self.edge_map_len += 1;
44 }
45 segment[off] = row_idx as u32;
46 Ok(())
47 }
48
49 pub(crate) fn map_remove(&mut self, edge_id: EdgeId) -> Option<usize> {
55 let slot = edge_id.0 as usize;
56 let (seg, off) = Self::segment_of(slot);
57 let segment = self.edge_map_segments.get_mut(seg)?.as_mut()?;
58 if segment[off] == UNMAPPED_ROW {
59 return None;
60 }
61 let former = segment[off] as usize;
62 segment[off] = UNMAPPED_ROW;
63 self.edge_map_len = self.edge_map_len.saturating_sub(1);
64 if segment.iter().all(|pos| *pos == UNMAPPED_ROW) {
65 self.edge_map_segments[seg] = None;
66 }
67 self.truncate_empty_tail_segments();
68 Some(former)
69 }
70
71 fn truncate_empty_tail_segments(&mut self) {
73 while self
74 .edge_map_segments
75 .last()
76 .is_some_and(|seg| seg.is_none())
77 {
78 self.edge_map_segments.pop();
79 }
80 }
81
82 pub(crate) fn ensure_row_aux_len(&mut self, len: usize) {
83 if self.row_to_edge.len() < len {
84 self.row_to_edge.resize(len, None);
85 }
86 }
87
88 pub(crate) fn allocate_row(
91 &mut self,
92 values: &[(Arc<str>, Value)],
93 create_ts: Timestamp,
94 ) -> StorageResult<usize> {
95 let mut positioned: Vec<(usize, Value)> = Vec::with_capacity(values.len());
100 for (name, value) in values {
101 if let Some(&idx) = self.column_index.get(&**name) {
102 positioned.push((idx, value.clone()));
103 }
104 }
105 self.allocate_row_at(&positioned, create_ts)
106 }
107
108 pub(crate) fn allocate_row_at(
118 &mut self,
119 positioned: &[(usize, Value)],
120 create_ts: Timestamp,
121 ) -> StorageResult<usize> {
122 if !linkrs_core::types::is_allocatable_timestamp(create_ts) {
123 return Err(StorageError::invalid_input(format!(
124 "property row carries reserved creation timestamp {}",
125 create_ts
126 )));
127 }
128 for (idx, _) in positioned {
129 if *idx >= self.property_schema.len() {
130 return Err(StorageError::column_not_found(format!(
131 "property column position out of range: {}",
132 idx
133 )));
134 }
135 }
136 let row_idx = if let Some(free_off) = self.free_list.pop() {
137 let idx = free_off as usize;
138 if idx >= self.visibility.len() {
139 self.visibility.resize(idx + 1, RowVisibility::new(0));
140 }
141 self.visibility[idx] = RowVisibility::new(create_ts);
142 self.row_count += 1;
143 idx
144 } else {
145 let idx = self.visibility.len();
146 self.visibility.push(RowVisibility::new(create_ts));
147 self.row_count += 1;
148 idx
149 };
150 self.ensure_row_aux_len(self.visibility.len());
151 self.row_to_edge[row_idx] = None;
152 let mut provided: Vec<Option<&Value>> = vec![None; self.property_schema.len()];
157 for (idx, value) in positioned {
158 if provided[*idx].is_none() {
161 provided[*idx] = Some(value);
162 }
163 }
164 for (i, slot) in provided.into_iter().enumerate() {
165 let col = &mut self.property_columns[i];
166 match slot {
167 Some(v) => {
168 col.set_versioned(row_idx, Some(v), create_ts)?;
170 }
171 None => {
172 let default_val = self.property_schema[i].default_value.clone();
174 col.set_with_timestamp(row_idx, default_val.as_ref(), create_ts)?;
175 }
176 }
177 self.mark_column_dirty_at(i);
178 }
179 Ok(row_idx)
180 }
181
182 pub fn release_row(&mut self, row_idx: usize) {
192 if row_idx >= self.visibility.len() {
193 return;
194 }
195 if self.visibility[row_idx].is_virgin() {
196 if let Some(slot) = self.row_to_edge.get_mut(row_idx) {
197 if let Some(edge_id) = slot.take() {
198 self.map_remove(edge_id);
199 }
200 }
201 return;
202 }
203 self.visibility[row_idx].create_ts = 0;
204 self.visibility[row_idx].delete_ts = None;
205 self.row_count = self.row_count.saturating_sub(1);
206 if let Some(slot) = self.row_to_edge.get_mut(row_idx) {
207 if let Some(edge_id) = slot.take() {
208 self.map_remove(edge_id);
209 }
210 }
211 self.free_list.push(row_idx as u32);
215 }
216
217 pub fn get_row_for_edge(&self, edge_id: EdgeId) -> Option<usize> {
219 self.mapped_row(edge_id)
220 }
221
222 pub fn remove_edge_mapping(&mut self, edge_id: EdgeId) -> Option<usize> {
224 if self.inline {
225 return None;
226 }
227 let pos = self.map_remove(edge_id)?;
228 if let Some(slot) = self.row_to_edge.get_mut(pos) {
229 if *slot == Some(edge_id) {
230 *slot = None;
231 }
232 }
233 Some(pos)
234 }
235
236 pub fn contains_edge(&self, edge_id: EdgeId) -> bool {
238 self.mapped_row(edge_id).is_some()
239 }
240
241 pub fn edge_mappings(&self) -> impl Iterator<Item = (EdgeId, u32)> + '_ {
243 self.edge_map_segments
244 .iter()
245 .enumerate()
246 .filter_map(|(seg, segment)| {
247 segment.as_ref().map(|values| {
248 let base = seg * Self::EDGE_MAP_SEGMENT_ROWS;
249 values.iter().enumerate().filter_map(move |(off, row)| {
250 (*row != UNMAPPED_ROW).then_some((EdgeId((base + off) as u64), *row))
251 })
252 })
253 })
254 .flatten()
255 }
256
257 pub fn edge_ids(&self) -> impl Iterator<Item = EdgeId> + '_ {
258 self.edge_mappings().map(|(edge_id, _)| edge_id)
259 }
260
261 pub fn row_count(&self) -> usize {
262 self.row_count
263 }
264
265 pub const EDGE_MAP_SEGMENT_ROWS: usize = 1024;
269
270 pub fn nonempty_map_segments(&self) -> Vec<usize> {
272 self.edge_map_segments
273 .iter()
274 .enumerate()
275 .filter_map(|(seg, segment)| {
276 segment
277 .as_ref()
278 .and_then(|values| values.iter().any(|row| *row != UNMAPPED_ROW).then_some(seg))
279 })
280 .collect()
281 }
282
283 pub fn edge_mappings_in_segment(
285 &self,
286 segment: usize,
287 ) -> impl Iterator<Item = (EdgeId, u32)> + '_ {
288 let base = segment * Self::EDGE_MAP_SEGMENT_ROWS;
289 self.edge_map_segments
290 .get(segment)
291 .and_then(|segment| segment.as_ref())
292 .map(|values| {
293 values.iter().enumerate().filter_map(move |(off, row)| {
294 (*row != UNMAPPED_ROW).then_some((EdgeId((base + off) as u64), *row))
295 })
296 })
297 .into_iter()
298 .flatten()
299 }
300
301 pub fn edge_map_memory_bytes(&self) -> usize {
303 self.edge_map_segments
304 .iter()
305 .filter(|segment| segment.is_some())
306 .count()
307 * Self::EDGE_MAP_SEGMENT_ROWS
308 * std::mem::size_of::<u32>()
309 }
310}