1use ::kvdb::{DBTransaction, KeyValueDB};
20use std::collections::HashMap;
21#[cfg(debug_assertions)]
22use std::collections::HashSet;
23
24use crate::{error, Change, ColumnId, Database, Transaction};
25
26struct DbAdapter<D: KeyValueDB + 'static>(D);
27
28fn handle_err<T>(result: std::io::Result<T>) -> T {
29 match result {
30 Ok(r) => r,
31 Err(e) => {
32 panic!("Critical database error: {:?}", e);
33 },
34 }
35}
36
37fn read_counter(
39 db: &dyn KeyValueDB,
40 col: ColumnId,
41 key: &[u8],
42) -> error::Result<(Vec<u8>, Option<u32>)> {
43 let mut counter_key = key.to_vec();
44 counter_key.push(0);
45 Ok(match db.get(col, &counter_key).map_err(|e| error::DatabaseError(Box::new(e)))? {
46 Some(data) => {
47 let mut counter_data = [0; 4];
48 if data.len() != 4 {
49 return Err(error::DatabaseError(Box::new(std::io::Error::other(format!(
50 "Unexpected counter len {}",
51 data.len(),
52 )))));
53 }
54 counter_data.copy_from_slice(&data);
55 let counter = u32::from_le_bytes(counter_data);
56 (counter_key, Some(counter))
57 },
58 None => (counter_key, None),
59 })
60}
61
62enum RefCountedOp {
63 Store(Vec<u8>),
64 Reference,
65 Release,
66}
67
68fn commit_impl<H: Clone + AsRef<[u8]>>(
79 db: &dyn KeyValueDB,
80 transaction: Transaction<H>,
81) -> error::Result<()> {
82 let mut tx = DBTransaction::new();
83 let mut ref_counted: HashMap<(ColumnId, Vec<u8>), Vec<RefCountedOp>> = HashMap::new();
84 #[cfg(debug_assertions)]
85 let mut raw_keys: HashSet<(ColumnId, Vec<u8>)> = HashSet::new();
86
87 for change in transaction.0.into_iter() {
88 match change {
89 Change::Set(col, key, value) => {
90 #[cfg(debug_assertions)]
91 raw_keys.insert((col, key.clone()));
92 tx.put_vec(col, &key, value);
93 },
94 Change::Remove(col, key) => {
95 #[cfg(debug_assertions)]
96 raw_keys.insert((col, key.clone()));
97 tx.delete(col, &key);
98 },
99 Change::Store(col, key, value) => {
100 ref_counted
101 .entry((col, key.as_ref().to_vec()))
102 .or_default()
103 .push(RefCountedOp::Store(value));
104 },
105 Change::Reference(col, key) => {
106 ref_counted
107 .entry((col, key.as_ref().to_vec()))
108 .or_default()
109 .push(RefCountedOp::Reference);
110 },
111 Change::Release(col, key) => {
112 ref_counted
113 .entry((col, key.as_ref().to_vec()))
114 .or_default()
115 .push(RefCountedOp::Release);
116 },
117 }
118 }
119
120 #[cfg(debug_assertions)]
121 for raw_key in &raw_keys {
122 debug_assert!(
123 !ref_counted.contains_key(raw_key),
124 "mixed raw/ref-counted database ops on column {}, key {:02x?}",
125 raw_key.0,
126 raw_key.1,
127 );
128 }
129
130 for ((col, key), ops) in ref_counted {
131 let (counter_key, mut counter) = read_counter(db, col, &key)?;
132
133 let mut value_to_write = None;
134 for op in ops {
135 match op {
136 RefCountedOp::Store(value) => match counter {
137 Some(c) => counter = Some(c + 1),
138 None => {
139 counter = Some(1);
140 value_to_write = Some(value);
141 },
142 },
143 RefCountedOp::Reference => {
144 if let Some(c) = counter {
145 counter = Some(c + 1);
146 }
147 },
148 RefCountedOp::Release => match counter {
149 Some(1) => {
150 counter = None;
151 value_to_write = None;
152 },
153 Some(c) => counter = Some(c - 1),
154 None => {},
155 },
156 }
157 }
158
159 match counter {
160 Some(counter) => {
161 tx.put(col, &counter_key, &counter.to_le_bytes());
162 if let Some(value) = value_to_write {
163 tx.put_vec(col, &key, value);
164 }
165 },
166 None => {
167 tx.delete(col, &counter_key);
168 tx.delete(col, &key);
169 },
170 }
171 }
172
173 db.write(tx).map_err(|e| error::DatabaseError(Box::new(e)))
174}
175
176pub fn as_database<D, H>(db: D) -> std::sync::Arc<dyn Database<H>>
178where
179 D: KeyValueDB + 'static,
180 H: Clone + AsRef<[u8]>,
181{
182 std::sync::Arc::new(DbAdapter(db))
183}
184
185impl<D: KeyValueDB, H: Clone + AsRef<[u8]>> Database<H> for DbAdapter<D> {
186 fn commit(&self, transaction: Transaction<H>) -> error::Result<()> {
187 commit_impl(&self.0, transaction)
188 }
189
190 fn get(&self, col: ColumnId, key: &[u8]) -> Option<Vec<u8>> {
191 handle_err(self.0.get(col, key))
192 }
193
194 fn contains(&self, col: ColumnId, key: &[u8]) -> bool {
195 handle_err(self.0.has_key(col, key))
196 }
197}
198
199#[cfg(feature = "rocksdb")]
201pub struct RocksDbAdapter(kvdb_rocksdb::Database);
202
203#[cfg(feature = "rocksdb")]
204impl<H: Clone + AsRef<[u8]>> Database<H> for RocksDbAdapter {
205 fn commit(&self, transaction: Transaction<H>) -> error::Result<()> {
206 commit_impl(&self.0, transaction)
207 }
208
209 fn get(&self, col: ColumnId, key: &[u8]) -> Option<Vec<u8>> {
210 handle_err(self.0.get(col, key))
211 }
212
213 fn contains(&self, col: ColumnId, key: &[u8]) -> bool {
214 handle_err(self.0.has_key(col, key))
215 }
216
217 fn optimize_db_col(&self, col: ColumnId) -> error::Result<()> {
218 self.0.force_compact(col).map_err(|e| error::DatabaseError(Box::new(e)))
219 }
220}
221
222#[cfg(feature = "rocksdb")]
224pub fn as_rocksdb_database<H>(db: kvdb_rocksdb::Database) -> std::sync::Arc<dyn Database<H>>
225where
226 H: Clone + AsRef<[u8]>,
227{
228 std::sync::Arc::new(RocksDbAdapter(db))
229}