Compare commits
12 Commits
987e93b190
...
733f32b22e
Author | SHA1 | Date | |
---|---|---|---|
733f32b22e | |||
3fb8bf7c3b | |||
0c8cb8faa5 | |||
93e487c8fb | |||
d91538dcad | |||
43a768d152 | |||
2989866a6d | |||
60df97847c | |||
0038382661 | |||
782abc70d6 | |||
8802d17acd | |||
02afd47d8f |
341
ConflictSet.cpp
341
ConflictSet.cpp
@@ -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();
|
||||||
|
Reference in New Issue
Block a user