1use std::collections::BTreeSet;
10
11use sha2::{Digest, Sha256};
12
13use crate::{GraphEntityKind, KeyValueBatch, StorageBackendResult};
14
15#[derive(Clone, Copy)]
16pub struct GraphIdentifierNamespace {
17 scope: [u8; 32],
18 generation: [u8; 16],
19}
20
21impl GraphIdentifierNamespace {
22 pub fn new(scope: Option<&str>, generation: [u8; 16]) -> Self {
23 let mut digest = Sha256::new();
24 digest.update([u8::from(scope.is_some())]);
25 digest.update(scope.unwrap_or_default().as_bytes());
26 Self {
27 scope: digest.finalize().into(),
28 generation,
29 }
30 }
31
32 pub fn key(&self, tag: u8, kind: GraphEntityKind, prefix: u32, graph: [u8; 16]) -> [u8; 78] {
33 let mut key = [0; 78];
34 key[..8].copy_from_slice(b"UQAGRPH1");
35 key[8..40].copy_from_slice(&self.scope);
36 key[40..56].copy_from_slice(&self.generation);
37 key[56] = tag;
38 key[57] = match kind {
39 GraphEntityKind::Vertex => b'v',
40 GraphEntityKind::Edge => b'e',
41 };
42 key[58..62].copy_from_slice(&prefix.to_be_bytes());
43 key[62..].copy_from_slice(&graph);
44 key
45 }
46
47 pub fn legacy_graph_identity(&self, graph: &str) -> [u8; 16] {
49 let mut digest = Sha256::new();
50 digest.update(self.key(b'g', GraphEntityKind::Vertex, 0, [0; 16]));
51 digest.update(graph.as_bytes());
52 let mut identity = [0; 16];
53 identity.copy_from_slice(&digest.finalize()[..16]);
54 if identity == [0; 16] {
55 identity[15] = 1;
56 }
57 identity
58 }
59
60 pub fn label_key(&self, graph: [u8; 16]) -> [u8; 78] {
61 self.key(b'l', GraphEntityKind::Vertex, 0, graph)
62 }
63
64 pub fn serializable_entity_object(&self) -> [u8; 16] {
66 self.serializable_object(b'r')
67 }
68
69 pub fn serializable_scope_object(&self) -> [u8; 16] {
71 Self {
72 generation: [0; 16],
73 ..*self
74 }
75 .serializable_object(b'R')
76 }
77
78 fn serializable_object(&self, tag: u8) -> [u8; 16] {
79 let digest = Sha256::digest(self.key(tag, GraphEntityKind::Vertex, 0, [0; 16]));
80 let mut identity = [0; 16];
81 identity.copy_from_slice(&digest[..16]);
82 if identity == [0; 16] {
83 identity[15] = 1;
84 }
85 identity
86 }
87
88 pub fn sequence_key(&self, graph: [u8; 16], label: u32) -> [u8; 78] {
89 self.key(b's', GraphEntityKind::Vertex, label, graph)
90 }
91
92 pub fn entity_key(&self, kind: GraphEntityKind, prefix: u32) -> [u8; 78] {
93 self.key(b'e', kind, prefix, [0; 16])
94 }
95
96 pub fn entity_seed_key(&self, kind: GraphEntityKind, prefix: u32) -> [u8; 78] {
97 self.key(b'i', kind, prefix, [0; 16])
98 }
99
100 pub fn hint_key(&self, kind: GraphEntityKind) -> [u8; 78] {
101 self.key(b'h', kind, 0, [0; 16])
102 }
103
104 pub fn observe_entity(
106 &self,
107 batch: &mut dyn KeyValueBatch,
108 kind: GraphEntityKind,
109 id: u64,
110 ) -> StorageBackendResult<()> {
111 let bytes = id.to_be_bytes();
112 let prefix = u32::from(u16::from_be_bytes([bytes[0], bytes[1]]));
113 let ordinal = id & (u64::MAX >> 16);
114 batch.observe_identifier(&self.entity_key(kind, prefix), ordinal)?;
115 batch.observe_identifier(&self.hint_key(kind), id)
116 }
117
118 pub fn observe_registry(
120 &self,
121 batch: &mut dyn KeyValueBatch,
122 graph: &str,
123 source: &str,
124 ) -> StorageBackendResult<()> {
125 let Ok(value) = serde_json::from_str::<serde_json::Value>(source) else {
126 return Ok(());
127 };
128 let Some(object) = value.as_object() else {
129 return Ok(());
130 };
131 let identity = object
132 .get("allocation_id")
133 .map(|value| serde_json::from_value::<[u8; 16]>(value.clone()))
134 .transpose()?
135 .filter(|id| *id != [0; 16])
136 .unwrap_or_else(|| self.legacy_graph_identity(graph));
137 if let Some(next) = object
138 .get("next_label_id")
139 .and_then(serde_json::Value::as_u64)
140 {
141 batch.observe_identifier(&self.label_key(identity), next.saturating_sub(1))?;
142 }
143 if let Some(sequences) = object
144 .get("sequences")
145 .and_then(serde_json::Value::as_object)
146 {
147 for (label, counter) in sequences {
148 let (Ok(label), Some(last)) = (label.parse::<u32>(), counter.as_u64()) else {
149 continue;
150 };
151 batch.observe_identifier(&self.sequence_key(identity, label), last)?;
152 if let Ok(prefix) = u16::try_from(label) {
153 if last != 0 && last <= (u64::MAX >> 16) {
154 let mut bytes = last.to_be_bytes();
155 bytes[..2].copy_from_slice(&prefix.to_be_bytes());
156 let id = u64::from_be_bytes(bytes);
157 let kind = registry_kind(&value, label);
158 for candidate in [GraphEntityKind::Vertex, GraphEntityKind::Edge] {
159 if kind.is_none_or(|kind| kind == candidate) {
160 self.observe_entity(batch, candidate, id)?;
161 }
162 }
163 }
164 }
165 }
166 }
167 Ok(())
168 }
169}
170
171fn registry_kind(value: &serde_json::Value, label: u32) -> Option<GraphEntityKind> {
172 match label {
173 1 => Some(GraphEntityKind::Vertex),
174 2 => Some(GraphEntityKind::Edge),
175 _ => value
176 .get("labels")?
177 .as_object()?
178 .iter()
179 .find_map(|(name, id)| {
180 if id.as_u64() != Some(u64::from(label)) {
181 return None;
182 }
183 match value
184 .get("kinds")
185 .and_then(|kinds| kinds.get(name))
186 .and_then(serde_json::Value::as_str)
187 {
188 Some("v") => Some(GraphEntityKind::Vertex),
189 Some("e") => Some(GraphEntityKind::Edge),
190 _ => None,
191 }
192 }),
193 }
194}
195
196pub fn export_registry(
198 source: &str,
199 graph: &str,
200 namespace: GraphIdentifierNamespace,
201 mut watermark: impl FnMut(&[u8]) -> StorageBackendResult<Option<u64>>,
202) -> StorageBackendResult<String> {
203 let mut value: serde_json::Value = if source.is_empty() {
204 serde_json::json!({})
205 } else {
206 match serde_json::from_str(source) {
207 Ok(value) => value,
208 Err(_) => return Ok(source.to_owned()),
209 }
210 };
211 let Some(object) = value.as_object_mut() else {
212 return Ok(source.to_owned());
213 };
214 let identity = object
215 .get("allocation_id")
216 .map(|value| serde_json::from_value::<[u8; 16]>(value.clone()))
217 .transpose()?
218 .filter(|id| *id != [0; 16])
219 .unwrap_or_else(|| namespace.legacy_graph_identity(graph));
220 let mut changed = false;
221 if let Some(last) = watermark(&namespace.label_key(identity))? {
222 let next = last.checked_add(1).ok_or_else(|| {
223 crate::StorageBackendError::Other("graph label watermark overflow".into())
224 })?;
225 if object
226 .get("next_label_id")
227 .and_then(serde_json::Value::as_u64)
228 .is_none_or(|old| old < next)
229 {
230 object.insert("next_label_id".into(), next.into());
231 changed = true;
232 }
233 }
234 let mut labels = BTreeSet::from([1_u32, 2]);
235 if let Some(registered) = object.get("labels").and_then(serde_json::Value::as_object) {
236 for value in registered.values() {
237 if let Some(label) = value.as_u64().and_then(|id| u32::try_from(id).ok()) {
238 labels.insert(label);
239 }
240 }
241 }
242 if let Some(sequences) = object
243 .get("sequences")
244 .and_then(serde_json::Value::as_object)
245 {
246 for label in sequences.keys() {
247 if let Ok(label) = label.parse() {
248 labels.insert(label);
249 }
250 }
251 }
252 for label in labels {
253 let Some(last) = watermark(&namespace.sequence_key(identity, label))? else {
254 continue;
255 };
256 let sequences = object
257 .entry("sequences")
258 .or_insert_with(|| serde_json::json!({}));
259 let sequences = sequences.as_object_mut().ok_or_else(|| {
260 crate::StorageBackendError::Other("invalid graph registry sequences".into())
261 })?;
262 let key = label.to_string();
263 if sequences
264 .get(&key)
265 .and_then(serde_json::Value::as_u64)
266 .is_none_or(|old| old < last)
267 {
268 sequences.insert(key, last.into());
269 changed = true;
270 }
271 }
272 if changed {
273 object.insert("allocation_id".into(), serde_json::to_value(identity)?);
274 Ok(serde_json::to_string(&value)?)
275 } else {
276 Ok(source.to_owned())
277 }
278}