diff --git a/FacadeFuzz.cpp b/FacadeFuzz.cpp index 6751a3a..a98f8fb 100644 --- a/FacadeFuzz.cpp +++ b/FacadeFuzz.cpp @@ -8,6 +8,113 @@ #define DEBUG 0 +struct Reference { + + explicit Reference(int64_t version) + : version(version), oldestVersion(version) { + versioned[version] = std::map(); + } + + struct View { + + std::vector> rangeRead(const String &begin, + const String &end, + int limit, + bool reverse) const { + std::vector> 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 *map) : map(map) {} + + private: + const std::map *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> 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 + // Set oldest version 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; } }