1use redb::{ReadableMultimapTable, ReadableTable};
4
5use crate::error::StoreError;
6use crate::store::{FILE_HASH, FILE_SCOPE, FileStamp, META, NODES, OUT_EDGES, RESOLVE_META, Store};
7
8impl Store {
9 pub fn snapshot_token(&self) -> Result<String, StoreError> {
17 let txn = self.db.begin_read()?;
18 let mut fingerprint = SnapshotFingerprint::new();
19 let schema = match txn.open_table(META) {
20 Ok(table) => table.get("schema")?.map(|guard| guard.value()).unwrap_or(0),
21 Err(redb::TableError::TableDoesNotExist(_)) => 0,
22 Err(error) => return Err(error.into()),
23 };
24 fingerprint.field(b"schema");
25 fingerprint.field(&schema.to_le_bytes());
26
27 let hashes = txn.open_table(FILE_HASH)?;
28 let mut file_count = 0usize;
29 for entry in hashes.iter()? {
30 let (file, stamp) = entry?;
31 fingerprint.field(file.value().as_bytes());
32 fingerprint.field(FileStamp::decode(stamp.value()).hash.as_bytes());
33 file_count += 1;
34 }
35 drop(hashes);
36
37 let scopes = txn.open_table(FILE_SCOPE)?;
38 for entry in scopes.iter()? {
39 let (file, scope) = entry?;
40 fingerprint.field(file.value().as_bytes());
41 fingerprint.field(scope.value().as_bytes());
42 }
43 drop(scopes);
44
45 let resolution = txn.open_table(RESOLVE_META)?;
46 for entry in resolution.iter()? {
47 let (kind, value) = entry?;
48 fingerprint.field(kind.value().as_bytes());
49 fingerprint.field(value.value().as_bytes());
50 }
51 drop(resolution);
52
53 if file_count == 0 {
54 let nodes = txn.open_table(NODES)?;
55 for entry in nodes.iter()? {
56 let (id, bytes) = entry?;
57 fingerprint.field(id.value().as_bytes());
58 fingerprint.field(bytes.value());
59 }
60 drop(nodes);
61 let edges = txn.open_multimap_table(OUT_EDGES)?;
62 for entry in edges.iter()? {
63 let (id, values) = entry?;
64 fingerprint.field(id.value().as_bytes());
65 for value in values {
66 fingerprint.field(value?.value());
67 }
68 }
69 }
70
71 Ok(format!(
72 "graph-v{schema}-{:016x}{:016x}",
73 fingerprint.left, fingerprint.right
74 ))
75 }
76}
77
78struct SnapshotFingerprint {
83 left: u64,
84 right: u64,
85}
86
87impl SnapshotFingerprint {
88 fn new() -> Self {
89 Self {
90 left: 0xcbf29ce484222325,
91 right: 0x6c62272e07bb0142,
92 }
93 }
94
95 fn field(&mut self, bytes: &[u8]) {
96 self.write(&(bytes.len() as u64).to_le_bytes());
97 self.write(bytes);
98 }
99
100 fn write(&mut self, bytes: &[u8]) {
101 for byte in bytes {
102 self.left ^= u64::from(*byte);
103 self.left = self.left.wrapping_mul(0x0000_0100_0000_01b3);
104 self.right ^= u64::from(*byte).rotate_left(1);
105 self.right = self.right.wrapping_mul(0x9e37_79b1_85eb_ca87);
106 }
107 }
108}