Line data Source code
1 : // Copyright (c) 2012-2021 The Bitcoin Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #ifndef BITCOIN_DBWRAPPER_H
6 : #define BITCOIN_DBWRAPPER_H
7 :
8 : #include <assert.h>
9 : #include <clientversion.h>
10 : #include <fs.h>
11 : #include <logging.h>
12 : #include <serialize.h>
13 : #include <span.h>
14 : #include <streams.h>
15 :
16 : #include <sys/types.h>
17 :
18 : #include <algorithm>
19 : #include <cstddef>
20 : #include <cstdint>
21 : #include <exception>
22 : #include <leveldb/db.h>
23 : #include <leveldb/iterator.h>
24 : #include <leveldb/options.h>
25 : #include <leveldb/slice.h>
26 : #include <leveldb/status.h>
27 : #include <leveldb/write_batch.h>
28 : #include <map>
29 : #include <memory>
30 : #include <set>
31 : #include <stdexcept>
32 : #include <string>
33 : #include <type_traits>
34 : #include <utility>
35 : #include <vector>
36 :
37 : namespace leveldb {
38 : class Env;
39 : }
40 :
41 : static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
42 : static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
43 :
44 13421804 : inline auto CharCast(const std::byte* data) { return reinterpret_cast<const char*>(data); }
45 :
46 : class dbwrapper_error : public std::runtime_error
47 : {
48 : public:
49 0 : explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
50 : };
51 :
52 : class CDBWrapper;
53 :
54 : /** These should be considered an implementation detail of the specific database.
55 : */
56 : namespace dbwrapper_private {
57 :
58 : /** Handle database error by throwing dbwrapper_error exception.
59 : */
60 : void HandleError(const leveldb::Status& status);
61 :
62 : /** Work around circular dependency, as well as for testing in dbwrapper_tests.
63 : * Database obfuscation should be considered an implementation detail of the
64 : * specific database.
65 : */
66 : const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w);
67 :
68 : };
69 :
70 : /** Batch of changes queued to be written to a CDBWrapper */
71 : class CDBBatch
72 : {
73 : friend class CDBWrapper;
74 :
75 : private:
76 : const CDBWrapper &parent;
77 : leveldb::WriteBatch batch;
78 :
79 : CDataStream ssKey;
80 : CDataStream ssValue;
81 :
82 26502 : size_t size_estimate{0};
83 :
84 : public:
85 : /**
86 : * @param[in] _parent CDBWrapper that this batch is to be submitted to
87 : */
88 53004 : explicit CDBBatch(const CDBWrapper& _parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION) {};
89 :
90 739 : void Clear()
91 : {
92 739 : batch.Clear();
93 739 : size_estimate = 0;
94 739 : }
95 :
96 : template <typename K, typename V>
97 60953 : void Write(const K& key, const V& value)
98 : {
99 60953 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
100 60953 : ssKey << key;
101 60953 : Write(ssKey, value);
102 60953 : ssKey.clear();
103 60953 : }
104 :
105 : template <typename V>
106 60961 : void Write(const CDataStream& _ssKey, const V& value)
107 : {
108 60961 : leveldb::Slice slKey(CharCast(_ssKey.data()), _ssKey.size());
109 :
110 60961 : ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
111 60961 : ssValue << value;
112 60961 : ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
113 60961 : leveldb::Slice slValue(CharCast(ssValue.data()), ssValue.size());
114 :
115 60961 : batch.Put(slKey, slValue);
116 : // - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...)
117 : // - byte[]: key
118 : // - varint: value length
119 : // - byte[]: value
120 : // The formula below assumes the key and value are both less than 16k.
121 60961 : size_estimate += 3 + (slKey.size() > 127) + slKey.size() + (slValue.size() > 127) + slValue.size();
122 60961 : ssValue.clear();
123 60961 : }
124 :
125 : template <typename K>
126 14329 : void Erase(const K& key)
127 : {
128 14329 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
129 14329 : ssKey << key;
130 14329 : Erase(ssKey);
131 14329 : ssKey.clear();
132 14329 : }
133 :
134 14329 : void Erase(const CDataStream& _ssKey) {
135 14329 : leveldb::Slice slKey(CharCast(_ssKey.data()), _ssKey.size());
136 :
137 14329 : batch.Delete(slKey);
138 : // - byte: header
139 : // - varint: key length
140 : // - byte[]: key
141 : // The formula below assumes the key is less than 16kB.
142 14329 : size_estimate += 2 + (slKey.size() > 127) + slKey.size();
143 14329 : }
144 :
145 104160 : size_t SizeEstimate() const { return size_estimate; }
146 : };
147 :
148 : class CDBIterator
149 : {
150 : private:
151 : const CDBWrapper &parent;
152 : leveldb::Iterator *piter;
153 :
154 : public:
155 :
156 : /**
157 : * @param[in] _parent Parent CDBWrapper instance.
158 : * @param[in] _piter The original leveldb iterator.
159 : */
160 441472 : CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) :
161 441472 : parent(_parent), piter(_piter) { };
162 : ~CDBIterator();
163 :
164 : bool Valid() const;
165 :
166 : void SeekToFirst();
167 :
168 1720 : template<typename K> void Seek(const K& key) {
169 1720 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
170 1720 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
171 1720 : ssKey << key;
172 1720 : Seek(ssKey);
173 1720 : }
174 :
175 220540 : void Seek(const CDataStream& ssKey) {
176 220540 : leveldb::Slice slKey(CharCast(ssKey.data()), ssKey.size());
177 220540 : piter->Seek(slKey);
178 220540 : }
179 :
180 : void Next();
181 :
182 3473 : template<typename K> bool GetKey(K& key) {
183 : try {
184 3473 : CDataStream ssKey = GetKey();
185 3473 : ssKey >> key;
186 3473 : } catch (const std::exception&) {
187 0 : return false;
188 0 : }
189 3473 : return true;
190 3473 : }
191 :
192 3473 : CDataStream GetKey() {
193 3473 : leveldb::Slice slKey = piter->key();
194 3473 : return CDataStream{MakeByteSpan(slKey), SER_DISK, CLIENT_VERSION};
195 : }
196 :
197 : unsigned int GetKeySize() {
198 : return piter->key().size();
199 : }
200 :
201 3283 : template<typename V> bool GetValue(V& value) {
202 3283 : leveldb::Slice slValue = piter->value();
203 : try {
204 3283 : CDataStream ssValue{MakeByteSpan(slValue), SER_DISK, CLIENT_VERSION};
205 3283 : ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
206 3283 : ssValue >> value;
207 3283 : } catch (const std::exception&) {
208 0 : return false;
209 0 : }
210 3283 : return true;
211 3283 : }
212 : };
213 :
214 : class CDBWrapper
215 : {
216 : friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w);
217 : private:
218 : //! custom environment this database is using (may be nullptr in case of default environment)
219 : leveldb::Env* penv;
220 :
221 : //! database options used
222 : leveldb::Options options;
223 :
224 : //! options used when reading from the database
225 : leveldb::ReadOptions readoptions;
226 :
227 : //! options used when iterating over values of the database
228 : leveldb::ReadOptions iteroptions;
229 :
230 : //! options used when writing to the database
231 : leveldb::WriteOptions writeoptions;
232 :
233 : //! options used when sync writing to the database
234 : leveldb::WriteOptions syncoptions;
235 :
236 : //! the database itself
237 : leveldb::DB* pdb;
238 :
239 : //! the name of this database
240 : std::string m_name;
241 :
242 : //! a key used for optional XOR-obfuscation of the database
243 : std::vector<unsigned char> obfuscate_key;
244 :
245 : //! the key under which the obfuscation key is stored
246 : static const std::string OBFUSCATE_KEY_KEY;
247 :
248 : //! the length of the obfuscate key in number of bytes
249 : static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
250 :
251 : std::vector<unsigned char> CreateObfuscateKey() const;
252 :
253 : public:
254 : /**
255 : * @param[in] path Location in the filesystem where leveldb data will be stored.
256 : * @param[in] nCacheSize Configures various leveldb cache settings.
257 : * @param[in] fMemory If true, use leveldb's memory environment.
258 : * @param[in] fWipe If true, remove all existing data.
259 : * @param[in] obfuscate If true, store data obfuscated via simple XOR. If false, XOR
260 : * with a zero'd byte array.
261 : */
262 : CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
263 : ~CDBWrapper();
264 :
265 : CDBWrapper(const CDBWrapper&) = delete;
266 : CDBWrapper& operator=(const CDBWrapper&) = delete;
267 :
268 : template <typename K>
269 0 : bool ReadDataStream(const K& key, CDataStream& ssValue) const
270 : {
271 0 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
272 0 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
273 0 : ssKey << key;
274 0 : return ReadDataStream(ssKey, ssValue);
275 0 : }
276 :
277 12465108 : bool ReadDataStream(const CDataStream& ssKey, CDataStream& ssValue) const
278 : {
279 12465108 : leveldb::Slice slKey(CharCast(ssKey.data()), ssKey.size());
280 :
281 12465108 : std::string strValue;
282 12465108 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
283 12465108 : if (!status.ok()) {
284 12404773 : if (status.IsNotFound())
285 12404773 : return false;
286 0 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
287 0 : dbwrapper_private::HandleError(status);
288 0 : }
289 60335 : CDataStream ssValueTmp{MakeByteSpan(strValue), SER_DISK, CLIENT_VERSION};
290 60335 : ssValueTmp.Xor(obfuscate_key);
291 60335 : ssValue = std::move(ssValueTmp);
292 60335 : return true;
293 12465108 : }
294 :
295 : template <typename K, typename V>
296 12463823 : bool Read(const K& key, V& value) const
297 : {
298 12463823 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
299 12463823 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
300 12463823 : ssKey << key;
301 12463823 : return Read(ssKey, value);
302 12463823 : }
303 :
304 : template <typename V>
305 12465108 : bool Read(const CDataStream& ssKey, V& value) const
306 : {
307 12465108 : CDataStream ssValue(SER_DISK, CLIENT_VERSION);
308 12465108 : if (!ReadDataStream(ssKey, ssValue)) {
309 12404773 : return false;
310 : }
311 :
312 : try {
313 60335 : ssValue >> value;
314 60335 : } catch (const std::exception&) {
315 0 : return false;
316 0 : }
317 60335 : return true;
318 12465108 : }
319 :
320 : template <typename K, typename V>
321 1062 : bool Write(const K& key, const V& value, bool fSync = false)
322 : {
323 1062 : CDBBatch batch(*this);
324 1062 : batch.Write(key, value);
325 1062 : return WriteBatch(batch, fSync);
326 1062 : }
327 :
328 : template <typename K>
329 598269 : bool Exists(const K& key) const
330 : {
331 598269 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
332 598269 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
333 598269 : ssKey << key;
334 598269 : return Exists(ssKey);
335 598269 : }
336 :
337 599882 : bool Exists(const CDataStream& key) const
338 : {
339 599882 : leveldb::Slice slKey(CharCast(key.data()), key.size());
340 :
341 599882 : std::string strValue;
342 599882 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
343 599882 : if (!status.ok()) {
344 599862 : if (status.IsNotFound())
345 599862 : return false;
346 0 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
347 0 : dbwrapper_private::HandleError(status);
348 0 : }
349 20 : return true;
350 599882 : }
351 :
352 : template <typename K>
353 0 : bool Erase(const K& key, bool fSync = false)
354 : {
355 0 : CDBBatch batch(*this);
356 0 : batch.Erase(key);
357 0 : return WriteBatch(batch, fSync);
358 0 : }
359 :
360 : bool WriteBatch(CDBBatch& batch, bool fSync = false);
361 :
362 : // Get an estimate of LevelDB memory usage (in bytes).
363 : size_t DynamicMemoryUsage() const;
364 :
365 220736 : CDBIterator *NewIterator()
366 : {
367 220736 : return new CDBIterator(*this, pdb->NewIterator(iteroptions));
368 0 : }
369 :
370 : /**
371 : * Return true if the database managed by this class contains no entries.
372 : */
373 : bool IsEmpty();
374 :
375 : template<typename K>
376 12 : size_t EstimateSize(const K& key_begin, const K& key_end) const
377 : {
378 12 : CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
379 12 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
380 12 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
381 12 : ssKey1 << key_begin;
382 12 : ssKey2 << key_end;
383 12 : leveldb::Slice slKey1(CharCast(ssKey1.data()), ssKey1.size());
384 12 : leveldb::Slice slKey2(CharCast(ssKey2.data()), ssKey2.size());
385 12 : uint64_t size = 0;
386 12 : leveldb::Range range(slKey1, slKey2);
387 12 : pdb->GetApproximateSizes(&range, 1, &size);
388 12 : return size;
389 12 : }
390 :
391 180 : void CompactFull() const
392 : {
393 180 : pdb->CompactRange(nullptr, nullptr);
394 180 : }
395 :
396 : /**
397 : * Compact a specific range of keys in the database.
398 : */
399 : template<typename K>
400 0 : void CompactRange(const K& key_begin, const K& key_end) const
401 : {
402 0 : CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
403 0 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
404 0 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
405 0 : ssKey1 << key_begin;
406 0 : ssKey2 << key_end;
407 0 : leveldb::Slice slKey1(CharCast(ssKey1.data()), ssKey1.size());
408 0 : leveldb::Slice slKey2(CharCast(ssKey2.data()), ssKey2.size());
409 0 : pdb->CompactRange(&slKey1, &slKey2);
410 0 : }
411 : };
412 :
413 : template<typename CDBTransaction>
414 : class CDBTransactionIterator
415 : {
416 : private:
417 : CDBTransaction& transaction;
418 :
419 : typedef typename std::remove_pointer<decltype(transaction.parent.NewIterator())>::type ParentIterator;
420 :
421 : // We maintain 2 iterators, one for the transaction and one for the parent
422 : // At all times, only one of both provides the current value. The decision is made by comparing the current keys
423 : // of both iterators, so that always the smaller key is the current one. On Next(), the previously chosen iterator
424 : // is advanced.
425 : typename CDBTransaction::WritesMap::iterator transactionIt;
426 : std::unique_ptr<ParentIterator> parentIt;
427 : CDataStream parentKey;
428 437640 : bool curIsParent{false};
429 :
430 : public:
431 1312920 : explicit CDBTransactionIterator(CDBTransaction& _transaction) :
432 437640 : transaction(_transaction),
433 437640 : parentKey(SER_DISK, CLIENT_VERSION)
434 437640 : {
435 437640 : transactionIt = transaction.writes.end();
436 437640 : parentIt = std::unique_ptr<ParentIterator>(transaction.parent.NewIterator());
437 875280 : }
438 :
439 : void SeekToFirst() {
440 : transactionIt = transaction.writes.begin();
441 : parentIt->SeekToFirst();
442 : SkipDeletedAndOverwritten();
443 : DecideCur();
444 : }
445 :
446 : template<typename K>
447 218820 : void Seek(const K& key) {
448 218820 : Seek(CDBTransaction::KeyToDataStream(key));
449 218820 : }
450 :
451 437640 : void Seek(const CDataStream& ssKey) {
452 437640 : transactionIt = transaction.writes.lower_bound(ssKey);
453 437640 : parentIt->Seek(ssKey);
454 437640 : SkipDeletedAndOverwritten();
455 437640 : DecideCur();
456 437640 : }
457 :
458 1239244 : bool Valid() {
459 1239244 : return transactionIt != transaction.writes.end() || parentIt->Valid();
460 : }
461 :
462 4 : void Next() {
463 4 : if (transactionIt == transaction.writes.end() && !parentIt->Valid()) {
464 0 : return;
465 : }
466 4 : if (curIsParent) {
467 0 : assert(parentIt->Valid());
468 0 : parentIt->Next();
469 0 : SkipDeletedAndOverwritten();
470 0 : } else {
471 4 : assert(transactionIt != transaction.writes.end());
472 4 : ++transactionIt;
473 : }
474 4 : DecideCur();
475 4 : }
476 :
477 : template<typename K>
478 121320 : bool GetKey(K& key) {
479 121320 : if (!Valid()) {
480 0 : return false;
481 : }
482 :
483 : try {
484 : // TODO try to avoid copy transactionIt->first (we need a stream that allows reading from external buffers)
485 121320 : (curIsParent ? parentKey : CDataStream{transactionIt->first}) >> key;
486 121320 : } catch (const std::exception&) {
487 0 : return false;
488 0 : }
489 121320 : return true;
490 121320 : }
491 :
492 121324 : CDataStream GetKey() {
493 121324 : if (!Valid()) {
494 0 : return CDataStream(SER_DISK, CLIENT_VERSION);
495 : }
496 121324 : if (curIsParent) {
497 0 : return parentKey;
498 : } else {
499 121324 : return transactionIt->first;
500 : }
501 121324 : }
502 :
503 : unsigned int GetKeySize() {
504 : if (!Valid()) {
505 : return 0;
506 : }
507 : if (curIsParent) {
508 : return parentIt->GetKeySize();
509 : } else {
510 : return transactionIt->first.vKey.size();
511 : }
512 : }
513 :
514 : template<typename V>
515 0 : bool GetValue(V& value) {
516 0 : if (!Valid()) {
517 0 : return false;
518 : }
519 0 : if (curIsParent) {
520 0 : return transaction.Read(parentKey, value);
521 : } else {
522 0 : return transaction.Read(transactionIt->first, value);
523 : }
524 0 : };
525 :
526 : private:
527 437640 : void SkipDeletedAndOverwritten() {
528 437644 : while (parentIt->Valid()) {
529 121324 : parentKey = parentIt->GetKey();
530 121324 : if (!transaction.deletes.count(parentKey) && !transaction.writes.count(parentKey)) {
531 121320 : break;
532 : }
533 4 : parentIt->Next();
534 : }
535 437640 : }
536 :
537 437644 : void DecideCur() {
538 437644 : if (transactionIt != transaction.writes.end() && !parentIt->Valid()) {
539 121324 : curIsParent = false;
540 437644 : } else if (transactionIt == transaction.writes.end() && parentIt->Valid()) {
541 121316 : curIsParent = true;
542 316320 : } else if (transactionIt != transaction.writes.end() && parentIt->Valid()) {
543 4 : if (CDBTransaction::DataStreamCmp::less(transactionIt->first, parentKey)) {
544 4 : curIsParent = false;
545 4 : } else {
546 0 : curIsParent = true;
547 : }
548 4 : }
549 437644 : }
550 : };
551 :
552 : template<typename Parent, typename CommitTarget>
553 : class CDBTransaction {
554 : friend class CDBTransactionIterator<CDBTransaction>;
555 :
556 : protected:
557 : Parent &parent;
558 : CommitTarget &commitTarget;
559 1610 : ssize_t memoryUsage{0}; // signed, just in case we made an error in the calculations so that we don't get an overflow
560 :
561 : struct DataStreamCmp {
562 2952073 : static bool less(const CDataStream& a, const CDataStream& b) {
563 2952073 : return std::lexicographical_compare(
564 2952073 : (const uint8_t*)a.data(), (const uint8_t*)a.data() + a.size(),
565 2952073 : (const uint8_t*)b.data(), (const uint8_t*)b.data() + b.size());
566 : }
567 2952069 : bool operator()(const CDataStream& a, const CDataStream& b) const {
568 2952069 : return less(a, b);
569 : }
570 : };
571 :
572 : struct ValueHolder {
573 : size_t memoryUsage;
574 157411 : explicit ValueHolder(size_t _memoryUsage) : memoryUsage(_memoryUsage) {}
575 157411 : virtual ~ValueHolder() = default;
576 : virtual void Write(const CDataStream& ssKey, CommitTarget &parent) = 0;
577 : };
578 : typedef std::unique_ptr<ValueHolder> ValueHolderPtr;
579 :
580 : template <typename V>
581 : struct ValueHolderImpl : ValueHolder {
582 314822 : ValueHolderImpl(const V &_value, size_t _memoryUsage) : ValueHolder(_memoryUsage), value(_value) {}
583 :
584 66440 : virtual void Write(const CDataStream& ssKey, CommitTarget &commitTarget) override {
585 : // we're moving the value instead of copying it. This means that Write() can only be called once per
586 : // ValueHolderImpl instance. Commit() clears the write maps, so this ok.
587 66440 : commitTarget.Write(ssKey, std::move(value));
588 66440 : }
589 : V value;
590 : };
591 :
592 : template<typename K>
593 330937 : static CDataStream KeyToDataStream(const K& key) {
594 330937 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
595 330937 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
596 330937 : ssKey << key;
597 330937 : return ssKey;
598 330937 : }
599 :
600 : typedef std::map<CDataStream, ValueHolderPtr, DataStreamCmp> WritesMap;
601 : typedef std::set<CDataStream, DataStreamCmp> DeletesSet;
602 :
603 : WritesMap writes;
604 : DeletesSet deletes;
605 :
606 : public:
607 4830 : CDBTransaction(Parent &_parent, CommitTarget &_commitTarget) : parent(_parent), commitTarget(_commitTarget) {}
608 :
609 : template <typename K, typename V>
610 90979 : void Write(const K& key, const V& v) {
611 90979 : Write(KeyToDataStream(key), v);
612 90979 : }
613 :
614 : template <typename V>
615 157411 : void Write(const CDataStream& ssKey, const V& v) {
616 157411 : auto valueMemoryUsage = ::GetSerializeSize(v, CLIENT_VERSION);
617 :
618 157411 : if (deletes.erase(ssKey)) {
619 0 : memoryUsage -= ssKey.size();
620 0 : }
621 157411 : auto it = writes.emplace(ssKey, nullptr).first;
622 157411 : if (it->second) {
623 49620 : memoryUsage -= ssKey.size() + it->second->memoryUsage;
624 49620 : }
625 157411 : it->second = std::make_unique<ValueHolderImpl<V>>(v, valueMemoryUsage);
626 :
627 157411 : memoryUsage += ssKey.size() + valueMemoryUsage;
628 157411 : }
629 :
630 : template <typename K, typename V>
631 19525 : bool Read(const K& key, V& value) {
632 19525 : return Read(KeyToDataStream(key), value);
633 0 : }
634 :
635 : template <typename V>
636 39047 : bool Read(const CDataStream& ssKey, V& value) {
637 39047 : if (deletes.count(ssKey)) {
638 0 : return false;
639 : }
640 :
641 39047 : auto it = writes.find(ssKey);
642 39047 : if (it != writes.end()) {
643 18240 : auto *impl = dynamic_cast<ValueHolderImpl<V> *>(it->second.get());
644 18240 : if (!impl) {
645 0 : throw std::runtime_error("Read called with V != previously written type");
646 : }
647 18240 : value = impl->value;
648 18240 : return true;
649 : }
650 :
651 20807 : return parent.Read(ssKey, value);
652 39047 : }
653 :
654 : template <typename K>
655 1613 : bool Exists(const K& key) {
656 1613 : return Exists(KeyToDataStream(key));
657 0 : }
658 :
659 3226 : bool Exists(const CDataStream& ssKey) {
660 3226 : if (deletes.count(ssKey)) {
661 0 : return false;
662 : }
663 :
664 3226 : if (writes.count(ssKey)) {
665 0 : return true;
666 : }
667 :
668 3226 : return parent.Exists(ssKey);
669 3226 : }
670 :
671 : template <typename K>
672 0 : void Erase(const K& key) {
673 0 : return Erase(KeyToDataStream(key));
674 0 : }
675 :
676 0 : void Erase(const CDataStream& ssKey) {
677 0 : auto it = writes.find(ssKey);
678 0 : if (it != writes.end()) {
679 0 : memoryUsage -= ssKey.size() + it->second->memoryUsage;
680 0 : writes.erase(it);
681 0 : }
682 0 : if (deletes.emplace(ssKey).second) {
683 0 : memoryUsage += ssKey.size();
684 0 : }
685 0 : }
686 :
687 50318 : void Clear() {
688 50318 : writes.clear();
689 50318 : deletes.clear();
690 50318 : memoryUsage = 0;
691 50318 : }
692 :
693 25797 : void Commit() {
694 25797 : for (const auto &k : deletes) {
695 0 : commitTarget.Erase(k);
696 : }
697 92237 : for (auto &p : writes) {
698 66440 : p.second->Write(p.first, commitTarget);
699 : }
700 25797 : Clear();
701 25797 : }
702 :
703 559 : bool IsClean() const {
704 559 : return writes.empty() && deletes.empty();
705 : }
706 :
707 50128 : size_t GetMemoryUsage() const {
708 50128 : if (memoryUsage < 0) {
709 : // something went wrong when we accounted/calculated used memory...
710 : static volatile bool didPrint = false;
711 0 : if (!didPrint) {
712 0 : LogPrintf("CDBTransaction::%s -- negative memoryUsage (%d)\n", __func__, memoryUsage);
713 0 : didPrint = true;
714 0 : }
715 0 : return 0;
716 : }
717 50128 : return (size_t)memoryUsage;
718 50128 : }
719 :
720 218820 : CDBTransactionIterator<CDBTransaction>* NewIterator() {
721 218820 : return new CDBTransactionIterator<CDBTransaction>(*this);
722 0 : }
723 218820 : std::unique_ptr<CDBTransactionIterator<CDBTransaction>> NewIteratorUniquePtr() {
724 218820 : return std::make_unique<CDBTransactionIterator<CDBTransaction>>(*this);
725 : }
726 : };
727 :
728 : namespace util {
729 : struct DbWrapperParams
730 : {
731 : const fs::path path{""};
732 : const bool memory{false};
733 : const bool wipe{false};
734 : const size_t cache_size{1 << 20};
735 : };
736 :
737 1525 : static inline std::unique_ptr<CDBWrapper> MakeDbWrapper(const DbWrapperParams& params)
738 : {
739 1525 : return std::make_unique<CDBWrapper>(params.path, params.cache_size, params.memory, params.wipe);
740 : }
741 : } // namespace util
742 :
743 : #endif // BITCOIN_DBWRAPPER_H
|