Skip to content

Commit

Permalink
chore: tidy btree related code
Browse files Browse the repository at this point in the history
  • Loading branch information
zz-jason committed Aug 27, 2024
1 parent 553012d commit 0a8e7cd
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 201 deletions.
2 changes: 1 addition & 1 deletion include/leanstore/btree/core/BTreeGeneric.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ inline ParentSwipHandler BTreeGeneric::FindParent(BTreeGeneric& btree, BufferFra
}

auto& nodeToFind = *reinterpret_cast<BTreeNode*>(bfToFind.mPage.mPayload);
const auto isInfinity = nodeToFind.mUpperFence.mOffset == 0;
const auto isInfinity = nodeToFind.mUpperFence.IsInfinity();
const auto keyToFind = nodeToFind.GetUpperFence();

auto posInParent = std::numeric_limits<uint32_t>::max();
Expand Down
172 changes: 103 additions & 69 deletions include/leanstore/btree/core/BTreeNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "leanstore/utils/Log.hpp"
#include "leanstore/utils/UserThread.hpp"

#include <cstdint>
#include <cstring>

namespace leanstore::storage::btree {
Expand Down Expand Up @@ -37,9 +38,12 @@ class BTreeNodeHeader {
uint16_t mOffset;

uint16_t mLength;

bool IsInfinity() {
return mOffset == 0;
}
};

public:
//! The swip of the right-most child.
//! TODO(zz-jason): can it be moved to the slot array?
Swip mRightMostChildSwip = nullptr;
Expand Down Expand Up @@ -77,88 +81,98 @@ class BTreeNodeHeader {
//! Needed for GC
bool mHasGarbage = false;

public:
//! Constructs a BTreeNodeHeader.
BTreeNodeHeader(bool isLeaf, uint16_t size) : mIsLeaf(isLeaf), mDataOffset(size) {
}

~BTreeNodeHeader() {
}
//! Destructs a BTreeNodeHeader.
~BTreeNodeHeader() = default;

public:
uint8_t* RawPtr() {
//! Returns the start address of the node.
uint8_t* NodeBegin() {
return reinterpret_cast<uint8_t*>(this);
}

//! Whether the node is an inner node.
bool IsInner() {
return !mIsLeaf;
}

//! Get the lower fence key slice.
Slice GetLowerFence() {
return Slice(GetLowerFenceKey(), mLowerFence.mLength);
return Slice(LowerFenceAddr(), mLowerFence.mLength);
}

uint8_t* GetLowerFenceKey() {
return mLowerFence.mOffset ? RawPtr() + mLowerFence.mOffset : nullptr;
//! Get the address of lower fence key. nullptr if the lower fence is infinity.
uint8_t* LowerFenceAddr() {
return mLowerFence.IsInfinity() ? nullptr : NodeBegin() + mLowerFence.mOffset;
}

//! Get the upper fence key slice.
Slice GetUpperFence() {
return Slice(GetUpperFenceKey(), mUpperFence.mLength);
}

uint8_t* GetUpperFenceKey() {
return mUpperFence.mOffset ? RawPtr() + mUpperFence.mOffset : nullptr;
return Slice(UpperFenceAddr(), mUpperFence.mLength);
}

bool IsUpperFenceInfinity() {
return !mUpperFence.mOffset;
}

bool IsLowerFenceInfinity() {
return !mLowerFence.mOffset;
//! Get the address of upper fence key. nullptr if the upper fence is infinity.
uint8_t* UpperFenceAddr() {
return mUpperFence.IsInfinity() ? nullptr : NodeBegin() + mUpperFence.mOffset;
}
};

class BTreeNode : public BTreeNodeHeader {
public:
//! The slot inside a btree node. Slot records the metadata for the key-value position inside a
//! page. Common prefix among all keys are removed in a btree node. Slot key-value layout:
//! | key without prefix | value |
struct __attribute__((packed)) Slot {
//! The slot inside a btree node. Slot records the metadata for the key-value position inside a
//! page. Common prefix among all keys are removed in a btree node. Slot key-value layout:
//! | key without prefix | value |
struct __attribute__((packed)) BTreeNodeSlot {
//! Data offset of the slot, also the offset of the slot key
uint16_t mOffset;

//! Data offset of the slot, also the offset of the slot key
uint16_t mOffset;
//! Slot key size
uint16_t mKeySizeWithoutPrefix;

//! Slot key size
uint16_t mKeySizeWithoutPrefix;
//! Slot value size
uint16_t mValSize;

//! Slot value size
uint16_t mValSize;
//! The key header, used to improve key comparation performance
union {
HeadType mHead;

//! The key header, used for improve key comparation performance
union {
HeadType mHead;
uint8_t mHeadBytes[4];
};
uint8_t mHeadBytes[4];
};
};

//! The slot array, which stores all the key-value positions inside a page.
Slot mSlot[];
class BTreeNode : public BTreeNodeHeader {
public:
//! The slot array, which stores all the key-value positions inside a BTreeNode.
BTreeNodeSlot mSlot[];

//! Creates a BTreeNode. Since BTreeNode creations and utilizations are critical, please use
//! ExclusiveGuardedBufferFrame::InitPayload() or BTreeNode::Init() to construct a BTreeNode on an
//! ExclusiveGuardedBufferFrame::InitPayload() or BTreeNode::New() to construct a BTreeNode on an
//! existing buffer which has at least BTreeNode::Size() bytes:
//! 1. ExclusiveGuardedBufferFrame::InitPayload() creates a BTreeNode on the holding BufferFrame.
//! 2. BTreeNode::Init(): creates a BTreeNode on the providing buffer. The size of the underlying
//! 2. BTreeNode::New(): creates a BTreeNode on the providing buffer. The size of the underlying
//! buffer to store a BTreeNode can be obtained through BTreeNode::Size()
BTreeNode(bool isLeaf) : BTreeNodeHeader(isLeaf, BTreeNode::Size()) {
}

//! Creates a BTreeNode on the providing buffer. Callers should ensure the buffer has at least
//! BTreeNode::Size() bytes to store the BTreeNode.
//! @param buf: the buffer to store the BTreeNode.
//! @param isLeaf: whether the BTreeNode is a leaf node.
//! @param lowerFence: the lower fence of the BTreeNode.
//! @param upperFence: the upper fence of the BTreeNode.
//! @return the created BTreeNode.
static BTreeNode* New(void* buf, bool isLeaf, Slice lowerFence, Slice upperFence) {
auto* node = new (buf) BTreeNode(isLeaf);
node->setFences(lowerFence, upperFence);
return node;
}

uint16_t FreeSpace() {
return mDataOffset - (reinterpret_cast<uint8_t*>(mSlot + mNumSeps) - RawPtr());
return mDataOffset - (reinterpret_cast<uint8_t*>(mSlot + mNumSeps) - NodeBegin());
}

uint16_t FreeSpaceAfterCompaction() {
return BTreeNode::Size() - (reinterpret_cast<uint8_t*>(mSlot + mNumSeps) - RawPtr()) -
return BTreeNode::Size() - (reinterpret_cast<uint8_t*>(mSlot + mNumSeps) - NodeBegin()) -
mSpaceUsed;
}

Expand Down Expand Up @@ -186,7 +200,7 @@ class BTreeNode : public BTreeNodeHeader {
}

uint8_t* KeyDataWithoutPrefix(uint16_t slotId) {
return RawPtr() + mSlot[slotId].mOffset;
return NodeBegin() + mSlot[slotId].mOffset;
}

uint16_t KeySizeWithoutPrefix(uint16_t slotId) {
Expand All @@ -200,7 +214,8 @@ class BTreeNode : public BTreeNodeHeader {
// Each slot is composed of:
// key (mKeySizeWithoutPrefix), payload (mValSize)
uint8_t* ValData(uint16_t slotId) {
return RawPtr() + mSlot[slotId].mOffset + mSlot[slotId].mKeySizeWithoutPrefix;
auto valOffset = mSlot[slotId].mOffset + mSlot[slotId].mKeySizeWithoutPrefix;
return NodeBegin() + valOffset;
}

uint16_t ValSize(uint16_t slotId) {
Expand All @@ -221,7 +236,7 @@ class BTreeNode : public BTreeNodeHeader {
}

uint16_t GetKVConsumedSpace(uint16_t slotId) {
return sizeof(Slot) + KeySizeWithoutPrefix(slotId) + ValSize(slotId);
return sizeof(BTreeNodeSlot) + KeySizeWithoutPrefix(slotId) + ValSize(slotId);
}

// Attention: the caller has to hold a copy of the existing payload
Expand Down Expand Up @@ -257,33 +272,31 @@ class BTreeNode : public BTreeNodeHeader {
std::memcpy(copiedKey, KeyDataWithoutPrefix(slotId), keySizeWithoutPrefix);

// release the old space occupied by the payload (keyWithoutPrefix + value)
mSpaceUsed -= oldTotalSize;
mDataOffset += oldTotalSize;
retreatDataOffset(oldTotalSize);

mSlot[slotId].mValSize = 0;
mSlot[slotId].mKeySizeWithoutPrefix = 0;
if (FreeSpace() < newTotalSize) {
Compactify();
}
LS_DCHECK(FreeSpace() >= newTotalSize);
mSpaceUsed += newTotalSize;
mDataOffset -= newTotalSize;
advanceDataOffset(newTotalSize);
mSlot[slotId].mOffset = mDataOffset;
mSlot[slotId].mKeySizeWithoutPrefix = keySizeWithoutPrefix;
mSlot[slotId].mValSize = targetSize;
std::memcpy(KeyDataWithoutPrefix(slotId), copiedKey, keySizeWithoutPrefix);
}

Slice KeyPrefix() {
return Slice(GetLowerFenceKey(), mPrefixSize);
return Slice(LowerFenceAddr(), mPrefixSize);
}

uint8_t* GetPrefix() {
return GetLowerFenceKey();
return LowerFenceAddr();
}

void CopyPrefix(uint8_t* out) {
memcpy(out, GetLowerFenceKey(), mPrefixSize);
memcpy(out, LowerFenceAddr(), mPrefixSize);
}

void CopyKeyWithoutPrefix(uint16_t slotId, uint8_t* dest) {
Expand Down Expand Up @@ -347,7 +360,7 @@ class BTreeNode : public BTreeNodeHeader {
uint32_t MergeSpaceUpperBound(ExclusiveGuardedBufferFrame<BTreeNode>& xGuardedRight);

uint32_t SpaceUsedBySlot(uint16_t slotId) {
return sizeof(BTreeNode::Slot) + KeySizeWithoutPrefix(slotId) + ValSize(slotId);
return sizeof(BTreeNodeSlot) + KeySizeWithoutPrefix(slotId) + ValSize(slotId);
}

// NOLINTNEXTLINE
Expand All @@ -364,8 +377,6 @@ class BTreeNode : public BTreeNodeHeader {

void InsertFence(FenceKey& fk, Slice key);

void SetFences(Slice lowerKey, Slice upperKey);

void Split(ExclusiveGuardedBufferFrame<BTreeNode>& xGuardedParent,
ExclusiveGuardedBufferFrame<BTreeNode>& xGuardedNewLeft,
const BTreeNode::SeparatorInfo& sepInfo);
Expand All @@ -384,9 +395,11 @@ class BTreeNode : public BTreeNodeHeader {
void Reset();

private:
void setFences(Slice lowerKey, Slice upperKey);

void generateSeparator(const SeparatorInfo& sepInfo, uint8_t* sepKey) {
// prefix
memcpy(sepKey, GetLowerFenceKey(), mPrefixSize);
memcpy(sepKey, LowerFenceAddr(), mPrefixSize);

if (sepInfo.mTrunc) {
memcpy(sepKey + mPrefixSize, KeyDataWithoutPrefix(sepInfo.mSlotId + 1),
Expand Down Expand Up @@ -447,12 +460,7 @@ class BTreeNode : public BTreeNodeHeader {
static int32_t CmpKeys(Slice lhs, Slice rhs);

static uint16_t SpaceNeeded(uint16_t keySize, uint16_t valSize, uint16_t prefixSize) {
return sizeof(Slot) + (keySize - prefixSize) + valSize;
}

template <typename... Args>
static BTreeNode* Init(void* addr, Args&&... args) {
return new (addr) BTreeNode(std::forward<Args>(args)...);
return sizeof(BTreeNodeSlot) + (keySize - prefixSize) + valSize;
}

static uint16_t Size() {
Expand All @@ -462,15 +470,28 @@ class BTreeNode : public BTreeNodeHeader {
static uint16_t UnderFullSize() {
return BTreeNode::Size() * 0.6;
}

private:
//! Advance the data offset by size
void advanceDataOffset(uint16_t size) {
mDataOffset -= size;
mSpaceUsed += size;
}

//! Oppsite of advanceDataOffset
void retreatDataOffset(uint16_t size) {
mDataOffset += size;
mSpaceUsed -= size;
}
};

template <bool equality_only>
int16_t BTreeNode::LinearSearchWithBias(Slice key, uint16_t startPos, bool higher) {
if (key.size() < mPrefixSize || (bcmp(key.data(), GetLowerFenceKey(), mPrefixSize) != 0)) {
inline int16_t BTreeNode::LinearSearchWithBias(Slice key, uint16_t startPos, bool higher) {
if (key.size() < mPrefixSize || (bcmp(key.data(), LowerFenceAddr(), mPrefixSize) != 0)) {
return -1;
}

LS_DCHECK(key.size() >= mPrefixSize && bcmp(key.data(), GetLowerFenceKey(), mPrefixSize) == 0);
LS_DCHECK(key.size() >= mPrefixSize && bcmp(key.data(), LowerFenceAddr(), mPrefixSize) == 0);

// the compared key has the same prefix
key.remove_prefix(mPrefixSize);
Expand All @@ -497,19 +518,19 @@ int16_t BTreeNode::LinearSearchWithBias(Slice key, uint16_t startPos, bool highe
}

template <bool equalityOnly>
int16_t BTreeNode::LowerBound(Slice key, bool* isEqual) {
inline int16_t BTreeNode::LowerBound(Slice key, bool* isEqual) {
if (isEqual != nullptr && mIsLeaf) {
*isEqual = false;
}

// compare prefix firstly
if (equalityOnly) {
if ((key.size() < mPrefixSize) || (bcmp(key.data(), GetLowerFenceKey(), mPrefixSize) != 0)) {
if ((key.size() < mPrefixSize) || (bcmp(key.data(), LowerFenceAddr(), mPrefixSize) != 0)) {
return -1;
}
} else if (mPrefixSize != 0) {
Slice keyPrefix(key.data(), std::min<uint16_t>(key.size(), mPrefixSize));
Slice lowerFencePrefix(GetLowerFenceKey(), mPrefixSize);
Slice lowerFencePrefix(LowerFenceAddr(), mPrefixSize);
int cmpPrefix = CmpKeys(keyPrefix, lowerFencePrefix);
if (cmpPrefix < 0) {
return 0;
Expand Down Expand Up @@ -544,4 +565,17 @@ int16_t BTreeNode::LowerBound(Slice key, bool* isEqual) {
return equalityOnly ? -1 : lower;
}

inline void BTreeNode::setFences(Slice lowerKey, Slice upperKey) {
InsertFence(mLowerFence, lowerKey);
InsertFence(mUpperFence, upperKey);
LS_DCHECK(LowerFenceAddr() == nullptr || UpperFenceAddr() == nullptr ||
*LowerFenceAddr() <= *UpperFenceAddr());

// prefix compression
for (mPrefixSize = 0; (mPrefixSize < std::min(lowerKey.size(), upperKey.size())) &&
(lowerKey[mPrefixSize] == upperKey[mPrefixSize]);
mPrefixSize++)
;
}

} // namespace leanstore::storage::btree
4 changes: 2 additions & 2 deletions include/leanstore/btree/core/PessimisticIterator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ class PessimisticIterator : public Iterator {
if (mBuffer.size() < mFenceSize) {
mBuffer.resize(mFenceSize, 0);
}
std::memcpy(mBuffer.data(), mGuardedLeaf->GetUpperFenceKey(),
std::memcpy(mBuffer.data(), mGuardedLeaf->UpperFenceAddr(),
mGuardedLeaf->mUpperFence.mLength);
mBuffer[mFenceSize - 1] = 0;
}
Expand Down Expand Up @@ -446,7 +446,7 @@ inline void PessimisticIterator::Prev() {
if (mBuffer.size() < mFenceSize) {
mBuffer.resize(mFenceSize, 0);
}
std::memcpy(&mBuffer[0], mGuardedLeaf->GetLowerFenceKey(), mFenceSize);
std::memcpy(&mBuffer[0], mGuardedLeaf->LowerFenceAddr(), mFenceSize);

// callback before exiting current leaf
if (mFuncExitLeaf != nullptr) {
Expand Down
Loading

0 comments on commit 0a8e7cd

Please sign in to comment.