Attribute index support for non-special attributes

* Add AttributeIndex class.
* Each attribute does now have an attribute index cookie. The new
  attribute index service methods Node::IndexAttribute() and
  IndexCookieForAttribute() create+set/retrieve the cookie. The cookie
  is actually the attribute index's tree node.
* Add OldNodeAttribute::IndexCookieForAttribute() so the cookie is
  available when the node changes.
This commit is contained in:
Ingo Weinhold 2011-07-08 16:14:46 +02:00
parent 1cf4d082ab
commit 03aed3a314
22 changed files with 722 additions and 16 deletions

View File

@ -0,0 +1,410 @@
/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "AttributeIndex.h"
#include <new>
#include <TypeConstants.h>
#include <util/AVLTree.h>
#include <util/SinglyLinkedList.h>
#include <file_systems/QueryParserUtils.h>
#include "AttributeIndexer.h"
#include "DebugSupport.h"
#include "IndexImpl.h"
#include "Node.h"
#include "Volume.h"
struct AttributeIndexTreeKey {
const void* data;
size_t length;
AttributeIndexTreeKey()
{
}
AttributeIndexTreeKey(const void* data, size_t length)
:
data(data),
length(length)
{
}
};
struct AttributeIndexTreeValue : AVLTreeNode {
Node* node;
IndexedAttributeOwner* owner;
void* attributeCookie;
size_t length;
uint8 data[0];
static AttributeIndexTreeValue* Create(IndexedAttributeOwner* owner,
void* attributeCookie, size_t length)
{
AttributeIndexTreeValue* self = (AttributeIndexTreeValue*)malloc(
sizeof(AttributeIndexTreeValue) + length);
if (self == NULL)
return NULL;
self->owner = owner;
self->attributeCookie = attributeCookie;
self->length = length;
return self;
}
void Delete()
{
free(this);
}
};
struct AttributeIndex::TreeDefinition {
typedef TreeKey Key;
typedef TreeValue Value;
TreeDefinition(uint32 type)
:
fType(type)
{
}
AVLTreeNode* GetAVLTreeNode(Value* value) const
{
return value;
}
Value* GetValue(AVLTreeNode* node) const
{
return static_cast<Value*>(node);
}
int Compare(const Key& a, const Value* b) const
{
return QueryParser::compareKeys(fType, a.data, a.length, b->data,
b->length);
}
int Compare(const Value* a, const Value* b) const
{
return QueryParser::compareKeys(fType, a->data, a->length, b->data,
b->length);
}
private:
uint32 fType;
};
// #pragma mark - NodeTree
struct AttributeIndex::NodeTree : public AVLTree<TreeDefinition> {
typedef TreeValue Node;
NodeTree(const TreeDefinition& definition)
:
AVLTree<TreeDefinition>(definition)
{
}
};
// #pragma mark - IteratorList
class AttributeIndex::IteratorList : public SinglyLinkedList<Iterator> {};
// #pragma mark - Iterator
struct AttributeIndex::IteratorPolicy {
typedef AttributeIndex Index;
typedef TreeKey Value;
typedef AttributeIndex::NodeTree NodeTree;
typedef TreeValue TreeNode;
typedef IteratorPolicy TreePolicy;
static NodeTree* GetNodeTree(Index* index)
{
return index->fNodes;
}
static void GetTreeNodeValue(TreeNode* node, void* buffer,
size_t* _keyLength)
{
if (node->length > 0)
memcpy(buffer, node->data, node->length);
*_keyLength = node->length;
}
static Node* GetNode(TreeNode* treeNode)
{
return treeNode->node;
}
static TreeNode* GetFirstTreeNode(Index* index)
{
return index->fNodes->GetIterator().Next();
}
static TreeNode* FindClosestTreeNode(Index* index, const Value& value)
{
return index->fNodes->FindClosest(value, false);
}
};
class AttributeIndex::Iterator : public GenericIndexIterator<IteratorPolicy>,
public SinglyLinkedListLinkImpl<Iterator> {
public:
virtual void NodeChanged(Node* node, uint32 statFields,
const OldNodeAttributes& oldAttributes);
};
// #pragma mark - AttributeIndexer
AttributeIndexer::AttributeIndexer(AttributeIndex* index)
:
fIndex(index),
fIndexName(index->Name()),
fIndexType(index->Type()),
fCookie(NULL)
{
}
AttributeIndexer::~AttributeIndexer()
{
}
status_t
AttributeIndexer::CreateCookie(IndexedAttributeOwner* owner,
void* attributeCookie, uint32 attributeType, size_t attributeSize,
void*& _data, size_t& _toRead)
{
// check the attribute type and size
if (attributeType != fIndexType)
return B_ERROR;
if (fIndex->HasFixedKeyLength()) {
if (attributeSize != fIndex->KeyLength())
return B_ERROR;
} else if (attributeSize > kMaxIndexKeyLength)
attributeSize = kMaxIndexKeyLength;
// create the tree value
fCookie = AttributeIndexTreeValue::Create(owner, attributeCookie,
attributeSize);
if (fCookie == NULL)
return B_NO_MEMORY;
_data = fCookie->data;
_toRead = attributeSize;
return B_OK;
}
void
AttributeIndexer::DeleteCookie()
{
fCookie->Delete();
fCookie = NULL;
}
// #pragma mark - AttributeIndex
AttributeIndex::AttributeIndex()
:
Index(),
fNodes(NULL),
fIteratorsToUpdate(NULL),
fIndexer(NULL)
{
}
AttributeIndex::~AttributeIndex()
{
if (IsListening())
fVolume->RemoveNodeListener(this);
ASSERT(fIteratorsToUpdate->IsEmpty());
delete fIteratorsToUpdate;
delete fNodes;
delete fIndexer;
}
status_t
AttributeIndex::Init(Volume* volume, const char* name, uint32 type,
size_t keyLength)
{
status_t error = Index::Init(volume, name, type, keyLength > 0, keyLength);
if (error != B_OK)
return error;
// TODO: Letting each attribute index be a listener is gets more expensive
// the more attribute indices we have. Since most attribute indices are
// rather sparse, it might be a good idea to rather let Volume iterate
// through the actual attributes of an added node and look up and call the
// index for each one explicitly. When removing the node, the volume would
// iterate through the attributes again and determine based on the index
// cookie whether an index has to be notified.
fVolume->AddNodeListener(this, NULL);
fNodes = new(std::nothrow) NodeTree(TreeDefinition(type));
fIteratorsToUpdate = new(std::nothrow) IteratorList;
fIndexer = new(std::nothrow) AttributeIndexer(this);
if (fNodes == NULL || fIteratorsToUpdate == NULL || fIndexer == NULL)
return B_NO_MEMORY;
return B_OK;
}
int32
AttributeIndex::CountEntries() const
{
return fNodes->Count();
}
void
AttributeIndex::NodeAdded(Node* node)
{
if (node->IndexAttribute(fIndexer) != B_OK)
return;
TreeValue* treeValue = fIndexer->Cookie();
treeValue->node = node;
fNodes->Insert(treeValue);
}
void
AttributeIndex::NodeRemoved(Node* node)
{
TreeValue* treeValue = (TreeValue*)node->IndexCookieForAttribute(Name());
if (treeValue == NULL)
return;
treeValue->owner->UnsetIndexCookie(treeValue->attributeCookie);
fNodes->Remove(treeValue);
}
void
AttributeIndex::NodeChanged(Node* node, uint32 statFields,
const OldNodeAttributes& oldAttributes)
{
IteratorList iterators;
iterators.MoveFrom(fIteratorsToUpdate);
TreeValue* oldTreeValue
= (TreeValue*)oldAttributes.IndexCookieForAttribute(Name());
TreeValue* treeValue = (TreeValue*)node->IndexCookieForAttribute(Name());
if (treeValue == NULL && oldTreeValue == NULL)
return;
// move the iterators that point to the node to the previous node
if (oldTreeValue != NULL) {
for (IteratorList::Iterator it = iterators.GetIterator();
Iterator* iterator = it.Next();) {
iterator->NodeChangeBegin(node);
}
// remove the node
fNodes->Remove(oldTreeValue);
}
// re-insert the node
if (treeValue != NULL)
fNodes->Insert(treeValue);
// Move the iterators to the next node again. If the node hasn't changed
// its place, they will point to it again, otherwise to the node originally
// succeeding it.
if (oldTreeValue != NULL) {
for (IteratorList::Iterator it = iterators.GetIterator();
Iterator* iterator = it.Next();) {
iterator->NodeChangeEnd(node);
}
}
// update live queries
fVolume->UpdateLiveQueries(node, Name(), Type(),
oldTreeValue != NULL ? oldTreeValue->data : NULL,
oldTreeValue != NULL ? oldTreeValue->length : 0,
treeValue != NULL ? treeValue->data : NULL,
treeValue != NULL ? treeValue->length : 0);
if (oldTreeValue != NULL)
oldTreeValue->Delete();
}
AbstractIndexIterator*
AttributeIndex::InternalGetIterator()
{
Iterator* iterator = new(std::nothrow) Iterator;
if (iterator != NULL) {
if (!iterator->SetTo(this, TreeKey(), true)) {
delete iterator;
iterator = NULL;
}
}
return iterator;
}
AbstractIndexIterator*
AttributeIndex::InternalFind(const void* key, size_t length)
{
if (key == NULL)
return NULL;
Iterator* iterator = new(std::nothrow) Iterator;
if (iterator != NULL) {
if (!iterator->SetTo(this, TreeKey(key, length))) {
delete iterator;
iterator = NULL;
}
}
return iterator;
}
void
AttributeIndex::_AddIteratorToUpdate(Iterator* iterator)
{
fIteratorsToUpdate->Add(iterator);
}
// #pragma mark - Iterator
void
AttributeIndex::Iterator::NodeChanged(Node* node, uint32 statFields,
const OldNodeAttributes& oldAttributes)
{
fIndex->_AddIteratorToUpdate(this);
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef ATTRIBUTE_INDEX_H
#define ATTRIBUTE_INDEX_H
#include "Index.h"
#include "NodeListener.h"
class AttributeIndexer;
struct AttributeIndexTreeKey;
struct AttributeIndexTreeValue;
class AttributeIndex : public Index, private NodeListener {
public:
AttributeIndex();
virtual ~AttributeIndex();
status_t Init(Volume* volume, const char* name,
uint32 type, size_t keyLength);
virtual int32 CountEntries() const;
private:
virtual void NodeAdded(Node* node);
virtual void NodeRemoved(Node* node);
virtual void NodeChanged(Node* node, uint32 statFields,
const OldNodeAttributes& oldAttributes);
protected:
virtual AbstractIndexIterator* InternalGetIterator();
virtual AbstractIndexIterator* InternalFind(const void* key,
size_t length);
private:
typedef AttributeIndexTreeKey TreeKey;
typedef AttributeIndexTreeValue TreeValue;
struct TreeDefinition;
struct NodeTree;
struct IteratorPolicy;
class Iterator;
class IteratorList;
friend class Iterator;
friend struct IteratorPolicy;
friend class AttributeIndexer;
private:
void _AddIteratorToUpdate(Iterator* iterator);
private:
NodeTree* fNodes;
IteratorList* fIteratorsToUpdate;
AttributeIndexer* fIndexer;
};
#endif // ATTRIBUTE_INDEX_H

View File

@ -0,0 +1,42 @@
/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef ATTRIBUTE_INDEXER_H
#define ATTRIBUTE_INDEXER_H
#include <SupportDefs.h>
class AttributeIndex;
class AttributeIndexTreeValue;
class IndexedAttributeOwner;
class AttributeIndexer {
public:
AttributeIndexer(AttributeIndex* index);
~AttributeIndexer();
status_t CreateCookie(IndexedAttributeOwner* owner,
void* attributeCookie, uint32 attributeType,
size_t attributeSize, void*& _data,
size_t& _toRead);
void DeleteCookie();
AttributeIndexTreeValue* Cookie() const
{ return fCookie; }
const char* IndexName() const
{ return fIndexName; }
private:
AttributeIndex* fIndex;
const char* fIndexName;
uint32 fIndexType;
AttributeIndexTreeValue* fCookie;
};
#endif // ATTRIBUTE_INDEX_H

View File

@ -0,0 +1,12 @@
/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "IndexedAttributeOwner.h"
IndexedAttributeOwner::~IndexedAttributeOwner()
{
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef INDEXED_ATTRIBUTE_OWNER_H
#define INDEXED_ATTRIBUTE_OWNER_H
#include <SupportDefs.h>
class IndexedAttributeOwner {
public:
virtual ~IndexedAttributeOwner();
virtual void UnsetIndexCookie(void* attributeCookie) = 0;
};
#endif // INDEXED_ATTRIBUTE_OWNER_H

View File

@ -9,6 +9,7 @@ UsePrivateHeaders shared storage ;
HAIKU_PACKAGE_FS_SOURCES = HAIKU_PACKAGE_FS_SOURCES =
AttributeCookie.cpp AttributeCookie.cpp
AttributeDirectoryCookie.cpp AttributeDirectoryCookie.cpp
AttributeIndex.cpp
AutoPackageAttributes.cpp AutoPackageAttributes.cpp
BlockBufferCacheKernel.cpp BlockBufferCacheKernel.cpp
DebugSupport.cpp DebugSupport.cpp
@ -17,6 +18,7 @@ HAIKU_PACKAGE_FS_SOURCES =
EmptyAttributeDirectoryCookie.cpp EmptyAttributeDirectoryCookie.cpp
GlobalFactory.cpp GlobalFactory.cpp
Index.cpp Index.cpp
IndexedAttributeOwner.cpp
kernel_interface.cpp kernel_interface.cpp
LastModifiedIndex.cpp LastModifiedIndex.cpp
NameIndex.cpp NameIndex.cpp

View File

@ -77,3 +77,17 @@ Node::SetParent(Directory* parent)
{ {
fParent = parent; fParent = parent;
} }
status_t
Node::IndexAttribute(AttributeIndexer* indexer)
{
return B_NOT_SUPPORTED;
}
void*
Node::IndexCookieForAttribute(const char* name) const
{
return NULL;
}

View File

@ -19,6 +19,7 @@
class AttributeCookie; class AttributeCookie;
class AttributeDirectoryCookie; class AttributeDirectoryCookie;
class AttributeIndexer;
class Directory; class Directory;
class PackageNode; class PackageNode;
@ -83,6 +84,9 @@ public:
virtual status_t OpenAttribute(const char* name, int openMode, virtual status_t OpenAttribute(const char* name, int openMode,
AttributeCookie*& _cookie) = 0; AttributeCookie*& _cookie) = 0;
virtual status_t IndexAttribute(AttributeIndexer* indexer);
virtual void* IndexCookieForAttribute(const char* name) const;
protected: protected:
rw_lock fLock; rw_lock fLock;
ino_t fID; ino_t fID;

View File

@ -15,6 +15,13 @@ OldNodeAttributes::~OldNodeAttributes()
} }
void*
OldNodeAttributes::IndexCookieForAttribute(const char* name) const
{
return NULL;
}
// #pragma mark - NodeListener // #pragma mark - NodeListener

