mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 05:32:22 +02:00
This is a refactor on 64-bit systems, because size_t is equal to u64.
However, on 32-bit systems, it fixes an integer overflow while calculating the cache sizes:
src/node/caches.cpp:71:49: runtime error: unsigned integer overflow: 471859200 * 10 cannot be represented in type size_t (aka "unsigned int")
This happens while multiplying the default cache size (450MiB) by 10:
index_sizes.tx_index = std::min(total_cache * 10 / 100, ...)
^^^^^^^^^^^^^^^^
The issue was introduced in commit d06dabf26b.
====
Also, add missing includes in touched files, according to IWYU.
293 lines
8.0 KiB
C++
293 lines
8.0 KiB
C++
// Copyright (c) 2012-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef BITCOIN_DBWRAPPER_H
|
|
#define BITCOIN_DBWRAPPER_H
|
|
|
|
#include <attributes.h>
|
|
#include <serialize.h>
|
|
#include <span.h>
|
|
#include <streams.h>
|
|
#include <util/byte_units.h>
|
|
#include <util/check.h>
|
|
#include <util/fs.h>
|
|
#include <util/obfuscation.h>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <exception>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <span>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
namespace leveldb {
|
|
class Env;
|
|
} // namespace leveldb
|
|
|
|
static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
|
|
static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
|
|
static const size_t DBWRAPPER_MAX_FILE_SIZE{32_MiB};
|
|
|
|
//! User-controlled performance and debug options.
|
|
struct DBOptions {
|
|
//! Compact database on startup.
|
|
bool force_compact = false;
|
|
};
|
|
|
|
//! Application-specific storage settings.
|
|
struct DBParams {
|
|
//! Location in the filesystem where leveldb data will be stored.
|
|
fs::path path;
|
|
//! Configures various leveldb cache settings.
|
|
uint64_t cache_bytes;
|
|
//! If true, use leveldb's memory environment.
|
|
bool memory_only = false;
|
|
//! If true, remove all existing data.
|
|
bool wipe_data = false;
|
|
//! If true, store data obfuscated via simple XOR. If false, XOR with a
|
|
//! zero'd byte array.
|
|
bool obfuscate = false;
|
|
//! Passed-through options.
|
|
DBOptions options{};
|
|
//! If non-null, use this as the leveldb::Env instead of the default.
|
|
//! Caller retains ownership.
|
|
leveldb::Env* testing_env = nullptr;
|
|
//! Maximum LevelDB SST file size. Larger values reduce the frequency
|
|
//! of compactions but increase their duration.
|
|
size_t max_file_size = DBWRAPPER_MAX_FILE_SIZE;
|
|
};
|
|
|
|
class dbwrapper_error : public std::runtime_error
|
|
{
|
|
public:
|
|
explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
|
|
};
|
|
|
|
class CDBWrapper;
|
|
|
|
/** These should be considered an implementation detail of the specific database.
|
|
*/
|
|
namespace dbwrapper_private {
|
|
|
|
/** Work around circular dependency, as well as for testing in dbwrapper_tests.
|
|
* Database obfuscation should be considered an implementation detail of the
|
|
* specific database.
|
|
*/
|
|
const Obfuscation& GetObfuscation(const CDBWrapper&);
|
|
}; // namespace dbwrapper_private
|
|
|
|
bool DestroyDB(const std::string& path_str);
|
|
|
|
/** Batch of changes queued to be written to a CDBWrapper */
|
|
class CDBBatch
|
|
{
|
|
friend class CDBWrapper;
|
|
|
|
private:
|
|
const CDBWrapper &parent;
|
|
|
|
struct WriteBatchImpl;
|
|
const std::unique_ptr<WriteBatchImpl> m_impl_batch;
|
|
|
|
DataStream m_key_scratch{};
|
|
DataStream m_value_scratch{};
|
|
|
|
void WriteImpl(std::span<const std::byte> key, DataStream& value);
|
|
void EraseImpl(std::span<const std::byte> key);
|
|
|
|
public:
|
|
/**
|
|
* @param[in] _parent CDBWrapper that this batch is to be submitted to
|
|
*/
|
|
explicit CDBBatch(const CDBWrapper& _parent);
|
|
~CDBBatch();
|
|
void Clear();
|
|
|
|
template <typename K, typename V>
|
|
void Write(const K& key, const V& value)
|
|
{
|
|
ScopedDataStreamUsage scoped_key{m_key_scratch}, scoped_value{m_value_scratch};
|
|
m_key_scratch << key;
|
|
m_value_scratch << value;
|
|
WriteImpl(m_key_scratch, m_value_scratch);
|
|
}
|
|
|
|
template <typename K>
|
|
void Erase(const K& key)
|
|
{
|
|
ScopedDataStreamUsage scoped_key{m_key_scratch};
|
|
m_key_scratch << key;
|
|
EraseImpl(m_key_scratch);
|
|
}
|
|
|
|
size_t ApproximateSize() const;
|
|
};
|
|
|
|
class CDBIterator
|
|
{
|
|
public:
|
|
struct IteratorImpl;
|
|
|
|
private:
|
|
const CDBWrapper &parent;
|
|
const std::unique_ptr<IteratorImpl> m_impl_iter;
|
|
DataStream m_scratch{};
|
|
|
|
void SeekImpl(std::span<const std::byte> key);
|
|
std::span<const std::byte> GetKeyImpl() const;
|
|
std::span<const std::byte> GetValueImpl() const;
|
|
|
|
public:
|
|
|
|
/**
|
|
* @param[in] _parent Parent CDBWrapper instance.
|
|
* @param[in] _piter The original leveldb iterator.
|
|
*/
|
|
CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
|
|
~CDBIterator();
|
|
|
|
bool Valid() const;
|
|
|
|
void SeekToFirst();
|
|
|
|
template<typename K> void Seek(const K& key) {
|
|
ScopedDataStreamUsage scoped_scratch{m_scratch};
|
|
m_scratch << key;
|
|
SeekImpl(m_scratch);
|
|
}
|
|
|
|
void Next();
|
|
|
|
template<typename K> bool GetKey(K& key) {
|
|
try {
|
|
SpanReader ssKey{GetKeyImpl()};
|
|
ssKey >> key;
|
|
} catch (const std::exception&) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<typename V> bool GetValue(V& value) {
|
|
try {
|
|
ScopedDataStreamUsage scoped_scratch{m_scratch};
|
|
m_scratch.write(GetValueImpl());
|
|
dbwrapper_private::GetObfuscation(parent)(m_scratch);
|
|
m_scratch >> value;
|
|
} catch (const std::exception&) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct LevelDBContext;
|
|
|
|
class CDBWrapper
|
|
{
|
|
friend const Obfuscation& dbwrapper_private::GetObfuscation(const CDBWrapper&);
|
|
private:
|
|
//! holds all leveldb-specific fields of this class
|
|
std::unique_ptr<LevelDBContext> m_db_context;
|
|
|
|
//! the name of this database
|
|
std::string m_name;
|
|
|
|
//! optional XOR-obfuscation of the database
|
|
Obfuscation m_obfuscation;
|
|
|
|
//! obfuscation key storage key, null-prefixed to avoid collisions
|
|
inline static const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0
|
|
|
|
std::optional<std::string> ReadImpl(std::span<const std::byte> key) const;
|
|
bool ExistsImpl(std::span<const std::byte> key) const;
|
|
size_t EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const;
|
|
auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); }
|
|
|
|
public:
|
|
CDBWrapper(const DBParams& params);
|
|
~CDBWrapper();
|
|
|
|
CDBWrapper(const CDBWrapper&) = delete;
|
|
CDBWrapper& operator=(const CDBWrapper&) = delete;
|
|
|
|
template <typename K, typename V>
|
|
bool Read(const K& key, V& value) const
|
|
{
|
|
DataStream ssKey{};
|
|
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
|
|
ssKey << key;
|
|
std::optional<std::string> strValue{ReadImpl(ssKey)};
|
|
if (!strValue) {
|
|
return false;
|
|
}
|
|
try {
|
|
std::span ssValue{MakeWritableByteSpan(*strValue)};
|
|
m_obfuscation(ssValue);
|
|
SpanReader{ssValue} >> value;
|
|
} catch (const std::exception&) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename K, typename V>
|
|
void Write(const K& key, const V& value, bool fSync = false)
|
|
{
|
|
CDBBatch batch(*this);
|
|
batch.Write(key, value);
|
|
WriteBatch(batch, fSync);
|
|
}
|
|
|
|
template <typename K>
|
|
bool Exists(const K& key) const
|
|
{
|
|
DataStream ssKey{};
|
|
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
|
|
ssKey << key;
|
|
return ExistsImpl(ssKey);
|
|
}
|
|
|
|
template <typename K>
|
|
void Erase(const K& key, bool fSync = false)
|
|
{
|
|
CDBBatch batch(*this);
|
|
batch.Erase(key);
|
|
WriteBatch(batch, fSync);
|
|
}
|
|
|
|
void WriteBatch(CDBBatch& batch, bool fSync = false);
|
|
|
|
//! Perform a blocking full compaction of the underlying LevelDB.
|
|
void CompactFull();
|
|
|
|
//! Return a LevelDB property value, if available.
|
|
std::optional<std::string> GetProperty(const std::string& property) const;
|
|
|
|
// Get an estimate of LevelDB memory usage (in bytes).
|
|
size_t DynamicMemoryUsage() const;
|
|
|
|
CDBIterator* NewIterator();
|
|
|
|
/**
|
|
* Return true if the database managed by this class contains no entries.
|
|
*/
|
|
bool IsEmpty();
|
|
|
|
template<typename K>
|
|
size_t EstimateSize(const K& key_begin, const K& key_end) const
|
|
{
|
|
DataStream ssKey1{}, ssKey2{};
|
|
ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
|
|
ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
|
|
ssKey1 << key_begin;
|
|
ssKey2 << key_end;
|
|
return EstimateSizeImpl(ssKey1, ssKey2);
|
|
}
|
|
};
|
|
|
|
#endif // BITCOIN_DBWRAPPER_H
|