12 Commits

Author SHA1 Message Date
733f32b22e Bring back precommit check for SHOW_MEMORY
Some checks failed
Tests / Release [gcc] total: 827, passed: 827
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |1|0|1|0|:zzz:
Tests / Release [gcc,aarch64] total: 826, passed: 826
Tests / Coverage total: 825, failed: 1, passed: 824
weaselab/conflict-set/pipeline/head There was a failure building this commit
2024-03-08 14:44:35 -08:00
3fb8bf7c3b Bring back custom allocator 2024-03-08 14:43:18 -08:00
0c8cb8faa5 Add specializations for partialKey() 2024-03-08 14:01:56 -08:00
93e487c8fb Only track partialKeyCapacity in tests 2024-03-08 13:58:40 -08:00
d91538dcad Variable length partial keys 2024-03-08 13:50:40 -08:00
43a768d152 Reorder some Node fields 2024-03-08 13:33:38 -08:00
2989866a6d Move type field to end of Node 2024-03-08 13:29:02 -08:00
60df97847c Fix missed memcpy update
Everything should be in terms of kNodeCopyBegin and kNodeCopySize now
2024-03-08 13:23:43 -08:00
0038382661 Prepare to bitpack node fields if desired 2024-03-08 13:11:46 -08:00
782abc70d6 Remove custom allocator
To prepare for variable size partial keys
2024-03-08 13:02:33 -08:00
8802d17acd Remove Node::Invalid 2024-03-08 12:57:06 -08:00
02afd47d8f Node1 -> Node0 2024-03-08 12:07:47 -08:00

View File