View File

@ -24,6 +24,7 @@ public:
virtual timespec ModifiedTime() const = 0; virtual timespec ModifiedTime() const = 0;
virtual off_t FileSize() const = 0; virtual off_t FileSize() const = 0;
virtual void* IndexCookieForAttribute(const char* name) const;
}; };

View File

@ -33,3 +33,11 @@ OldUnpackingNodeAttributes::FileSize() const
{ {
return fPackageNode != NULL ? fPackageNode->FileSize() : 0; return fPackageNode != NULL ? fPackageNode->FileSize() : 0;
} }
void*
OldUnpackingNodeAttributes::IndexCookieForAttribute(const char* name) const
{
return fPackageNode != NULL
? fPackageNode->IndexCookieForAttribute(name) : NULL;
}

View File

@ -19,6 +19,7 @@ public:
virtual timespec ModifiedTime() const; virtual timespec ModifiedTime() const;
virtual off_t FileSize() const; virtual off_t FileSize() const;
virtual void* IndexCookieForAttribute(const char* name) const;
private: private:
PackageNode* fPackageNode; PackageNode* fPackageNode;

View File

@ -90,3 +90,10 @@ PackageNode::FindAttribute(const char* name) const
return NULL; return NULL;
} }
void
PackageNode::UnsetIndexCookie(void* attributeCookie)
{
((PackageNodeAttribute*)attributeCookie)->SetIndexCookie(NULL);
}

