root/src/add-ons/kernel/file_systems/ramfs/AttributeIndexImpl.cpp
/*
 * Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
 * All rights reserved. Distributed under the terms of the MIT license.
 */

#include <TypeConstants.h>

#include "AttributeIndexImpl.h"
#include "DebugSupport.h"
#include "Entry.h"
#include "EntryListener.h"
#include "IndexImpl.h"
#include "Misc.h"
#include "Node.h"
#include "NodeListener.h"
#include "ramfs.h"
#include "TwoKeyAVLTree.h"
#include "Volume.h"

#include <util/AutoLock.h>


// compare_integral
template<typename Key>
static inline
int
compare_integral(const Key &a, const Key &b)
{
        if (a < b)
                return -1;
        else if (a > b)
                return 1;
        return 0;
}

// compare_keys
static
int
compare_keys(const uint8 *key1, size_t length1, const uint8 *key2,
                         size_t length2, uint32 type)
{
        switch (type) {
                case B_INT32_TYPE:
                        return compare_integral(*(int32*)key1, *(int32*)key2);
                case B_UINT32_TYPE:
                        return compare_integral(*(uint32*)key1, *(uint32*)key2);
                case B_INT64_TYPE:
                        return compare_integral(*(int64*)key1, *(int64*)key2);
                case B_UINT64_TYPE:
                        return compare_integral(*(uint64*)key1, *(uint64*)key2);
                case B_FLOAT_TYPE:
                        return compare_integral(*(float*)key1, *(float*)key2);
                case B_DOUBLE_TYPE:
                        return compare_integral(*(double*)key1, *(double*)key2);
                case B_STRING_TYPE:
                {
                        int result = strncmp((const char*)key1, (const char*)key2,
                                                                 min(length1, length2));
                        if (result == 0) {
                                result = compare_integral(strnlen((const char*)key1, length1),
                                                                                  strnlen((const char*)key2, length2));
                        }
                        return result;
                }
        }
        return -1;
}

// PrimaryKey
class AttributeIndexImpl::PrimaryKey {
public:
        PrimaryKey(Attribute *attribute, const uint8 *theKey,
                           size_t length)
                : attribute(attribute), length(length)
                        { memcpy(key, theKey, length); }
        PrimaryKey(Attribute *attribute)
                : attribute(attribute) { attribute->GetKey(key, &length); }
        PrimaryKey(const uint8 *theKey, size_t length)
                : attribute(NULL), length(length)
                        { memcpy(key, theKey, length); }

        Attribute       *attribute;
        uint8           key[kMaxIndexKeyLength];
        size_t          length;
};

// GetPrimaryKey
class AttributeIndexImpl::GetPrimaryKey {
public:
        inline PrimaryKey operator()(Attribute *a)
        {
                return PrimaryKey(a);
        }

        inline PrimaryKey operator()(Attribute *a) const
        {
                return PrimaryKey(a);
        }
};

// PrimaryKeyCompare
class AttributeIndexImpl::PrimaryKeyCompare
{
public:
        PrimaryKeyCompare(uint32 type) : fType(type) {}

        inline int operator()(const PrimaryKey &a,
                                                  const PrimaryKey &b) const
        {
                if (a.attribute != NULL && a.attribute == b.attribute)
                        return 0;
                return compare_keys(a.key, a.length, b.key, b.length, fType);
        }

        uint32  fType;
};

// AttributeNodeIterator
template<typename AttributeIterator>
class AttributeNodeIterator {
public:
        inline Node **GetCurrent()
        {
                if (Attribute **attribute = fIterator.GetCurrent()) {
                        fNode = (*attribute)->GetNode();
                        return &fNode;
                }
                return NULL;
        }

        inline Node **GetNext()
        {
                if (Attribute **attribute = fIterator.GetNext()) {
                        fNode = (*attribute)->GetNode();
                        return &fNode;
                }
                return NULL;
        }

        AttributeIterator       fIterator;
        Node                            *fNode;
};


// AttributeTree
class AttributeIndexImpl::AttributeTree
        : public TwoKeyAVLTree<Attribute*, PrimaryKey, PrimaryKeyCompare,
                                                   GetPrimaryKey> {
public:
        AttributeTree(uint32 type)
                : TwoKeyAVLTree<Attribute*, PrimaryKey, PrimaryKeyCompare,
                                                GetPrimaryKey>(PrimaryKeyCompare(type),
                        GetPrimaryKey(), TwoKeyAVLTreeStandardCompare<Attribute*>(),
                        TwoKeyAVLTreeStandardGetKey<Attribute*, Attribute*>())
        {
        }
};


// Iterator
class AttributeIndexImpl::Iterator
        : public NodeEntryIterator<
                AttributeNodeIterator<AttributeTree::Iterator> >,
          public DoublyLinkedListLinkImpl<Iterator>, public EntryListener,
          public NodeListener {
public:
        Iterator();
        virtual ~Iterator();

        virtual Entry *GetCurrent();
        virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);

        virtual status_t Suspend();
        virtual status_t Resume();

        bool SetTo(AttributeIndexImpl *index, const uint8 *key, size_t length,
                           bool ignoreValue = false);
        void Unset();

        virtual void EntryRemoved(Entry *entry);
        virtual void NodeRemoved(Node *node);