@@ -46,65 +46,6 @@ struct Entry {
int64_t rangeVersion; int64_t rangeVersion;
}; };
template <class T, size_t kMemoryBound = (1 << 20)>
struct BoundedFreeListAllocator {
static_assert(sizeof(T) >= sizeof(void *));
T *allocate() {
#if SHOW_MEMORY
++liveAllocations;
maxLiveAllocations = std::max(maxLiveAllocations, liveAllocations);
#endif
if (freeListSize == 0) {
assert(freeList == nullptr);
return new (safe_malloc(sizeof(T))) T;
}
assert(freeList != nullptr);
void *buffer = freeList;
VALGRIND_MAKE_MEM_DEFINED(freeList, sizeof(freeList));
memcpy(&freeList, freeList, sizeof(freeList));
--freeListSize;
VALGRIND_MAKE_MEM_UNDEFINED(buffer, sizeof(T));
return new (buffer) T;
}
void release(T *p) {
#if SHOW_MEMORY
--liveAllocations;
#endif
p->~T();
if (freeListSize == kMaxFreeListSize) {
return free(p);
}
memcpy((void *)p, &freeList, sizeof(freeList));
freeList = p;
++freeListSize;
VALGRIND_MAKE_MEM_NOACCESS(p, sizeof(T));
}
~BoundedFreeListAllocator() {
for (void *iter = freeList; iter != nullptr;) {
VALGRIND_MAKE_MEM_DEFINED(iter, sizeof(iter));
auto *tmp = iter;
memcpy(&iter, iter, sizeof(void *));
free(tmp);
}
}
#if SHOW_MEMORY
int64_t highWaterMarkBytes() const { return maxLiveAllocations * sizeof(T); }
#endif
private:
static constexpr int kMaxFreeListSize = kMemoryBound / sizeof(T);
int freeListSize = 0;
void *freeList = nullptr;
#if SHOW_MEMORY
int64_t maxLiveAllocations = 0;
int64_t liveAllocations = 0;
#endif
};
struct BitSet { struct BitSet {
bool test(int i) const; bool test(int i) const;
void set(int i); void set(int i);
@@ -220,50 +161,46 @@ int BitSet::firstSetGeq(int i) const {
} }
enum class Type : int8_t { enum class Type : int8_t {
Node1, Node0,
Node4, Node4,
Node16, Node16,
Node48, Node48,
Node256, Node256,
Invalid,
}; };
constexpr static int kPartialKeyMaxLenEntryPresent = 24;
struct Node { struct Node {
Type type = Type::Invalid;
/* begin section that's copied to the next node */ /* begin section that's copied to the next node */
bool entryPresent = false;
uint8_t parentsIndex = 0;
int8_t partialKeyLen = 0;
int32_t numChildren = 0;
Node *parent = nullptr; Node *parent = nullptr;
union { Entry entry;
uint8_t partialKey[kPartialKeyMaxLenEntryPresent + sizeof(Entry)]; int32_t partialKeyLen = 0;
struct { int16_t numChildren : 15 = 0;
uint8_t padding[kPartialKeyMaxLenEntryPresent]; bool entryPresent : 1 = false;
Entry entry; uint8_t parentsIndex = 0;
};
};
/* end section that's copied to the next node */ /* end section that's copied to the next node */
Type type;
#ifndef NDEBUG
int32_t partialKeyCapacity;
#endif
uint8_t *partialKey();
}; };
static_assert(offsetof(Node, entry) == constexpr int kNodeCopyBegin = offsetof(Node, parent);
offsetof(Node, partialKey) + kPartialKeyMaxLenEntryPresent); constexpr int kNodeCopySize = offsetof(Node, type) - kNodeCopyBegin;
static_assert(std::is_trivial_v<Entry>);
struct Child { struct Child {
int64_t childMaxVersion; int64_t childMaxVersion;
Node *child; Node *child;
}; };
struct Node1 : Node { struct Node0 : Node {
// Sorted // Sorted
uint8_t index[16]; // 16 so that we can use the same simd index search uint8_t index[16]; // 16 so that we can use the same simd index search
// implementation as Node16 // implementation as Node16
Child children[1]; Node0() { this->type = Type::Node0; }
Node1() { this->type = Type::Node1; } uint8_t *partialKey() { return (uint8_t *)(this + 1); }
}; };
struct Node4 : Node { struct Node4 : Node {
@@ -272,6 +209,7 @@ struct Node4 : Node {
// implementation as Node16 // implementation as Node16
Child children[4]; Child children[4];
Node4() { this->type = Type::Node4; } Node4() { this->type = Type::Node4; }
uint8_t *partialKey() { return (uint8_t *)(this + 1); }
}; };
struct Node16 : Node { struct Node16 : Node {
@@ -279,6 +217,7 @@ struct Node16 : Node {
uint8_t index[16]; uint8_t index[16];
Child children[16]; Child children[16];
Node16() { this->type = Type::Node16; } Node16() { this->type = Type::Node16; }
uint8_t *partialKey() { return (uint8_t *)(this + 1); }
}; };
struct Node48 : Node { struct Node48 : Node {
@@ -290,6 +229,7 @@ struct Node48 : Node {
memset(index, -1, 256); memset(index, -1, 256);
this->type = Type::Node48; this->type = Type::Node48;
} }
uint8_t *partialKey() { return (uint8_t *)(this + 1); }
}; };
struct Node256 : Node { struct Node256 : Node {
@@ -301,10 +241,94 @@ struct Node256 : Node {
children[i].child = nullptr; children[i].child = nullptr;
} }
} }
uint8_t *partialKey() { return (uint8_t *)(this + 1); }
}; };
// Bounds memory usage in free list, but does not account for memory for partial
// keys.
template <class T, size_t kMemoryBound = (1 << 20)>
struct BoundedFreeListAllocator {
static_assert(sizeof(T) >= sizeof(void *));
static_assert(std::derived_from<T, Node>);
T *allocate(int partialKeyCapacity) {
#if SHOW_MEMORY
++liveAllocations;
maxLiveAllocations = std::max(maxLiveAllocations, liveAllocations);
#endif
if (freeList != nullptr) {
T *n = (T *)freeList;
VALGRIND_MAKE_MEM_DEFINED(n, sizeof(T));
if (n->partialKeyLen >= partialKeyCapacity) {
memcpy(&freeList, freeList, sizeof(freeList));
--freeListSize;
VALGRIND_MAKE_MEM_UNDEFINED(n, sizeof(T));
return new (n) T;
}
VALGRIND_MAKE_MEM_NOACCESS(n, sizeof(T));
}
auto *result = new (safe_malloc(sizeof(T) + partialKeyCapacity)) T;
#ifndef NDEBUG
result->partialKeyCapacity = partialKeyCapacity;
#endif
return result;
}
void release(T *p) {
#if SHOW_MEMORY
--liveAllocations;
#endif
p->~T();
if (freeListSize == kMaxFreeListSize) {
return free(p);
}
memcpy((void *)p, &freeList, sizeof(freeList));
freeList = p;
++freeListSize;
VALGRIND_MAKE_MEM_NOACCESS(freeList, sizeof(T));
}
~BoundedFreeListAllocator() {
for (void *iter = freeList; iter != nullptr;) {
VALGRIND_MAKE_MEM_DEFINED(iter, sizeof(iter));
auto *tmp = iter;
memcpy(&iter, iter, sizeof(void *));
free(tmp);
}
}
#if SHOW_MEMORY
int64_t highWaterMarkBytes() const { return maxLiveAllocations * sizeof(T); }
#endif
private:
static constexpr int kMaxFreeListSize = kMemoryBound / sizeof(T);
int freeListSize = 0;
void *freeList = nullptr;
#if SHOW_MEMORY
int64_t maxLiveAllocations = 0;
int64_t liveAllocations = 0;
#endif
};
uint8_t *Node::partialKey() {
switch (type) {
case Type::Node0:
return ((Node0 *)this)->partialKey();
case Type::Node4:
return ((Node4 *)this)->partialKey();
case Type::Node16:
return ((Node16 *)this)->partialKey();
case Type::Node48:
return ((Node48 *)this)->partialKey();
case Type::Node256:
return ((Node256 *)this)->partialKey();
}
}
struct NodeAllocators { struct NodeAllocators {
BoundedFreeListAllocator<Node1> node1; BoundedFreeListAllocator<Node0> node0;
BoundedFreeListAllocator<Node4> node4; BoundedFreeListAllocator<Node4> node4;
BoundedFreeListAllocator<Node16> node16; BoundedFreeListAllocator<Node16> node16;
BoundedFreeListAllocator<Node48> node48; BoundedFreeListAllocator<Node48> node48;
@@ -540,19 +564,15 @@ Node *&getOrCreateChild(Node *&self, uint8_t index,
} }
} }
if (self->type == Type::Node1) { if (self->type == Type::Node0) {
auto *self1 = static_cast<Node1 *>(self); auto *self0 = static_cast<Node0 *>(self);
if (self->numChildren == 1) { auto *newSelf = allocators->node4.allocate(self->partialKeyLen);
auto *newSelf = allocators->node4.allocate(); memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
memcpy((void *)newSelf, self, sizeof(Node1)); kNodeCopySize);
newSelf->type = Type::Node4; memcpy(newSelf->partialKey(), self0->partialKey(), self->partialKeyLen);
allocators->node1.release(self1); allocators->node0.release(self0);
setChildrenParents(newSelf); self = newSelf;
self = newSelf;
} else {
assert(self->numChildren == 0);
}
goto insert16; goto insert16;
@@ -560,9 +580,15 @@ Node *&getOrCreateChild(Node *&self, uint8_t index,
auto *self4 = static_cast<Node4 *>(self); auto *self4 = static_cast<Node4 *>(self);
if (self->numChildren == 4) { if (self->numChildren == 4) {
auto *newSelf = allocators->node16.allocate(); auto *newSelf = allocators->node16.allocate(self->partialKeyLen);
memcpy((void *)newSelf, self, sizeof(Node4)); memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
newSelf->type = Type::Node16; kNodeCopySize);
memcpy(newSelf->partialKey(), self4->partialKey(), self->partialKeyLen);
// TODO replace with memcpy?
for (int i = 0; i < 4; ++i) {
newSelf->index[i] = self4->index[i];
newSelf->children[i] = self4->children[i];
}
allocators->node4.release(self4); allocators->node4.release(self4);
setChildrenParents(newSelf); setChildrenParents(newSelf);
self = newSelf; self = newSelf;
@@ -574,10 +600,10 @@ Node *&getOrCreateChild(Node *&self, uint8_t index,
if (self->numChildren == 16) { if (self->numChildren == 16) {
auto *self16 = static_cast<Node16 *>(self); auto *self16 = static_cast<Node16 *>(self);
auto *newSelf = allocators->node48.allocate(); auto *newSelf = allocators->node48.allocate(self->partialKeyLen);
memcpy((char *)newSelf + sizeof(Node::type), memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
(char *)self + sizeof(Node::type), kNodeCopySize);
sizeof(Node) - sizeof(Node::type)); memcpy(newSelf->partialKey(), self16->partialKey(), self->partialKeyLen);
newSelf->nextFree = 16; newSelf->nextFree = 16;
int i = 0; int i = 0;
for (auto x : self16->index) { for (auto x : self16->index) {
@@ -615,10 +641,10 @@ Node *&getOrCreateChild(Node *&self, uint8_t index,
if (self->numChildren == 48) { if (self->numChildren == 48) {
auto *self48 = static_cast<Node48 *>(self); auto *self48 = static_cast<Node48 *>(self);
auto *newSelf = allocators->node256.allocate(); auto *newSelf = allocators->node256.allocate(self->partialKeyLen);
memcpy((char *)newSelf + sizeof(Node::type), memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
(char *)self + sizeof(Node::type), kNodeCopySize);
sizeof(Node) - sizeof(Node::type)); memcpy(newSelf->partialKey(), self48->partialKey(), self->partialKeyLen);
newSelf->bitSet = self48->bitSet; newSelf->bitSet = self48->bitSet;
newSelf->bitSet.forEachInRange( newSelf->bitSet.forEachInRange(
[&](int i) { [&](int i) {
@@ -655,8 +681,8 @@ Node *&getOrCreateChild(Node *&self, uint8_t index,
void eraseChild(Node *self, uint8_t index, NodeAllocators *allocators) { void eraseChild(Node *self, uint8_t index, NodeAllocators *allocators) {
auto *child = getChildExists(self, index); auto *child = getChildExists(self, index);
switch (child->type) { switch (child->type) {
case Type::Node1: case Type::Node0:
allocators->node1.release((Node1 *)child); allocators->node0.release((Node0 *)child);
break; break;
case Type::Node4: case Type::Node4:
allocators->node4.release((Node4 *)child); allocators->node4.release((Node4 *)child);
@@ -670,8 +696,6 @@ void eraseChild(Node *self, uint8_t index, NodeAllocators *allocators) {
case Type::Node256: case Type::Node256:
allocators->node256.release((Node256 *)child); allocators->node256.release((Node256 *)child);
break; break;
case Type::Invalid:
__builtin_unreachable(); // GCOVR_EXCL_LINE
} }
if (self->type <= Type::Node16) { if (self->type <= Type::Node16) {
@@ -901,14 +925,7 @@ bytes:
int longestCommonPrefixPartialKey(const uint8_t *ap, const uint8_t *bp, int longestCommonPrefixPartialKey(const uint8_t *ap, const uint8_t *bp,
int cl) { int cl) {
assert(cl <= kPartialKeyMaxLenEntryPresent + int(sizeof(Entry))); return longestCommonPrefix(ap, bp, cl);
int i = 0;
for (; i < cl; ++i) {
if (*ap++ != *bp++) {
break;
}
}
return i;
} }
// Performs a physical search for remaining // Performs a physical search for remaining
@@ -931,7 +948,7 @@ struct SearchStepWise {
return true; return true;
} }
int cl = std::min<int>(child->partialKeyLen, remaining.size() - 1); int cl = std::min<int>(child->partialKeyLen, remaining.size() - 1);
int i = longestCommonPrefixPartialKey(child->partialKey, int i = longestCommonPrefixPartialKey(child->partialKey(),
remaining.data() + 1, cl); remaining.data() + 1, cl);
if (i != child->partialKeyLen) { if (i != child->partialKeyLen) {
return true; return true;
@@ -988,10 +1005,10 @@ bool checkPointRead(Node *n, const std::span<const uint8_t> key,
if (n->partialKeyLen > 0) { if (n->partialKeyLen > 0) {
int commonLen = std::min<int>(n->partialKeyLen, remaining.size()); int commonLen = std::min<int>(n->partialKeyLen, remaining.size());
int i = longestCommonPrefixPartialKey(n->partialKey, remaining.data(), int i = longestCommonPrefixPartialKey(n->partialKey(), remaining.data(),
commonLen); commonLen);
if (i < commonLen) { if (i < commonLen) {
auto c = n->partialKey[i] <=> remaining[i]; auto c = n->partialKey()[i] <=> remaining[i];
if (c > 0) { if (c > 0) {
goto downLeftSpine; goto downLeftSpine;
} else { } else {
@@ -1045,7 +1062,7 @@ int64_t maxBetweenExclusive(Node *n, int begin, int end) {
} }
} }
switch (n->type) { switch (n->type) {
case Type::Node1: case Type::Node0:
[[fallthrough]]; [[fallthrough]];
case Type::Node4: case Type::Node4:
[[fallthrough]]; [[fallthrough]];
@@ -1077,8 +1094,6 @@ int64_t maxBetweenExclusive(Node *n, int begin, int end) {
begin, end); begin, end);
break; break;
} }
case Type::Invalid:
__builtin_unreachable(); // GCOVR_EXCL_LINE
} }
#if DEBUG_VERBOSE && !defined(NDEBUG) #if DEBUG_VERBOSE && !defined(NDEBUG)
fprintf(stderr, "At `%s', max version in (%02x, %02x) is %" PRId64 "\n", fprintf(stderr, "At `%s', max version in (%02x, %02x) is %" PRId64 "\n",
@@ -1092,7 +1107,7 @@ Vector<uint8_t> getSearchPath(Arena &arena, Node *n) {
auto result = vector<uint8_t>(arena); auto result = vector<uint8_t>(arena);
for (;;) { for (;;) {
for (int i = n->partialKeyLen - 1; i >= 0; --i) { for (int i = n->partialKeyLen - 1; i >= 0; --i) {
result.push_back(n->partialKey[i]); result.push_back(n->partialKey()[i]);
} }
if (n->parent == nullptr) { if (n->parent == nullptr) {
break; break;
@@ -1138,10 +1153,10 @@ bool checkRangeStartsWith(Node *n, std::span<const uint8_t> key, int begin,
if (n->partialKeyLen > 0) { if (n->partialKeyLen > 0) {
int commonLen = std::min<int>(n->partialKeyLen, remaining.size()); int commonLen = std::min<int>(n->partialKeyLen, remaining.size());
int i = longestCommonPrefixPartialKey(n->partialKey, remaining.data(), int i = longestCommonPrefixPartialKey(n->partialKey(), remaining.data(),
commonLen); commonLen);
if (i < commonLen) { if (i < commonLen) {
auto c = n->partialKey[i] <=> remaining[i]; auto c = n->partialKey()[i] <=> remaining[i];
if (c > 0) { if (c > 0) {
goto downLeftSpine; goto downLeftSpine;
} else { } else {
@@ -1153,8 +1168,8 @@ bool checkRangeStartsWith(Node *n, std::span<const uint8_t> key, int begin,
// partial key matches // partial key matches
remaining = remaining.subspan(commonLen, remaining.size() - commonLen); remaining = remaining.subspan(commonLen, remaining.size() - commonLen);
} else if (n->partialKeyLen > int(remaining.size())) { } else if (n->partialKeyLen > int(remaining.size())) {
if (begin < n->partialKey[remaining.size()] && if (begin < n->partialKey()[remaining.size()] &&
n->partialKey[remaining.size()] < end) { n->partialKey()[remaining.size()] < end) {
if (n->entryPresent && n->entry.rangeVersion > readVersion) { if (n->entryPresent && n->entry.rangeVersion > readVersion) {
return false; return false;
} }
@@ -1245,11 +1260,11 @@ struct CheckRangeLeftSide {
if (n->partialKeyLen > 0) { if (n->partialKeyLen > 0) {
int commonLen = std::min<int>(n->partialKeyLen, remaining.size()); int commonLen = std::min<int>(n->partialKeyLen, remaining.size());
int i = longestCommonPrefixPartialKey(n->partialKey, remaining.data(), int i = longestCommonPrefixPartialKey(n->partialKey(), remaining.data(),
commonLen); commonLen);
searchPathLen += i; searchPathLen += i;
if (i < commonLen) { if (i < commonLen) {
auto c = n->partialKey[i] <=> remaining[i]; auto c = n->partialKey()[i] <=> remaining[i];
if (c > 0) { if (c > 0) {
if (searchPathLen < prefixLen) { if (searchPathLen < prefixLen) {
return downLeftSpine(); return downLeftSpine();
@@ -1383,12 +1398,12 @@ struct CheckRangeRightSide {
if (n->partialKeyLen > 0) { if (n->partialKeyLen > 0) {
int commonLen = std::min<int>(n->partialKeyLen, remaining.size()); int commonLen = std::min<int>(n->partialKeyLen, remaining.size());
int i = longestCommonPrefixPartialKey(n->partialKey, remaining.data(), int i = longestCommonPrefixPartialKey(n->partialKey(), remaining.data(),
commonLen); commonLen);
searchPathLen += i; searchPathLen += i;
if (i < commonLen) { if (i < commonLen) {
++searchPathLen; ++searchPathLen;
auto c = n->partialKey[i] <=> remaining[i]; auto c = n->partialKey()[i] <=> remaining[i];
if (c > 0) { if (c > 0) {
return downLeftSpine(); return downLeftSpine();
} else { } else {
@@ -1540,35 +1555,31 @@ template <bool kBegin>
for (;;) { for (;;) {
if ((*self)->partialKeyLen > 0) { if ((*self)->partialKeyLen > 0) {
const bool wouldBePresent =
key.size() <= kPartialKeyMaxLenEntryPresent + int(sizeof(Entry));
// Handle an existing partial key // Handle an existing partial key
int commonLen = std::min<int>((*self)->partialKeyLen, key.size()); int commonLen = std::min<int>((*self)->partialKeyLen, key.size());
if (wouldBePresent) {
commonLen = std::min(commonLen, kPartialKeyMaxLenEntryPresent);
}
int partialKeyIndex = longestCommonPrefixPartialKey( int partialKeyIndex = longestCommonPrefixPartialKey(
(*self)->partialKey, key.data(), commonLen); (*self)->partialKey(), key.data(), commonLen);
if (partialKeyIndex < (*self)->partialKeyLen) { if (partialKeyIndex < (*self)->partialKeyLen) {
auto *old = *self; auto *old = *self;
int64_t oldMaxVersion = maxVersion(old, impl); int64_t oldMaxVersion = maxVersion(old, impl);
*self = allocators->node1.allocate(); *self = allocators->node4.allocate(partialKeyIndex);
memcpy((char *)*self + sizeof(Node::type), memcpy((char *)*self + kNodeCopyBegin, (char *)old + kNodeCopyBegin,
(char *)old + sizeof(Node::type), kNodeCopySize);
sizeof(Node) - sizeof(Node::type));
(*self)->partialKeyLen = partialKeyIndex; (*self)->partialKeyLen = partialKeyIndex;
(*self)->entryPresent = false; (*self)->entryPresent = false;
(*self)->numChildren = 0; (*self)->numChildren = 0;
memcpy((*self)->partialKey(), old->partialKey(),
(*self)->partialKeyLen);
getOrCreateChild(*self, old->partialKey[partialKeyIndex], allocators) = getOrCreateChild(*self, old->partialKey()[partialKeyIndex],
old; allocators) = old;
old->parent = *self; old->parent = *self;
old->parentsIndex = old->partialKey[partialKeyIndex]; old->parentsIndex = old->partialKey()[partialKeyIndex];
maxVersion(old, impl) = oldMaxVersion; maxVersion(old, impl) = oldMaxVersion;
memmove(old->partialKey, old->partialKey + partialKeyIndex + 1, memmove(old->partialKey(), old->partialKey() + partialKeyIndex + 1,
old->partialKeyLen - (partialKeyIndex + 1)); old->partialKeyLen - (partialKeyIndex + 1));
old->partialKeyLen -= partialKeyIndex + 1; old->partialKeyLen -= partialKeyIndex + 1;
} }
@@ -1577,13 +1588,9 @@ template <bool kBegin>
} else { } else {
// Consider adding a partial key // Consider adding a partial key
if ((*self)->numChildren == 0 && !(*self)->entryPresent) { if ((*self)->numChildren == 0 && !(*self)->entryPresent) {
const bool willNotBePresent = assert((*self)->partialKeyCapacity >= int(key.size()));
key.size() > kPartialKeyMaxLenEntryPresent + int(sizeof(Entry)); (*self)->partialKeyLen = key.size();
(*self)->partialKeyLen = std::min<int>( memcpy((*self)->partialKey(), key.data(), (*self)->partialKeyLen);
key.size(), willNotBePresent
? kPartialKeyMaxLenEntryPresent + int(sizeof(Entry))
: kPartialKeyMaxLenEntryPresent);
memcpy((*self)->partialKey, key.data(), (*self)->partialKeyLen);
key = key.subspan((*self)->partialKeyLen, key = key.subspan((*self)->partialKeyLen,
key.size() - (*self)->partialKeyLen); key.size() - (*self)->partialKeyLen);
} }
@@ -1607,7 +1614,7 @@ template <bool kBegin>
auto &child = getOrCreateChild(*self, key.front(), allocators); auto &child = getOrCreateChild(*self, key.front(), allocators);
if (!child) { if (!child) {
child = allocators->node1.allocate(); child = allocators->node0.allocate(key.size() - 1);
child->parent = *self; child->parent = *self;
child->parentsIndex = key.front(); child->parentsIndex = key.front();
maxVersion(child, impl) = maxVersion(child, impl) =
@@ -1674,7 +1681,7 @@ void addWriteRange(Node *&root, int64_t oldestVersion,
if (int(remaining.size()) <= n->partialKeyLen) { if (int(remaining.size()) <= n->partialKeyLen) {
break; break;
} }
int i = longestCommonPrefixPartialKey(n->partialKey, remaining.data(), int i = longestCommonPrefixPartialKey(n->partialKey(), remaining.data(),
n->partialKeyLen); n->partialKeyLen);
if (i != n->partialKeyLen) { if (i != n->partialKeyLen) {
break; break;
@@ -1706,7 +1713,8 @@ void addWriteRange(Node *&root, int64_t oldestVersion,
auto *beginNode = auto *beginNode =
insert<true>(useAsRoot, begin, writeVersion, allocators, impl); insert<true>(useAsRoot, begin, writeVersion, allocators, impl);
const bool insertedBegin = !std::exchange(beginNode->entryPresent, true); const bool insertedBegin = !beginNode->entryPresent;
beginNode->entryPresent = true;
if (insertedBegin) { if (insertedBegin) {
auto *p = nextLogical(beginNode); auto *p = nextLogical(beginNode);
@@ -1723,7 +1731,8 @@ void addWriteRange(Node *&root, int64_t oldestVersion,
auto *endNode = insert<false>(useAsRoot, end, writeVersion, allocators, impl); auto *endNode = insert<false>(useAsRoot, end, writeVersion, allocators, impl);
const bool insertedEnd = !std::exchange(endNode->entryPresent, true); const bool insertedEnd = !endNode->entryPresent;
endNode->entryPresent = true;
if (insertedEnd) { if (insertedEnd) {
auto *p = nextLogical(endNode); auto *p = nextLogical(endNode);
@@ -1780,10 +1789,10 @@ Iterator firstGeq(Node *n, const std::span<const uint8_t> key) {
if (n->partialKeyLen > 0) { if (n->partialKeyLen > 0) {
int commonLen = std::min<int>(n->partialKeyLen, remaining.size()); int commonLen = std::min<int>(n->partialKeyLen, remaining.size());
int i = longestCommonPrefixPartialKey(n->partialKey, remaining.data(), int i = longestCommonPrefixPartialKey(n->partialKey(), remaining.data(),
commonLen); commonLen);
if (i < commonLen) { if (i < commonLen) {
auto c = n->partialKey[i] <=> remaining[i]; auto c = n->partialKey()[i] <=> remaining[i];
if (c > 0) { if (c > 0) {
goto downLeftSpine; goto downLeftSpine;
} else { } else {
@@ -1889,7 +1898,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
explicit Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) { explicit Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) {
// Insert "" // Insert ""
root = allocators.node1.allocate(); root = allocators.node0.allocate(0);
rootMaxVersion = oldestVersion; rootMaxVersion = oldestVersion;
root->entry.pointVersion = oldestVersion; root->entry.pointVersion = oldestVersion;
root->entry.rangeVersion = oldestVersion; root->entry.rangeVersion = oldestVersion;
@@ -1962,8 +1971,8 @@ ConflictSet::~ConflictSet() {
__attribute__((visibility("default"))) void showMemory(const ConflictSet &cs) { __attribute__((visibility("default"))) void showMemory(const ConflictSet &cs) {
ConflictSet::Impl *impl; ConflictSet::Impl *impl;
memcpy(&impl, &cs, sizeof(impl)); // NOLINT memcpy(&impl, &cs, sizeof(impl)); // NOLINT
fprintf(stderr, "Max Node1 memory usage: %" PRId64 "\n", fprintf(stderr, "Max Node0 memory usage: %" PRId64 "\n",
impl->allocators.node1.highWaterMarkBytes()); impl->allocators.node0.highWaterMarkBytes());
fprintf(stderr, "Max Node4 memory usage: %" PRId64 "\n", fprintf(stderr, "Max Node4 memory usage: %" PRId64 "\n",
impl->allocators.node4.highWaterMarkBytes()); impl->allocators.node4.highWaterMarkBytes());
fprintf(stderr, "Max Node16 memory usage: %" PRId64 "\n", fprintf(stderr, "Max Node16 memory usage: %" PRId64 "\n",
@@ -2025,7 +2034,7 @@ std::string getSearchPathPrintable(Node *n) {
auto result = vector<char>(arena); auto result = vector<char>(arena);
for (;;) { for (;;) {
for (int i = n->partialKeyLen - 1; i >= 0; --i) { for (int i = n->partialKeyLen - 1; i >= 0; --i) {
result.push_back(n->partialKey[i]); result.push_back(n->partialKey()[i]);
} }
if (n->parent == nullptr) { if (n->parent == nullptr) {
break; break;
@@ -2049,7 +2058,7 @@ std::string getPartialKeyPrintable(Node *n) {
} }
auto result = std::string((const char *)&n->parentsIndex, auto result = std::string((const char *)&n->parentsIndex,
n->parent == nullptr ? 0 : 1) + n->parent == nullptr ? 0 : 1) +
std::string((const char *)n->partialKey, n->partialKeyLen); std::string((const char *)n->partialKey(), n->partialKeyLen);
return printable(result); // NOLINT return printable(result); // NOLINT
} }
@@ -2251,7 +2260,7 @@ int main(void) {
ConflictSet::Impl cs{0}; ConflictSet::Impl cs{0};
for (int j = 0; j < 256; ++j) { for (int j = 0; j < 256; ++j) {
getOrCreateChild(cs.root, j, &cs.allocators) = getOrCreateChild(cs.root, j, &cs.allocators) =
cs.allocators.node1.allocate(); cs.allocators.node0.allocate(0);
if (j % 10 == 0) { if (j % 10 == 0) {
bench.run("MaxExclusive " + std::to_string(j), [&]() { bench.run("MaxExclusive " + std::to_string(j), [&]() {
bench.doNotOptimizeAway(maxBetweenExclusive(cs.root, 0, 256)); bench.doNotOptimizeAway(maxBetweenExclusive(cs.root, 0, 256));
@@ -2265,8 +2274,6 @@ int main(void) {
#ifdef ENABLE_FUZZ #ifdef ENABLE_FUZZ
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
TestDriver<ConflictSet::Impl> driver{data, size}; TestDriver<ConflictSet::Impl> driver{data, size};
static_assert(driver.kMaxKeyLen >
kPartialKeyMaxLenEntryPresent + sizeof(Entry));
for (;;) { for (;;) {
bool done = driver.next(); bool done = driver.next();