View File

@ -12,14 +12,16 @@
#include <util/SinglyLinkedList.h> #include <util/SinglyLinkedList.h>
#include "IndexedAttributeOwner.h"
#include "PackageNodeAttribute.h" #include "PackageNodeAttribute.h"
class AttributeIndexer;
class Package; class Package;
class PackageDirectory; class PackageDirectory;
class PackageNode : public BReferenceable, class PackageNode : public BReferenceable, public IndexedAttributeOwner,
public SinglyLinkedListLinkImpl<PackageNode> { public SinglyLinkedListLinkImpl<PackageNode> {
public: public:
PackageNode(Package* package, mode_t mode); PackageNode(Package* package, mode_t mode);
@ -59,6 +61,10 @@ public:
PackageNodeAttribute* FindAttribute(const char* name) const; PackageNodeAttribute* FindAttribute(const char* name) const;
virtual void UnsetIndexCookie(void* attributeCookie);
inline void* IndexCookieForAttribute(const char* name) const;
protected: protected:
Package* fPackage; Package* fPackage;
PackageDirectory* fParent; PackageDirectory* fParent;
@ -71,6 +77,14 @@ protected:
}; };
void*
PackageNode::IndexCookieForAttribute(const char* name) const
{
PackageNodeAttribute* attribute = FindAttribute(name);
return attribute != NULL ? attribute->IndexCookie() : NULL;
}
typedef SinglyLinkedList<PackageNode> PackageNodeList; typedef SinglyLinkedList<PackageNode> PackageNodeList;

