LCOV - code coverage report
Current view: top level - src - dbwrapper.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 302 339 89.1 %
Date: 2026-06-25 07:23:43 Functions: 419 440 95.2 %

          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

Generated by: LCOV version 1.16