haiku_loader: Reuse BPlusTree implementation of the BFS add-on.

Instead of having an almost exact, albeit read only, duplicate of the
implementation.
This commit is contained in:
Michael Lotz
2013-05-02 23:26:03 +02:00
parent 6b65a838e0
commit 1a5e87cc64
6 changed files with 149 additions and 1272 deletions
@@ -12,9 +12,21 @@
#include "Debug.h" #include "Debug.h"
#include "BPlusTree.h" #include "BPlusTree.h"
#include "Inode.h"
#include "Utility.h" #include "Utility.h"
#if !_BOOT_MODE
#include "Inode.h"
#else
#include "Stream.h"
// BFS::Stream from the bootloader has the same API as Inode.
#define Inode BFS::Stream
#define strerror(x) "error message unavailable"
namespace BFS {
#endif
/*! Simple array used for the duplicate handling in the B+Tree. This is an /*! Simple array used for the duplicate handling in the B+Tree. This is an
on disk structure. on disk structure.
@@ -91,6 +103,7 @@ private:
#endif // DEBUG #endif // DEBUG
#if !_BOOT_MODE
class BitmapArray { class BitmapArray {
public: public:
BitmapArray(size_t numBits); BitmapArray(size_t numBits);
@@ -243,6 +256,7 @@ CachedNode::UnsetUnchanged(Transaction& transaction)
fNode = NULL; fNode = NULL;
} }
} }
#endif // !_BOOT_MODE
void void
@@ -252,6 +266,7 @@ CachedNode::Unset()
return; return;
if (fNode != NULL) { if (fNode != NULL) {
#if !_BOOT_MODE
if (fWritable && fOffset == 0) { if (fWritable && fOffset == 0) {
// The B+tree header has been updated - we need to update the // The B+tree header has been updated - we need to update the
// BPlusTrees copy of it, as well. // BPlusTrees copy of it, as well.
@@ -260,6 +275,8 @@ CachedNode::Unset()
block_cache_put(fTree->fStream->GetVolume()->BlockCache(), block_cache_put(fTree->fStream->GetVolume()->BlockCache(),
fBlockNumber); fBlockNumber);
#endif // !_BOOT_MODE
fNode = NULL; fNode = NULL;
} }
} }
@@ -308,6 +325,7 @@ CachedNode::SetTo(off_t offset, const bplustree_node** _node, bool check)
} }
#if !_BOOT_MODE
bplustree_node* bplustree_node*
CachedNode::SetToWritable(Transaction& transaction, off_t offset, bool check) CachedNode::SetToWritable(Transaction& transaction, off_t offset, bool check)
{ {
@@ -352,6 +370,7 @@ CachedNode::MakeWritable(Transaction& transaction)
return NULL; return NULL;
} }
#endif // !_BOOT_MODE
const bplustree_header* const bplustree_header*
@@ -369,6 +388,7 @@ CachedNode::SetToHeader()
} }
#if !_BOOT_MODE
bplustree_header* bplustree_header*
CachedNode::SetToWritableHeader(Transaction& transaction) CachedNode::SetToWritableHeader(Transaction& transaction)
{ {
@@ -393,6 +413,7 @@ CachedNode::SetToWritableHeader(Transaction& transaction)
return (bplustree_header*)fNode; return (bplustree_header*)fNode;
} }
#endif // !_BOOT_MODE
bplustree_node* bplustree_node*
@@ -405,12 +426,18 @@ CachedNode::InternalSetTo(Transaction* transaction, off_t offset)
block_run run; block_run run;
if (offset < fTree->fStream->Size() if (offset < fTree->fStream->Size()
&& fTree->fStream->FindBlockRun(offset, run, fileOffset) == B_OK) { && fTree->fStream->FindBlockRun(offset, run, fileOffset) == B_OK) {
#if !_BOOT_MODE
Volume* volume = fTree->fStream->GetVolume(); Volume* volume = fTree->fStream->GetVolume();
#else
Volume* volume = &fTree->fStream->GetVolume();
#endif
int32 blockOffset = (offset - fileOffset) / volume->BlockSize(); int32 blockOffset = (offset - fileOffset) / volume->BlockSize();
fBlockNumber = volume->ToBlock(run) + blockOffset; fBlockNumber = volume->ToBlock(run) + blockOffset;
uint8* block; uint8* block = NULL;
#if !_BOOT_MODE
if (transaction != NULL) { if (transaction != NULL) {
block = (uint8*)block_cache_get_writable(volume->BlockCache(), block = (uint8*)block_cache_get_writable(volume->BlockCache(),
fBlockNumber, transaction->ID()); fBlockNumber, transaction->ID());
@@ -419,6 +446,20 @@ CachedNode::InternalSetTo(Transaction* transaction, off_t offset)
block = (uint8*)block_cache_get(volume->BlockCache(), fBlockNumber); block = (uint8*)block_cache_get(volume->BlockCache(), fBlockNumber);
fWritable = false; fWritable = false;
} }
#else // !_BOOT_MODE
if (fBlock == NULL) {
fBlock = (uint8*)malloc(volume->BlockSize());
if (fBlock == NULL)
return NULL;
}
if (read_pos(volume->Device(), fBlockNumber << volume->BlockShift(),
fBlock, volume->BlockSize()) == (ssize_t)volume->BlockSize()) {
block = fBlock;
}
fWritable = false;
#endif // _BOOT_MODE
if (block != NULL) { if (block != NULL) {
// The node is somewhere in that block... // The node is somewhere in that block...
@@ -432,6 +473,7 @@ CachedNode::InternalSetTo(Transaction* transaction, off_t offset)
} }
#if !_BOOT_MODE
status_t status_t
CachedNode::Free(Transaction& transaction, off_t offset) CachedNode::Free(Transaction& transaction, off_t offset)
{ {
@@ -542,6 +584,7 @@ BPlusTree::BPlusTree(Transaction& transaction, Inode* stream, int32 nodeSize)
mutex_init(&fIteratorLock, "bfs b+tree iterator"); mutex_init(&fIteratorLock, "bfs b+tree iterator");
SetTo(transaction, stream); SetTo(transaction, stream);
} }
#endif // !_BOOT_MODE
BPlusTree::BPlusTree(Inode* stream) BPlusTree::BPlusTree(Inode* stream)
@@ -549,7 +592,10 @@ BPlusTree::BPlusTree(Inode* stream)
fStream(NULL), fStream(NULL),
fInTransaction(false) fInTransaction(false)
{ {
#if !_BOOT_MODE
mutex_init(&fIteratorLock, "bfs b+tree iterator"); mutex_init(&fIteratorLock, "bfs b+tree iterator");
#endif
SetTo(stream); SetTo(stream);
} }
@@ -562,12 +608,15 @@ BPlusTree::BPlusTree()
fInTransaction(false), fInTransaction(false),
fStatus(B_NO_INIT) fStatus(B_NO_INIT)
{ {
#if !_BOOT_MODE
mutex_init(&fIteratorLock, "bfs b+tree iterator"); mutex_init(&fIteratorLock, "bfs b+tree iterator");
#endif
} }
BPlusTree::~BPlusTree() BPlusTree::~BPlusTree()
{ {
#if !_BOOT_MODE
// if there are any TreeIterators left, we need to stop them // if there are any TreeIterators left, we need to stop them
// (can happen when the tree's inode gets deleted while // (can happen when the tree's inode gets deleted while
// traversing the tree - a TreeIterator doesn't lock the inode) // traversing the tree - a TreeIterator doesn't lock the inode)
@@ -581,9 +630,11 @@ BPlusTree::~BPlusTree()
mutex_destroy(&fIteratorLock); mutex_destroy(&fIteratorLock);
ASSERT(!fInTransaction); ASSERT(!fInTransaction);
#endif // !_BOOT_MODE
} }
#if !_BOOT_MODE
/*! Create a new B+Tree on the specified stream */ /*! Create a new B+Tree on the specified stream */
status_t status_t
BPlusTree::SetTo(Transaction& transaction, Inode* stream, int32 nodeSize) BPlusTree::SetTo(Transaction& transaction, Inode* stream, int32 nodeSize)
@@ -631,6 +682,7 @@ BPlusTree::SetTo(Transaction& transaction, Inode* stream, int32 nodeSize)
return fStatus = B_OK; return fStatus = B_OK;
} }
#endif // !_BOOT_MODE
status_t status_t
@@ -703,6 +755,7 @@ BPlusTree::InitCheck()
} }
#if !_BOOT_MODE
status_t status_t
BPlusTree::Validate(bool repair, bool& _errorsFound) BPlusTree::Validate(bool repair, bool& _errorsFound)
{ {
@@ -885,11 +938,13 @@ BPlusTree::ModeToKeyType(mode_t mode)
return BPLUSTREE_STRING_TYPE; return BPLUSTREE_STRING_TYPE;
} }
} }
#endif // !_BOOT_MODE
// #pragma mark - TransactionListener implementation // #pragma mark - TransactionListener implementation
#if !_BOOT_MODE
void void
BPlusTree::TransactionDone(bool success) BPlusTree::TransactionDone(bool success)
{ {
@@ -946,6 +1001,7 @@ BPlusTree::_RemoveIterator(TreeIterator* iterator)
MutexLocker _(fIteratorLock); MutexLocker _(fIteratorLock);
fIterators.Remove(iterator); fIterators.Remove(iterator);
} }
#endif // !_BOOT_MODE
int32 int32
@@ -1008,7 +1064,9 @@ BPlusTree::_FindKey(const bplustree_node* node, const uint8* key,
if (searchKey + searchLength + sizeof(off_t) + sizeof(uint16) if (searchKey + searchLength + sizeof(off_t) + sizeof(uint16)
> (uint8*)node + fNodeSize > (uint8*)node + fNodeSize
|| searchLength > BPLUSTREE_MAX_KEY_LENGTH) { || searchLength > BPLUSTREE_MAX_KEY_LENGTH) {
#if !_BOOT_MODE
fStream->GetVolume()->Panic(); fStream->GetVolume()->Panic();
#endif
RETURN_ERROR(B_BAD_DATA); RETURN_ERROR(B_BAD_DATA);
} }
@@ -1039,6 +1097,7 @@ BPlusTree::_FindKey(const bplustree_node* node, const uint8* key,
} }
#if !_BOOT_MODE
/*! Prepares the stack to contain all nodes that were passed while /*! Prepares the stack to contain all nodes that were passed while
following the key, from the root node to the leaf node that could following the key, from the root node to the leaf node that could
or should contain that key. or should contain that key.
@@ -2198,6 +2257,7 @@ BPlusTree::Replace(Transaction& transaction, const uint8* key, uint16 keyLength,
} }
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
} }
#endif // !_BOOT_MODE
/*! Searches the key in the tree, and stores the offset found in _value, /*! Searches the key in the tree, and stores the offset found in _value,
@@ -2222,7 +2282,9 @@ BPlusTree::Find(const uint8* key, uint16 keyLength, off_t* _value)
if (fAllowDuplicates) if (fAllowDuplicates)
RETURN_ERROR(B_BAD_TYPE); RETURN_ERROR(B_BAD_TYPE);
#if !_BOOT_MODE
ASSERT_READ_LOCKED_INODE(fStream); ASSERT_READ_LOCKED_INODE(fStream);
#endif
off_t nodeOffset = fHeader.RootNode(); off_t nodeOffset = fHeader.RootNode();
CachedNode cached(this); CachedNode cached(this);
@@ -2261,6 +2323,7 @@ BPlusTree::Find(const uint8* key, uint16 keyLength, off_t* _value)
} }
#if !_BOOT_MODE
status_t status_t
BPlusTree::_ValidateChildren(TreeCheck& check, uint32 level, off_t offset, BPlusTree::_ValidateChildren(TreeCheck& check, uint32 level, off_t offset,
const uint8* largestKey, uint16 largestKeyLength, const uint8* largestKey, uint16 largestKeyLength,
@@ -2484,6 +2547,7 @@ BPlusTree::_ValidateChild(TreeCheck& check, CachedNode& cached, uint32 level,
return _ValidateChildren(check, level + 1, offset, key, keyLength, node); return _ValidateChildren(check, level + 1, offset, key, keyLength, node);
} }
#endif // !_BOOT_MODE
// #pragma mark - // #pragma mark -
@@ -2494,14 +2558,18 @@ TreeIterator::TreeIterator(BPlusTree* tree)
fTree(tree), fTree(tree),
fCurrentNodeOffset(BPLUSTREE_NULL) fCurrentNodeOffset(BPLUSTREE_NULL)
{ {
#if !_BOOT_MODE
tree->_AddIterator(this); tree->_AddIterator(this);
#endif
} }
TreeIterator::~TreeIterator() TreeIterator::~TreeIterator()
{ {
#if !_BOOT_MODE
if (fTree) if (fTree)
fTree->_RemoveIterator(this); fTree->_RemoveIterator(this);
#endif
} }
@@ -2511,8 +2579,10 @@ TreeIterator::Goto(int8 to)
if (fTree == NULL || fTree->fStream == NULL) if (fTree == NULL || fTree->fStream == NULL)
RETURN_ERROR(B_BAD_VALUE); RETURN_ERROR(B_BAD_VALUE);
#if !_BOOT_MODE
// lock access to stream // lock access to stream
InodeReadLocker locker(fTree->fStream); InodeReadLocker locker(fTree->fStream);
#endif
off_t nodeOffset = fTree->fHeader.RootNode(); off_t nodeOffset = fTree->fHeader.RootNode();
CachedNode cached(fTree); CachedNode cached(fTree);
@@ -2580,8 +2650,10 @@ TreeIterator::Traverse(int8 direction, void* key, uint16* keyLength,
if (fCurrentNodeOffset == BPLUSTREE_FREE) if (fCurrentNodeOffset == BPLUSTREE_FREE)
return B_ENTRY_NOT_FOUND; return B_ENTRY_NOT_FOUND;
#if !_BOOT_MODE
// lock access to stream // lock access to stream
InodeReadLocker locker(fTree->fStream); InodeReadLocker locker(fTree->fStream);
#endif
CachedNode cached(fTree); CachedNode cached(fTree);
const bplustree_node* node; const bplustree_node* node;
@@ -2661,7 +2733,9 @@ TreeIterator::Traverse(int8 direction, void* key, uint16* keyLength,
if (keyStart + length + sizeof(off_t) + sizeof(uint16) if (keyStart + length + sizeof(off_t) + sizeof(uint16)
> (uint8*)node + fTree->fNodeSize > (uint8*)node + fTree->fNodeSize
|| length > BPLUSTREE_MAX_KEY_LENGTH) { || length > BPLUSTREE_MAX_KEY_LENGTH) {
#if !_BOOT_MODE
fTree->fStream->GetVolume()->Panic(); fTree->fStream->GetVolume()->Panic();
#endif
RETURN_ERROR(B_BAD_DATA); RETURN_ERROR(B_BAD_DATA);
} }
@@ -2729,8 +2803,10 @@ TreeIterator::Find(const uint8* key, uint16 keyLength)
|| key == NULL) || key == NULL)
RETURN_ERROR(B_BAD_VALUE); RETURN_ERROR(B_BAD_VALUE);
#if !_BOOT_MODE
// lock access to stream // lock access to stream
InodeReadLocker locker(fTree->fStream); InodeReadLocker locker(fTree->fStream);
#endif
off_t nodeOffset = fTree->fHeader.RootNode(); off_t nodeOffset = fTree->fHeader.RootNode();
@@ -2932,6 +3008,7 @@ bplustree_node::CheckIntegrity(uint32 nodeSize) const
// #pragma mark - // #pragma mark -
#if !_BOOT_MODE
BitmapArray::BitmapArray(size_t numBits) BitmapArray::BitmapArray(size_t numBits)
{ {
fSize = (numBits + 7) / 8; fSize = (numBits + 7) / 8;
@@ -2979,6 +3056,7 @@ BitmapArray::Set(size_t index, bool set)
fCountSet--; fCountSet--;
} }
} }
#endif // !_BOOT_MODE
// #pragma mark - // #pragma mark -
@@ -3134,3 +3212,7 @@ compareKeys(type_code type, const void* key1, int keyLength1,
return -1; return -1;
} }
#if _BOOT_MODE
} // namespace BFS
#endif
@@ -7,8 +7,21 @@
#include "bfs.h" #include "bfs.h"
#include "Journal.h"
#include "Utility.h"
#if !_BOOT_MODE
#include "Journal.h"
class Inode;
#else
#define Inode BFS::Stream
namespace BFS {
class Stream;
class Transaction;
class TransactionListener {};
#endif // _BOOT_MODE
// #pragma mark - on-disk structures // #pragma mark - on-disk structures
@@ -132,18 +145,21 @@ enum bplustree_traversing {
// #pragma mark - in-memory structures // #pragma mark - in-memory structures
template<class T> class Stack;
class BPlusTree; class BPlusTree;
class TreeIterator;
class CachedNode;
class Inode;
struct TreeCheck; struct TreeCheck;
class TreeIterator;
#if !_BOOT_MODE
template<class T> class Stack;
// needed for searching (utilizing a stack) // needed for searching (utilizing a stack)
struct node_and_key { struct node_and_key {
off_t nodeOffset; off_t nodeOffset;
uint16 keyIndex; uint16 keyIndex;
}; };
#endif // !_BOOT_MODE
class CachedNode { class CachedNode {
@@ -153,6 +169,9 @@ public:
fTree(tree), fTree(tree),
fNode(NULL) fNode(NULL)
{ {
#if _BOOT_MODE
fBlock = NULL;
#endif
} }
CachedNode(BPlusTree* tree, off_t offset, bool check = true) CachedNode(BPlusTree* tree, off_t offset, bool check = true)
@@ -160,30 +179,40 @@ public:
fTree(tree), fTree(tree),
fNode(NULL) fNode(NULL)
{ {
#if _BOOT_MODE
fBlock = NULL;
#endif
SetTo(offset, check); SetTo(offset, check);
} }
~CachedNode() ~CachedNode()
{ {
Unset(); Unset();
#if _BOOT_MODE
free(fBlock);
#endif
} }
const bplustree_node* SetTo(off_t offset, bool check = true); const bplustree_node* SetTo(off_t offset, bool check = true);
status_t SetTo(off_t offset, status_t SetTo(off_t offset,
const bplustree_node** _node, const bplustree_node** _node,
bool check = true); bool check = true);
const bplustree_header* SetToHeader();
void Unset();
#if !_BOOT_MODE
bplustree_node* SetToWritable(Transaction& transaction, bplustree_node* SetToWritable(Transaction& transaction,
off_t offset, bool check = true); off_t offset, bool check = true);
bplustree_node* MakeWritable(Transaction& transaction); bplustree_node* MakeWritable(Transaction& transaction);
const bplustree_header* SetToHeader();
bplustree_header* SetToWritableHeader(Transaction& transaction); bplustree_header* SetToWritableHeader(Transaction& transaction);
void UnsetUnchanged(Transaction& transaction); void UnsetUnchanged(Transaction& transaction);
void Unset();
status_t Free(Transaction& transaction, off_t offset); status_t Free(Transaction& transaction, off_t offset);
status_t Allocate(Transaction& transaction, status_t Allocate(Transaction& transaction,
bplustree_node** _node, off_t* _offset); bplustree_node** _node, off_t* _offset);
#endif // !_BOOT_MODE
bool IsWritable() const { return fWritable; } bool IsWritable() const { return fWritable; }
bplustree_node* Node() const { return fNode; } bplustree_node* Node() const { return fNode; }
@@ -197,20 +226,27 @@ protected:
off_t fOffset; off_t fOffset;
off_t fBlockNumber; off_t fBlockNumber;
bool fWritable; bool fWritable;
#if _BOOT_MODE
uint8* fBlock;
#endif
}; };
class BPlusTree : public TransactionListener { class BPlusTree : public TransactionListener {
public: public:
#if !_BOOT_MODE
BPlusTree(Transaction& transaction, BPlusTree(Transaction& transaction,
Inode* stream, Inode* stream,
int32 nodeSize = BPLUSTREE_NODE_SIZE); int32 nodeSize = BPLUSTREE_NODE_SIZE);
#endif
BPlusTree(Inode* stream); BPlusTree(Inode* stream);
BPlusTree(); BPlusTree();
~BPlusTree(); ~BPlusTree();
#if !_BOOT_MODE
status_t SetTo(Transaction& transaction, Inode* stream, status_t SetTo(Transaction& transaction, Inode* stream,
int32 nodeSize = BPLUSTREE_NODE_SIZE); int32 nodeSize = BPLUSTREE_NODE_SIZE);
#endif
status_t SetTo(Inode* stream); status_t SetTo(Inode* stream);
status_t SetStream(Inode* stream); status_t SetStream(Inode* stream);
@@ -219,6 +255,7 @@ public:
size_t NodeSize() const { return fNodeSize; } size_t NodeSize() const { return fNodeSize; }
Inode* Stream() const { return fStream; } Inode* Stream() const { return fStream; }
#if !_BOOT_MODE
status_t Validate(bool repair, bool& _errorsFound); status_t Validate(bool repair, bool& _errorsFound);
status_t MakeEmpty(); status_t MakeEmpty();
@@ -249,15 +286,19 @@ public:
status_t Replace(Transaction& transaction, status_t Replace(Transaction& transaction,
const uint8* key, uint16 keyLength, const uint8* key, uint16 keyLength,
off_t value); off_t value);
#endif // !_BOOT_MODE
status_t Find(const uint8* key, uint16 keyLength, status_t Find(const uint8* key, uint16 keyLength,
off_t* value); off_t* value);
#if !_BOOT_MODE
static int32 TypeCodeToKeyType(type_code code); static int32 TypeCodeToKeyType(type_code code);
static int32 ModeToKeyType(mode_t mode); static int32 ModeToKeyType(mode_t mode);
protected: protected:
virtual void TransactionDone(bool success); virtual void TransactionDone(bool success);
virtual void RemovedFromTransaction(); virtual void RemovedFromTransaction();
#endif // !_BOOT_MODE
private: private:
BPlusTree(const BPlusTree& other); BPlusTree(const BPlusTree& other);
@@ -269,6 +310,7 @@ private:
status_t _FindKey(const bplustree_node* node, status_t _FindKey(const bplustree_node* node,
const uint8* key, uint16 keyLength, const uint8* key, uint16 keyLength,
uint16* index = NULL, off_t* next = NULL); uint16* index = NULL, off_t* next = NULL);
#if !_BOOT_MODE
status_t _SeekDown(Stack<node_and_key>& stack, status_t _SeekDown(Stack<node_and_key>& stack,
const uint8* key, uint16 keyLength); const uint8* key, uint16 keyLength);
@@ -310,6 +352,7 @@ private:
off_t offset, off_t lastOffset, off_t offset, off_t lastOffset,
off_t nextOffset, const uint8* key, off_t nextOffset, const uint8* key,
uint16 keyLength); uint16 keyLength);
#endif // !_BOOT_MODE
private: private:
friend class TreeIterator; friend class TreeIterator;
@@ -322,8 +365,11 @@ private:
bool fAllowDuplicates; bool fAllowDuplicates;
bool fInTransaction; bool fInTransaction;
status_t fStatus; status_t fStatus;
#if !_BOOT_MODE
mutex fIteratorLock; mutex fIteratorLock;
SinglyLinkedList<TreeIterator> fIterators; SinglyLinkedList<TreeIterator> fIterators;
#endif
}; };
@@ -385,6 +431,7 @@ private:
// (most of them may not be needed) // (most of them may not be needed)
#if !_BOOT_MODE
inline status_t inline status_t
BPlusTree::Remove(Transaction& transaction, const char* key, off_t value) BPlusTree::Remove(Transaction& transaction, const char* key, off_t value)
{ {
@@ -455,6 +502,7 @@ BPlusTree::Insert(Transaction& transaction, double key, off_t value)
return B_BAD_TYPE; return B_BAD_TYPE;
return Insert(transaction, (uint8*)&key, sizeof(key), value); return Insert(transaction, (uint8*)&key, sizeof(key), value);
} }
#endif // !_BOOT_MODE
// #pragma mark - TreeIterator inline functions // #pragma mark - TreeIterator inline functions
@@ -606,4 +654,10 @@ bplustree_node::MaxFragments(uint32 nodeSize)
} }
#if _BOOT_MODE
} // namespace BFS
#undef Inode
#endif
#endif // B_PLUS_TREE_H #endif // B_PLUS_TREE_H
@@ -1,889 +0,0 @@
/*
* Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*
* Roughly based on 'btlib' written by Marcus J. Ranum - it shares
* no code but achieves binary compatibility with the on disk format.
*/
#include "BPlusTree.h"
#include <boot/platform.h>
#include <util/kernel_cpp.h>
#include <util/Stack.h>
#include <TypeConstants.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
using namespace BFS;
// Node Caching for the BPlusTree class
//
// With write support, there is the need for a function that allocates new
// nodes by either returning empty nodes, or by growing the file's data stream
//
// !! The CachedNode class assumes that you have properly locked the stream
// !! before asking for nodes.
//
// Note: This code will fail if the block size is smaller than the node size!
// Since BFS supports block sizes of 1024 bytes or greater, and the node size
// is hard-coded to 1024 bytes, that's not an issue now.
void
CachedNode::Unset()
{
if (fTree == NULL || fTree->fStream == NULL)
return;
if (fBlock != NULL)
fNode = NULL;
}
bplustree_node *
CachedNode::SetTo(off_t offset, bool check)
{
if (fTree == NULL || fTree->fStream == NULL)
return NULL;
Unset();
// You can only ask for nodes at valid positions - you can't
// even access the b+tree header with this method (use SetToHeader()
// instead)
if (offset > fTree->fHeader->MaximumSize() - fTree->fNodeSize
|| offset <= 0
|| (offset % fTree->fNodeSize) != 0)
return NULL;
if (InternalSetTo(offset) != NULL && check) {
// sanity checks (links, all_key_count)
bplustree_header *header = fTree->fHeader;
if (!header->IsValidLink(fNode->LeftLink())
|| !header->IsValidLink(fNode->RightLink())
|| !header->IsValidLink(fNode->OverflowLink())
|| (int8 *)fNode->Values() + fNode->NumKeys() * sizeof(off_t)
> (int8 *)fNode + fTree->fNodeSize) {
dprintf("invalid node read from offset %Ld, inode at %Ld\n",
offset, fTree->fStream->ID());
return NULL;
}
}
return fNode;
}
bplustree_header *
CachedNode::SetToHeader()
{
if (fTree == NULL || fTree->fStream == NULL)
return NULL;
Unset();
InternalSetTo(0LL);
return (bplustree_header *)fNode;
}
bplustree_node *
CachedNode::InternalSetTo(off_t offset)
{
fNode = NULL;
off_t fileOffset;
block_run run;
if (offset < fTree->fStream->Size()
&& fTree->fStream->FindBlockRun(offset, run, fileOffset) == B_OK) {
Volume &volume = fTree->fStream->GetVolume();
int32 blockOffset = (offset - fileOffset) / volume.BlockSize();
fBlockNumber = volume.ToBlock(run) + blockOffset;
if (fBlock == NULL) {
fBlock = (uint8 *)malloc(volume.BlockSize());
if (fBlock == NULL)
return NULL;
}
if (read_pos(volume.Device(), fBlockNumber << volume.BlockShift(),
fBlock, volume.BlockSize()) < (ssize_t)volume.BlockSize())
return NULL;
// the node is somewhere in that block... (confusing offset calculation)
fNode = (bplustree_node *)(fBlock + offset -
(fileOffset + (blockOffset << volume.BlockShift())));
}
return fNode;
}
// #pragma mark -
BPlusTree::BPlusTree(Stream *stream)
:
fStream(NULL),
fHeader(NULL),
fCachedHeader(this)
{
SetTo(stream);
}
BPlusTree::~BPlusTree()
{
}
status_t
BPlusTree::SetTo(Stream *stream)
{
if (stream == NULL || stream->InitCheck() != B_OK)
return fStatus = B_BAD_VALUE;
// get on-disk B+Tree header
fCachedHeader.Unset();
fStream = stream;
fHeader = fCachedHeader.SetToHeader();
if (fHeader == NULL)
return fStatus = B_NO_INIT;
// is header valid?
if (fHeader->Magic() != BPLUSTREE_MAGIC
|| fHeader->MaximumSize() != stream->Size()
|| (fHeader->RootNode() % fHeader->NodeSize()) != 0
|| !fHeader->IsValidLink(fHeader->RootNode()))
return fStatus = B_BAD_DATA;
fNodeSize = fHeader->NodeSize();
{
uint32 toMode[] = {S_STR_INDEX, S_INT_INDEX, S_UINT_INDEX,
S_LONG_LONG_INDEX, S_ULONG_LONG_INDEX, S_FLOAT_INDEX,
S_DOUBLE_INDEX};
uint32 mode = stream->Mode() & (S_STR_INDEX | S_INT_INDEX
| S_UINT_INDEX | S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX
| S_FLOAT_INDEX | S_DOUBLE_INDEX);
if (fHeader->DataType() > BPLUSTREE_DOUBLE_TYPE
|| ((stream->Mode() & S_INDEX_DIR) != 0
&& toMode[fHeader->DataType()] != mode)
|| !stream->IsContainer()) {
return fStatus = B_BAD_TYPE;
}
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
// although it's in stat.h, the S_ALLOW_DUPS flag is obviously unused
// in the original BFS code - we will honour it nevertheless
fAllowDuplicates = ((stream->Mode() & S_INDEX_DIR) == S_INDEX_DIR
&& stream->BlockRun() != stream->Parent())
|| (stream->Mode() & S_ALLOW_DUPS) != 0;
#endif
}
CachedNode cached(this, fHeader->RootNode());
return fStatus = cached.Node() ? B_OK : B_BAD_DATA;
}
status_t
BPlusTree::InitCheck()
{
return fStatus;
}
int32
BPlusTree::TypeCodeToKeyType(type_code code)
{
switch (code) {
case B_STRING_TYPE:
return BPLUSTREE_STRING_TYPE;
case B_INT32_TYPE:
case B_SSIZE_T_TYPE:
return BPLUSTREE_INT32_TYPE;
case B_UINT32_TYPE:
case B_SIZE_T_TYPE:
return BPLUSTREE_UINT32_TYPE;
case B_OFF_T_TYPE:
case B_INT64_TYPE:
return BPLUSTREE_INT64_TYPE;
case B_UINT64_TYPE:
return BPLUSTREE_UINT64_TYPE;
case B_FLOAT_TYPE:
return BPLUSTREE_FLOAT_TYPE;
case B_DOUBLE_TYPE:
return BPLUSTREE_DOUBLE_TYPE;
}
return -1;
}
int32
BPlusTree::ModeToKeyType(mode_t mode)
{
switch (mode & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX
| S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX)) {
case S_INT_INDEX:
return BPLUSTREE_INT32_TYPE;
case S_UINT_INDEX:
return BPLUSTREE_UINT32_TYPE;
case S_LONG_LONG_INDEX:
return BPLUSTREE_INT64_TYPE;
case S_ULONG_LONG_INDEX:
return BPLUSTREE_UINT64_TYPE;
case S_FLOAT_INDEX:
return BPLUSTREE_FLOAT_TYPE;
case S_DOUBLE_INDEX:
return BPLUSTREE_DOUBLE_TYPE;
case S_STR_INDEX:
default:
// default is for standard directories
return BPLUSTREE_STRING_TYPE;
}
}
int32
BPlusTree::CompareKeys(const void *key1, int keyLength1, const void *key2, int keyLength2)
{
type_code type = 0;
switch (fHeader->DataType())
{
case BPLUSTREE_STRING_TYPE:
type = B_STRING_TYPE;
break;
case BPLUSTREE_INT32_TYPE:
type = B_INT32_TYPE;
break;
case BPLUSTREE_UINT32_TYPE:
type = B_UINT32_TYPE;
break;
case BPLUSTREE_INT64_TYPE:
type = B_INT64_TYPE;
break;
case BPLUSTREE_UINT64_TYPE:
type = B_UINT64_TYPE;
break;
case BPLUSTREE_FLOAT_TYPE:
type = B_FLOAT_TYPE;
break;
case BPLUSTREE_DOUBLE_TYPE:
type = B_DOUBLE_TYPE;
break;
}
return compareKeys(type, key1, keyLength1, key2, keyLength2);
}
status_t
BPlusTree::FindKey(bplustree_node *node, const uint8 *key, uint16 keyLength, uint16 *index,
off_t *next)
{
if (node->all_key_count == 0) {
if (index)
*index = 0;
if (next)
*next = node->OverflowLink();
return B_ENTRY_NOT_FOUND;
}
off_t *values = node->Values();
int16 saveIndex = -1;
// binary search in the key array
for (int16 first = 0, last = node->NumKeys() - 1; first <= last;) {
uint16 i = (first + last) >> 1;
uint16 searchLength;
uint8 *searchKey = node->KeyAt(i, &searchLength);
if (searchKey + searchLength + sizeof(off_t) + sizeof(uint16) > (uint8 *)node + fNodeSize
|| searchLength > BPLUSTREE_MAX_KEY_LENGTH) {
dprintf("bfs: bad B+tree data\n");
return B_BAD_DATA;
}
int32 cmp = CompareKeys(key, keyLength, searchKey, searchLength);
if (cmp < 0) {
last = i - 1;
saveIndex = i;
} else if (cmp > 0) {
saveIndex = first = i + 1;
} else {
if (index)
*index = i;
if (next)
*next = BFS_ENDIAN_TO_HOST_INT64(values[i]);
return B_OK;
}
}
if (index)
*index = saveIndex;
if (next) {
if (saveIndex == node->NumKeys())
*next = node->OverflowLink();
else
*next = BFS_ENDIAN_TO_HOST_INT64(values[saveIndex]);
}
return B_ENTRY_NOT_FOUND;
}
/** Prepares the stack to contain all nodes that were passed while
* following the key, from the root node to the leaf node that could
* or should contain that key.
*/
status_t
BPlusTree::SeekDown(Stack<node_and_key> &stack, const uint8 *key, uint16 keyLength)
{
// set the root node to begin with
node_and_key nodeAndKey;
nodeAndKey.nodeOffset = fHeader->RootNode();
CachedNode cached(this);
bplustree_node *node;
while ((node = cached.SetTo(nodeAndKey.nodeOffset)) != NULL) {
// if we are already on leaf level, we're done
if (node->OverflowLink() == BPLUSTREE_NULL) {
// node that the keyIndex is not properly set here (but it's not
// needed in the calling functions anyway)!
nodeAndKey.keyIndex = 0;
stack.Push(nodeAndKey);
return B_OK;
}
off_t nextOffset;
status_t status = FindKey(node, key, keyLength, &nodeAndKey.keyIndex, &nextOffset);
if (status == B_ENTRY_NOT_FOUND && nextOffset == nodeAndKey.nodeOffset)
return B_ERROR;
// put the node offset & the correct keyIndex on the stack
stack.Push(nodeAndKey);
nodeAndKey.nodeOffset = nextOffset;
}
return B_ERROR;
}
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
status_t
BPlusTree::FindFreeDuplicateFragment(bplustree_node *node, CachedNode *cached, off_t *_offset,
bplustree_node **_fragment, uint32 *_index)
{
off_t *values = node->Values();
for (int32 i = 0;i < node->all_key_count;i++) {
// does the value link to a duplicate fragment?
if (bplustree_node::LinkType(values[i]) != BPLUSTREE_DUPLICATE_FRAGMENT)
continue;
bplustree_node *fragment = cached->SetTo(bplustree_node::FragmentOffset(
BFS_ENDIAN_TO_HOST_INT64(values[i])), false);
if (fragment == NULL) {
FATAL(("Could not get duplicate fragment at %Ld\n",values[i]));
continue;
}
// see if there is some space left for us
int32 num = (fNodeSize >> 3) / (NUM_FRAGMENT_VALUES + 1);
for (int32 j = 0;j < num;j++) {
duplicate_array *array = fragment->FragmentAt(j);
if (array->count == 0) {
*_offset = bplustree_node::FragmentOffset(
BFS_ENDIAN_TO_HOST_INT64(values[i]));
*_fragment = fragment;
*_index = j;
return B_OK;
}
}
}
return B_ENTRY_NOT_FOUND;
}
#endif
/** Searches the key in the tree, and stores the offset found in
* _value, if successful.
* It's very similar to BPlusTree::SeekDown(), but doesn't fill
* a stack while it descends the tree.
* Returns B_OK when the key could be found, B_ENTRY_NOT_FOUND
* if not. It can also return other errors to indicate that
* something went wrong.
* Note that this doesn't work with duplicates - it will just
* return B_BAD_TYPE if you call this function on a tree where
* duplicates are allowed.
*/
status_t
BPlusTree::Find(const uint8 *key, uint16 keyLength, off_t *_value)
{
if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH
|| key == NULL)
return B_BAD_VALUE;
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
if (fAllowDuplicates)
return B_BAD_TYPE;
#endif
off_t nodeOffset = fHeader->RootNode();
CachedNode cached(this);
bplustree_node *node;
while ((node = cached.SetTo(nodeOffset)) != NULL) {
uint16 keyIndex = 0;
off_t nextOffset;
status_t status = FindKey(node, key, keyLength, &keyIndex, &nextOffset);
if (node->OverflowLink() == BPLUSTREE_NULL) {
if (status == B_OK && _value != NULL)
*_value = BFS_ENDIAN_TO_HOST_INT64(node->Values()[keyIndex]);
return status;
} else if (nextOffset == nodeOffset)
return B_ERROR;
nodeOffset = nextOffset;
}
return B_ERROR;
}
// #pragma mark -
TreeIterator::TreeIterator(BPlusTree *tree)
:
fTree(tree),
fCurrentNodeOffset(BPLUSTREE_NULL)
{
}
TreeIterator::~TreeIterator()
{
}
status_t
TreeIterator::Goto(int8 to)
{
if (fTree == NULL || fTree->fHeader == NULL)
return B_BAD_VALUE;
off_t nodeOffset = fTree->fHeader->RootNode();
CachedNode cached(fTree);
bplustree_node *node;
while ((node = cached.SetTo(nodeOffset)) != NULL) {
// is the node a leaf node?
if (node->OverflowLink() == BPLUSTREE_NULL) {
fCurrentNodeOffset = nodeOffset;
fCurrentKey = to == BPLUSTREE_BEGIN ? -1 : node->NumKeys();
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
fDuplicateNode = BPLUSTREE_NULL;
#endif
return B_OK;
}
// get the next node offset depending on the direction (and if there
// are any keys in that node at all)
off_t nextOffset;
if (to == BPLUSTREE_END || node->all_key_count == 0)
nextOffset = node->OverflowLink();
else {
if (node->AllKeyLength() > fTree->fNodeSize
|| (uint32)node->Values() > (uint32)node + fTree->fNodeSize - 8 * node->NumKeys())
return B_ERROR;
nextOffset = BFS_ENDIAN_TO_HOST_INT64(node->Values()[0]);
}
if (nextOffset == nodeOffset)
break;
nodeOffset = nextOffset;
}
return B_ERROR;
}
/** Iterates through the tree in the specified direction.
* When it iterates through duplicates, the "key" is only updated for the
* first entry - if you need to know when this happens, use the "duplicate"
* parameter which is 0 for no duplicate, 1 for the first, and 2 for all
* the other duplicates.
* That's not too nice, but saves the 256 bytes that would be needed to
* store the last key - if this will ever become an issue, it will be
* easy to change.
* The other advantage of this is, that the queries can skip all duplicates
* at once when they are not relevant to them.
*/
status_t
TreeIterator::Traverse(int8 direction, void *key, uint16 *keyLength, uint16 maxLength,
off_t *value, uint16 *duplicate)
{
if (fTree == NULL)
return B_INTERRUPTED;
if (fCurrentNodeOffset == BPLUSTREE_NULL
&& Goto(direction == BPLUSTREE_FORWARD ? BPLUSTREE_BEGIN : BPLUSTREE_END) < B_OK)
return B_ERROR;
// if the tree was emptied since the last call
if (fCurrentNodeOffset == BPLUSTREE_FREE)
return B_ENTRY_NOT_FOUND;
CachedNode cached(fTree);
bplustree_node *node;
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
if (fDuplicateNode != BPLUSTREE_NULL) {
// regardless of traverse direction the duplicates are always presented
// in the same order; since they are all considered as equal, this
// shouldn't cause any problems
if (!fIsFragment || fDuplicate < fNumDuplicates) {
node = cached.SetTo(bplustree_node::FragmentOffset(fDuplicateNode),
false);
} else
node = NULL;
if (node != NULL) {
if (!fIsFragment && fDuplicate >= fNumDuplicates) {
// if the node is out of duplicates, we go directly to the next
// one
fDuplicateNode = node->right_link;
if (fDuplicateNode != BPLUSTREE_NULL
&& (node = cached.SetTo(fDuplicateNode, false)) != NULL) {
fNumDuplicates
= node->CountDuplicates(fDuplicateNode, false);
fDuplicate = 0;
}
}
if (fDuplicate < fNumDuplicates) {
*value = node->DuplicateAt(fDuplicateNode, fIsFragment,
fDuplicate++);
if (duplicate)
*duplicate = 2;
return B_OK;
}
}
fDuplicateNode = BPLUSTREE_NULL;
}
#endif /* BPLUSTREE_SUPPORTS_DUPLICATES */
off_t savedNodeOffset = fCurrentNodeOffset;
if ((node = cached.SetTo(fCurrentNodeOffset)) == NULL)
return B_ERROR;
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
if (duplicate)
*duplicate = 0;
#endif
fCurrentKey += direction;
// is the current key in the current node?
while ((direction == BPLUSTREE_FORWARD && fCurrentKey >= node->NumKeys())
|| (direction == BPLUSTREE_BACKWARD && fCurrentKey < 0))
{
fCurrentNodeOffset = direction == BPLUSTREE_FORWARD ? node->RightLink() : node->LeftLink();
// are there any more nodes?
if (fCurrentNodeOffset != BPLUSTREE_NULL)
{
node = cached.SetTo(fCurrentNodeOffset);
if (!node)
return B_ERROR;
// reset current key
fCurrentKey = direction == BPLUSTREE_FORWARD ? 0 : node->NumKeys();
}
else
{
// there are no nodes left, so turn back to the last key
fCurrentNodeOffset = savedNodeOffset;
fCurrentKey = direction == BPLUSTREE_FORWARD ? node->NumKeys() : -1;
return B_ENTRY_NOT_FOUND;
}
}
if (node->all_key_count == 0)
return B_ERROR; // B_ENTRY_NOT_FOUND ?
uint16 length;
uint8 *keyStart = node->KeyAt(fCurrentKey, &length);
if (keyStart + length + sizeof(off_t) + sizeof(uint16) > (uint8 *)node + fTree->fNodeSize
|| length > BPLUSTREE_MAX_KEY_LENGTH) {
dprintf("bfs: bad b+tree data\n");
return B_BAD_DATA;
}
length = min_c(length, maxLength);
memcpy(key, keyStart, length);
if (fTree->fHeader->DataType() == BPLUSTREE_STRING_TYPE) // terminate string type
{
if (length == maxLength)
length--;
((char *)key)[length] = '\0';
}
*keyLength = length;
off_t offset = BFS_ENDIAN_TO_HOST_INT64(node->Values()[fCurrentKey]);
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
// duplicate fragments?
uint8 type = bplustree_node::LinkType(offset);
if (type == BPLUSTREE_DUPLICATE_FRAGMENT || type == BPLUSTREE_DUPLICATE_NODE)
{
fDuplicateNode = offset;
node = cached.SetTo(bplustree_node::FragmentOffset(fDuplicateNode), false);
if (node == NULL)
return B_ERROR;
fIsFragment = type == BPLUSTREE_DUPLICATE_FRAGMENT;
fNumDuplicates = node->CountDuplicates(offset, fIsFragment);
if (fNumDuplicates)
{
offset = node->DuplicateAt(offset, fIsFragment, 0);
fDuplicate = 1;
if (duplicate)
*duplicate = 1;
}
else
{
// shouldn't happen, but we're dealing here with potentially corrupt disks...
fDuplicateNode = BPLUSTREE_NULL;
offset = 0;
}
}
#endif /* BPLUSTREE_SUPPORTS_DUPLICATES */
*value = offset;
return B_OK;
}
/** This is more or less a copy of BPlusTree::Find() - but it just
* sets the current position in the iterator, regardless of if the
* key could be found or not.
*/
status_t
TreeIterator::Find(const uint8 *key, uint16 keyLength)
{
if (fTree == NULL)
return B_INTERRUPTED;
if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH
|| key == NULL)
return B_BAD_VALUE;
off_t nodeOffset = fTree->fHeader->RootNode();
CachedNode cached(fTree);
bplustree_node *node;
while ((node = cached.SetTo(nodeOffset)) != NULL) {
uint16 keyIndex = 0;
off_t nextOffset;
status_t status = fTree->FindKey(node, key, keyLength, &keyIndex, &nextOffset);
if (node->OverflowLink() == BPLUSTREE_NULL) {
fCurrentNodeOffset = nodeOffset;
fCurrentKey = keyIndex - 1;
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
fDuplicateNode = BPLUSTREE_NULL;
#endif
return status;
} else if (nextOffset == nodeOffset)
return B_ERROR;
nodeOffset = nextOffset;
}
return B_ERROR;
}
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
void
TreeIterator::SkipDuplicates()
{
fDuplicateNode = BPLUSTREE_NULL;
}
#endif
// #pragma mark -
void
bplustree_node::Initialize()
{
left_link = right_link = overflow_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL);
all_key_count = 0;
all_key_length = 0;
}
uint8 *
bplustree_node::KeyAt(int32 index, uint16 *keyLength) const
{
if (index < 0 || index > NumKeys())
return NULL;
uint8 *keyStart = Keys();
uint16 *keyLengths = KeyLengths();
*keyLength = BFS_ENDIAN_TO_HOST_INT16(keyLengths[index])
- (index != 0 ? BFS_ENDIAN_TO_HOST_INT16(keyLengths[index - 1]) : 0);
if (index > 0)
keyStart += BFS_ENDIAN_TO_HOST_INT16(keyLengths[index - 1]);
return keyStart;
}
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
uint8
bplustree_node::CountDuplicates(off_t offset, bool isFragment) const
{
// the duplicate fragment handling is currently hard-coded to a node size
// of 1024 bytes - with future versions of BFS, this may be a problem
if (isFragment) {
uint32 fragment = (NUM_FRAGMENT_VALUES + 1) * ((uint64)offset & 0x3ff);
return ((off_t *)this)[fragment];
}
return OverflowLink();
}
off_t
bplustree_node::DuplicateAt(off_t offset, bool isFragment, int8 index) const
{
uint32 start;
if (isFragment)
start = 8 * ((uint64)offset & 0x3ff);
else
start = 2;
return ((off_t *)this)[start + 1 + index];
}
/** Although the name suggests it, this function doesn't return the real
* used fragment count; at least, it can only count to two: it returns
* 0, if there is no fragment used, 1 if there is only one fragment
* used, and 2 if there are at least 2 fragments used.
*/
int32
bplustree_node::FragmentsUsed(uint32 nodeSize)
{
uint32 used = 0;
for (uint32 i = 0; i < nodeSize / ((NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); i++) {
duplicate_array *array = FragmentAt(i);
if (array->count > 0 && ++used > 1)
return used;
}
return used;
}
#endif /* BPLUSTREE_SUPPORTS_DUPLICATES */
// #pragma mark -
int32
BFS::compareKeys(type_code type, const void *key1, int keyLength1, const void *key2, int keyLength2)
{
// if one of the keys is NULL, bail out gracefully
if (key1 == NULL || key2 == NULL)
return -1;
switch (type)
{
case B_STRING_TYPE:
{
int len = min_c(keyLength1, keyLength2);
int result = strncmp((const char *)key1, (const char *)key2, len);
if (result == 0
&& !(((const char *)key1)[len] == '\0' && ((const char *)key2)[len] == '\0'))
result = keyLength1 - keyLength2;
return result;
}
case B_SSIZE_T_TYPE:
case B_INT32_TYPE:
return *(int32 *)key1 - *(int32 *)key2;
case B_SIZE_T_TYPE:
case B_UINT32_TYPE:
if (*(uint32 *)key1 == *(uint32 *)key2)
return 0;
else if (*(uint32 *)key1 > *(uint32 *)key2)
return 1;
return -1;
case B_OFF_T_TYPE:
case B_INT64_TYPE:
if (*(int64 *)key1 == *(int64 *)key2)
return 0;
else if (*(int64 *)key1 > *(int64 *)key2)
return 1;
return -1;
case B_UINT64_TYPE:
if (*(uint64 *)key1 == *(uint64 *)key2)
return 0;
else if (*(uint64 *)key1 > *(uint64 *)key2)
return 1;
return -1;
case B_FLOAT_TYPE:
{
float result = *(float *)key1 - *(float *)key2;
if (result == 0.0f)
return 0;
return (result < 0.0f) ? -1 : 1;
}
case B_DOUBLE_TYPE:
{
double result = *(double *)key1 - *(double *)key2;
if (result == 0.0)
return 0;
return (result < 0.0) ? -1 : 1;
}
}
// if the type is unknown, the entries don't match...
return -1;
}
@@ -1,374 +0,0 @@
/*
* Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#ifndef B_PLUS_TREE_H
#define B_PLUS_TREE_H
#include <stdlib.h>
#include "Stream.h"
#include "Utility.h"
template<class T> class Stack;
namespace BFS {
//****************** on-disk structures ********************
#define BPLUSTREE_NULL -1LL
#define BPLUSTREE_FREE -2LL
struct bplustree_header {
uint32 magic;
uint32 node_size;
uint32 max_number_of_levels;
uint32 data_type;
off_t root_node_pointer;
off_t free_node_pointer;
off_t maximum_size;
uint32 Magic() const { return BFS_ENDIAN_TO_HOST_INT32(magic); }
uint32 NodeSize() const { return BFS_ENDIAN_TO_HOST_INT32(node_size); }
uint32 DataType() const { return BFS_ENDIAN_TO_HOST_INT32(data_type); }
off_t RootNode() const { return BFS_ENDIAN_TO_HOST_INT64(root_node_pointer); }
off_t MaximumSize() const { return BFS_ENDIAN_TO_HOST_INT64(maximum_size); }
inline bool IsValidLink(off_t link);
};
#define BPLUSTREE_MAGIC 0x69f6c2e8
#define BPLUSTREE_NODE_SIZE 1024
#define BPLUSTREE_MAX_KEY_LENGTH 256
#define BPLUSTREE_MIN_KEY_LENGTH 1
enum bplustree_types {
BPLUSTREE_STRING_TYPE = 0,
BPLUSTREE_INT32_TYPE = 1,
BPLUSTREE_UINT32_TYPE = 2,
BPLUSTREE_INT64_TYPE = 3,
BPLUSTREE_UINT64_TYPE = 4,
BPLUSTREE_FLOAT_TYPE = 5,
BPLUSTREE_DOUBLE_TYPE = 6
};
struct sorted_array;
typedef sorted_array duplicate_array;
struct bplustree_node {
off_t left_link;
off_t right_link;
off_t overflow_link;
uint16 all_key_count;
uint16 all_key_length;
off_t LeftLink() const { return BFS_ENDIAN_TO_HOST_INT64(left_link); }
off_t RightLink() const { return BFS_ENDIAN_TO_HOST_INT64(right_link); }
off_t OverflowLink() const { return BFS_ENDIAN_TO_HOST_INT64(overflow_link); }
uint16 NumKeys() const { return BFS_ENDIAN_TO_HOST_INT16(all_key_count); }
uint16 AllKeyLength() const { return BFS_ENDIAN_TO_HOST_INT16(all_key_length); }
inline uint16 *KeyLengths() const;
inline off_t *Values() const;
inline uint8 *Keys() const;
inline int32 Used() const;
uint8 *KeyAt(int32 index,uint16 *keyLength) const;
inline bool IsLeaf() const;
void Initialize();
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
uint8 CountDuplicates(off_t offset, bool isFragment) const;
off_t DuplicateAt(off_t offset, bool isFragment, int8 index) const;
int32 FragmentsUsed(uint32 nodeSize);
inline duplicate_array *FragmentAt(int8 index);
inline duplicate_array *DuplicateArray();
static inline uint8 LinkType(off_t link);
static inline off_t MakeLink(uint8 type, off_t link, uint32 fragmentIndex = 0);
static inline bool IsDuplicate(off_t link);
static inline off_t FragmentOffset(off_t link);
static inline uint32 FragmentIndex(off_t link);
#endif
} __attribute__ ((__packed__));
//#define BPLUSTREE_NODE 0
#define BPLUSTREE_DUPLICATE_NODE 2
#define BPLUSTREE_DUPLICATE_FRAGMENT 3
#define NUM_FRAGMENT_VALUES 7
#define NUM_DUPLICATE_VALUES 125
//**************************************
enum bplustree_traversing {
BPLUSTREE_FORWARD = 1,
BPLUSTREE_BACKWARD = -1,
BPLUSTREE_BEGIN = 0,
BPLUSTREE_END = 1
};
//****************** in-memory structures ********************
class BPlusTree;
class TreeIterator;
class CachedNode;
// needed for searching (utilizing a stack)
struct node_and_key {
off_t nodeOffset;
uint16 keyIndex;
};
//***** Cache handling *****
class CachedNode {
public:
CachedNode(BPlusTree *tree)
:
fTree(tree),
fNode(NULL),
fBlock(NULL)
{
}
CachedNode(BPlusTree *tree, off_t offset, bool check = true)
:
fTree(tree),
fNode(NULL),
fBlock(NULL)
{
SetTo(offset, check);
}
~CachedNode()
{
Unset();
free(fBlock);
}
bplustree_node *SetTo(off_t offset, bool check = true);
bplustree_header *SetToHeader();
void Unset();
bplustree_node *Node() const { return fNode; }
protected:
bplustree_node *InternalSetTo(off_t offset);
BPlusTree *fTree;
bplustree_node *fNode;
uint8 *fBlock;
off_t fBlockNumber;
};
//******** B+tree class *********
class BPlusTree {
public:
BPlusTree(Stream *stream);
~BPlusTree();
status_t SetTo(Stream *stream);
status_t InitCheck();
status_t Validate();
status_t Find(const uint8 *key, uint16 keyLength, off_t *value);
static int32 TypeCodeToKeyType(type_code code);
static int32 ModeToKeyType(mode_t mode);
private:
BPlusTree(const BPlusTree &);
BPlusTree &operator=(const BPlusTree &);
// no implementation
int32 CompareKeys(const void *key1, int keylength1, const void *key2, int keylength2);
status_t FindKey(bplustree_node *node, const uint8 *key, uint16 keyLength,
uint16 *index = NULL, off_t *next = NULL);
status_t SeekDown(Stack<node_and_key> &stack, const uint8 *key, uint16 keyLength);
private:
friend class CachedNode;
friend class TreeIterator;
Stream *fStream;
bplustree_header *fHeader;
CachedNode fCachedHeader;
int32 fNodeSize;
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
bool fAllowDuplicates;
#endif
status_t fStatus;
};
//***** helper classes/functions *****
extern int32 compareKeys(type_code type, const void *key1, int keyLength1,
const void *key2, int keyLength2);
class TreeIterator {
public:
TreeIterator(BPlusTree *tree);
~TreeIterator();
status_t Goto(int8 to);
status_t Traverse(int8 direction, void *key, uint16 *keyLength, uint16 maxLength,
off_t *value, uint16 *duplicate = NULL);
status_t Find(const uint8 *key, uint16 keyLength);
status_t Rewind();
status_t GetNextEntry(void *key, uint16 *keyLength, uint16 maxLength,
off_t *value, uint16 *duplicate = NULL);
status_t GetPreviousEntry(void *key, uint16 *keyLength, uint16 maxLength,
off_t *value, uint16 *duplicate = NULL);
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
void SkipDuplicates();
#endif
private:
BPlusTree *fTree;
off_t fCurrentNodeOffset; // traverse position
int32 fCurrentKey;
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
off_t fDuplicateNode;
uint16 fDuplicate, fNumDuplicates;
bool fIsFragment;
#endif
};
/************************ TreeIterator inline functions ************************/
// #pragma mark -
inline status_t
TreeIterator::Rewind()
{
return Goto(BPLUSTREE_BEGIN);
}
inline status_t
TreeIterator::GetNextEntry(void *key, uint16 *keyLength, uint16 maxLength,
off_t *value, uint16 *duplicate)
{
return Traverse(BPLUSTREE_FORWARD, key, keyLength, maxLength, value, duplicate);
}
inline status_t
TreeIterator::GetPreviousEntry(void *key, uint16 *keyLength, uint16 maxLength,
off_t *value, uint16 *duplicate)
{
return Traverse(BPLUSTREE_BACKWARD, key, keyLength, maxLength, value, duplicate);
}
/************************ bplustree_header inline functions ************************/
// #pragma mark -
inline bool
bplustree_header::IsValidLink(off_t link)
{
return link == BPLUSTREE_NULL || (link > 0 && link <= MaximumSize() - NodeSize());
}
/************************ bplustree_node inline functions ************************/
// #pragma mark -
inline uint16 *
bplustree_node::KeyLengths() const
{
return (uint16 *)(((char *)this)
+ key_align(sizeof(bplustree_node) + AllKeyLength()));
}
inline off_t *
bplustree_node::Values() const
{
return (off_t *)((char *)KeyLengths() + NumKeys() * sizeof(uint16));
}
inline uint8 *
bplustree_node::Keys() const
{
return (uint8 *)this + sizeof(bplustree_node);
}
inline int32
bplustree_node::Used() const
{
return key_align(sizeof(bplustree_node) + AllKeyLength())
+ NumKeys() * (sizeof(uint16) + sizeof(off_t));
}
inline bool
bplustree_node::IsLeaf() const
{
return OverflowLink() == BPLUSTREE_NULL;
}
#ifdef BPLUSTREE_SUPPORTS_DUPLICATES
inline duplicate_array *
bplustree_node::FragmentAt(int8 index)
{
return (duplicate_array *)((off_t *)this + index * (NUM_FRAGMENT_VALUES + 1));
}
inline duplicate_array *
bplustree_node::DuplicateArray()
{
return (duplicate_array *)&this->overflow_link;
}
inline uint8
bplustree_node::LinkType(off_t link)
{
return *(uint64 *)&link >> 62;
}
inline off_t
bplustree_node::MakeLink(uint8 type, off_t link, uint32 fragmentIndex)
{
return ((off_t)type << 62) | (link & 0x3ffffffffffffc00LL) | (fragmentIndex & 0x3ff);
}
inline bool
bplustree_node::IsDuplicate(off_t link)
{
return (LinkType(link) & (BPLUSTREE_DUPLICATE_NODE | BPLUSTREE_DUPLICATE_FRAGMENT)) > 0;
}
inline off_t
bplustree_node::FragmentOffset(off_t link)
{
return link & 0x3ffffffffffffc00LL;
}
inline uint32
bplustree_node::FragmentIndex(off_t link)
{
return (uint32)(link & 0x3ff);
}
#endif /* BPLUSTREE_SUPPORTS_DUPLICATES */
} // namespace BFS
#endif /* B_PLUS_TREE_H */
@@ -19,3 +19,6 @@ BootStaticLibrary boot_bfs :
BPlusTree.cpp BPlusTree.cpp
: -fno-pic : -fno-pic
; ;
SEARCH on [ FGristFiles BPlusTree.cpp ]
= [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems bfs ] ;
@@ -35,6 +35,7 @@ class Stream : public bfs_inode {
bool IsContainer() const { return Mode() & (S_IFDIR | S_INDEX_DIR | S_ATTR_DIR); } bool IsContainer() const { return Mode() & (S_IFDIR | S_INDEX_DIR | S_ATTR_DIR); }
bool IsSymlink() const { return S_ISLNK(Mode()); } bool IsSymlink() const { return S_ISLNK(Mode()); }
bool IsIndex() const { return false; }
static Node *NodeFactory(Volume &volume, off_t id); static Node *NodeFactory(Volume &volume, off_t id);