14#include <leveldb/env.h>
15#include <leveldb/helpers/memenv/memenv.h>
51class DeterministicEnv final :
public leveldb::EnvWrapper
53 using WorkFunction = void (*)(
void*);
56 WorkFunction function;
60 std::deque<Work> m_queue;
63 explicit DeterministicEnv(leveldb::Env* base) : EnvWrapper(base) {}
65 void Schedule(WorkFunction function,
void* arg)
override
67 m_queue.push_back({function, arg});
74 if (m_queue.empty())
return false;
75 const Work work{m_queue.front()};
77 work.function(work.arg);
82 void DrainWork() {
while (RunOne()) {} }
85constexpr size_t MAX_VALUE_LEN{4096};
86constexpr uint8_t MAX_VALUE_MULTIPLIER{8};
87constexpr size_t WRITE_BATCH_HEADER{12};
91const std::string OBFUSCATION_KEY{
"\000obfuscate_key", 14};
96std::vector<uint8_t> MakeValue(uint16_t key, uint32_t size)
98 std::vector<uint8_t> v(size);
99 std::iota(v.begin(), v.end(),
static_cast<uint8_t
>(key ^ (key >> 8)));
105struct LevelDBBytewiseU16Cmp {
110using Oracle = std::map<uint16_t, uint32_t, LevelDBBytewiseU16Cmp>;
112struct FailUnserialize {
113 template <
typename Stream>
114 void Unserialize(Stream&) {
throw std::ios_base::failure{
"always fail"}; }
122 return static_cast<uint32_t
>(len) * multiplier;
127void VerifyIterator(
CDBWrapper& dbw,
const Oracle& oracle,
128 bool obfuscate, std::optional<uint16_t> seek_key = std::nullopt)
130 const std::unique_ptr<CDBIterator> it{dbw.
NewIterator()};
131 auto oracle_it{seek_key ? oracle.lower_bound(*seek_key) : oracle.begin()};
137 for (; it->Valid(); it->Next()) {
139 assert(it->GetKey(db_key));
140 if (oracle_it != oracle.end() && db_key == oracle_it->first) {
141 std::vector<uint8_t> db_value;
142 assert(it->GetValue(db_value));
143 assert(db_value == MakeValue(db_key, oracle_it->second));
148 assert(it->GetKey(key_str));
149 assert(key_str == OBFUSCATION_KEY);
152 assert(oracle_it == oracle.end());
155template <
typename DrainWorkFn,
typename RunOneFn>
157 leveldb::Env* testing_env,
158 DrainWorkFn drain_work,
160 bool allow_force_compact)
166 const auto make_db{[&](
DBOptions options = {}) {
167 return std::make_unique<CDBWrapper>(
DBParams{
168 .
path =
"dbwrapper_fuzz",
170 .obfuscate = obfuscate,
172 .testing_env = testing_env,
178 std::unique_ptr<CDBWrapper> dbw{make_db()};
189 const auto key{ConsumeKey(provider)};
190 const auto size{ConsumeValueSize(provider)};
196 const auto key{ConsumeKey(provider)};
203 std::map<uint16_t, uint32_t> batch_writes;
204 std::set<uint16_t> batch_erases;
205 const auto fill{[&] {
208 const auto key{ConsumeKey(provider)};
210 const auto size{ConsumeValueSize(provider)};
211 batch.Write(key, MakeValue(key, size));
212 batch_writes[key] = size;
213 batch_erases.erase(key);
216 batch_erases.insert(key);
217 batch_writes.erase(key);
223 assert(batch.ApproximateSize() >= WRITE_BATCH_HEADER);
225 assert(batch.ApproximateSize() == WRITE_BATCH_HEADER);
226 batch_writes.clear();
227 batch_erases.clear();
232 for (
const auto& [k, v] : batch_writes) oracle[
k] = v;
233 for (
const auto& k : batch_erases) oracle.erase(k);
239 if (allow_force_compact && provider.
ConsumeBool()) {
242 dbw = make_db(options);
243 VerifyIterator(*dbw, oracle, obfuscate);
247 const auto key{ConsumeKey(provider)};
248 std::vector<uint8_t> value;
249 const bool found{dbw->
Read(key, value)};
250 if (
const auto it{oracle.find(key)}; it != oracle.end()) {
251 assert(found && value == MakeValue(key, it->second));
257 const auto key{ConsumeKey(provider)};
263 auto it{oracle.begin()};
267 key = ConsumeKey(provider);
269 FailUnserialize wrong_type;
274 ? std::optional<uint16_t>{ConsumeKey(provider)}
276 VerifyIterator(*dbw, oracle, obfuscate, seek_key);
283 const auto [k1, k2]{std::minmax({ConsumeKey(provider), ConsumeKey(provider)}, LevelDBBytewiseU16Cmp{})};
285 if (k1 == k2)
assert(estimate_size == 0);
296 VerifyIterator(*dbw, oracle, obfuscate);
306 const auto memenv{std::unique_ptr<leveldb::Env>{leveldb::NewMemEnv(leveldb::Env::Default())}};
311 [&] {
return det_env.RunOne(); },
319 const auto memenv{std::unique_ptr<leveldb::Env>{leveldb::NewMemEnv(leveldb::Env::Default())}};
323 [] { return false; },
BSWAP_CONSTEXPR uint16_t internal_bswap_16(uint16_t x)
Batch of changes queued to be written to a CDBWrapper.
size_t DynamicMemoryUsage() const
bool Read(const K &key, V &value) const
CDBIterator * NewIterator()
bool Exists(const K &key) const
void Erase(const K &key, bool fSync=false)
void WriteBatch(CDBBatch &batch, bool fSync=false)
void Write(const K &key, const V &value, bool fSync=false)
bool IsEmpty()
Return true if the database managed by this class contains no entries.
size_t EstimateSize(const K &key_begin, const K &key_end) const
T ConsumeIntegralInRange(T min, T max)
static const size_t DBWRAPPER_MAX_FILE_SIZE
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
void Unserialize(Stream &, V)=delete
User-controlled performance and debug options.
bool force_compact
Compact database on startup.
Application-specific storage settings.
fs::path path
Location in the filesystem where leveldb data will be stored.
TestDbWrapper(provider, &det_env, [&] { det_env.DrainWork();}, [&] { return det_env.RunOne();}, false)
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
@ ZEROS
Seed with a compile time constant of zeros.