private:
        typedef NodeEntryIterator<
                AttributeNodeIterator<AttributeTree::Iterator> > BaseClass;

private:
        AttributeIndexImpl      *fIndex;
};


// IteratorList
class AttributeIndexImpl::IteratorList : public DoublyLinkedList<Iterator> {};


// AttributeIndexImpl

// constructor
AttributeIndexImpl::AttributeIndexImpl(Volume *volume, const char *name,
                                                                           uint32 type, size_t keyLength)
        : AttributeIndex(volume, name, type, (keyLength > 0), keyLength),
          fAttributes(new(nothrow) AttributeTree(type)),
          fIterators(new(nothrow) IteratorList)
{
        if (fInitStatus == B_OK && (!fAttributes || !fIterators))
                fInitStatus = B_NO_MEMORY;
}

// destructor
AttributeIndexImpl::~AttributeIndexImpl()
{
        if (fIterators) {
                // unset the iterators
                for (Iterator *iterator = fIterators->First();
                         iterator;
                         iterator = fIterators->GetNext(iterator)) {
                        iterator->SetTo(NULL, NULL, 0);
                }
                delete fIterators;
        }
        // unset all attributes and delete the tree
        if (fAttributes) {
                AttributeTree::Iterator it;
                fAttributes->GetIterator(&it);
                for (Attribute **attribute = it.GetCurrent(); attribute; it.GetNext())
                        (*attribute)->SetIndex(NULL, false);
                delete fAttributes;
        }
}

// CountEntries
int32
AttributeIndexImpl::CountEntries() const
{
        return fAttributes->CountItems();
}

// Changed
status_t
AttributeIndexImpl::Changed(Attribute *attribute, const uint8 *oldKey, size_t oldLength)
{
        fVolume->AssertWriteLocked();

        status_t error = B_BAD_VALUE;
        if (attribute && attribute->GetIndex() == this) {
                // update the iterators and remove the attribute from the tree
                error = B_OK;
                if (attribute->IsInIndex()) {
                        AttributeTree::Iterator it;
                        Attribute **foundAttribute = fAttributes->Find(
                                PrimaryKey(attribute, oldKey, oldLength), attribute, &it);
                        if (foundAttribute && *foundAttribute == attribute) {
                                Node *node = attribute->GetNode();
                                // update the iterators
                                for (Iterator *iterator = fIterators->First();
                                         iterator;
                                         iterator = fIterators->GetNext(iterator)) {
                                        if (iterator->GetCurrentNode() == node)
                                                iterator->NodeRemoved(node);
                                }
                                // remove and re-insert the attribute
                                it.Remove();
                                attribute->SetIndex(this, false);
                        }
                }
                // re-insert the attribute
                if (fKeyLength > 0 && attribute->GetSize() != (off_t)fKeyLength) {
                        ASSERT(!attribute->IsInIndex());
                } else {
                        error = fAttributes->Insert(attribute);
                        if (error == B_OK)
                                attribute->SetIndex(this, true);
                }
        }
        return error;
}

// Added
status_t
AttributeIndexImpl::Added(Attribute *attribute)
{
        PRINT("AttributeIndex::Add(%p)\n", attribute);
        fVolume->AssertWriteLocked();

        status_t error = (attribute ? B_OK : B_BAD_VALUE);
        if (error == B_OK) {
                size_t size = attribute->GetSize();
                if (fKeyLength > 0 && size != fKeyLength) {
                        attribute->SetIndex(this, false);
                } else {
                        error = fAttributes->Insert(attribute);
                        if (error == B_OK)
                                attribute->SetIndex(this, true);
                }
        }
        return error;
}

// Removed
bool
AttributeIndexImpl::Removed(Attribute *attribute)
{
        PRINT("AttributeIndex::Removed(%p)\n", attribute);
        fVolume->AssertWriteLocked();

        if (attribute == NULL || attribute->GetIndex() != this)
                return false;

        bool result = true;
        if (attribute->IsInIndex())
                result = (fAttributes->Remove(attribute, attribute) == B_OK);
        if (result)
                attribute->SetIndex(NULL, false);
        return result;
}

// InternalGetIterator
AbstractIndexEntryIterator *
AttributeIndexImpl::InternalGetIterator()
{
        Iterator *iterator = new(nothrow) Iterator;
        if (iterator) {
                if (!iterator->SetTo(this, NULL, 0, true)) {
                        delete iterator;
                        iterator = NULL;
                }
        }
        return iterator;
}

// InternalFind
AbstractIndexEntryIterator *
AttributeIndexImpl::InternalFind(const uint8 *key, size_t length)
{
        if (!key || (fKeyLength > 0 && length != fKeyLength))
                return NULL;
        Iterator *iterator = new(nothrow) Iterator;
        if (iterator) {
                if (!iterator->SetTo(this, key, length)) {
                        delete iterator;
                        iterator = NULL;
                }
        }
        return iterator;
}

