271 lines
7.9 KiB
C++
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;
|
|
} |