Fix lastLeq bug

This commit is contained in:
2024-02-01 11:24:57 -08:00
parent 96aca4a1da
commit 0eff5628bd
133 changed files with 86 additions and 30 deletions

View File

@@ -544,7 +544,9 @@ Node *&getOrCreateChild(Node *&self, uint8_t index) {
}
// Precondition - an entry for index must exist in the node
[[maybe_unused]] void eraseChild(Node *self, uint8_t index) {
void eraseChild(Node *self, uint8_t index) {
free(getChildExists(self, index));
if (self->type == Type::Node4) {
auto *self4 = static_cast<Node4 *>(self);
int nodeIndex = getNodeIndex(self4, index);
@@ -654,7 +656,8 @@ std::string_view getSearchPath(Arena &arena, Node *n) {
}
std::reverse(result.begin(), result.end());
if (result.size() > 0) {
return std::string_view((const char *)&result[0], result.size()); // NOLINT
return std::string_view((const char *)&result[0],
result.size()); // NOLINT
} else {
return std::string_view();
}
@@ -672,8 +675,10 @@ Iterator lastLeq(Node *n, const std::span<const uint8_t> key) {
}
if (c > 0) {
n = prevPhysical(n);
goto scanBackward;
} else {
goto downRightSpine;
}
goto outerLoop;
}
if (commonLen == n->partialKeyLen) {
// partial key matches
@@ -682,7 +687,7 @@ Iterator lastLeq(Node *n, const std::span<const uint8_t> key) {
// n is the first physical node greater than remaining, and there's no
// eq node
n = prevPhysical(n);
goto outerLoop;
goto scanBackward;
}
}
{
@@ -696,7 +701,7 @@ Iterator lastLeq(Node *n, const std::span<const uint8_t> key) {
if (n->entryPresent) {
return {n, 0};
} else {
goto outerLoop;
goto scanBackward;
}
} else {
int c = getChildLeq(n, remaining[0]);
@@ -704,22 +709,29 @@ Iterator lastLeq(Node *n, const std::span<const uint8_t> key) {
n = getChildExists(n, c);
remaining = remaining.subspan(1, remaining.size() - 1);
} else {
// The physical node corresponding to search path `key` does not exist.
// Let's find the physical node corresponding to the highest search key
// (not necessarily present) less than key
if (c >= 0) {
n = getChildExists(n, c);
goto downRightSpine;
} else {
goto scanBackward;
}
}
}
}
downRightSpine:
// The physical node corresponding to search path `key` does not
// exist. Let's find the physical node corresponding to the highest
// search key (not necessarily present) less than key.
// Move down the right spine
for (;;) {
int c = getChildLeq(n, 255);
if (c >= 0) {
n = getChildExists(n, c);
} else {
goto outerLoop;
}
c = getChildLeq(n, 255);
goto scanBackward;
}
}
}
}
outerLoop:
scanBackward:
// Iterate backwards along existing physical nodes until we find a present
// entry
for (; !n->entryPresent; n = prevPhysical(n)) {
@@ -802,6 +814,21 @@ void destroyTree(Node *root) {
}
}
void invalidateMax(Node *self) {
int64_t expectedMax = std::numeric_limits<int64_t>::lowest();
if (self->entryPresent) {
expectedMax = std::max(expectedMax, self->entry.pointVersion);
expectedMax = std::max(expectedMax, self->entry.rangeVersion);
}
for (int i = getChildGeq(self, 0); i >= 0; i = getChildGeq(self, i + 1)) {
expectedMax = std::max(expectedMax, getChildExists(self, i)->maxVersion);
}
self->maxVersion = expectedMax;
if (self->parent != nullptr) {
invalidateMax(self->parent);
}
}
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
void check(const ReadRange *reads, Result *result, int count) const {
for (int i = 0; i < count; ++i) {
@@ -816,7 +843,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
lastLeq(root, std::span<const uint8_t>(r.begin.p, r.begin.len));
#if DEBUG_VERBOSE && !defined(NDEBUG)
Arena arena;
printf("LastLeq for `%s' got `%s'\n", printable(r.begin).c_str(),
fprintf(stderr, "LastLeq for `%s' got `%s'\n", printable(r.begin).c_str(),
printable(getSearchPath(arena, l)).c_str());
#endif
assert(l != nullptr);
@@ -839,22 +866,25 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
n->entryPresent = true;
n->entry.pointVersion = p->entry.rangeVersion;
n->entry.rangeVersion = p->entry.rangeVersion;
n->maxVersion = p->entry.rangeVersion;
}
auto *end = n;
n = insert(&root, std::span<const uint8_t>(w.begin.p, w.begin.len),
w.writeVersion);
std::numeric_limits<int64_t>::lowest());
auto *begin = n;
n->entryPresent = true;
n->entry.pointVersion = w.writeVersion;
n->entry.rangeVersion = w.writeVersion;
for (n = nextLogical(n); n != end;) {
auto *old = n;
n = nextLogical(n);
old->entryPresent = false;
if (old->numChildren == 0 && old->parent != nullptr) {
eraseChild(old->parent, old->parentsIndex);
}
}
invalidateMax(begin);
invalidateMax(end);
} else {
auto *n =
insert(&root, std::span<const uint8_t>(w.begin.p, w.begin.len),
@@ -1138,14 +1168,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
TestDriver<ConflictSet::Impl> driver{data, size};
static_assert(driver.kMaxKeyLen > Node::kPartialKeyMaxLen);
do {
for (;;) {
bool done = driver.next();
if (!driver.ok) {
debugPrintDot(stdout, driver.cs.root);
fflush(stdout);
abort();
}
bool success = checkCorrectness(driver.cs.root, driver.refImpl);
if (!success) {
debugPrintDot(stdout, driver.cs.root);
fflush(stdout);
abort();
}
} while (!driver.next());
if (done) {
break;
}
}
return 0;
}

View File

@@ -7,6 +7,7 @@
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <inttypes.h>
#include <map>
#include <set>
#include <span>
@@ -14,7 +15,7 @@
#include <utility>
#include <vector>
#define DEBUG_VERBOSE 1
#define DEBUG_VERBOSE 0
// This header contains code that we want to reuse outside of ConflictSet.cpp or
// want to exclude from coverage since it's only testing related.
@@ -451,6 +452,19 @@ template <class ConflictSetImpl> struct TestDriver {
constexpr static auto kMaxKeyLen = 24;
bool ok = true;
static const char *resultToStr(ConflictSet::Result r) {
switch (r) {
case ConflictSet::Commit:
return "commit";
case ConflictSet::Conflict:
return "conflict";
case ConflictSet::TooOld:
return "too old";
}
}
// Call until it returns true, for "done". Check internal invariants etc
// between calls to next.
bool next() {
@@ -537,10 +551,12 @@ template <class ConflictSetImpl> struct TestDriver {
refImpl.check(reads, results2, numReads);
for (int i = 0; i < numReads; ++i) {
if (results1[i] != results2[i]) {
fprintf(stderr, "Expected %d, got %d for read of %s at version %d\n",
results2[i], results1[i], printable(reads[i].begin).c_str(),
int(reads[i].readVersion));
abort();
fprintf(stderr,
"Expected %s, got %s for read of %s at version %" PRId64 "\n",
resultToStr(results2[i]), resultToStr(results1[i]),
printable(reads[i].begin).c_str(), reads[i].readVersion);
ok = false;
return true;
}
}
}

View File

@@ -13,5 +13,8 @@ int main(int argc, char **argv) {
TestDriver<ConflictSet> driver{(const uint8_t *)str.data(), str.size()};
while (!driver.next())
;
if (!driver.ok) {
abort();
}
}
}

View File

@@ -1 +0,0 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>'<27><><EFBFBD><EFBFBD><EFBFBD><07>‚<EFBFBD><C282><EFBFBD><EFBFBD><EFBFBD><16>

Some files were not shown because too many files have changed in this diff Show More