// _AddIterator
void
AttributeIndexImpl::_AddIterator(Iterator *iterator)
{
        RecursiveLocker locker(fVolume->GetAttributeIteratorLock());
        fIterators->Insert(iterator);
}

// _RemoveIterator
void
AttributeIndexImpl::_RemoveIterator(Iterator *iterator)
{
        RecursiveLocker locker(fVolume->GetAttributeIteratorLock());
        fIterators->Remove(iterator);
}


// Iterator

// constructor
AttributeIndexImpl::Iterator::Iterator()
        : BaseClass(),
          fIndex(NULL)
{
}

// destructor
AttributeIndexImpl::Iterator::~Iterator()
{
        SetTo(NULL, NULL, 0);
}

// GetCurrent
Entry *
AttributeIndexImpl::Iterator::GetCurrent()
{
        return BaseClass::GetCurrent();
}

// GetCurrent
Entry *
AttributeIndexImpl::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength)
{
        Entry *entry = GetCurrent();
        if (entry) {
                if (Attribute **attribute = fIterator.fIterator.GetCurrent()) {
                        if ((*attribute)->GetNode() == entry->GetNode()) {
                                (*attribute)->GetKey(buffer, keyLength);
                        } else {
                                FATAL("Node of current attribute and node of current entry "
                                           "differ: %" B_PRIdINO " vs. %" B_PRIdINO "\n",
                                           (*attribute)->GetNode()->GetID(),
                                           entry->GetNode()->GetID());
                                entry = NULL;
                        }
                } else {
                        FATAL("We have a current entry (`%s', node: %" B_PRIdINO "), but no current "
                                   "attribute.\n", entry->GetName(),
                                   entry->GetNode()->GetID());
                        entry = NULL;
                }
        }
        return entry;
}

// Suspend
status_t
AttributeIndexImpl::Iterator::Suspend()
{
        status_t error = BaseClass::Suspend();
        if (error == B_OK) {
                if (fNode) {
                        error = fIndex->GetVolume()->AddNodeListener(this, fNode,
                                                                                                                 NODE_LISTEN_REMOVED);
                        if (error == B_OK && fEntry) {
                                error = fIndex->GetVolume()->AddEntryListener(this, fEntry,
                                        ENTRY_LISTEN_REMOVED);
                                if (error != B_OK)
                                        fIndex->GetVolume()->RemoveNodeListener(this, fNode);
                        }
                        if (error != B_OK)
                                BaseClass::Resume();
                }
        }
        return error;
}

// Resume
status_t
AttributeIndexImpl::Iterator::Resume()
{
        status_t error = BaseClass::Resume();
        if (error == B_OK) {
                if (fEntry)
                        error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry);
                if (fNode) {
                        if (error == B_OK)
                                error = fIndex->GetVolume()->RemoveNodeListener(this, fNode);
                        else
                                fIndex->GetVolume()->RemoveNodeListener(this, fNode);
                }
        }
        return error;
}

// SetTo
bool
AttributeIndexImpl::Iterator::SetTo(AttributeIndexImpl *index,
        const uint8 *key, size_t length, bool ignoreValue)
{
        Resume();
        Unset();
        // set the new values
        fIndex = index;
        if (fIndex)
                fIndex->_AddIterator(this);
        fInitialized = fIndex;
        // get the attribute node's first entry
        if (fIndex) {
                // get the first node
                bool found = true;
                if (ignoreValue)
                        fIndex->fAttributes->GetIterator(&fIterator.fIterator);
                else {
                        found = fIndex->fAttributes->FindFirst(PrimaryKey(key, length),
                                                                                                   &(fIterator.fIterator));
                }
                // get the first entry
                if (found) {
                        if (Node **nodeP = fIterator.GetCurrent()) {
                                fNode = *nodeP;
                                fEntry = fNode->GetFirstReferrer();
                                if (!fEntry)
                                        BaseClass::GetNext();
                                if (Attribute **attribute = fIterator.fIterator.GetCurrent()) {
                                        uint8 attrKey[kMaxIndexKeyLength];
                                        size_t attrKeyLength;
                                        (*attribute)->GetKey(attrKey, &attrKeyLength);
                                        if (!ignoreValue
                                                && compare_keys(attrKey, attrKeyLength, key, length,
                                                                                fIndex->GetType()) != 0) {
                                                Unset();
                                        }
                                }
                        }
                }
        }
        return fEntry;
}

// Unset
void
AttributeIndexImpl::Iterator::Unset()
{
        if (fIndex) {
                fIndex->_RemoveIterator(this);
                fIndex = NULL;
        }
        BaseClass::Unset();
}

// EntryRemoved
void
AttributeIndexImpl::Iterator::EntryRemoved(Entry */*entry*/)
{
        Resume();
        fIsNext = BaseClass::GetNext();
        Suspend();
}

// NodeRemoved
void
AttributeIndexImpl::Iterator::NodeRemoved(Node */*node*/)
{
        Resume();
        fEntry = NULL;
        fIsNext = BaseClass::GetNext();
        Suspend();
}