View File

@ -15,6 +15,7 @@ PackageNodeAttribute::PackageNodeAttribute(uint32 type,
: :
fData(data), fData(data),
fName(NULL), fName(NULL),
fIndexCookie(NULL),
fType(type) fType(type)
{ {
} }

View File

@ -29,10 +29,15 @@ public:
status_t Init(const char* name); status_t Init(const char* name);
void SetIndexCookie(void* cookie)
{ fIndexCookie = cookie; }
void* IndexCookie() const
{ return fIndexCookie; }
protected: protected:
BPackageData fData; BPackageData fData;
char* fName; char* fName;
void* fIndexCookie;
uint32 fType; uint32 fType;
}; };

View File

@ -15,6 +15,7 @@
#include <AutoDeleter.h> #include <AutoDeleter.h>
#include "AttributeIndexer.h"
#include "AutoPackageAttributes.h" #include "AutoPackageAttributes.h"
#include "DebugSupport.h" #include "DebugSupport.h"
#include "GlobalFactory.h" #include "GlobalFactory.h"
@ -108,21 +109,7 @@ status_t
UnpackingAttributeCookie::ReadAttribute(off_t offset, void* buffer, UnpackingAttributeCookie::ReadAttribute(off_t offset, void* buffer,
size_t* bufferSize) size_t* bufferSize)
{ {
const BPackageData& data = fAttribute->Data(); return ReadAttribute(fPackageNode, fAttribute, offset, buffer, bufferSize);
if (data.IsEncodedInline()) {
// inline data
BBufferDataReader dataReader(data.InlineData(), data.CompressedSize());
return read_package_data(data, &dataReader, offset, buffer, bufferSize);
}
// data not inline -- open the package
int fd = fPackage->Open();
if (fd < 0)
RETURN_ERROR(fd);
PackageCloser packageCloser(fPackage);
BFDDataReader dataReader(fd);
return read_package_data(data, &dataReader, offset, buffer, bufferSize);
} }
@ -134,3 +121,63 @@ UnpackingAttributeCookie::ReadAttributeStat(struct stat* st)
return B_OK; return B_OK;
} }
/*static*/ status_t
UnpackingAttributeCookie::ReadAttribute(PackageNode* packageNode,
PackageNodeAttribute* attribute, off_t offset, void* buffer,
size_t* bufferSize)
{
const BPackageData& data = attribute->Data();
if (data.IsEncodedInline()) {
// inline data
BBufferDataReader dataReader(data.InlineData(), data.CompressedSize());
return read_package_data(data, &dataReader, offset, buffer, bufferSize);
}
// data not inline -- open the package
Package* package = packageNode->GetPackage();
int fd = package->Open();
if (fd < 0)
RETURN_ERROR(fd);
PackageCloser packageCloser(package);
BFDDataReader dataReader(fd);
return read_package_data(data, &dataReader, offset, buffer, bufferSize);
}
/*static*/ status_t
UnpackingAttributeCookie::IndexAttribute(PackageNode* packageNode,
AttributeIndexer* indexer)
{
if (packageNode == NULL)
return B_ENTRY_NOT_FOUND;
// get the attribute
PackageNodeAttribute* attribute = packageNode->FindAttribute(
indexer->IndexName());
if (attribute == NULL)
return B_ENTRY_NOT_FOUND;
// create the index cookie
void* data;
size_t toRead;
status_t error = indexer->CreateCookie(packageNode, attribute,
attribute->Type(), attribute->Data().UncompressedSize(), data, toRead);
if (error != B_OK)
return error;
// read the attribute
if (toRead > 0) {
error = ReadAttribute(packageNode, attribute, 0, data, &toRead);
if (error != B_OK) {
indexer->DeleteCookie();
return error;
}
}
attribute->SetIndexCookie(indexer->Cookie());
return B_OK;
}

