// Tree.cpp
//
// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de)
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can alternatively use *this file* under the terms of the the MIT
// license included in this package.
 
#include <stdio.h>
 
#include "Tree.h"
#include "Block.h"
#include "BlockCache.h"
#include "Debug.h"
#include "DirItem.h"
#include "Iterators.h"
#include "StatItem.h"
#include "Volume.h"
 
const uint32 kMaxTreeHeight = 5;
 
/*!
	\class Tree
	\brief Represents the on-disk S+Tree.
 
	This class actually doesn't have that much functionality. GetBlock()
	and GetNode() are used to request a block/tree node from disk,
	FindDirEntry() searches an entry in a directory and FindStatItem()
	gets the stat data for an object. The mammoth part of the code is
	located in the iterator code (Iterators.{cpp,h}).
*/
 
// constructor
Tree::Tree()
	: fVolume(NULL),
	  fBlockCache(NULL),
	  fRootNode(NULL),
	  fTreeHeight(0)
{
}
 
// destructor
Tree::~Tree()
{
	if (fRootNode)
		fRootNode->Put();
}
 
// Init
status_t
Tree::Init(Volume *volume, Node *rootNode, uint32 treeHeight)
{
	status_t error = (volume && volume->GetBlockCache() && rootNode
					  ? B_OK : B_BAD_VALUE);
	if (error == B_OK) {
		if (treeHeight > kMaxTreeHeight) {
			// we don't need to fail, as we can deal with that gracefully
			INFORM(("WARNING: tree height greater maximal height: %lu\n",
					treeHeight));
		}
		fVolume = volume;
		fBlockCache = fVolume->GetBlockCache();
		fRootNode = rootNode;
		fRootNode->Get();
		fTreeHeight = treeHeight;
	}
	return error;
}
 
// InitCheck
status_t
Tree::InitCheck() const
{
	return (fBlockCache && fRootNode && fTreeHeight ? B_OK : B_NO_INIT);
}
 
// GetTreeHeight
uint32
Tree::GetTreeHeight() const
{
	return fTreeHeight;
}
 
// GetBlockSize
uint32
Tree::GetBlockSize() const
{
	if (fBlockCache)
		return fBlockCache->GetBlockSize();
	return 0;
}
 
 
// GetBlockCache
BlockCache *
Tree::GetBlockCache() const
{
	return fBlockCache;
}
 
// GetRootNode
Node *
Tree::GetRootNode() const
{
	return fRootNode;
}
 
// GetBlock
status_t
Tree::GetBlock(uint64 blockNumber, Block **block)
{
	status_t error = (block ? InitCheck() : B_BAD_VALUE);
	if (error == B_OK)
		error = fBlockCache->GetBlock(blockNumber, block);
	return error;
}
 
// GetNode
status_t
Tree::GetNode(uint64 blockNumber, Node **node)
{
	status_t error = (node ? InitCheck() : B_BAD_VALUE);
	if (error == B_OK) {
		Block *block;
		error = fBlockCache->GetBlock(blockNumber, &block);
		if (error == B_OK) {
			if (block->GetKind() == Block::KIND_UNKNOWN)
				block->SetKind(Block::KIND_FORMATTED);
			if (block->GetKind() == Block::KIND_FORMATTED) {
				*node = block->ToNode();
				// check the node
				if (!(*node)->IsChecked()) {
					if ((*node)->IsInternal())
						error = (*node)->ToInternalNode()->Check();
					else if ((*node)->IsLeaf())
						error = (*node)->ToLeafNode()->Check();
					else
						error = B_BAD_DATA;
					if (error == B_OK)
						(*node)->SetChecked(true);
				}
			} else {
				block->Put();
				error = B_BAD_DATA;
			}
		}
	}
	return error;
}
 
// FindDirEntry
status_t
Tree::FindDirEntry(uint32 dirID, uint32 objectID, const char *name,
				   DirItem *foundItem, int32 *entryIndex)
{
	status_t error = (name && foundItem && entryIndex ? InitCheck()
													  : B_BAD_VALUE);
	if (error == B_OK) {
		error = FindDirEntry(dirID, objectID, name, strlen(name), foundItem,
							 entryIndex);
	}
	return error;
}
 
// FindDirEntry
status_t
Tree::FindDirEntry(uint32 dirID, uint32 objectID, const char *name,
				   size_t nameLen, DirItem *foundItem, int32 *entryIndex)
{
	status_t error = (name && foundItem && entryIndex ? InitCheck()
													  : B_BAD_VALUE);
	if (error == B_OK) {
		uint64 offset = 0;
		if (fVolume->GetKeyOffsetForName(name, nameLen, &offset) == B_OK) {
//PRINT(("Tree::FindDirEntry(): hash function: offset: %Lu (`%.*s', %lu)\n",
//offset, (int)nameLen, name, nameLen));
			// offset computed -- we can directly iterate only over entries
			// with that offset
			DirEntryIterator iterator(this, dirID, objectID, offset, true);
			DirItem dirItem;
			int32 index = 0;
			error = B_ENTRY_NOT_FOUND;
			while (iterator.GetPrevious(&dirItem, &index) == B_OK) {
				size_t itemNameLen = 0;
				if (const char *itemName
					= dirItem.EntryNameAt(index, &itemNameLen)) {
					if (itemNameLen == nameLen
						&& !strncmp(name, itemName, nameLen)) {
						// item found
						*foundItem = dirItem;
						*entryIndex = index;
						error = B_OK;
						break;
					}
				}
			}
		} else {
//PRINT(("Tree::FindDirEntry(): no hash function\n"));
			// no offset (no hash function) -- do it the slow way
			ObjectItemIterator iterator(this, dirID, objectID);
			DirItem dirItem;
			error = B_ENTRY_NOT_FOUND;
			while (iterator.GetNext(&dirItem, TYPE_DIRENTRY) == B_OK) {
				if (dirItem.Check() != B_OK) // bad data: skip item
					continue;
				int32 index = dirItem.IndexOfName(name);
				if (index >= 0) {
					// item found
					*foundItem = dirItem;
					*entryIndex = index;
					error = B_OK;
//PRINT(("  offset: %lu\n", dirItem.EntryAt(index)->GetOffset()));
					break;
				}
			}
		}
	}
	return error;
}
 
// FindStatItem
status_t
Tree::FindStatItem(uint32 dirID, uint32 objectID, StatItem *item)
{
	status_t error = (item ? InitCheck() : B_BAD_VALUE);
	if (error == B_OK) {
		ItemIterator iterator(this);
		error = iterator.InitCheck();
		VKey k(dirID, objectID, SD_OFFSET, V1_SD_UNIQUENESS, KEY_FORMAT_3_5);
		if (error == B_OK)
			error = iterator.FindRightMost(&k, item);
	}
	return error;
}
 

V576 Incorrect format. Consider checking the second actual argument of the 'dprintf' function. The memsize type argument is expected.