LCOV - code coverage report
Current view: top level - src - dbwrapper.h (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 258 339 76.1 %
Date: 2026-06-25 07:23:51 Functions: 255 440 58.0 %

          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

Generated by: LCOV version 1.16