View File

@ -9,6 +9,7 @@
#include "AttributeCookie.h" #include "AttributeCookie.h"
class AttributeIndexer;
class Package; class Package;
class PackageNode; class PackageNode;
class PackageNodeAttribute; class PackageNodeAttribute;
@ -29,6 +30,13 @@ public:
size_t* bufferSize); size_t* bufferSize);
virtual status_t ReadAttributeStat(struct stat* st); virtual status_t ReadAttributeStat(struct stat* st);
static status_t ReadAttribute(PackageNode* packageNode,
PackageNodeAttribute* attribute,
off_t offset, void* buffer,
size_t* bufferSize);
static status_t IndexAttribute(PackageNode* packageNode,
AttributeIndexer* indexer);
private: private:
PackageNode* fPackageNode; PackageNode* fPackageNode;
Package* fPackage; Package* fPackage;

View File

@ -149,6 +149,23 @@ UnpackingDirectory::OpenAttribute(const char* name, int openMode,
} }
status_t
UnpackingDirectory::IndexAttribute(AttributeIndexer* indexer)
{
return UnpackingAttributeCookie::IndexAttribute(fPackageDirectories.Head(),
indexer);
}
void*
UnpackingDirectory::IndexCookieForAttribute(const char* name) const
{
if (PackageDirectory* packageDirectory = fPackageDirectories.Head())
return packageDirectory->IndexCookieForAttribute(name);
return NULL;
}
// #pragma mark - RootDirectory // #pragma mark - RootDirectory

