Add reference implementation

This commit is contained in:
2024-05-19 17:02:27 -07:00
parent 445aee2ce9
commit e23f11c1f0

View File

@@ -8,6 +8,113 @@
#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) {
@@ -32,6 +139,7 @@ struct KeyComp {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
initFuzz(data, size);
Reference reference{0};
Facade facade{0};
Arena arena;
@@ -78,22 +186,34 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
}
}
facade.addMutations(mutations.data(), mutations.size(),
facade.getVersion() + 1);
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
#if DEBUG
printf("Set oldest version\n");
#endif
const int64_t newOldestVersion =
facade.getOldestVersion() +
gArbitrary.bounded(facade.getVersion() - facade.getOldestVersion() +
1);
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: {
@@ -103,16 +223,46 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
printf("Check range read\n");
#endif
const int64_t version = facade.getOldestVersion() +
gArbitrary.bounded(facade.getVersion() -
facade.getOldestVersion() + 1);
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);
facade.viewAt(version).rangeRead(String(begin.p, begin.len),
String(end.p, end.len), limit, reverse);
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;
}
}