root/src/add-ons/kernel/file_systems/packagefs/indices/AttributeIndex.cpp
/*
 * 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
        {
                int cmp = QueryParser::compareKeys(fType, a.data, a.length, b->data,
                        b->length);
                if (cmp != 0)
                        return cmp;

                // The attribute value is the same. Since the tree value is the tree
                // node itself, we must not return 0, though. We consider a node-less
                // key always less than an actual tree node with the same attribute
                // value.
                return -1;
        }

        int Compare(const Value* a, const Value* b) const
        {
                if (a == b)
                        return 0;

                int cmp = QueryParser::compareKeys(fType, a->data, a->length, b->data,
                        b->length);
                if (cmp != 0)
                        return cmp;

                return a < b ? -1 : 1;
        }

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.TakeFrom(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::ConstIterator 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::ConstIterator 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 (HasFixedKeyLength() && length != KeyLength())
                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);
}