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:
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user