1// Iterators.h
2//
3// Copyright (c) 2003-2010, Ingo Weinhold (bonefish@cs.tu-berlin.de)
4//
5// This program is free software; you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation; either version 2 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to the Free Software
17// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18//
19// You can alternatively use *this file* under the terms of the the MIT
20// license included in this package.
21
22
23#include "Iterators.h"
24
25#include "Block.h"
26#include "BlockCache.h"
27#include "Key.h"
28#include "IndirectItem.h"
29#include "StatItem.h"
30#include "Tree.h"
31
32
33// min and max
34// We don't want to include <algobase.h> otherwise we also get <iostream.h>
35// and other undesired things.
36template<typename C>
37static inline C min(const C &a, const C &b) { return (a < b ? a : b); }
38template<typename C>
39static inline C max(const C &a, const C &b) { return (a > b ? a : b); }
40
41/*!
42	\class TreePath
43	\brief Represents a path in the tree.
44
45	It is composed of TreePath::Elements each one storing the block number
46	of a node and the index of a child node.
47*/
48
49// constructor
50TreePath::TreePath(uint32 maxLength)
51	: fMaxLength(maxLength),
52	  fLength(0),
53	  fElements(NULL)
54{
55	fElements = new(nothrow) Element[fMaxLength];
56}
57
58// destructor
59TreePath::~TreePath()
60{
61	if (fElements)
62		delete[] fElements;
63}
64
65// InitCheck
66status_t
67TreePath::InitCheck() const
68{
69	return (fElements ? B_OK : B_NO_MEMORY);
70}
71
72// GetMaxLength
73uint32
74TreePath::GetMaxLength() const
75{
76	return fMaxLength;
77}
78
79// GetLength
80uint32
81TreePath::GetLength() const
82{
83	return fLength;
84}
85
86// ElementAt
87const TreePath::Element *
88TreePath::ElementAt(int32 index) const
89{
90	Element *element = NULL;
91	if (InitCheck() == B_OK && index >= 0 && (uint32)index < GetLength())
92		element = fElements + index;
93	return element;
94}
95
96// GetTopElement
97const TreePath::Element *
98TreePath::GetTopElement() const
99{
100	return ElementAt(GetLength() - 1);
101}
102
103// PushElement
104status_t
105TreePath::PushElement(uint64 blockNumber, int32 index)
106{
107	status_t error = (fLength < fMaxLength ? InitCheck() : B_BAD_VALUE);
108	if (error == B_OK) {
109		error = fElements[fLength].SetTo(blockNumber, index);
110		if (error == B_OK)
111			fLength++;
112	}
113	return error;
114}
115
116// PopElement
117status_t
118TreePath::PopElement()
119{
120	status_t error = InitCheck();
121	if (error == B_OK) {
122		if (fLength > 0)
123			fLength--;
124		else
125			error = B_ERROR;
126	}
127	return error;
128}
129
130
131/*!
132	\class TreePath::Element
133	\brief Store information about one element in a tree path.
134*/
135
136// SetTo
137status_t
138TreePath::Element::SetTo(uint64 blockNumber, int32 index)
139{
140	fBlockNumber = blockNumber;
141	fIndex = index;
142	return B_OK;
143}
144
145// GetBlockNumber
146uint64
147TreePath::Element::GetBlockNumber() const
148{
149	return fBlockNumber;
150}
151
152// GetIndex
153int32
154TreePath::Element::GetIndex() const
155{
156	return fIndex;
157}
158
159
160/*!
161	\class TreeIterator
162	\brief Class used to iterate and navigate in trees.
163
164	A TreeIterator is usually initialized to the root node of the tree
165	and can be used to navigate and search in the tree.
166*/
167
168// constructor
169TreeIterator::TreeIterator(Tree *tree)
170	: fTree(NULL),
171	  fCurrentNode(NULL),
172	  fPath(NULL)
173{
174	SetTo(tree);
175}
176
177// destructor
178TreeIterator::~TreeIterator()
179{
180	Unset();
181}
182
183// SetTo
184status_t
185TreeIterator::SetTo(Tree *tree)
186{
187	Unset();
188	fTree = tree;
189	fCurrentNode = NULL;
190	fPath = NULL;
191	if (fTree) {
192		fCurrentNode = fTree->GetRootNode();
193		if (fCurrentNode)
194			fCurrentNode->Get();
195		fPath = new(nothrow) TreePath(tree->GetTreeHeight());
196	}
197	return InitCheck();
198}
199
200// Unset
201void
202TreeIterator::Unset()
203{
204	if (fPath) {
205		delete fPath;
206		fPath = NULL;
207	}
208	if (fCurrentNode) {
209		fCurrentNode->Put();
210		fCurrentNode = NULL;
211	}
212}
213
214// InitCheck
215status_t
216TreeIterator::InitCheck() const
217{
218	return (fTree && fCurrentNode && fPath ? fPath->InitCheck() : B_NO_INIT);
219}
220
221// GetNode
222Node *
223TreeIterator::GetNode() const
224{
225	return fCurrentNode;
226}
227
228// GetLevel
229int32
230TreeIterator::GetLevel() const
231{
232	int32 level = 0;
233	if (fCurrentNode)
234		level = fCurrentNode->GetLevel();
235	return level;
236}
237
238// GoTo
239/*!	\brief Goes into a given direction.
240
241	\a direction can be
242	- \c FORWARD: Forward/to the right. Goes to the next child node of the
243	  current node.
244	- \c BACKWARDS: Forward/to the right. Goes to the previous child node of
245	  the current node.
246	- \c UP: Up one level (in root direction). Goes to the parent node of
247	  the current node. The current node is the child node, it is pointed
248	  to afterward.
249	- \c DOWN: Down one level (in leaf direction). Goes to the child node of
250	  the current node the iterator currently points at. Points afterwards to
251	  the 0th child node of the new current node (unless it is a leaf node).
252
253	\c FORWARD and \c BACKWARDS do not change the current node!
254
255	\param direction \c FORWARD, \c BACKWARDS, \c UP or \c DOWN
256	\return \c B_OK, if everything went fine
257*/
258status_t
259TreeIterator::GoTo(uint32 direction)
260{
261	status_t error = InitCheck();
262	if (error == B_OK) {
263		switch (direction) {
264			case FORWARD:
265			{
266				if (fCurrentNode->IsInternal()
267					&& fIndex < fCurrentNode->CountItems()) {
268					fIndex++;
269				} else {
270					error = B_ENTRY_NOT_FOUND;
271				}
272				break;
273			}
274			case BACKWARDS:
275			{
276				if (fCurrentNode->IsInternal() && fIndex > 0)
277					fIndex--;
278				else
279					error = B_ENTRY_NOT_FOUND;
280				break;
281			}
282			case UP:
283			{
284				error = _PopTopNode();
285				break;
286			}
287			case DOWN:
288			{
289				if (fCurrentNode->IsInternal()) {
290					InternalNode *internal = fCurrentNode->ToInternalNode();
291					const DiskChild *child = internal->ChildAt(fIndex);
292					if (child) {
293						Node *node = NULL;
294						error = fTree->GetNode(child->GetBlockNumber(), &node);
295						if (error == B_OK)
296							error = _PushCurrentNode(node, 0);
297					} else
298						error = B_ENTRY_NOT_FOUND;
299				} else
300					error = B_ENTRY_NOT_FOUND;
301				break;
302			}
303		}
304	}
305	return error;
306}
307
308// GoToPreviousLeaf
309/*!	\brief Goes to the previous leaf node.
310
311	This method operates only at leaf node level. It finds the next leaf
312	node to the left. Fails, if the current node is no leaf node.
313
314	\param node Pointer to a pre-allocated LeafNode pointer that shall be set
315		   to the found node.
316	\return \c B_OK, if everything went fine
317*/
318status_t
319TreeIterator::GoToPreviousLeaf(LeafNode **node)
320{
321	status_t error = InitCheck();
322	if (error == B_OK) {
323		if (fCurrentNode->IsLeaf()) {
324			// find downmost branch on our path, that leads further left
325			bool found = false;
326			while (error == B_OK && !found) {
327				error = GoTo(UP);
328				if (error == B_OK)
329					found = (GoTo(BACKWARDS) == B_OK);
330			}
331			// descend the branch to the rightmost leaf
332			if (error == B_OK) {
333				// one level down
334				error = GoTo(DOWN);
335				// then keep right
336				while (error == B_OK && fCurrentNode->IsInternal()) {
337					fIndex = fCurrentNode->CountItems();
338					error = GoTo(DOWN);
339				}
340			}
341			// set the result
342			if (error == B_OK && node)
343				*node = fCurrentNode->ToLeafNode();
344		} else
345			error = B_ENTRY_NOT_FOUND;
346	}
347	return error;
348}
349
350// GoToNextLeaf
351/*!	\brief Goes to the next leaf node.
352
353	This method operates only at leaf node level. It finds the next leaf
354	node to the right. Fails, if the current node is no leaf node.
355
356	\param node Pointer to a pre-allocated LeafNode pointer that shall be set
357		   to the found node.
358	\return \c B_OK, if everything went fine
359*/
360status_t
361TreeIterator::GoToNextLeaf(LeafNode **node)
362{
363	status_t error = InitCheck();
364	if (error == B_OK) {
365		if (fCurrentNode->IsLeaf()) {
366			// find downmost branch on our path, that leads further right
367			bool found = false;
368			while (error == B_OK && !found) {
369				error = GoTo(UP);
370				if (error == B_OK)
371					found = (GoTo(FORWARD) == B_OK);
372			}
373			// descend the branch to the leftmost leaf
374			while (error == B_OK && fCurrentNode->IsInternal())
375				error = GoTo(DOWN);
376			// set the result
377			if (error == B_OK && node)
378				*node = fCurrentNode->ToLeafNode();
379		} else
380			error = B_ENTRY_NOT_FOUND;
381	}
382	return error;
383}
384
385// FindRightMostLeaf
386/*!	\brief Finds the rightmost leaf node that may contain the supplied key.
387
388	The method starts at the current node and only goes downwards.
389	In the spanned subtree it finds the rightmost leaf node whose left
390	delimiting key is not greater than the supplied key.
391
392	\param key The key to be found.
393	\param node Pointer to a pre-allocated LeafNode pointer that shall be set
394		   to the found node.
395	\return \c B_OK, if everything went fine.
396*/
397status_t
398TreeIterator::FindRightMostLeaf(const VKey *k, LeafNode **node)
399{
400//printf("TreeIterator::FindRightMostLeaf()\n");
401	status_t error = (k ? InitCheck() : B_BAD_VALUE);
402	while (error == B_OK && fCurrentNode->IsInternal()) {
403		int32 index = 0;
404		error = _SearchRightMost(fCurrentNode->ToInternalNode(), k, &index);
405		if (error == B_OK) {
406			fIndex = index;
407			error = GoTo(DOWN);
408		}
409	}
410//if (error == B_OK)
411//printf("found node: index: %ld\n", fIndex);
412	// set the result
413	if (error == B_OK && node)
414		*node = fCurrentNode->ToLeafNode();
415//printf("TreeIterator::FindRightMostLeaf() done: %s\n", strerror(error));
416	return error;
417}
418
419// Suspend
420/*!	\brief Suspends the iterator.
421
422	This means, the current node is Put() and its block number and child node
423	index pushed onto the tree path. This should be done, when the iteration
424	is not to be continued immediately (or for the foreseeable future) and
425	the node block shall not be locked in memory for that time.
426
427	Resume() is to be called, before the iteration can be continued.
428
429	\return \c B_OK, if everything went fine.
430*/
431status_t
432TreeIterator::Suspend()
433{
434	status_t error = InitCheck();
435	if (error == B_OK)
436		error = _PushCurrentNode();
437	if (error == B_OK)
438		fCurrentNode = NULL;
439	return error;
440}
441
442// Resume
443/*!	\brief Resumes the iteration.
444
445	To be called after a Suspend(), before the iteration can be continued.
446
447	\return \c B_OK, if everything went fine.
448*/
449status_t
450TreeIterator::Resume()
451{
452	status_t error
453		= (fTree && !fCurrentNode && fPath ? fPath->InitCheck() : B_NO_INIT);
454	if (error == B_OK)
455		error = _PopTopNode();
456	return error;
457}
458
459// _PushCurrentNode
460status_t
461TreeIterator::_PushCurrentNode(Node *newTopNode, int32 newIndex)
462{
463	status_t error = fPath->PushElement(fCurrentNode->GetNumber(), fIndex);
464	if (error == B_OK) {
465		fCurrentNode->Put();
466		fCurrentNode = newTopNode;
467		fIndex = newIndex;
468	}
469	return error;
470}
471
472// _PopTopNode
473status_t
474TreeIterator::_PopTopNode()
475{
476	status_t error = B_OK;
477	if (fPath->GetLength() > 0) {
478		const TreePath::Element *element = fPath->GetTopElement();
479		Node *node = NULL;
480		error = fTree->GetNode(element->GetBlockNumber(), &node);
481		if (error == B_OK) {
482			if (fCurrentNode)
483				fCurrentNode->Put();
484			fCurrentNode = node;
485			fIndex = element->GetIndex();
486			fPath->PopElement();
487		}
488	} else
489		error = B_BAD_VALUE;
490	return error;
491}
492
493// _SearchRightMost
494status_t
495TreeIterator::_SearchRightMost(InternalNode *node, const VKey *k, int32 *index)
496{
497//FUNCTION_START();
498	// find the last node that may contain the key, i.e. the node with the
499	// greatest index whose left delimiting key is less or equal the given key
500	status_t error = (node && k && index ? B_OK : B_BAD_VALUE);
501	if (error == B_OK) {
502//PRINT(("  searching: "));
503//k->Dump();
504		// binary search: lower and upper are node indices, mid is a key index
505		int32 lower = 0;
506		int32 upper = node->CountItems();
507		while (lower < upper) {
508			int32 mid = (lower + upper) / 2;	// lower <= mid < upper <= count
509			VKey midKey(node->KeyAt(mid));
510//PRINT(("  mid: %3ld: ", mid));
511//midKey.Dump();
512			if (*k < midKey)			// => node index <= mid
513				upper = mid;					// lower <= upper < count
514			else								// => node index > mid
515				lower = mid + 1;				// lower <= upper <= count
516//PRINT(("  lower: %ld, upper: %ld\n", lower, upper));
517		}
518		if (error == B_OK)
519			*index = lower;
520	}
521//RETURN_ERROR(error);
522	return error;
523}
524
525
526/*!
527	\class ItemIterator
528	\brief Class used to iterate through leaf node items.
529
530	A ItemIterator is usually initialized to the root node of the tree,
531	and before it can be used for iteration, FindRightMostClose() or
532	FindRightMost() must be invoked. They set the iterator to an item
533	and afterwards one can use GoToPrevious() and GoToNext() to get the
534	previous/next ones.
535*/
536
537// constructor
538ItemIterator::ItemIterator(Tree *tree)
539	: fTreeIterator(tree),
540	  fIndex(0)
541{
542}
543
544// SetTo
545status_t
546ItemIterator::SetTo(Tree *tree)
547{
548	status_t error = fTreeIterator.SetTo(tree);
549	fIndex = 0;
550	return error;
551}
552
553// InitCheck
554status_t
555ItemIterator::InitCheck() const
556{
557	return fTreeIterator.InitCheck();
558}
559
560// GetCurrent
561status_t
562ItemIterator::GetCurrent(Item *item)
563{
564	LeafNode *node = NULL;
565	status_t error = (item ? _GetLeafNode(&node) : B_BAD_VALUE);
566	if (error == B_OK) {
567		if (fIndex >= 0 && fIndex < node->CountItems())
568			error = item->SetTo(node, fIndex);
569		else
570			error = B_ENTRY_NOT_FOUND;
571	}
572	return error;
573}
574
575// GoToPrevious
576status_t
577ItemIterator::GoToPrevious(Item *item)
578{
579	LeafNode *node = NULL;
580	status_t error = _GetLeafNode(&node);
581	if (error == B_OK) {
582		// get the leaf node on which the next item resides
583		int32 newIndex = fIndex - 1;
584		while (error == B_OK
585			   && (newIndex < 0 || newIndex >= node->CountItems())) {
586			error = fTreeIterator.GoToPreviousLeaf(&node);
587			if (error == B_OK)
588				newIndex = node->CountItems() - 1;
589		}
590		// got the node, get the item
591		if (error == B_OK) {
592			fIndex = newIndex;
593			if (item)
594				error = item->SetTo(node, fIndex);
595		}
596	}
597	return error;
598}
599
600// GoToNext
601status_t
602ItemIterator::GoToNext(Item *item)
603{
604//PRINT(("ItemIterator::GoToNext()\n"));
605	LeafNode *node = NULL;
606	status_t error = _GetLeafNode(&node);
607	if (error == B_OK) {
608//PRINT(("  leaf node: %lld\n", node->GetNumber()));
609		// get the leaf node on which the next item resides
610		int32 newIndex = fIndex + 1;
611		while (error == B_OK && newIndex >= node->CountItems()) {
612			error = fTreeIterator.GoToNextLeaf(&node);
613			newIndex = 0;
614		}
615		// got the node, get the item
616		if (error == B_OK) {
617//PRINT(("  leaf node now: %lld\n", node->GetNumber()));
618			fIndex = newIndex;
619//PRINT(("  index now: %ld\n", fIndex));
620			if (item)
621				error = item->SetTo(node, fIndex);
622		}
623	}
624//PRINT(("ItemIterator::GoToNext() done: %s\n", strerror(error)));
625	return error;
626}
627
628// FindRightMostClose
629/*!	\brief Finds the rightmost item that may contain the supplied key.
630
631	The method finds the rightmost item whose left delimiting key is not
632	greater than the supplied key.
633
634	\param key The key to be found.
635	\param item Pointer to a pre-allocated Item that shall be set
636		   to the found item.
637	\return \c B_OK, if everything went fine.
638*/
639status_t
640ItemIterator::FindRightMostClose(const VKey *k, Item *item)
641{
642//printf("ItemIterator::FindRightMostClose()\n");
643	status_t error = (k ? InitCheck() : B_BAD_VALUE);
644	// find rightmost leaf node with the given key
645	if (error == B_OK)
646		error = fTreeIterator.FindRightMostLeaf(k);
647	// search the node for the key
648	if (error == B_OK) {
649		LeafNode *node = fTreeIterator.GetNode()->ToLeafNode();
650		error = _SearchRightMost(node, k, &fIndex);
651		if (error == B_OK && item)
652{
653			error = item->SetTo(node, fIndex);
654//VKey itemKey;
655//printf("  found item: ");
656//item->GetKey(&itemKey)->Dump();
657}
658	}
659//printf("ItemIterator::FindRightMostClose() done: %s\n", strerror(error));
660	return error;
661}
662
663// FindRightMost
664/*!	\brief Finds the rightmost item that starts with the supplied key.
665
666	The method finds the rightmost item whose left delimiting key is equal
667	to the supplied key.
668
669	\param key The key to be found.
670	\param item Pointer to a pre-allocated Item that shall be set
671		   to the found item.
672	\return \c B_OK, if everything went fine.
673*/
674status_t
675ItemIterator::FindRightMost(const VKey *k, Item *item)
676{
677//TFUNCTION_START();
678	// find the first item with a greater or equal key, and check whether the
679	// key is equal
680	Item closeItem;
681	status_t error = FindRightMostClose(k, &closeItem);
682//REPORT_ERROR(error);
683	if (error == B_OK) {
684		VKey itemKey;
685		closeItem.GetHeader()->GetKey(&itemKey);
686		if (*k == itemKey) {
687			if (item)
688				*item = closeItem;
689		} else
690{
691//PRINT(("keys not equal: dirID: %lu, objectID: %lu, offset: %Lu, type: %hu\n",
692//itemKey.GetDirID(), itemKey.GetObjectID(), itemKey.GetOffset(), itemKey.GetType()));
693			error = B_ENTRY_NOT_FOUND;
694}
695	}
696//REPORT_ERROR(error);
697	return error;
698}
699
700// Suspend
701/*! \brief Suspends the iterator.
702	\see TreeIterator::Suspend().
703	\return \c B_OK, if everything went fine.
704*/
705status_t
706ItemIterator::Suspend()
707{
708	return fTreeIterator.Suspend();
709}
710
711// Resume
712/*! \brief Resumes the iteration.
713	\see TreeIterator::Resume().
714	\return \c B_OK, if everything went fine.
715*/
716status_t
717ItemIterator::Resume()
718{
719	return fTreeIterator.Resume();
720}
721
722// _GetLeafNode
723status_t
724ItemIterator::_GetLeafNode(LeafNode **leafNode) const
725{
726	status_t error = InitCheck();
727	if (error == B_OK) {
728		if (Node *node = fTreeIterator.GetNode()) {
729			if (node->IsLeaf())
730				*leafNode = node->ToLeafNode();
731			else
732				error = B_ENTRY_NOT_FOUND;
733		} else
734			error = B_ERROR;
735	}
736	return error;
737}
738
739// _SearchRightMost
740status_t
741ItemIterator::_SearchRightMost(LeafNode *node, const VKey *k,
742							   int32 *index) const
743{
744//FUNCTION_START();
745	status_t error = (node && k && index ? B_OK : B_BAD_VALUE);
746	// find the first item with a less or equal key
747	if (error == B_OK) {
748//PRINT(("  searching: "));
749//k->Dump();
750/*
751		// simple linear search backwards
752		int32 foundIndex = -1;
753		for (int32 i = node->CountItems() - 1; foundIndex < 0 && i >= 0; i--) {
754			VKey itemKey;
755			node->ItemHeaderAt(i)->GetKey(&itemKey);
756			if (itemKey <= *k) {
757				foundIndex = i;
758				error = B_OK;
759				break;
760			}
761		}
762		// check whether all item keys are greater
763		if (foundIndex < 0)
764			error = B_ENTRY_NOT_FOUND;
765		// set result
766		if (error == B_OK)
767			*index = foundIndex;
768*/
769		// binary search
770		// check lower bound first
771		VKey lowerKey;
772		node->ItemHeaderAt(0)->GetKey(&lowerKey);
773		if (lowerKey <= *k) {
774			// pre-conditions hold
775			int32 lower = 0;
776			int32 upper = node->CountItems() - 1;
777			while (lower < upper) {
778				int32 mid = (lower + upper + 1) / 2;
779				VKey midKey;
780				node->ItemHeaderAt(mid)->GetKey(&midKey);
781//PRINT(("  mid: %3ld: ", mid));
782//midKey.Dump();
783				if (midKey > *k)
784					upper = mid - 1;
785				else
786					lower = mid;
787//PRINT(("  lower: %ld, upper: %ld\n", lower, upper));
788			}
789			*index = lower;
790		} else
791			error = B_ENTRY_NOT_FOUND;
792	}
793//RETURN_ERROR(error);
794	return error;
795}
796
797
798/*!
799	\class ObjectItemIterator
800	\brief Class used to iterate through leaf node items.
801
802	This class basically wraps the ItemIterator class and provides a
803	more convenient interface for iteration through items of one given
804	object (directory, link or file). It finds only items of the object
805	identified by the dir and object ID specified on initialization.
806*/
807
808// constructor
809/*!	\brief Creates and initializes a ObjectItemIterator.
810
811	The iterator is initialized to find only items belonging to the object
812	identified by \a dirID and \a objectID. \a startOffset specifies the
813	offset (key offset) at which the iteration shall begin.
814
815	Note, that offsets don't need to be unique for an object, at least not
816	for files -- all indirect (and direct?) items have the same offset (1).
817	This has the ugly side effect, when iterating forward there may be items
818	with the same offset on previous nodes that will be skipped at the
819	beginning. The GetNext() does always find the item on the rightmost
820	possible node. Therefore searching forward starting with FIRST_ITEM_OFFSET
821	doesn't work for files!
822
823	\param tree The tree.
824	\param dirID The directory ID of the object.
825	\param objectID The object ID of the object.
826	\param startOffset The offset at which to begin the iteration.
827*/
828ObjectItemIterator::ObjectItemIterator(Tree *tree, uint32 dirID,
829									   uint32 objectID, uint64 startOffset)
830	: fItemIterator(tree),
831	  fDirID(dirID),
832	  fObjectID(objectID),
833	  fOffset(startOffset),
834	  fFindFirst(true),
835	  fDone(false)
836{
837}
838
839// SetTo
840/*!	\brief Re-initializes a ObjectItemIterator.
841
842	The iterator is initialized to find only items belonging to the object
843	identified by \a dirID and \a objectID. \a startOffset specifies the
844	offset (key offset) at which the iteration shall begin.
845
846	\param tree The tree.
847	\param dirID The directory ID of the object.
848	\param objectID The object ID of the object.
849	\param startOffset The offset at which to begin the iteration.
850	\return \c B_OK, if everything went fine.
851*/
852status_t
853ObjectItemIterator::SetTo(Tree *tree, uint32 dirID, uint32 objectID,
854						  uint64 startOffset)
855{
856	status_t error = fItemIterator.SetTo(tree);
857	fDirID = dirID;
858	fObjectID = objectID;
859	fOffset = startOffset;
860	fFindFirst = true;
861	fDone = false;
862	return error;
863}
864
865// InitCheck
866status_t
867ObjectItemIterator::InitCheck() const
868{
869	return fItemIterator.InitCheck();
870}
871
872// GetNext
873/*!	\brief Returns the next item belonging to the object.
874
875	Note, that offsets don't need to be unique for an object, at least not
876	for files -- all indirect (and direct?) items have the same offset (1).
877	This has the ugly side effect, when iterating forward there may be items
878	with the same offset on previous nodes that will be skipped at the
879	beginning. The GetNext() does always find the item on the rightmost
880	possible node. Therefore searching forward starting with FIRST_ITEM_OFFSET
881	doesn't work for files!
882
883	\param foundItem Pointer to a pre-allocated Item that shall be set
884		   to the found item.
885	\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
886			through.
887*/
888status_t
889ObjectItemIterator::GetNext(Item *foundItem)
890{
891//PRINT(("ObjectItemIterator::GetNext(): fDirID: %lu, fObjectID: %lu\n", fDirID, fObjectID));
892	status_t error = (foundItem ? InitCheck() : B_BAD_VALUE);
893	if (error == B_OK && fDone)
894		error = B_ENTRY_NOT_FOUND;
895	if (error == B_OK) {
896		// get the next item
897		Item item;
898		if (fFindFirst) {
899			// first invocation: find the item with the given IDs and offset
900			VKey k(fDirID, fObjectID, fOffset, 0, KEY_FORMAT_3_5);
901			error = fItemIterator.FindRightMostClose(&k, &item);
902			fFindFirst = false;
903		} else {
904			// item iterator positioned, get the next item
905			error = fItemIterator.GoToNext(&item);
906//REPORT_ERROR(error);
907		}
908		// check whether the item belongs to our object
909		if (error == B_OK) {
910			VKey itemKey;
911			if (item.GetDirID() == fDirID && item.GetObjectID() == fObjectID)
912				*foundItem = item;
913			else
914{
915//PRINT(("  found item for another object: (%lu, %lu)\n", item.GetDirID(), item.GetObjectID()));
916				error = B_ENTRY_NOT_FOUND;
917}
918		}
919		if (error != B_OK)
920			fDone = true;
921	}
922//	return error;
923RETURN_ERROR(error)
924}
925
926// GetNext
927/*!	\brief Returns the next item belonging to the object.
928
929	This version finds only items of the specified type.
930
931	\param foundItem Pointer to a pre-allocated Item that shall be set
932		   to the found item.
933	\param type The type the found item must have.
934	\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
935			through.
936*/
937status_t
938ObjectItemIterator::GetNext(Item *foundItem, uint32 type)
939{
940	status_t error = B_OK;
941	do {
942		error = GetNext(foundItem);
943	} while (error == B_OK && foundItem->GetType() != type);
944	return error;
945}
946
947// GetPrevious
948/*!	\brief Returns the previous item belonging to the object.
949	\param foundItem Pointer to a pre-allocated Item that shall be set
950		   to the found item.
951	\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
952			through.
953*/
954status_t
955ObjectItemIterator::GetPrevious(Item *foundItem)
956{
957//PRINT(("ObjectItemIterator::GetPrevious()\n"));
958	status_t error = (foundItem ? InitCheck() : B_BAD_VALUE);
959	if (error == B_OK && fDone)
960		error = B_ENTRY_NOT_FOUND;
961	if (error == B_OK) {
962		// get the next item
963		Item item;
964		if (fFindFirst) {
965			// first invocation: find the rightmost item of the object
966			VKey k(fDirID, fObjectID, fOffset, 0xffffffffUL, KEY_FORMAT_3_5);
967			error = fItemIterator.FindRightMostClose(&k, &item);
968			fFindFirst = false;
969		} else {
970			// item iterator positioned, get the previous item
971			error = fItemIterator.GoToPrevious(&item);
972		}
973		// check whether the item belongs to our object
974		if (error == B_OK) {
975			VKey itemKey;
976			if (item.GetDirID() == fDirID && item.GetObjectID() == fObjectID) {
977//PRINT(("  found item: %lu, %lu, %Lu\n", fDirID, fObjectID, item.GetOffset()));
978				*foundItem = item;
979			} else
980{
981//PRINT(("  item belongs to different object: (%lu, %lu)\n", item.GetDirID(), item.GetObjectID()));
982				error = B_ENTRY_NOT_FOUND;
983}
984		}
985		if (error != B_OK)
986			fDone = true;
987	}
988//PRINT(("ObjectItemIterator::GetPrevious() done: %s\n", strerror(error)));
989	return error;
990}
991
992// GetPrevious
993/*!	\brief Returns the previous item belonging to the object.
994
995	This version finds only items of the specified type.
996
997	\param foundItem Pointer to a pre-allocated Item that shall be set
998		   to the found item.
999	\param type The type the found item must have.
1000	\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
1001			through.
1002*/
1003status_t
1004ObjectItemIterator::GetPrevious(Item *foundItem, uint32 type)
1005{
1006	status_t error = B_OK;
1007	do {
1008		error = GetPrevious(foundItem);
1009	} while (error == B_OK && foundItem->GetType() != type);
1010	return error;
1011}
1012
1013// Suspend
1014/*! \brief Suspends the iterator.
1015	\see ItemIterator::Suspend().
1016	\return \c B_OK, if everything went fine.
1017*/
1018status_t
1019ObjectItemIterator::Suspend()
1020{
1021	return fItemIterator.Suspend();
1022}
1023
1024// Resume
1025/*! \brief Resumes the iteration.
1026	\see ItemIterator::Resume().
1027	\return \c B_OK, if everything went fine.
1028*/
1029status_t
1030ObjectItemIterator::Resume()
1031{
1032	return fItemIterator.Resume();
1033}
1034
1035
1036/*!
1037	\class DirEntryIterator
1038	\brief Class used to iterate through DirEntries.
1039*/
1040
1041// constructor
1042/*!	\brief Creates and initializes a DirEntryIterator.
1043
1044	The iterator is initialized to find only entries belonging to the directory
1045	identified by \a dirID and \a objectID. \a startOffset specifies the
1046	offset (key offset) at which the iteration shall begin. If \a fixedHash
1047	is \c true, only items that have the same hash value as \a startOffset
1048	are found.
1049
1050	\param tree The tree.
1051	\param dirID The directory ID of the object.
1052	\param objectID The object ID of the object.
1053	\param startOffset The offset at which to begin the iteration.
1054	\param fixedHash \c true to find only entries with the same hash as
1055		   \a startOffset
1056*/
1057DirEntryIterator::DirEntryIterator(Tree *tree, uint32 dirID, uint32 objectID,
1058								   uint64 startOffset, bool fixedHash)
1059	: fItemIterator(tree, dirID, objectID, startOffset),
1060	  fDirItem(),
1061	  fIndex(-1),
1062	  fFixedHash(fixedHash),
1063	  fDone(false)
1064{
1065}
1066
1067// SetTo
1068/*!	\brief Re-initializes a DirEntryIterator.
1069
1070	The iterator is initialized to find only entries belonging to the directory
1071	identified by \a dirID and \a objectID. \a startOffset specifies the
1072	offset (key offset) at which the iteration shall begin. If \a fixedHash
1073	is \c true, only items that have the same hash value as \a startOffset
1074	are found.
1075
1076	\param tree The tree.
1077	\param dirID The directory ID of the object.
1078	\param objectID The object ID of the object.
1079	\param startOffset The offset at which to begin the iteration.
1080	\param fixedHash \c true to find only entries with the same hash as
1081		   \a startOffset
1082*/
1083status_t
1084DirEntryIterator::SetTo(Tree *tree, uint32 dirID, uint32 objectID,
1085						uint64 startOffset, bool fixedHash)
1086{
1087	fDirItem.Unset();
1088	status_t error = fItemIterator.SetTo(tree, dirID, objectID, startOffset);
1089	fIndex = -1;
1090	fFixedHash = fixedHash;
1091	fDone = false;
1092	return error;
1093}
1094
1095// InitCheck
1096status_t
1097DirEntryIterator::InitCheck() const
1098{
1099	return fItemIterator.InitCheck();
1100}
1101
1102// Rewind
1103/*!	\brief Rewinds the iterator.
1104
1105	Simply re-initializes the iterator to the parameters of the last
1106	initialization.
1107
1108	\return \c B_OK, if everything went fine.
1109*/
1110status_t
1111DirEntryIterator::Rewind()
1112{
1113	return SetTo(GetTree(), GetDirID(), GetObjectID(), GetOffset(),
1114				 fFixedHash);
1115}
1116
1117// GetNext
1118/*!	\brief Returns the next entry belonging to the directory.
1119	\param foundItem Pointer to a pre-allocated Item that shall be set
1120		   to the found item.
1121	\param entryIndex Pointer to a pre-allocated int32 that shall be set
1122		   to the found entry index.
1123	\param _entry Pointer to a pre-allocated DirEntry pointer that shall be set
1124		   to the found entry. May be \c NULL.
1125	\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
1126			through.
1127*/
1128status_t
1129DirEntryIterator::GetNext(DirItem *foundItem, int32 *entryIndex,
1130						  DirEntry **_entry)
1131{
1132	status_t error = (foundItem && entryIndex ? InitCheck() : B_BAD_VALUE);
1133	// get the next DirItem, if necessary
1134	// the loop skips empty DirItems gracefully
1135	while (error == B_OK
1136		   && (fIndex < 0 || fIndex >= fDirItem.GetEntryCount())) {
1137		error = fItemIterator.GetNext(&fDirItem, TYPE_DIRENTRY);
1138		if (error == B_OK) {
1139			if (fDirItem.Check() == B_OK)
1140				fIndex = 0;
1141			else	// bad data: skip the item
1142				fIndex = -1;
1143		}
1144	}
1145	// get the next entry and check whether it has the correct offset
1146	if (error == B_OK) {
1147		DirEntry *entry = fDirItem.EntryAt(fIndex);
1148		if (!fFixedHash
1149			|| offset_hash_value(entry->GetOffset())
1150			   == offset_hash_value(GetOffset())) {
1151			*foundItem = fDirItem;
1152			*entryIndex = fIndex;
1153			if (_entry)
1154				*_entry = entry;
1155			fIndex++;
1156		} else
1157			error = B_ENTRY_NOT_FOUND;
1158	}
1159	return error;
1160}
1161
1162// GetPrevious
1163/*!	\brief Returns the previous entry belonging to the directory.
1164	\param foundItem Pointer to a pre-allocated Item that shall be set
1165		   to the found item.
1166	\param entryIndex Pointer to a pre-allocated int32 that shall be set
1167		   to the found entry index.
1168	\param _entry Pointer to a pre-allocated DirEntry pointer that shall be set
1169		   to the found entry. May be \c NULL.
1170	\return \c B_OK, if everything went fine, \c B_ENTRY_NOT_FOUND, if we're
1171			through.
1172*/
1173status_t
1174DirEntryIterator::GetPrevious(DirItem *foundItem, int32 *entryIndex,
1175							  DirEntry **_entry)
1176{
1177//printf("DirEntryIterator::GetPrevious()\n");
1178	status_t error = (foundItem && entryIndex ? InitCheck() : B_BAD_VALUE);
1179	if (error == B_OK && fDone)
1180		error = B_ENTRY_NOT_FOUND;
1181	// get the next DirItem, if necessary
1182	// the loop skips empty DirItems gracefully
1183	while (error == B_OK
1184		   && (fIndex < 0 || fIndex >= fDirItem.GetEntryCount())) {
1185		error = fItemIterator.GetPrevious(&fDirItem, TYPE_DIRENTRY);
1186		if (error == B_OK) {
1187			if (fDirItem.Check() == B_OK)
1188				fIndex = fDirItem.GetEntryCount() - 1;
1189			else	// bad data: skip the item
1190				fIndex = -1;
1191		}
1192	}
1193//printf("  found dir item: %s\n", strerror(error));
1194	// skip entries with a greater offset
1195	while (error == B_OK && fIndex >= 0
1196		   && fDirItem.EntryAt(fIndex)->GetOffset() > GetOffset()) {
1197//printf("  skipping entry %ld: offset %lu\n", fIndex, fDirItem.EntryAt(fIndex)->GetOffset());
1198		fIndex--;
1199	}
1200	// get the entry and check whether it has the correct offset
1201	if (error == B_OK) {
1202//printf("  entries with greater offsets skipped: index: %ld\n", fIndex);
1203		if (fIndex >= 0
1204//&& (printf("  entry index %ld: offset %lu\n", fIndex, fDirItem.EntryAt(fIndex)->GetOffset()), true)
1205			&& (!fFixedHash
1206				|| offset_hash_value(fDirItem.EntryAt(fIndex)->GetOffset())
1207				   == offset_hash_value(GetOffset()))) {
1208//printf("  entry found\n");
1209			DirEntry *entry = fDirItem.EntryAt(fIndex);
1210			*foundItem = fDirItem;
1211			*entryIndex = fIndex;
1212			fDone = (fFixedHash
1213					 && offset_generation_number(entry->GetOffset()) == 0);
1214			if (_entry)
1215				*_entry = entry;
1216			fIndex--;
1217		} else
1218			error = B_ENTRY_NOT_FOUND;
1219	}
1220//printf("DirEntryIterator::GetPrevious() done: %s\n", strerror(error));
1221	return error;
1222}
1223
1224// Suspend
1225/*! \brief Suspends the iterator.
1226	\see ObjectItemIterator::Suspend().
1227	\return \c B_OK, if everything went fine.
1228*/
1229status_t
1230DirEntryIterator::Suspend()
1231{
1232	return fItemIterator.Suspend();
1233}
1234
1235// Resume
1236/*! \brief Resumes the iteration.
1237	\see ObjectItemIterator::Resume().
1238	\return \c B_OK, if everything went fine.
1239*/
1240status_t
1241DirEntryIterator::Resume()
1242{
1243	return fItemIterator.Resume();
1244}
1245
1246
1247/*!
1248	\class StreamReader
1249	\brief Class used to read object streams (files, links).
1250*/
1251
1252// constructor
1253/*!	\brief Creates and initializes a StreamReader.
1254
1255	The reader is initialized to read the stream of the object
1256	identified by \a dirID and \a objectID.
1257
1258	\param tree The tree.
1259	\param dirID The directory ID of the object.
1260	\param objectID The object ID of the object.
1261*/
1262StreamReader::StreamReader(Tree *tree, uint32 dirID, uint32 objectID)
1263	: fItemIterator(tree, dirID, objectID, SD_OFFSET),
1264	  fItem(),
1265	  fStreamSize(-1),
1266	  fItemOffset(-1),
1267	  fItemSize(0),
1268	  fBlockSize(0)
1269{
1270	fBlockSize = tree->GetBlockSize();
1271}
1272
1273// SetTo
1274/*!	\brief Creates and initializes a StreamReader.
1275
1276	The reader is initialized to read the stream of the object
1277	identified by \a dirID and \a objectID.
1278
1279	\param tree The tree.
1280	\param dirID The directory ID of the object.
1281	\param objectID The object ID of the object.
1282	\return \c B_OK, if everything went fine.
1283*/
1284status_t
1285StreamReader::SetTo(Tree *tree, uint32 dirID, uint32 objectID)
1286{
1287	fItem.Unset();
1288	status_t error = fItemIterator.SetTo(tree, dirID, objectID, SD_OFFSET);
1289	fStreamSize = -1;
1290	fItemOffset = -1;
1291	fItemSize = 0;
1292	fBlockSize = tree->GetBlockSize();
1293	return error;
1294}
1295
1296// InitCheck
1297status_t
1298StreamReader::InitCheck() const
1299{
1300	return fItemIterator.InitCheck();
1301}
1302
1303// ReadAt
1304/*!	\brief Tries to read the specified number of bytes from the stream into
1305		   the supplied buffer.
1306	\param position Stream position at which to start reading.
1307	\param buffer Pointer to a pre-allocated buffer into which the read data
1308		   shall be written.
1309	\param bufferSize Number of bytes to be read.
1310	\param _bytesRead Pointer to a pre-allocated size_t which shall be set to
1311		   the number of bytes actually read.
1312	\return \c B_OK, if everything went fine.
1313*/
1314status_t
1315StreamReader::ReadAt(off_t position, void *buffer, size_t bufferSize,
1316					 size_t *_bytesRead)
1317{
1318//PRINT(("StreamReader::ReadAt(%lld, %p, %lu)\n", position, buffer, bufferSize));
1319	status_t error = (position >= 0 && buffer && _bytesRead ? InitCheck()
1320															: B_BAD_VALUE);
1321	// get the size of the stream
1322	if (error == B_OK)
1323		error = _GetStreamSize();
1324	// compute the number of bytes that acually have to be read
1325	if (error == B_OK) {
1326		if (position < fStreamSize) {
1327			if (position + (off_t)bufferSize > fStreamSize)
1328				bufferSize = fStreamSize - position;
1329		} else
1330			bufferSize = 0;
1331	}
1332	// do the reading
1333	if (error == B_OK) {
1334		size_t bytesRead = 0;
1335		while (error == B_OK && bufferSize > 0
1336			   && (error = _SeekTo(position)) == B_OK) {
1337//PRINT(("  seeked to %lld: fItemOffset: %lld, fItemSize: %lld\n", position,
1338//fItemOffset, fItemSize));
1339			off_t inItemOffset = max_c(0LL, position - fItemOffset);
1340			off_t toRead = min_c(fItemSize - inItemOffset, (off_t)bufferSize);
1341			switch (fItem.GetType()) {
1342				case TYPE_INDIRECT:
1343					error = _ReadIndirectItem(inItemOffset, buffer, toRead);
1344					break;
1345				case TYPE_DIRECT:
1346					error = _ReadDirectItem(inItemOffset, buffer, toRead);
1347					break;
1348				case TYPE_STAT_DATA:
1349				case TYPE_DIRENTRY:
1350				case TYPE_ANY:
1351				default:
1352					FATAL(("Neither direct nor indirect item! type: %u\n",
1353						   fItem.GetType()));
1354					error = B_IO_ERROR;
1355					break;
1356			}
1357			if (error == B_OK) {
1358				buffer = (uint8*)buffer + toRead;
1359				position += toRead;
1360				bufferSize -= toRead;
1361				bytesRead += toRead;
1362			}
1363		}
1364		*_bytesRead = bytesRead;
1365	}
1366//if (error == B_OK)
1367//PRINT(("StreamReader::ReadAt() done: read: %lu bytes\n", *_bytesRead))
1368//else
1369//PRINT(("StreamReader::ReadAt() done: %s\n", strerror(error)))
1370	return error;
1371}
1372
1373// Suspend
1374/*! \brief Suspends the reader.
1375	\see ObjectItemIterator::Suspend().
1376	\return \c B_OK, if everything went fine.
1377*/
1378status_t
1379StreamReader::Suspend()
1380{
1381	return fItemIterator.Suspend();
1382}
1383
1384// Resume
1385/*! \brief Resumes the reader.
1386	\see ObjectItemIterator::Resume().
1387	\return \c B_OK, if everything went fine.
1388*/
1389status_t
1390StreamReader::Resume()
1391{
1392	return fItemIterator.Resume();
1393}
1394
1395// _GetStreamSize
1396status_t
1397StreamReader::_GetStreamSize()
1398{
1399	status_t error = B_OK;
1400	if (fStreamSize < 0) {
1401		// get the stat item
1402		error = fItemIterator.GetNext(&fItem, TYPE_STAT_DATA);
1403		if (error == B_OK) {
1404			StatData statData;
1405			error = (static_cast<StatItem*>(&fItem))->GetStatData(&statData);
1406			if (error == B_OK)
1407				fStreamSize = statData.GetSize();
1408		}
1409	}
1410	return error;
1411}
1412
1413// _SeekTo
1414status_t
1415StreamReader::_SeekTo(off_t position)
1416{
1417	status_t error = _GetStreamSize();
1418	if (error == B_OK && fItemOffset < 0)
1419		fItemOffset = 0;	// prepare for the loop
1420	if (error == B_OK) {
1421		if (2 * position < fItemOffset) {
1422			// seek backwards
1423			// since the position is closer to the beginning of the file than
1424			// to the current position, we simply reinit the item iterator
1425			// and seek forward
1426			error = fItemIterator.SetTo(GetTree(), GetDirID(), GetObjectID(),
1427										SD_OFFSET);
1428			fStreamSize = -1;
1429			fItemOffset = -1;
1430			fItemSize = 0;
1431			if (error == B_OK)
1432				error = _SeekTo(position);
1433		} else if (position < fItemOffset) {
1434			// seek backwards
1435			// iterate through the items
1436			while (error == B_OK && position < fItemOffset
1437				   && (error = fItemIterator.GetPrevious(&fItem)) == B_OK) {
1438				fItemSize = 0;
1439				switch (fItem.GetType()) {
1440					case TYPE_INDIRECT:
1441					{
1442						IndirectItem &indirect
1443							= *static_cast<IndirectItem*>(&fItem);
1444						// Note, that it is assumed, that the existence of a
1445						// next item implies, that all blocks are fully used.
1446						// This is safe, since otherwise we would have never
1447						// come to the next item.
1448						fItemSize = indirect.CountBlocks() * (off_t)fBlockSize;
1449						break;
1450					}
1451					case TYPE_DIRECT:
1452						// See the comment for indirect items.
1453						fItemSize = fItem.GetLen();
1454						break;
1455					case TYPE_STAT_DATA:
1456					case TYPE_DIRENTRY:
1457					case TYPE_ANY:
1458					default:
1459						// simply skip items of other kinds
1460						break;
1461				}
1462				fItemOffset -= fItemSize;
1463			}
1464		} else if (position >= fItemOffset + fItemSize) {
1465			// seek forward
1466			// iterate through the items
1467			while (error == B_OK && position >= fItemOffset + fItemSize
1468				   && (error = fItemIterator.GetNext(&fItem)) == B_OK) {
1469				fItemOffset += fItemSize;
1470				fItemSize = 0;
1471				switch (fItem.GetType()) {
1472					case TYPE_INDIRECT:
1473					{
1474						IndirectItem &indirect
1475							= *static_cast<IndirectItem*>(&fItem);
1476						fItemSize = min(indirect.CountBlocks()
1477											* (off_t)fBlockSize,
1478										fStreamSize - fItemOffset);
1479						break;
1480					}
1481					case TYPE_DIRECT:
1482						fItemSize = min((off_t)fItem.GetLen(),
1483										fStreamSize - fItemOffset);
1484						break;
1485					case TYPE_STAT_DATA:
1486					case TYPE_DIRENTRY:
1487					case TYPE_ANY:
1488					default:
1489						// simply skip items of other kinds
1490						break;
1491				}
1492			}
1493		}
1494	}
1495//	return error;
1496RETURN_ERROR(error);
1497}
1498
1499// _ReadIndirectItem
1500status_t
1501StreamReader::_ReadIndirectItem(off_t offset, void *buffer, size_t bufferSize)
1502{
1503//PRINT(("StreamReader::_ReadIndirectItem(%lld, %p, %lu)\n", offset, buffer, bufferSize));
1504	status_t error = B_OK;
1505	IndirectItem &indirect = *static_cast<IndirectItem*>(&fItem);
1506	// skip items until the offset is reached
1507	uint32 skipItems = 0;
1508	if (offset > 0) {
1509		skipItems = uint32(offset / fBlockSize);
1510		skipItems = min(skipItems, indirect.CountBlocks());	// not necessary
1511	}
1512//PRINT(("  skipItems: %lu\n", skipItems));
1513	for (uint32 i = skipItems;
1514		 error == B_OK && bufferSize > 0 && i < indirect.CountBlocks();
1515		 i++) {
1516//PRINT(("    child %lu\n", i));
1517		// get the block
1518		Block *block = NULL;
1519		error = GetTree()->GetBlock(indirect.BlockNumberAt(i), &block);
1520		if (error == B_OK) {
1521			// copy the data into the buffer
1522			off_t blockOffset = i * (off_t)fBlockSize;
1523			uint32 localOffset = max_c(0LL, offset - blockOffset);
1524			uint32 toRead = min_c(fBlockSize - localOffset, bufferSize);
1525			memcpy(buffer,
1526				reinterpret_cast<uint8*>(block->GetData()) + localOffset,
1527				toRead);
1528
1529			block->Put();
1530			bufferSize -= toRead;
1531			buffer = (uint8*)buffer + toRead;
1532		} else {
1533			FATAL(("failed to get block %" B_PRIu64 "\n",
1534				indirect.BlockNumberAt(i)));
1535			error = B_IO_ERROR;
1536		}
1537	}
1538//PRINT(("StreamReader::_ReadIndirectItem() done: %s\n", strerror(error)))
1539	return error;
1540}
1541
1542// _ReadDirectItem
1543status_t
1544StreamReader::_ReadDirectItem(off_t offset, void *buffer, size_t bufferSize)
1545{
1546//PRINT(("StreamReader::_ReadDirectItem(%lld, %p, %lu)\n", offset, buffer, bufferSize));
1547	// copy the data into the buffer
1548	memcpy(buffer,
1549		reinterpret_cast<uint8*>(fItem.GetData()) + offset, bufferSize);
1550	return B_OK;
1551}
1552
1553