#include "Iterators.h"
#include "Block.h"
#include "BlockCache.h"
#include "Key.h"
#include "IndirectItem.h"
#include "StatItem.h"
#include "Tree.h"
template<typename C>
static inline C min(const C &a, const C &b) { return (a < b ? a : b); }
template<typename C>
static inline C max(const C &a, const C &b) { return (a > b ? a : b); }
\class TreePath
\brief Represents a path in the tree.
It is composed of TreePath::Elements each one storing the block number
of a node and the index of a child node.
*/
TreePath::TreePath(uint32 maxLength)
: fMaxLength(maxLength),
fLength(0),
fElements(NULL)
{
fElements = new(nothrow) Element[fMaxLength];
}
TreePath::~TreePath()
{
if (fElements)
delete[] fElements;
}
status_t
TreePath::InitCheck() const
{
return (fElements ? B_OK : B_NO_MEMORY);
}
uint32
TreePath::GetMaxLength() const
{
return fMaxLength;
}
uint32
TreePath::GetLength() const
{
return fLength;
}
const TreePath::Element *
TreePath::ElementAt(int32 index) const
{
Element *element = NULL;
if (InitCheck() == B_OK && index >= 0 && (uint32)index < GetLength())
element = fElements + index;
return element;
}
const TreePath::Element *
TreePath::GetTopElement() const
{
return ElementAt(GetLength() - 1);
}
status_t
TreePath::PushElement(uint64 blockNumber, int32 index)
{
status_t error = (fLength < fMaxLength ? InitCheck() : B_BAD_VALUE);
if (error == B_OK) {
error = fElements[fLength].SetTo(blockNumber, index);
if (error == B_OK)
fLength++;
}
return error;
}
status_t
TreePath::PopElement()
{
status_t error = InitCheck();
if (error == B_OK) {
if (fLength > 0)
fLength--;
else
error = B_ERROR;
}
return error;
}
\class TreePath::Element
\brief Store information about one element in a tree path.
*/
status_t
TreePath::Element::SetTo(uint64 blockNumber, int32 index)
{
fBlockNumber = blockNumber;
fIndex = index;
return B_OK;
}
uint64
TreePath::Element::GetBlockNumber() const
{
return fBlockNumber;
}
int32
TreePath::Element::GetIndex() const
{
return fIndex;
}
\class TreeIterator
\brief Class used to iterate and navigate in trees.
A TreeIterator is usually initialized to the root node of the tree
and can be used to navigate and search in the tree.
*/
TreeIterator::TreeIterator(Tree *tree)
: fTree(NULL),
fCurrentNode(NULL),
fPath(NULL)
{
SetTo(tree);
}
TreeIterator::~TreeIterator()
{
Unset();
}
status_t
TreeIterator::SetTo(Tree *tree)
{
Unset();
fTree = tree;
fCurrentNode = NULL;
fPath = NULL;
if (fTree) {
fCurrentNode = fTree->GetRootNode();
if (fCurrentNode)
fCurrentNode->Get();
fPath = new(nothrow) TreePath(tree->GetTreeHeight());
}
return InitCheck();
}
void
TreeIterator::Unset()
{
if (fPath) {
delete fPath;
fPath = NULL;
}
if (fCurrentNode) {
fCurrentNode->Put();
fCurrentNode = NULL;
}
}
status_t
TreeIterator::InitCheck() const
{
return (fTree && fCurrentNode && fPath ? fPath->InitCheck() : B_NO_INIT);
}
Node *
TreeIterator::GetNode() const
{
return fCurrentNode;
}
int32
TreeIterator::GetLevel() const
{
int32 level = 0;
if (fCurrentNode)
level = fCurrentNode->GetLevel();
return level;
}
\a direction can be
- \c FORWARD: Forward/to the right. Goes to the next child node of the
current node.
- \c BACKWARDS: Forward/to the right. Goes to the previous child node of
the current node.
- \c UP: Up one level (in root direction). Goes to the parent node of
the current node. The current node is the child node, it is pointed
to afterward.
- \c DOWN: Down one level (in leaf direction). Goes to the child node of
the current node the iterator currently points at. Points afterwards to
the 0th child node of the new current node (unless it is a leaf node).
\c FORWARD and \c BACKWARDS do not change the current node!
\param direction \c FORWARD, \c BACKWARDS, \c UP or \c DOWN
\return \c B_OK, if everything went fine
*/
status_t
TreeIterator::GoTo(uint32 direction)
{
status_t error = InitCheck();
if (error == B_OK) {
switch (direction) {
case FORWARD:
{
if (fCurrentNode->IsInternal()
&& fIndex < fCurrentNode->CountItems()) {
fIndex++;
} else {
error = B_ENTRY_NOT_FOUND;
}
break;
}
case BACKWARDS:
{
if (fCurrentNode->IsInternal() && fIndex > 0)
fIndex--;
else
error = B_ENTRY_NOT_FOUND;
break;
}
case UP:
{
error = _PopTopNode();
break;
}
case DOWN:
{
if (fCurrentNode->IsInternal()) {
InternalNode *internal = fCurrentNode->ToInternalNode();
const DiskChild *child = internal->ChildAt(fIndex);
if (child) {
Node *node = NULL;
error = fTree->GetNode(child->GetBlockNumber(), &node);
if (error == B_OK)
error = _PushCurrentNode(node, 0);
} else
error = B_ENTRY_NOT_FOUND;
} else
error = B_ENTRY_NOT_FOUND;
break;
}
}
}
return error;
}
This method operates only at leaf node level. It finds the next leaf
node to the left. Fails, if the current node is no leaf node.
\param node Pointer to a pre-allocated LeafNode pointer that shall be set
to the found node.
\return \c B_OK, if everything went fine
*/
status_t
TreeIterator::GoToPreviousLeaf(LeafNode **node)
{
status_t error = InitCheck();
if (error == B_OK) {
if (fCurrentNode->IsLeaf()) {
bool found = false;
while (error == B_OK && !found) {
error = GoTo(UP);
if (error == B_OK)
found = (GoTo(BACKWARDS) == B_OK);
}
if (error == B_OK) {
error = GoTo(DOWN);
while (error == B_OK && fCurrentNode->IsInternal()) {
fIndex = fCurrentNode->CountItems();
error = GoTo(DOWN);
}
}
if (error == B_OK && node)
*node = fCurrentNode->ToLeafNode();
} else
error = B_ENTRY_NOT_FOUND;
}
return error;
}
This method operates only at leaf node level. It finds the next leaf
node to the right. Fails, if the current node is no leaf node.
\param node Pointer to a pre-allocated LeafNode pointer that shall be set
to the found node.
\return \c B_OK, if everything went fine
*/
status_t
TreeIterator::GoToNextLeaf(LeafNode **node)
{
status_t error = InitCheck();
if (error == B_OK) {
if (fCurrentNode->IsLeaf()) {
bool found = false;
while (error == B_OK && !found) {
error = GoTo(UP);
if (error == B_OK)
found = (GoTo(FORWARD) == B_OK);
}
while (error == B_OK && fCurrentNode->IsInternal())
error = GoTo(DOWN);
if (error == B_OK && node)
*node = fCurrentNode->ToLeafNode();
} else
error = B_ENTRY_NOT_FOUND;
}
return error;
}
The method starts at the current node and only goes downwards.
In the spanned subtree it finds the rightmost leaf node whose left
delimiting key is not greater than the supplied key.
\param key The key to be found.
\param node Pointer to a pre-allocated LeafNode pointer that shall be set
to the found node.
\return \c B_OK, if everything went fine.
*/
status_t
TreeIterator::FindRightMostLeaf(const VKey *k, LeafNode **node)
{
status_t error = (k ? InitCheck() : B_BAD_VALUE);
while (error == B_OK && fCurrentNode->IsInternal()) {
int32 index = 0;
error = _SearchRightMost(fCurrentNode->ToInternalNode(), k, &index);
if (error == B_OK) {
fIndex = index;
error = GoTo(DOWN);
}
}
if (error == B_OK && node)
*node = fCurrentNode->ToLeafNode();
return error;
}
This means, the current node is Put() and its block number and child node
index pushed onto the tree path. This should be done, when the iteration
is not to be continued immediately (or for the foreseeable future) and
the node block shall not be locked in memory for that time.
Resume() is to be called, before the iteration can be continued.
\return \c B_OK, if everything went fine.
*/
status_t
TreeIterator::Suspend()
{
status_t error = InitCheck();
if (error == B_OK)
error = _PushCurrentNode();
if (error == B_OK)
fCurrentNode = NULL;
return error;
}
To be called after a Suspend(), before the iteration can be continued.
\return \c B_OK, if everything went fine.
*/
status_t
TreeIterator::Resume()
{
status_t error
= (fTree && !fCurrentNode && fPath ? fPath->InitCheck() : B_NO_INIT);
if (error == B_OK)
error = _PopTopNode();
return error;
}
status_t
TreeIterator::_PushCurrentNode(Node *newTopNode, int32 newIndex)
{
status_t error = fPath->PushElement(fCurrentNode->GetNumber(), fIndex);
if (error == B_OK) {
fCurrentNode->Put();
fCurrentNode = newTopNode;
fIndex = newIndex;
}
return error;
}
status_t
TreeIterator::_PopTopNode()
{
status_t error = B_OK;
if (fPath->GetLength() > 0) {
const TreePath::Element *element = fPath->GetTopElement();
Node *node = NULL;
error = fTree->GetNode(element->GetBlockNumber(), &node);
if (error == B_OK) {
if (fCurrentNode)
fCurrentNode->Put();
fCurrentNode = node;
fIndex = element->GetIndex();
fPath->PopElement();
}
} else
error = B_BAD_VALUE;
return error;
}
status_t
TreeIterator::_SearchRightMost(InternalNode *node, const VKey *k, int32 *index)
{
status_t error = (node && k && index ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
int32 lower = 0;
int32 upper = node->CountItems();
while (lower < upper) {
int32 mid = (lower + upper) / 2;
VKey midKey(node->KeyAt(mid));
if (*k < midKey)
upper = mid;
else
lower = mid + 1;
}
if (error == B_OK)
*index = lower;
}
return error;
}
\class ItemIterator
\brief Class used to iterate through leaf node items.
A ItemIterator is usually initialized to the root node of the tree,
and before it can be used for iteration, FindRightMostClose() or
FindRightMost() must be invoked. They set the iterator to an item
and afterwards one can use GoToPrevious() and GoToNext() to get the
previous/next ones.
*/
ItemIterator::ItemIterator(Tree *tree)
: fTreeIterator(tree),
fIndex(0)
{
}
status_t
ItemIterator::SetTo(Tree *tree)
{
status_t error = fTreeIterator.SetTo(tree);
fIndex = 0;
return error;
}
status_t
ItemIterator::InitCheck() const
{
return fTreeIterator.InitCheck();
}
status_t
ItemIterator::GetCurrent(Item *item)
{
LeafNode *node = NULL;
status_t error = (item ? _GetLeafNode(&node) : B_BAD_VALUE);
if (error == B_OK) {
if (fIndex >= 0 && fIndex < node->CountItems())
error = item->SetTo(node, fIndex);
else
error = B_ENTRY_NOT_FOUND;
}
return error;
}
status_t
ItemIterator::GoToPrevious(Item *item)
{
LeafNode *node = NULL;
status_t error = _GetLeafNode(&node);
if (error == B_OK) {
int32 newIndex = fIndex - 1;
while (error == B_OK
&& (newIndex < 0 || newIndex >= node->CountItems())) {
error = fTreeIterator.GoToPreviousLeaf(&node);
if (error == B_OK)
newIndex = node->CountItems() - 1;
}
if (error == B_OK) {
fIndex = newIndex;
if (item)
error = item->SetTo(node, fIndex);
}
}
return error;
}
status_t
ItemIterator::GoToNext(Item *item)
{
LeafNode *node = NULL;
status_t error = _GetLeafNode(&node);
if (error == B_OK) {
int32 newIndex = fIndex + 1;
while (error == B_OK && newIndex >= node->CountItems()) {
error = fTreeIterator.GoToNextLeaf(&node);
newIndex = 0;
}
if (error == B_OK) {
fIndex = newIndex;
if (item)
error = item->SetTo(node, fIndex);
}
}
return error;
}
The method finds the rightmost item whose left delimiting key is not
greater than the supplied key.
\param key The key to be found.
\param item Pointer to a pre-allocated Item that shall be set
to the found item.
\return \c B_OK, if everything went fine.
*/
status_t
ItemIterator::FindRightMostClose(const VKey *k, Item *item)
{
status_t error = (k ? InitCheck() : B_BAD_VALUE);
if (error == B_OK)
error = fTreeIterator.FindRightMostLeaf(k);
if (error == B_OK) {
LeafNode *node = fTreeIterator.GetNode()->ToLeafNode();
error = _SearchRightMost(node, k, &fIndex);
if (error == B_OK && item)
{
error = item->SetTo(node, fIndex);
}
}
return error;
}
The method finds the rightmost item whose left delimiting key is equal
to the supplied key.
\param key The key to be found.
\param item Pointer to a pre-allocated Item that shall be set
to the found item.
\return \c B_OK, if everything went fine.
*/
status_t
ItemIterator::FindRightMost(const VKey *k, Item *item)
{
Item closeItem;
status_t error = FindRightMostClose(k, &closeItem);
if (error == B_OK) {
VKey itemKey;
closeItem.GetHeader()->GetKey(&itemKey);
if (*k == itemKey) {
if (item)
*item = closeItem;
} else
{
error = B_ENTRY_NOT_FOUND;
}
}
return error;
}
\see TreeIterator::Suspend().
\return \c B_OK, if everything went fine.
*/
status_t
ItemIterator::Suspend()
{
return fTreeIterator.Suspend();
}
\see TreeIterator::Resume().
\return \c B_OK, if everything went fine.
*/
status_t
ItemIterator::Resume()
{
return fTreeIterator.Resume();
}
status_t
ItemIterator::_GetLeafNode(LeafNode **leafNode) const
{
status_t error = InitCheck();
if (error == B_OK) {
if (Node *node = fTreeIterator.GetNode()) {
if (node->IsLeaf())
*leafNode = node->ToLeafNode();
else
error = B_ENTRY_NOT_FOUND;
} else
error = B_ERROR;
}
return error;
}
status_t
ItemIterator::_SearchRightMost(LeafNode *node, const VKey *k,
int32 *index) const
{
status_t error = (node && k && index ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
// simple linear search backwards
int32 foundIndex = -1;
for (int32 i = node->CountItems() - 1; foundIndex < 0 && i >= 0; i--) {
VKey itemKey;
node->ItemHeaderAt(i)->GetKey(&itemKey);
if (itemKey <= *k) {
foundIndex = i;
error = B_OK;
break;
}
}
// check whether all item keys are greater
if (foundIndex < 0)
error = B_ENTRY_NOT_FOUND;
// set result
if (error == B_OK)
*index = foundIndex;
*/
VKey lowerKey;
node->ItemHeaderAt(0)->GetKey(&lowerKey);
if (lowerKey <= *k) {
int32 lower = 0;
int32 upper = node->CountItems() - 1;
while (lower < upper) {
int32 mid = (lower + upper + 1) / 2;
VKey midKey;
node->ItemHeaderAt(mid)->GetKey(&midKey);
if (midKey > *k)
upper = mid - 1;
else
lower = mid;
}
*index = lower;
} else
error = B_ENTRY_NOT_FOUND;
}
return error;
}
\class ObjectItemIterator
\brief Class used to iterate through leaf node items.
This class basically wraps the ItemIterator class and provides a
more convenient interface for iteration through items of one given
object (directory, link or file). It finds only items of the object
identified by the dir and object ID specified on initialization.
*/
The iterator is initialized to find only items belonging to the object
identified by \a dirID and \a objectID. \a startOffset specifies the
offset (key offset) at which the iteration shall begin.
Note, that offsets don't need to be unique for an object, at least not
for files -- all indirect (and direct?) items have the same offset (1).
This has the ugly side effect, when iterating forward there may be items
with the same offset on previous nodes that will be skipped at the
beginning. The GetNext() does always find the item on the rightmost
possible node. Therefore searching forward starting with FIRST_ITEM_OFFSET
doesn't work for files!
\param tree The tree.
\param dirID The directory ID of the object.
\param objectID The object ID of the object.
\param startOffset The offset at which to begin the iteration.
*/
ObjectItemIterator::ObjectItemIterator(Tree *tree, uint32 dirID,
uint32 objectID, uint64 startOffset)
: fItemIterator(tree),
fDirID(dirID),
fObjectID(objectID),
fOffset(startOffset),
fFindFirst(true),
fDone(false)
{
}
The iterator is initialized to find only items belonging to the object
identified by \a dirID and \a objectID. \a startOffset specifies the
offset (key offset) at which the iteration shall begin.
\param tree The tree.
\param dirID The directory ID of the object.
\param objectID The object ID of the object.
\param startOffset The offset at which to begin the iteration.
\return \c B_OK, if everything went fine.
*/
status_t
ObjectItemIterator::SetTo(Tree *tree, uint32 dirID, uint32 objectID,
uint64 startOffset)
{
status_t error = fItemIterator.SetTo(tree);
fDirID = dirID;
fObjectID = objectID;
fOffset = startOffset;
fFindFirst = true;
fDone = false;
return error;
}
status_t
ObjectItemIterator::InitCheck() const
{
return fItemIterator.InitCheck();
}
Note, that offsets don't need to be unique for an object, at least not
for files -- all indirect (and direct?) items have the same offset (1).
This has the ugly side effect, when iterating forward there may be items
with the same offset on previous nodes that will be skipped at the
beginning. The GetNext() does always find the item on the rightmost
possible node. Therefore searching forward starting with FIRST_ITEM_OFFSET
doesn't work for files!
\param foundItem Pointer to a pre-allocated Item that shall be set
to the found item.
\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
through.
*/
status_t
ObjectItemIterator::GetNext(Item *foundItem)
{
status_t error = (foundItem ? InitCheck() : B_BAD_VALUE);
if (error == B_OK && fDone)
error = B_ENTRY_NOT_FOUND;
if (error == B_OK) {
Item item;
if (fFindFirst) {
VKey k(fDirID, fObjectID, fOffset, 0, KEY_FORMAT_3_5);
error = fItemIterator.FindRightMostClose(&k, &item);
fFindFirst = false;
} else {
error = fItemIterator.GoToNext(&item);
}
if (error == B_OK) {
VKey itemKey;
if (item.GetDirID() == fDirID && item.GetObjectID() == fObjectID)
*foundItem = item;
else
{
error = B_ENTRY_NOT_FOUND;
}
}
if (error != B_OK)
fDone = true;
}
RETURN_ERROR(error)
}
This version finds only items of the specified type.
\param foundItem Pointer to a pre-allocated Item that shall be set
to the found item.
\param type The type the found item must have.
\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
through.
*/
status_t
ObjectItemIterator::GetNext(Item *foundItem, uint32 type)
{
status_t error = B_OK;
do {
error = GetNext(foundItem);
} while (error == B_OK && foundItem->GetType() != type);
return error;
}
\param foundItem Pointer to a pre-allocated Item that shall be set
to the found item.
\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
through.
*/
status_t
ObjectItemIterator::GetPrevious(Item *foundItem)
{
status_t error = (foundItem ? InitCheck() : B_BAD_VALUE);
if (error == B_OK && fDone)
error = B_ENTRY_NOT_FOUND;
if (error == B_OK) {
Item item;
if (fFindFirst) {
VKey k(fDirID, fObjectID, fOffset, 0xffffffffUL, KEY_FORMAT_3_5);
error = fItemIterator.FindRightMostClose(&k, &item);
fFindFirst = false;
} else {
error = fItemIterator.GoToPrevious(&item);
}
if (error == B_OK) {
VKey itemKey;
if (item.GetDirID() == fDirID && item.GetObjectID() == fObjectID) {
*foundItem = item;
} else
{
error = B_ENTRY_NOT_FOUND;
}
}
if (error != B_OK)
fDone = true;
}
return error;
}
This version finds only items of the specified type.
\param foundItem Pointer to a pre-allocated Item that shall be set
to the found item.
\param type The type the found item must have.
\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
through.
*/
status_t
ObjectItemIterator::GetPrevious(Item *foundItem, uint32 type)
{
status_t error = B_OK;
do {
error = GetPrevious(foundItem);
} while (error == B_OK && foundItem->GetType() != type);
return error;
}
\see ItemIterator::Suspend().
\return \c B_OK, if everything went fine.
*/
status_t
ObjectItemIterator::Suspend()
{
return fItemIterator.Suspend();
}
\see ItemIterator::Resume().
\return \c B_OK, if everything went fine.
*/
status_t
ObjectItemIterator::Resume()
{
return fItemIterator.Resume();
}
\class DirEntryIterator
\brief Class used to iterate through DirEntries.
*/
The iterator is initialized to find only entries belonging to the directory
identified by \a dirID and \a objectID. \a startOffset specifies the
offset (key offset) at which the iteration shall begin. If \a fixedHash
is \c true, only items that have the same hash value as \a startOffset
are found.
\param tree The tree.
\param dirID The directory ID of the object.
\param objectID The object ID of the object.
\param startOffset The offset at which to begin the iteration.
\param fixedHash \c true to find only entries with the same hash as
\a startOffset
*/
DirEntryIterator::DirEntryIterator(Tree *tree, uint32 dirID, uint32 objectID,
uint64 startOffset, bool fixedHash)
: fItemIterator(tree, dirID, objectID, startOffset),
fDirItem(),
fIndex(-1),
fFixedHash(fixedHash),
fDone(false)
{
}
The iterator is initialized to find only entries belonging to the directory
identified by \a dirID and \a objectID. \a startOffset specifies the
offset (key offset) at which the iteration shall begin. If \a fixedHash
is \c true, only items that have the same hash value as \a startOffset
are found.
\param tree The tree.
\param dirID The directory ID of the object.
\param objectID The object ID of the object.
\param startOffset The offset at which to begin the iteration.
\param fixedHash \c true to find only entries with the same hash as
\a startOffset
*/
status_t
DirEntryIterator::SetTo(Tree *tree, uint32 dirID, uint32 objectID,
uint64 startOffset, bool fixedHash)
{
fDirItem.Unset();
status_t error = fItemIterator.SetTo(tree, dirID, objectID, startOffset);
fIndex = -1;
fFixedHash = fixedHash;
fDone = false;
return error;
}
status_t
DirEntryIterator::InitCheck() const
{
return fItemIterator.InitCheck();
}
Simply re-initializes the iterator to the parameters of the last
initialization.
\return \c B_OK, if everything went fine.
*/
status_t
DirEntryIterator::Rewind()
{
return SetTo(GetTree(), GetDirID(), GetObjectID(), GetOffset(),
fFixedHash);
}
\param foundItem Pointer to a pre-allocated Item that shall be set
to the found item.
\param entryIndex Pointer to a pre-allocated int32 that shall be set
to the found entry index.
\param _entry Pointer to a pre-allocated DirEntry pointer that shall be set
to the found entry. May be \c NULL.
\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
through.
*/
status_t
DirEntryIterator::GetNext(DirItem *foundItem, int32 *entryIndex,
DirEntry **_entry)
{
status_t error = (foundItem && entryIndex ? InitCheck() : B_BAD_VALUE);
while (error == B_OK
&& (fIndex < 0 || fIndex >= fDirItem.GetEntryCount())) {
error = fItemIterator.GetNext(&fDirItem, TYPE_DIRENTRY);
if (error == B_OK) {
if (fDirItem.Check() == B_OK)
fIndex = 0;
else
fIndex = -1;
}
}
if (error == B_OK) {
DirEntry *entry = fDirItem.EntryAt(fIndex);
if (!fFixedHash
|| offset_hash_value(entry->GetOffset())
== offset_hash_value(GetOffset())) {
*foundItem = fDirItem;
*entryIndex = fIndex;
if (_entry)
*_entry = entry;
fIndex++;
} else
error = B_ENTRY_NOT_FOUND;
}
return error;
}
\param foundItem Pointer to a pre-allocated Item that shall be set
to the found item.
\param entryIndex Pointer to a pre-allocated int32 that shall be set
to the found entry index.
\param _entry Pointer to a pre-allocated DirEntry pointer that shall be set
to the found entry. May be \c NULL.
\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
through.
*/
status_t
DirEntryIterator::GetPrevious(DirItem *foundItem, int32 *entryIndex,
DirEntry **_entry)
{
status_t error = (foundItem && entryIndex ? InitCheck() : B_BAD_VALUE);
if (error == B_OK && fDone)
error = B_ENTRY_NOT_FOUND;
while (error == B_OK
&& (fIndex < 0 || fIndex >= fDirItem.GetEntryCount())) {
error = fItemIterator.GetPrevious(&fDirItem, TYPE_DIRENTRY);
if (error == B_OK) {
if (fDirItem.Check() == B_OK)
fIndex = fDirItem.GetEntryCount() - 1;
else
fIndex = -1;
}
}
while (error == B_OK && fIndex >= 0
&& fDirItem.EntryAt(fIndex)->GetOffset() > GetOffset()) {
fIndex--;
}
if (error == B_OK) {
if (fIndex >= 0
&& (!fFixedHash
|| offset_hash_value(fDirItem.EntryAt(fIndex)->GetOffset())
== offset_hash_value(GetOffset()))) {
DirEntry *entry = fDirItem.EntryAt(fIndex);
*foundItem = fDirItem;
*entryIndex = fIndex;
fDone = (fFixedHash
&& offset_generation_number(entry->GetOffset()) == 0);
if (_entry)
*_entry = entry;
fIndex--;
} else
error = B_ENTRY_NOT_FOUND;
}
return error;
}
\see ObjectItemIterator::Suspend().
\return \c B_OK, if everything went fine.
*/
status_t
DirEntryIterator::Suspend()
{
return fItemIterator.Suspend();
}
\see ObjectItemIterator::Resume().
\return \c B_OK, if everything went fine.
*/
status_t
DirEntryIterator::Resume()
{
return fItemIterator.Resume();
}
\class StreamReader
\brief Class used to read object streams (files, links).
*/
The reader is initialized to read the stream of the object
identified by \a dirID and \a objectID.
\param tree The tree.
\param dirID The directory ID of the object.
\param objectID The object ID of the object.
*/
StreamReader::StreamReader(Tree *tree, uint32 dirID, uint32 objectID)
: fItemIterator(tree, dirID, objectID, SD_OFFSET),
fItem(),
fStreamSize(-1),
fItemOffset(-1),
fItemSize(0),
fBlockSize(0)
{
fBlockSize = tree->GetBlockSize();
}
The reader is initialized to read the stream of the object
identified by \a dirID and \a objectID.
\param tree The tree.
\param dirID The directory ID of the object.
\param objectID The object ID of the object.
\return \c B_OK, if everything went fine.
*/
status_t
StreamReader::SetTo(Tree *tree, uint32 dirID, uint32 objectID)
{
fItem.Unset();
status_t error = fItemIterator.SetTo(tree, dirID, objectID, SD_OFFSET);
fStreamSize = -1;
fItemOffset = -1;
fItemSize = 0;
fBlockSize = tree->GetBlockSize();
return error;
}
status_t
StreamReader::InitCheck() const
{
return fItemIterator.InitCheck();
}
the supplied buffer.
\param position Stream position at which to start reading.
\param buffer Pointer to a pre-allocated buffer into which the read data
shall be written.
\param bufferSize Number of bytes to be read.
\param _bytesRead Pointer to a pre-allocated size_t which shall be set to
the number of bytes actually read.
\return \c B_OK, if everything went fine.
*/
status_t
StreamReader::ReadAt(off_t position, void *buffer, size_t bufferSize,
size_t *_bytesRead)
{
status_t error = (position >= 0 && buffer && _bytesRead ? InitCheck()
: B_BAD_VALUE);
if (error == B_OK)
error = _GetStreamSize();
if (error == B_OK) {
if (position < fStreamSize) {
if (position + (off_t)bufferSize > fStreamSize)
bufferSize = fStreamSize - position;
} else
bufferSize = 0;
}
if (error == B_OK) {
size_t bytesRead = 0;
while (error == B_OK && bufferSize > 0
&& (error = _SeekTo(position)) == B_OK) {
off_t inItemOffset = max_c(0LL, position - fItemOffset);
off_t toRead = min_c(fItemSize - inItemOffset, (off_t)bufferSize);
switch (fItem.GetType()) {
case TYPE_INDIRECT:
error = _ReadIndirectItem(inItemOffset, buffer, toRead);
break;
case TYPE_DIRECT:
error = _ReadDirectItem(inItemOffset, buffer, toRead);
break;
case TYPE_STAT_DATA:
case TYPE_DIRENTRY:
case TYPE_ANY:
default:
FATAL(("Neither direct nor indirect item! type: %u\n",
fItem.GetType()));
error = B_IO_ERROR;
break;
}
if (error == B_OK) {
buffer = (uint8*)buffer + toRead;
position += toRead;
bufferSize -= toRead;
bytesRead += toRead;
}
}
*_bytesRead = bytesRead;
}
return error;
}
\see ObjectItemIterator::Suspend().
\return \c B_OK, if everything went fine.
*/
status_t
StreamReader::Suspend()
{
return fItemIterator.Suspend();
}
\see ObjectItemIterator::Resume().
\return \c B_OK, if everything went fine.
*/
status_t
StreamReader::Resume()
{
return fItemIterator.Resume();
}
status_t
StreamReader::_GetStreamSize()
{
status_t error = B_OK;
if (fStreamSize < 0) {
error = fItemIterator.GetNext(&fItem, TYPE_STAT_DATA);
if (error == B_OK) {
StatData statData;
error = (static_cast<StatItem*>(&fItem))->GetStatData(&statData);
if (error == B_OK)
fStreamSize = statData.GetSize();
}
}
return error;
}
status_t
StreamReader::_SeekTo(off_t position)
{
status_t error = _GetStreamSize();
if (error == B_OK && fItemOffset < 0)
fItemOffset = 0;
if (error == B_OK) {
if (2 * position < fItemOffset) {
error = fItemIterator.SetTo(GetTree(), GetDirID(), GetObjectID(),
SD_OFFSET);
fStreamSize = -1;
fItemOffset = -1;
fItemSize = 0;
if (error == B_OK)
error = _SeekTo(position);
} else if (position < fItemOffset) {
while (error == B_OK && position < fItemOffset
&& (error = fItemIterator.GetPrevious(&fItem)) == B_OK) {
fItemSize = 0;
switch (fItem.GetType()) {
case TYPE_INDIRECT:
{
IndirectItem &indirect
= *static_cast<IndirectItem*>(&fItem);
fItemSize = indirect.CountBlocks() * (off_t)fBlockSize;
break;
}
case TYPE_DIRECT:
fItemSize = fItem.GetLen();
break;
case TYPE_STAT_DATA:
case TYPE_DIRENTRY:
case TYPE_ANY:
default:
break;
}
fItemOffset -= fItemSize;
}
} else if (position >= fItemOffset + fItemSize) {
while (error == B_OK && position >= fItemOffset + fItemSize
&& (error = fItemIterator.GetNext(&fItem)) == B_OK) {
fItemOffset += fItemSize;
fItemSize = 0;
switch (fItem.GetType()) {
case TYPE_INDIRECT:
{
IndirectItem &indirect
= *static_cast<IndirectItem*>(&fItem);
fItemSize = min(indirect.CountBlocks()
* (off_t)fBlockSize,
fStreamSize - fItemOffset);
break;
}
case TYPE_DIRECT:
fItemSize = min((off_t)fItem.GetLen(),
fStreamSize - fItemOffset);
break;
case TYPE_STAT_DATA:
case TYPE_DIRENTRY:
case TYPE_ANY:
default:
break;
}
}
}
}
RETURN_ERROR(error);
}
status_t
StreamReader::_ReadIndirectItem(off_t offset, void *buffer, size_t bufferSize)
{
status_t error = B_OK;
IndirectItem &indirect = *static_cast<IndirectItem*>(&fItem);
uint32 skipItems = 0;
if (offset > 0) {
skipItems = uint32(offset / fBlockSize);
skipItems = min(skipItems, indirect.CountBlocks());
}
for (uint32 i = skipItems;
error == B_OK && bufferSize > 0 && i < indirect.CountBlocks();
i++) {
Block *block = NULL;
error = GetTree()->GetBlock(indirect.BlockNumberAt(i), &block);
if (error == B_OK) {
off_t blockOffset = i * (off_t)fBlockSize;
uint32 localOffset = max_c(0LL, offset - blockOffset);
uint32 toRead = min_c(fBlockSize - localOffset, bufferSize);
memcpy(buffer,
reinterpret_cast<uint8*>(block->GetData()) + localOffset,
toRead);
block->Put();
bufferSize -= toRead;
buffer = (uint8*)buffer + toRead;
} else {
FATAL(("failed to get block %" B_PRIu64 "\n",
indirect.BlockNumberAt(i)));
error = B_IO_ERROR;
}
}
return error;
}
status_t
StreamReader::_ReadDirectItem(off_t offset, void *buffer, size_t bufferSize)
{
memcpy(buffer,
reinterpret_cast<uint8*>(fItem.GetData()) + offset, bufferSize);
return B_OK;
}