text: // Copyright (c) 2009-2010 Satoshi Nakamoto
  // Copyright (c) 2009-2014 The Bitcoin developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #ifndef LTSOIN_DB_H
  #define LTSOIN_DB_H
  
  #include "clientversion.h"
  #include "serialize.h"
  #include "streams.h"
  #include "sync.h"
  #include "version.h"
  
  #include <map>
  #include <string>
  #include <vector>
  
  #include <boost/filesystem/path.hpp>
  
  #include <db_cxx.h>
  
  class CDiskBlockIndex;
  class COutPoint;
  
  struct CBlockLocator;
  
  extern unsigned int nWalletDBUpdated;
  
  void ThreadFlushWalletDB(const std::string& strWalletFile);
  
  
  class CDBEnv
  {
  private:
      bool fDbEnvInit;
      bool fMockDb;
      // Don't change into boost::filesystem::path, as that can result in
      // shutdown problems/crashes caused by a static initialized internal pointer.
      std::string strPath;
  
      void EnvShutdown();
  
  public:
      mutable CCriticalSection cs_db;
      DbEnv dbenv;
      std::map<std::string, int> mapFileUseCount;
      std::map<std::string, Db*> mapDb;
  
      CDBEnv();
      ~CDBEnv();
      void MakeMock();
      bool IsMock() { return fMockDb; }
  
      /**
       * Verify that database file strFile is OK. If it is not,
       * call the callback to try to recover.
       * This must be called BEFORE strFile is opened.
       * Returns true if strFile is OK.
       */
      enum VerifyResult { VERIFY_OK,
          RECOVER_OK,
          RECOVER_FAIL };
      VerifyResult Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile));
      /**
       * Salvage data from a file that Verify says is bad.
       * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
       * Appends binary key/value pairs to vResult, returns true if successful.
       * NOTE: reads the entire database into memory, so cannot be used
       * for huge databases.
       */
      typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
      bool Salvage(std::string strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
  
      bool Open(const boost::filesystem::path& path);
      void Close();
      void Flush(bool fShutdown);
      void CheckpointLSN(const std::string& strFile);
  
      void CloseDb(const std::string& strFile);
      bool RemoveDb(const std::string& strFile);
  
      DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
      {
          DbTxn* ptxn = nullptr;
          int ret = dbenv.txn_begin(nullptr, &ptxn, flags);
          if (!ptxn || ret != 0)
              return nullptr;
          return ptxn;
      }
  };
  
  extern CDBEnv bitdb;
  
  
  /** RAII class that provides access to a Berkeley database */
  class CDB
  {
  protected:
      Db* pdb;
      std::string strFile;
      DbTxn* activeTxn;
      bool fReadOnly;
  
      explicit CDB(const std::string& strFilename, const char* pszMode = "r+");
      ~CDB() { Close(); }
  
  public:
      void Flush();
      void Close();
  
  private:
      CDB(const CDB&);
      void operator=(const CDB&);
  
  protected:
      template <typename K, typename T>
      bool Read(const K& key, T& value)
      {
          if (!pdb)
              return false;
  
          // Key
          CDataStream ssKey(SER_DISK, CLIENT_VERSION);
          ssKey.reserve(1000);
          ssKey << key;
          Dbt datKey(&ssKey[0], ssKey.size());
  
          // Read
          Dbt datValue;
          datValue.set_flags(DB_DBT_MALLOC);
          int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
          memset(datKey.get_data(), 0, datKey.get_size());
          if (datValue.get_data() == nullptr)
              return false;
  
          // Unserialize value
          try {
              CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
              ssValue >> value;
          } catch (const std::exception&) {
              return false;
          }
  
          // Clear and free memory
          memset(datValue.get_data(), 0, datValue.get_size());
          free(datValue.get_data());
          return (ret == 0);
      }
  
      template <typename K, typename T>
      bool Write(const K& key, const T& value, bool fOverwrite = true)
      {
          if (!pdb)
              return false;
          if (fReadOnly)
              assert(!"Write called on database in read-only mode");
  
          // Key
          CDataStream ssKey(SER_DISK, CLIENT_VERSION);
          ssKey.reserve(1000);
          ssKey << key;
          Dbt datKey(&ssKey[0], ssKey.size());
  
          // Value
          CDataStream ssValue(SER_DISK, CLIENT_VERSION);
          ssValue.reserve(10000);
          ssValue << value;
          Dbt datValue(&ssValue[0], ssValue.size());
  
          // Write
          int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
  
          // Clear memory in case it was a private key
          memset(datKey.get_data(), 0, datKey.get_size());
          memset(datValue.get_data(), 0, datValue.get_size());
          return (ret == 0);
      }
  
      template <typename K>
      bool Erase(const K& key)
      {
          if (!pdb)
              return false;
          if (fReadOnly)
              assert(!"Erase called on database in read-only mode");
  
          // Key
          CDataStream ssKey(SER_DISK, CLIENT_VERSION);
          ssKey.reserve(1000);
          ssKey << key;
          Dbt datKey(&ssKey[0], ssKey.size());
  
          // Erase
          int ret = pdb->del(activeTxn, &datKey, 0);
  
          // Clear memory
          memset(datKey.get_data(), 0, datKey.get_size());
          return (ret == 0 || ret == DB_NOTFOUND);
      }
  
      template <typename K>
      bool Exists(const K& key)
      {
          if (!pdb)
              return false;
  
          // Key
          CDataStream ssKey(SER_DISK, CLIENT_VERSION);
          ssKey.reserve(1000);
          ssKey << key;
          Dbt datKey(&ssKey[0], ssKey.size());
  
          // Exists
          int ret = pdb->exists(activeTxn, &datKey, 0);
  
          // Clear memory
          memset(datKey.get_data(), 0, datKey.get_size());
          return (ret == 0);
      }
  
      Dbc* GetCursor()
      {
          if (!pdb)
              return nullptr;
          Dbc* pcursor = nullptr;
          int ret = pdb->cursor(nullptr, &pcursor, 0);
          if (ret != 0)
              return nullptr;
          return pcursor;
      }
  
      int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags = DB_NEXT)
      {
          // Read at cursor
          Dbt datKey;
          if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) {
              datKey.set_data(&ssKey[0]);
              datKey.set_size(ssKey.size());
          }
          Dbt datValue;
          if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) {
              datValue.set_data(&ssValue[0]);
              datValue.set_size(ssValue.size());
          }
          datKey.set_flags(DB_DBT_MALLOC);
          datValue.set_flags(DB_DBT_MALLOC);
          int ret = pcursor->get(&datKey, &datValue, fFlags);
          if (ret != 0)
              return ret;
          else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
              return 99999;
  
          // Convert to streams
          ssKey.SetType(SER_DISK);
          ssKey.clear();
          ssKey.write((char*)datKey.get_data(), datKey.get_size());
          ssValue.SetType(SER_DISK);
          ssValue.clear();
          ssValue.write((char*)datValue.get_data(), datValue.get_size());
  
          // Clear and free memory
          memset(datKey.get_data(), 0, datKey.get_size());
          memset(datValue.get_data(), 0, datValue.get_size());
          free(datKey.get_data());
          free(datValue.get_data());
          return 0;
      }
  
  public:
      bool TxnBegin()
      {
          if (!pdb || activeTxn)
              return false;
          DbTxn* ptxn = bitdb.TxnBegin();
          if (!ptxn)
              return false;
          activeTxn = ptxn;
          return true;
      }
  
      bool TxnCommit()
      {
          if (!pdb || !activeTxn)
              return false;
          int ret = activeTxn->commit(0);
          activeTxn = nullptr;
          return (ret == 0);
      }
  
      bool TxnAbort()
      {
          if (!pdb || !activeTxn)
              return false;
          int ret = activeTxn->abort();
          activeTxn = nullptr;
          return (ret == 0);
      }
  
      bool ReadVersion(int& nVersion)
      {
          nVersion = 0;
          return Read(std::string("version"), nVersion);
      }
  
      bool WriteVersion(int nVersion)
      {
          return Write(std::string("version"), nVersion);
      }
  
      bool static Rewrite(const std::string& strFile, const char* pszSkip = nullptr);
  };
  
  #endif // LTSOIN_DB_H
  