View File

@ -34,6 +34,9 @@ public:
virtual status_t OpenAttribute(const char* name, int openMode, virtual status_t OpenAttribute(const char* name, int openMode,
AttributeCookie*& _cookie); AttributeCookie*& _cookie);
virtual status_t IndexAttribute(AttributeIndexer* indexer);
virtual void* IndexCookieForAttribute(const char* name) const;
private: private:
PackageDirectoryList fPackageDirectories; PackageDirectoryList fPackageDirectories;
}; };

View File

@ -210,3 +210,20 @@ UnpackingLeafNode::OpenAttribute(const char* name, int openMode,
return UnpackingAttributeCookie::Open(fPackageNodes.Head(), name, openMode, return UnpackingAttributeCookie::Open(fPackageNodes.Head(), name, openMode,
_cookie); _cookie);
} }
status_t
UnpackingLeafNode::IndexAttribute(AttributeIndexer* indexer)
{
return UnpackingAttributeCookie::IndexAttribute(fPackageNodes.Head(),
indexer);
}
void*
UnpackingLeafNode::IndexCookieForAttribute(const char* name) const
{
if (PackageLeafNode* packageNode = fPackageNodes.Head())
return packageNode->IndexCookieForAttribute(name);
return NULL;
}

View File

@ -44,6 +44,9 @@ public:
virtual status_t OpenAttribute(const char* name, int openMode, virtual status_t OpenAttribute(const char* name, int openMode,
AttributeCookie*& _cookie); AttributeCookie*& _cookie);
virtual status_t IndexAttribute(AttributeIndexer* indexer);
virtual void* IndexCookieForAttribute(const char* name) const;
private: private:
PackageLeafNodeList fPackageNodes; PackageLeafNodeList fPackageNodes;
}; };