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