oxgraph_postgres/
overlay.rs1use alloc::{
4 collections::{BTreeMap, BTreeSet},
5 vec::Vec,
6};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct OverlayEdge {
11 pub source: u32,
13 pub target: u32,
15}
16
17#[derive(Clone, Debug, Default, PartialEq, Eq)]
19pub struct OverlayState {
20 pub added_edges: Vec<OverlayEdge>,
22 by_source: BTreeMap<u32, Vec<u32>>,
24 by_target: BTreeMap<u32, Vec<u32>>,
26 pub tombstoned_edges: BTreeSet<u32>,
28 pub tombstoned_nodes: BTreeSet<u32>,
30}
31
32impl OverlayState {
33 pub fn clear(&mut self) {
35 self.added_edges.clear();
36 self.by_source.clear();
37 self.by_target.clear();
38 self.tombstoned_edges.clear();
39 self.tombstoned_nodes.clear();
40 }
41
42 #[must_use]
44 pub fn has_edge_tombstones(&self) -> bool {
45 !self.tombstoned_edges.is_empty()
46 }
47
48 #[must_use]
50 pub fn has_node_tombstones(&self) -> bool {
51 !self.tombstoned_nodes.is_empty()
52 }
53
54 #[must_use]
60 pub fn edge_visible(&self, edge_id: u32) -> bool {
61 !self.tombstoned_edges.contains(&edge_id)
62 }
63
64 #[must_use]
70 pub fn node_visible(&self, node_id: u32) -> bool {
71 !self.tombstoned_nodes.contains(&node_id)
72 }
73
74 pub fn push_edge(&mut self, edge: OverlayEdge) {
80 self.added_edges.push(edge);
81 self.by_source
82 .entry(edge.source)
83 .or_default()
84 .push(edge.target);
85 self.by_target
86 .entry(edge.target)
87 .or_default()
88 .push(edge.source);
89 }
90
91 #[must_use]
97 pub fn overlay_targets(&self, source: u32) -> &[u32] {
98 self.by_source
99 .get(&source)
100 .map_or(&[] as &[u32], Vec::as_slice)
101 }
102
103 #[must_use]
109 pub fn overlay_sources(&self, target: u32) -> &[u32] {
110 self.by_target
111 .get(&target)
112 .map_or(&[] as &[u32], Vec::as_slice)
113 }
114
115 pub fn remove_edge(&mut self, source: u32, target: u32) {
121 self.added_edges
122 .retain(|edge| edge.source != source || edge.target != target);
123 self.rebuild_indexes();
124 }
125
126 pub fn rebuild_indexes(&mut self) {
132 self.by_source.clear();
133 self.by_target.clear();
134 for edge in &self.added_edges {
135 self.by_source
136 .entry(edge.source)
137 .or_default()
138 .push(edge.target);
139 self.by_target
140 .entry(edge.target)
141 .or_default()
142 .push(edge.source);
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::{OverlayEdge, OverlayState};
150
151 #[test]
152 fn indexes_match_linear_scan() {
153 let mut overlay = OverlayState::default();
154 overlay.push_edge(OverlayEdge {
155 source: 1,
156 target: 2,
157 });
158 overlay.push_edge(OverlayEdge {
159 source: 1,
160 target: 3,
161 });
162 overlay.push_edge(OverlayEdge {
163 source: 4,
164 target: 1,
165 });
166 assert_eq!(overlay.overlay_targets(1), &[2, 3]);
167 assert_eq!(overlay.overlay_sources(1), &[4]);
168 overlay.clear();
169 assert!(overlay.overlay_targets(1).is_empty());
170 }
171
172 #[test]
173 fn rebuild_indexes_after_manual_edges() {
174 let mut overlay = OverlayState {
175 added_edges: vec![
176 OverlayEdge {
177 source: 0,
178 target: 1,
179 },
180 OverlayEdge {
181 source: 2,
182 target: 0,
183 },
184 ],
185 ..OverlayState::default()
186 };
187 overlay.rebuild_indexes();
188 assert_eq!(overlay.overlay_targets(0), &[1]);
189 assert_eq!(overlay.overlay_sources(0), &[2]);
190 }
191}