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 24539518 : 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 12 : 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 708966 : size_t size_estimate{0};
83 :
84 : public:
85 : /**
86 : * @param[in] _parent CDBWrapper that this batch is to be submitted to
87 : */
88 1417933 : explicit CDBBatch(const CDBWrapper& _parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION) {};
89 :
90 18139 : void Clear()
91 : {
92 18139 : batch.Clear();
93 18139 : size_estimate = 0;
94 18139 : }
95 :
96 : template <typename K, typename V>
97 1960141 : void Write(const K& key, const V& value)
98 : {
99 1960141 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
100 1960141 : ssKey << key;
101 1960141 : Write(ssKey, value);
102 1960141 : ssKey.clear();
103 1960141 : }
104 :
105 : template <typename V>
106 2174071 : void Write(const CDataStream& _ssKey, const V& value)
107 : {
108 2174071 : leveldb::Slice slKey(CharCast(_ssKey.data()), _ssKey.size());
109 :
110 2174071 : ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
111 2174071 : ssValue << value;
112 2174071 : ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
113 2174071 : leveldb::Slice slValue(CharCast(ssValue.data()), ssValue.size());
114 :
115 2174071 : 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 2174071 : size_estimate += 3 + (slKey.size() > 127) + slKey.size() + (slValue.size() > 127) + slValue.size();
122 2174071 : ssValue.clear();
123 2174071 : }
124 :
125 : template <typename K>
126 76451 : void Erase(const K& key)
127 : {
128 76451 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
129 76451 : ssKey << key;
130 76451 : Erase(ssKey);
131 76451 : ssKey.clear();
132 76451 : }
133 :
134 76499 : void Erase(const CDataStream& _ssKey) {
135 76499 : leveldb::Slice slKey(CharCast(_ssKey.data()), _ssKey.size());
136 :
137 76499 : 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 76499 : size_estimate += 2 + (slKey.size() > 127) + slKey.size();
143 76499 : }
144 :
145 788414 : 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 4017732 : CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) :
161 4017732 : parent(_parent), piter(_piter) { };
162 : ~CDBIterator();
163 :
164 : bool Valid() const;
165 :
166 : void SeekToFirst();
167 :
168 98470 : template<typename K> void Seek(const K& key) {
169 98470 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
170 98470 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
171 98470 : ssKey << key;
172 98470 : Seek(ssKey);
173 98470 : }
174 :
175 2007521 : void Seek(const CDataStream& ssKey) {
176 2007521 : leveldb::Slice slKey(CharCast(ssKey.data()), ssKey.size());
177 2007521 : piter->Seek(slKey);
178 2007521 : }
179 :
180 : void Next();
181 :
182 670682 : template<typename K> bool GetKey(K& key) {
183 : try {
184 670682 : CDataStream ssKey = GetKey();
185 670682 : ssKey >> key;
186 670682 : } catch (const std::exception&) {
187 27315 : return false;
188 27315 : }
189 643367 : return true;
190 697997 : }
191 :
192 1225944 : CDataStream GetKey() {
193 1225944 : leveldb::Slice slKey = piter->key();
194 1225944 : return CDataStream{MakeByteSpan(slKey), SER_DISK, CLIENT_VERSION};
195 : }
196 :
197 : unsigned int GetKeySize() {
198 : return piter->key().size();
199 : }
200 :
201 617553 : template<typename V> bool GetValue(V& value) {
202 617553 : leveldb::Slice slValue = piter->value();
203 : try {
204 617553 : CDataStream ssValue{MakeByteSpan(slValue), SER_DISK, CLIENT_VERSION};
205 617553 : ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
206 617553 : ssValue >> value;
207 617553 : } catch (const std::exception&) {
208 0 : return false;
209 0 : }
210 617553 : return true;
211 617553 : }
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 24314 : bool ReadDataStream(const K& key, CDataStream& ssValue) const
270 : {
271 24314 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
272 24314 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
273 24314 : ssKey << key;
274 24314 : return ReadDataStream(ssKey, ssValue);
275 24314 : }
276 :
277 17278137 : bool ReadDataStream(const CDataStream& ssKey, CDataStream& ssValue) const
278 : {
279 17278137 : leveldb::Slice slKey(CharCast(ssKey.data()), ssKey.size());
280 :
281 17278137 : std::string strValue;
282 17278137 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
283 17278137 : if (!status.ok()) {
284 16708029 : if (status.IsNotFound())
285 16708027 : return false;
286 2 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
287 2 : dbwrapper_private::HandleError(status);
288 0 : }
289 570108 : CDataStream ssValueTmp{MakeByteSpan(strValue), SER_DISK, CLIENT_VERSION};
290 570108 : ssValueTmp.Xor(obfuscate_key);
291 570108 : ssValue = std::move(ssValueTmp);
292 570108 : return true;
293 17278137 : }
294 :
295 : template <typename K, typename V>
296 16484409 : bool Read(const K& key, V& value) const
297 : {
298 16484409 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
299 16484409 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
300 16484409 : ssKey << key;
301 16484409 : return Read(ssKey, value);
302 16484409 : }
303 :
304 : template <typename V>
305 17250993 : bool Read(const CDataStream& ssKey, V& value) const
306 : {
307 17250993 : CDataStream ssValue(SER_DISK, CLIENT_VERSION);
308 17250993 : if (!ReadDataStream(ssKey, ssValue)) {
309 16697182 : return false;
310 : }
311 :
312 : try {
313 553809 : ssValue >> value;
314 553809 : } catch (const std::exception&) {
315 0 : return false;
316 0 : }
317 553809 : return true;
318 17250993 : }
319 :
320 : template <typename K, typename V>
321 185090 : bool Write(const K& key, const V& value, bool fSync = false)
322 : {
323 185090 : CDBBatch batch(*this);
324 185090 : batch.Write(key, value);
325 185089 : return WriteBatch(batch, fSync);
326 185090 : }
327 :
328 : template <typename K>
329 796059 : bool Exists(const K& key) const
330 : {
331 796059 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
332 796059 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
333 796059 : ssKey << key;
334 796059 : return Exists(ssKey);
335 796059 : }
336 :
337 823719 : bool Exists(const CDataStream& key) const
338 : {
339 823719 : leveldb::Slice slKey(CharCast(key.data()), key.size());
340 :
341 823719 : std::string strValue;
342 823719 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
343 823719 : if (!status.ok()) {
344 809894 : if (status.IsNotFound())
345 809894 : return false;
346 0 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
347 0 : dbwrapper_private::HandleError(status);
348 0 : }
349 13825 : return true;
350 823719 : }
351 :
352 : template <typename K>
353 69 : bool Erase(const K& key, bool fSync = false)
354 : {
355 69 : CDBBatch batch(*this);
356 69 : batch.Erase(key);
357 69 : return WriteBatch(batch, fSync);
358 69 : }
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 2008866 : CDBIterator *NewIterator()
366 : {
367 2008866 : 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 111 : size_t EstimateSize(const K& key_begin, const K& key_end) const
377 : {
378 111 : CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
379 111 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
380 111 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
381 111 : ssKey1 << key_begin;
382 111 : ssKey2 << key_end;
383 111 : leveldb::Slice slKey1(CharCast(ssKey1.data()), ssKey1.size());
384 111 : leveldb::Slice slKey2(CharCast(ssKey2.data()), ssKey2.size());
385 111 : uint64_t size = 0;
386 111 : leveldb::Range range(slKey1, slKey2);
387 111 : pdb->GetApproximateSizes(&range, 1, &size);
388 111 : return size;
389 111 : }
390 :
391 2868 : void CompactFull() const
392 : {
393 2868 : pdb->CompactRange(nullptr, nullptr);
394 2868 : }
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 3818102 : bool curIsParent{false};
429 :
430 : public:
431 11454306 : explicit CDBTransactionIterator(CDBTransaction& _transaction) :
432 3818102 : transaction(_transaction),
433 3818102 : parentKey(SER_DISK, CLIENT_VERSION)
434 3818102 : {
435 3818102 : transactionIt = transaction.writes.end();
436 3818102 : parentIt = std::unique_ptr<ParentIterator>(transaction.parent.NewIterator());
437 7636204 : }
438 :
439 : void SeekToFirst() {
440 : transactionIt = transaction.writes.begin();
441 : parentIt->SeekToFirst();
442 : SkipDeletedAndOverwritten();
443 : DecideCur();
444 : }
445 :
446 : template<typename K>
447 1909051 : void Seek(const K& key) {
448 1909051 : Seek(CDBTransaction::KeyToDataStream(key));
449 1909051 : }
450 :
451 3818102 : void Seek(const CDataStream& ssKey) {
452 3818102 : transactionIt = transaction.writes.lower_bound(ssKey);
453 3818102 : parentIt->Seek(ssKey);
454 3818102 : SkipDeletedAndOverwritten();
455 3818102 : DecideCur();
456 3818102 : }
457 :
458 14039490 : bool Valid() {
459 14039490 : return transactionIt != transaction.writes.end() || parentIt->Valid();
460 : }
461 :
462 639294 : void Next() {
463 639294 : if (transactionIt == transaction.writes.end() && !parentIt->Valid()) {
464 0 : return;
465 : }
466 639294 : if (curIsParent) {
467 349958 : assert(parentIt->Valid());
468 349958 : parentIt->Next();
469 349958 : SkipDeletedAndOverwritten();
470 349958 : } else {
471 289336 : assert(transactionIt != transaction.writes.end());
472 289336 : ++transactionIt;
473 : }
474 639294 : DecideCur();
475 639294 : }
476 :
477 : template<typename K>
478 1191600 : bool GetKey(K& key) {
479 1191600 : 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 1191600 : (curIsParent ? parentKey : CDataStream{transactionIt->first}) >> key;
486 1191600 : } catch (const std::exception&) {
487 0 : return false;
488 0 : }
489 1191600 : return true;
490 1191600 : }
491 :
492 1297083 : CDataStream GetKey() {
493 1297083 : if (!Valid()) {
494 0 : return CDataStream(SER_DISK, CLIENT_VERSION);
495 : }
496 1297083 : if (curIsParent) {
497 200044 : return parentKey;
498 : } else {
499 1097039 : return transactionIt->first;
500 : }
501 1297083 : }
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 410358 : bool GetValue(V& value) {
516 410358 : if (!Valid()) {
517 0 : return false;
518 : }
519 410358 : if (curIsParent) {
520 410358 : return transaction.Read(parentKey, value);
521 : } else {
522 0 : return transaction.Read(transactionIt->first, value);
523 : }
524 410358 : };
525 :
526 : private:
527 4168060 : void SkipDeletedAndOverwritten() {
528 4168110 : while (parentIt->Valid()) {
529 1852345 : parentKey = parentIt->GetKey();
530 1852345 : if (!transaction.deletes.count(parentKey) && !transaction.writes.count(parentKey)) {
531 1852295 : break;
532 : }
533 50 : parentIt->Next();
534 : }
535 4168060 : }
536 :
537 4457396 : void DecideCur() {
538 4457396 : if (transactionIt != transaction.writes.end() && !parentIt->Valid()) {
539 547882 : curIsParent = false;
540 4457396 : } else if (transactionIt == transaction.writes.end() && parentIt->Valid()) {
541 1305571 : curIsParent = true;
542 3909514 : } else if (transactionIt != transaction.writes.end() && parentIt->Valid()) {
543 741018 : if (CDBTransaction::DataStreamCmp::less(transactionIt->first, parentKey)) {
544 550601 : curIsParent = false;
545 550601 : } else {
546 190417 : curIsParent = true;
547 : }
548 741018 : }
549 4457396 : }
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 7380 : 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 37709661 : static bool less(const CDataStream& a, const CDataStream& b) {
563 37709661 : return std::lexicographical_compare(
564 37709661 : (const uint8_t*)a.data(), (const uint8_t*)a.data() + a.size(),
565 37709661 : (const uint8_t*)b.data(), (const uint8_t*)b.data() + b.size());
566 : }
567 36968643 : bool operator()(const CDataStream& a, const CDataStream& b) const {
568 36968643 : return less(a, b);
569 : }
570 : };
571 :
572 : struct ValueHolder {
573 : size_t memoryUsage;
574 1632006 : explicit ValueHolder(size_t _memoryUsage) : memoryUsage(_memoryUsage) {}
575 1632006 : 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 3264012 : ValueHolderImpl(const V &_value, size_t _memoryUsage) : ValueHolder(_memoryUsage), value(_value) {}
583 :
584 972790 : 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 972790 : commitTarget.Write(ssKey, std::move(value));
588 972790 : }
589 : V value;
590 : };
591 :
592 : template<typename K>
593 3731840 : static CDataStream KeyToDataStream(const K& key) {
594 3731840 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
595 3731840 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
596 3731840 : ssKey << key;
597 3731840 : return ssKey;
598 3731840 : }
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 22140 : CDBTransaction(Parent &_parent, CommitTarget &_commitTarget) : parent(_parent), commitTarget(_commitTarget) {}
608 :
609 : template <typename K, typename V>
610 873144 : void Write(const K& key, const V& v) {
611 873144 : Write(KeyToDataStream(key), v);
612 873144 : }
613 :
614 : template <typename V>
615 1632006 : void Write(const CDataStream& ssKey, const V& v) {
616 1632006 : auto valueMemoryUsage = ::GetSerializeSize(v, CLIENT_VERSION);
617 :
618 1632006 : if (deletes.erase(ssKey)) {
619 104 : memoryUsage -= ssKey.size();
620 104 : }
621 1632006 : auto it = writes.emplace(ssKey, nullptr).first;
622 1632006 : if (it->second) {
623 550203 : memoryUsage -= ssKey.size() + it->second->memoryUsage;
624 550203 : }
625 1632006 : it->second = std::make_unique<ValueHolderImpl<V>>(v, valueMemoryUsage);
626 :
627 1632006 : memoryUsage += ssKey.size() + valueMemoryUsage;
628 1632006 : }
629 :
630 : template <typename K, typename V>
631 917090 : bool Read(const K& key, V& value) {
632 917090 : return Read(KeyToDataStream(key), value);
633 0 : }
634 :
635 : template <typename V>
636 2651303 : bool Read(const CDataStream& ssKey, V& value) {
637 2651303 : if (deletes.count(ssKey)) {
638 0 : return false;
639 : }
640 :
641 2651303 : auto it = writes.find(ssKey);
642 2651303 : if (it != writes.end()) {
643 560864 : auto *impl = dynamic_cast<ValueHolderImpl<V> *>(it->second.get());
644 560864 : if (!impl) {
645 0 : throw std::runtime_error("Read called with V != previously written type");
646 : }
647 560864 : value = impl->value;
648 560864 : return true;
649 : }
650 :
651 2090439 : return parent.Read(ssKey, value);
652 2651303 : }
653 :
654 : template <typename K>
655 32373 : bool Exists(const K& key) {
656 32373 : return Exists(KeyToDataStream(key));
657 0 : }
658 :
659 64746 : bool Exists(const CDataStream& ssKey) {
660 64746 : if (deletes.count(ssKey)) {
661 44 : return false;
662 : }
663 :
664 64702 : if (writes.count(ssKey)) {
665 4669 : return true;
666 : }
667 :
668 60033 : return parent.Exists(ssKey);
669 64746 : }
670 :
671 : template <typename K>
672 182 : void Erase(const K& key) {
673 182 : return Erase(KeyToDataStream(key));
674 0 : }
675 :
676 334 : void Erase(const CDataStream& ssKey) {
677 334 : auto it = writes.find(ssKey);
678 334 : if (it != writes.end()) {
679 152 : memoryUsage -= ssKey.size() + it->second->memoryUsage;
680 152 : writes.erase(it);
681 152 : }
682 334 : if (deletes.emplace(ssKey).second) {
683 334 : memoryUsage += ssKey.size();
684 334 : }
685 334 : }
686 :
687 378332 : void Clear() {
688 378332 : writes.clear();
689 378332 : deletes.clear();
690 378332 : memoryUsage = 0;
691 378332 : }
692 :
693 288299 : void Commit() {
694 288499 : for (const auto &k : deletes) {
695 200 : commitTarget.Erase(k);
696 : }
697 1261089 : for (auto &p : writes) {
698 972790 : p.second->Write(p.first, commitTarget);
699 : }
700 288299 : Clear();
701 288299 : }
702 :
703 16974 : bool IsClean() const {
704 16974 : return writes.empty() && deletes.empty();
705 : }
706 :
707 535584 : size_t GetMemoryUsage() const {
708 535584 : 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 535584 : return (size_t)memoryUsage;
718 535584 : }
719 :
720 1909051 : CDBTransactionIterator<CDBTransaction>* NewIterator() {
721 1909051 : return new CDBTransactionIterator<CDBTransaction>(*this);
722 0 : }
723 1909051 : std::unique_ptr<CDBTransactionIterator<CDBTransaction>> NewIteratorUniquePtr() {
724 1909051 : 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 14674 : static inline std::unique_ptr<CDBWrapper> MakeDbWrapper(const DbWrapperParams& params)
738 : {
739 14674 : return std::make_unique<CDBWrapper>(params.path, params.cache_size, params.memory, params.wipe);
740 : }
741 : } // namespace util
742 :
743 : #endif // BITCOIN_DBWRAPPER_H
|