Expand clears
This commit is contained in:
115
VersionedMap.cpp
115
VersionedMap.cpp
@@ -75,11 +75,12 @@ namespace weaselab {
|
||||
constexpr int kPathLengthUpperBound = 96;
|
||||
|
||||
struct Entry {
|
||||
// If there is a point mutation at key, then pointVersion is its version.
|
||||
// Otherwise it's negative.
|
||||
// If there is a point mutation at key, then pointVersion is >= 0 and key has
|
||||
// not been modified since pointVersion. Otherwise it's negative.
|
||||
int64_t pointVersion;
|
||||
// If there is a range mutation ending at key, then rangeVersion is its
|
||||
// version. Otherwise it's negative.
|
||||
// If there is a range mutation ending at key, then rangeVersion is >= 0 and
|
||||
// the range has not been modified since rangeVersion. Otherwise it's
|
||||
// negative.
|
||||
int64_t rangeVersion;
|
||||
int keyLen;
|
||||
// Negative if this key is cleared. Only meaningful if this is a point
|
||||
@@ -91,6 +92,7 @@ struct Entry {
|
||||
// True if the entry is a point mutation. If false, this entry's key should be
|
||||
// read through to the underlying data structure.
|
||||
bool pointMutation() const { return pointVersion >= 0; }
|
||||
bool pointSet() const { return pointVersion >= 0 && valLen >= 0; }
|
||||
bool pointClear() const { return pointVersion >= 0 && valLen < 0; }
|
||||
|
||||
// True if mutations in (pred, this) are cleared. If false, (pred, this)
|
||||
@@ -388,6 +390,18 @@ auto operator<=>(const VersionedMap::Key &lhs, const Node &rhs) {
|
||||
return lhs.len <=> rhs.entry->keyLen;
|
||||
}
|
||||
|
||||
auto operator<=>(const weaselab::VersionedMap::Key &lhs,
|
||||
const weaselab::VersionedMap::Key &rhs) {
|
||||
int cl = std::min(lhs.len, rhs.len);
|
||||
if (cl > 0) {
|
||||
int c = memcmp(lhs.p, rhs.p, cl);
|
||||
if (c != 0) {
|
||||
return c <=> 0;
|
||||
}
|
||||
}
|
||||
return lhs.len <=> rhs.len;
|
||||
}
|
||||
|
||||
constexpr int orderToInt(std::strong_ordering o) {
|
||||
return o == std::strong_ordering::less ? -1
|
||||
: o == std::strong_ordering::equal ? 0
|
||||
@@ -883,10 +897,38 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl {
|
||||
insert({m.param1, m.param1Len}, {{m.param2, m.param2Len}}, iter);
|
||||
} break;
|
||||
case Clear: {
|
||||
search<std::memory_order_relaxed>({m.param1, m.param1Len}, latestRoot,
|
||||
latestVersion, iter);
|
||||
insert({m.param1, m.param1Len}, {{nullptr, -1}}, iter);
|
||||
if (m.param2Len > 0) {
|
||||
// TODO we can avoid some insertions here. Complexity is getting out of
|
||||
// hand though.
|
||||
|
||||
if (m.param2Len == 0) {
|
||||
search<std::memory_order_relaxed>({m.param1, m.param1Len}, latestRoot,
|
||||
latestVersion, iter);
|
||||
insert({m.param1, m.param1Len}, {{nullptr, -1}}, iter);
|
||||
|
||||
const bool engulfLeft = mm.base[iter.backNode()].entry->clearTo();
|
||||
move<std::memory_order_relaxed, true>(iter, latestVersion);
|
||||
const auto *next = iter.searchPathSize() > 0
|
||||
? mm.base[iter.backNode()].entry
|
||||
: nullptr;
|
||||
if (engulfLeft && next && next->clearTo()) {
|
||||
insert({next->getKey(), next->keyLen}, {}, iter);
|
||||
move<std::memory_order_relaxed, false>(iter, latestVersion);
|
||||
remove(iter);
|
||||
}
|
||||
|
||||
} else {
|
||||
search<std::memory_order_relaxed>({m.param1, m.param1Len}, latestRoot,
|
||||
latestVersion, iter);
|
||||
insert({m.param1, m.param1Len}, {{nullptr, -1}}, iter);
|
||||
|
||||
// Check if we can engulf on the left
|
||||
{
|
||||
const auto *entry = mm.base[iter.backNode()].entry;
|
||||
if (entry->clearTo()) {
|
||||
remove(iter);
|
||||
}
|
||||
}
|
||||
|
||||
move<std::memory_order_relaxed, true>(iter, latestVersion);
|
||||
while (iter.searchPathSize() > 0 &&
|
||||
mm.base[iter.backNode()] < Key{m.param2, m.param2Len}) {
|
||||
@@ -898,6 +940,20 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl {
|
||||
search<std::memory_order_relaxed>({m.param2, m.param2Len}, latestRoot,
|
||||
latestVersion, iter);
|
||||
insert({m.param2, m.param2Len}, {}, iter);
|
||||
|
||||
// Check if we can engulf on the right
|
||||
{
|
||||
const auto *entry = mm.base[iter.backNode()].entry;
|
||||
move<std::memory_order_relaxed, true>(iter, latestVersion);
|
||||
const auto *next = iter.searchPathSize() > 0
|
||||
? mm.base[iter.backNode()].entry
|
||||
: nullptr;
|
||||
if (entry->pointClear() && next && next->clearTo()) {
|
||||
insert({next->getKey(), next->keyLen}, {}, iter);
|
||||
move<std::memory_order_relaxed, false>(iter, latestVersion);
|
||||
remove(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default: // GCOVR_EXCL_LINE
|
||||
@@ -1066,17 +1122,14 @@ void materializeMutations(VersionedMap::Iterator::Impl *impl, const Entry *prev,
|
||||
impl->mutations[impl->mutationCount++] = {
|
||||
prev->getKey(),
|
||||
entry.getKey(),
|
||||
prev->pointClear() && prev->pointVersion == entry.rangeVersion
|
||||
? prev->keyLen
|
||||
: prev->keyLen + 1,
|
||||
prev->pointClear() ? prev->keyLen : prev->keyLen + 1,
|
||||
entry.keyLen,
|
||||
VersionedMap::Clear,
|
||||
entry.rangeVersion};
|
||||
}
|
||||
if (entry.pointMutation()) {
|
||||
if (entry.valLen < 0 /* pointClear */) {
|
||||
if (next == nullptr ||
|
||||
!(next->clearTo() && next->rangeVersion == entry.pointVersion)) {
|
||||
if (next == nullptr || !next->clearTo()) {
|
||||
impl->mutations[impl->mutationCount++] = {
|
||||
entry.getKey(), nullptr, entry.keyLen, 0,
|
||||
VersionedMap::Clear, entry.pointVersion};
|
||||
@@ -1176,8 +1229,18 @@ bool VersionedMap::Iterator::operator==(const Iterator &other) const {
|
||||
return impl->equals(*other.impl);
|
||||
}
|
||||
|
||||
void VersionedMap::Impl::firstGeq(const Key *key, const int64_t *version,
|
||||
Iterator *iterator, int count) const {
|
||||
bool geq(const VersionedMap::Iterator::VersionedMutation &m,
|
||||
const VersionedMap::Key &k) {
|
||||
if (m.type == VersionedMap::Set || m.param2Len == 0) {
|
||||
return VersionedMap::Key{m.param1, m.param1Len} >= k;
|
||||
} else {
|
||||
return VersionedMap::Key{m.param2, m.param2Len} > k;
|
||||
}
|
||||
}
|
||||
|
||||
void VersionedMap::Impl::firstGeq(const weaselab::VersionedMap::Key *key,
|
||||
const int64_t *version, Iterator *iterator,
|
||||
int count) const {
|
||||
// TODO ILP!
|
||||
auto handle = roots.getThreadSafeHandle();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
@@ -1197,17 +1260,11 @@ void VersionedMap::Impl::firstGeq(const Key *key, const int64_t *version,
|
||||
Finger &finger = iterator[i].impl->finger;
|
||||
search<std::memory_order_acquire>(key[i], root, version[i], finger);
|
||||
|
||||
bool exact;
|
||||
if (finger.searchPathSize() == 0) {
|
||||
exact = false;
|
||||
} else if (finger.backNode() == 0) {
|
||||
exact = false;
|
||||
if (finger.searchPathSize() > 0 && finger.backNode() == 0) {
|
||||
move<std::memory_order_acquire, true>(finger, version[i]);
|
||||
if (finger.searchPathSize() > 0) {
|
||||
assert(finger.backNode() != 0);
|
||||
}
|
||||
} else {
|
||||
exact = true;
|
||||
}
|
||||
|
||||
iterator[i].impl->version = version[i];
|
||||
@@ -1227,10 +1284,18 @@ void VersionedMap::Impl::firstGeq(const Key *key, const int64_t *version,
|
||||
iterator[i].impl->map->move<std::memory_order_acquire, true>(
|
||||
finger, iterator[i].impl->version);
|
||||
}
|
||||
if (exact) {
|
||||
iterator[i].impl->mutationIndex = iterator[i].impl->mutationCount - 1;
|
||||
if (iterator[i].impl->mutationCount == 0) {
|
||||
assert(finger.searchPathSize() == 0);
|
||||
} else {
|
||||
iterator[i].impl->mutationIndex = 0;
|
||||
[[maybe_unused]] bool match = false;
|
||||
for (int j = 0; j < iterator[i].impl->mutationCount; ++j) {
|
||||
if (geq(iterator[i].impl->mutations[j], key[i])) {
|
||||
iterator[i].impl->mutationIndex = j;
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user