Files
versioned-map/FacadeFuzz.cpp

271 lines
7.9 KiB
C++

#include "Facade.h"
#include "Internal.h"
#include "KeyCompare.h"
#include <inttypes.h>
#include <map>
#include <set>
#define DEBUG 0
struct Reference {
explicit Reference(int64_t version)
: version(version), oldestVersion(version) {
versioned[version] = std::map<String, String>();
}
struct View {
std::vector<std::pair<String, String>> rangeRead(const String &begin,
const String &end,
int limit,
bool reverse) const {
std::vector<std::pair<String, String>> result;
if (begin >= end) {
return result;
}
if (reverse) {
auto iter = map->lower_bound(end);
const auto beginIter = map->begin();
if (iter == beginIter) {
return result;
}
--iter;
for (; iter->first >= begin && limit > 0; --iter) {
result.push_back(*iter);
--limit;
if (iter == beginIter) {
return result;
}
}
} else {
for (auto iter = map->lower_bound(begin),
iterEnd = map->lower_bound(end);
iter != iterEnd && limit > 0; ++iter) {
result.push_back(*iter);
--limit;
}
}
return result;
}
/// @private
explicit View(const std::map<String, String> *map) : map(map) {}
private:
const std::map<String, String> *map;
};
void addMutations(const weaselab::VersionedMap::Mutation *mutations,
int numMutations, int64_t version) {
assert(this->version < version);
this->version = version;
auto back = (--versioned.end())->second;
auto &latest = versioned[version];
latest = std::move(back);
for (int i = 0; i < numMutations; ++i) {
switch (mutations[i].type) {
case weaselab::VersionedMap::Set:
latest[String(mutations[i].param1, mutations[i].param1Len)] =
String(mutations[i].param2, mutations[i].param2Len);
break;
case weaselab::VersionedMap::Clear:
if (mutations[i].param2Len > 0) {
latest.erase(latest.lower_bound(
String(mutations[i].param1, mutations[i].param1Len)),
latest.lower_bound(String(mutations[i].param2,
mutations[i].param2Len)));
} else {
latest.erase(String(mutations[i].param1, mutations[i].param1Len));
}
break;
}
}
}
void setOldestVersion(int64_t version) {
oldestVersion = version;
while (versioned.size() > 1) {
auto first = versioned.begin();
auto second = first;
++second;
if (second->first <= version) {
versioned.erase(first);
} else {
break;
}
}
}
View viewAt(int64_t version) const {
auto iter = versioned.lower_bound(version);
if (iter->first > version) {
--iter;
}
return View{&iter->second};
}
int64_t getVersion() const { return version; }
int64_t getOldestVersion() const { return oldestVersion; }
private:
int64_t version;
int64_t oldestVersion;
std::map<int64_t, std::map<String, String>> versioned;
};
constexpr int kKeySize = 4;
weaselab::VersionedMap::Key randomKey(Arena &arena) {
auto *result = new (arena) uint8_t[kKeySize];
gRandom.randomHex(result, kKeySize);
return {result, kKeySize};
}
weaselab::VersionedMap::Key arbitraryKey(Arena &arena) {
auto *result = new (arena) uint8_t[kKeySize];
gArbitrary.randomHex(result, kKeySize);
return {result, kKeySize};
}
struct KeyComp {
bool operator()(const weaselab::VersionedMap::Key &lhs,
const weaselab::VersionedMap::Key &rhs) const {
return lhs < rhs;
}
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
initFuzz(data, size);
Reference reference{0};
Facade facade{0};
Arena arena;
while (gArbitrary.hasEntropy()) {
switch (gArbitrary.bounded(3)) {
case 0: {
// Add mutations
#if DEBUG
printf("Add mutations\n");
#endif
const int numKeys = gArbitrary.bounded(10);
std::set<weaselab::VersionedMap::Key, KeyComp> keySet;
while (int(keySet.size()) < numKeys) {
keySet.insert(randomKey(arena));
}
std::vector<weaselab::VersionedMap::Key> keys;
keys.insert(keys.end(), keySet.begin(), keySet.end());
std::vector<weaselab::VersionedMap::Mutation> mutations;
for (int i = 0; i < int(keys.size());) {
switch (gArbitrary.bounded(3)) {
case 0: {
// Point write
auto val = randomKey(arena);
mutations.push_back({keys[i].p, val.p, keys[i].len, val.len,
weaselab::VersionedMap::Set});
++i;
} break;
case 1: {
// Point clear
mutations.push_back({keys[i].p, nullptr, keys[i].len, 0,
weaselab::VersionedMap::Clear});
++i;
} break;
case 2: {
// Range clear
if (i + 1 < int(keys.size())) {
mutations.push_back({keys[i].p, keys[i + 1].p, keys[i].len,
keys[i + 1].len,
weaselab::VersionedMap::Clear});
i += 2;
}
} break;
}
}
assert(reference.getVersion() == facade.getVersion());
const int64_t newVersion = reference.getVersion() + 1;
#if DEBUG
for (const auto &m : mutations) {
printf("%s %.*s %.*s\n",
m.type == weaselab::VersionedMap::Set ? "set" : "clear",
m.param1Len, m.param1, m.param2Len, m.param2);
}
printf("Version: %" PRId64 "\n", newVersion);
#endif
facade.addMutations(mutations.data(), mutations.size(), newVersion);
reference.addMutations(mutations.data(), mutations.size(), newVersion);
} break;
case 1: {
// Set oldest version
const int64_t newOldestVersion =
reference.getOldestVersion() +
gArbitrary.bounded(reference.getVersion() -
reference.getOldestVersion() + 1);
#if DEBUG
printf("Set oldest version %" PRId64 "\n", newOldestVersion);
#endif
facade.setOldestVersion(newOldestVersion);
reference.setOldestVersion(newOldestVersion);
} break;
case 2: {
// Check range read
#if DEBUG
printf("Check range read\n");
#endif
assert(reference.getOldestVersion() == facade.getOldestVersion());
const int64_t version =
reference.getOldestVersion() +
gArbitrary.bounded(reference.getVersion() -
reference.getOldestVersion() + 1);
auto begin = arbitraryKey(arena);
auto end = arbitraryKey(arena);
const int limit = gArbitrary.bounded(100000);
const bool reverse = gArbitrary.bounded(2);
auto result = facade.viewAt(version).rangeRead(
String(begin.p, begin.len), String(end.p, end.len), limit, reverse);
auto expected = reference.viewAt(version).rangeRead(
String(begin.p, begin.len), String(end.p, end.len), limit, reverse);
#if DEBUG
printf("[%.*s, %.*s) version=%" PRId64 " limit=%d %s result:\n",
begin.len, begin.p, end.len, end.p, version, limit,
reverse ? "reverse" : "forward");
for (const auto &r : result) {
printf("%.*s %.*s\n", (int)r.first.length(), r.first.data(),
(int)r.second.length(), r.second.data());
}
if (result != expected) {
printf("[%.*s, %.*s) version=%" PRId64 " limit=%d %s expected:\n",
begin.len, begin.p, end.len, end.p, version, limit,
reverse ? "reverse" : "forward");
for (const auto &r : expected) {
printf("%.*s %.*s\n", (int)r.first.length(), r.first.data(),
(int)r.second.length(), r.second.data());
}
}
#endif
assert(result == expected);
} break;
}
}
return 0;
}