root/src/add-ons/kernel/file_systems/packagefs/indices/LastModifiedIndex.cpp
/*
 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "LastModifiedIndex.h"

#include <new>

#include <TypeConstants.h>

#include <util/SinglyLinkedList.h>

#include "DebugSupport.h"
#include "IndexImpl.h"
#include "Node.h"
#include "TwoKeyAVLTree.h"
#include "Volume.h"


// #pragma mark - LastModifiedIndexPrimaryKey


class LastModifiedIndexPrimaryKey {
public:
        LastModifiedIndexPrimaryKey(Node* node, time_t modified)
                :
                node(node),
                modified(modified)
        {
        }

        LastModifiedIndexPrimaryKey(Node* node)
                :
                node(node),
                modified(node->ModifiedTime().tv_sec)
        {
        }

        LastModifiedIndexPrimaryKey(time_t modified)
                :
                node(NULL),
                modified(modified)
        {
        }

        Node*   node;
        time_t  modified;
};


// #pragma mark - LastModifiedIndexGetPrimaryKey


class LastModifiedIndexGetPrimaryKey {
public:
        inline LastModifiedIndexPrimaryKey operator()(Node* a)
        {
                return LastModifiedIndexPrimaryKey(a);
        }

        inline LastModifiedIndexPrimaryKey operator()(Node* a) const
        {
                return LastModifiedIndexPrimaryKey(a);
        }
};


// #pragma mark - LastModifiedIndexPrimaryKeyCompare


class LastModifiedIndexPrimaryKeyCompare {
public:
        inline int operator()(const LastModifiedIndexPrimaryKey &a,
                const LastModifiedIndexPrimaryKey &b) const
        {
                if (a.node != NULL && a.node == b.node)
                        return 0;
                if (a.modified < b.modified)
                        return -1;
                if (a.modified > b.modified)
                        return 1;
                return 0;
        }
};


// #pragma mark - NodeTree


typedef TwoKeyAVLTree<Node*, LastModifiedIndexPrimaryKey,
                LastModifiedIndexPrimaryKeyCompare, LastModifiedIndexGetPrimaryKey>
        _NodeTree;
class LastModifiedIndex::NodeTree : public _NodeTree {};


// #pragma mark - IteratorList


class LastModifiedIndex::IteratorList : public SinglyLinkedList<Iterator> {};


// #pragma mark - Iterator


struct LastModifiedIndex::IteratorPolicy {
        typedef LastModifiedIndex                                                               Index;
        typedef time_t                                                                                  Value;
        typedef LastModifiedIndex::NodeTree                                             NodeTree;
        typedef GenericIndexIteratorTreePolicy<IteratorPolicy>  TreePolicy;

        static NodeTree* GetNodeTree(Index* index)
        {
                return index->fNodes;
        }

        static void GetNodeValue(Node* node, void* buffer, size_t* _keyLength)
        {
                *(time_t*)buffer = node->ModifiedTime().tv_sec;
                *_keyLength = sizeof(time_t);
        }
};


class LastModifiedIndex::Iterator : public GenericIndexIterator<IteratorPolicy>,
        public SinglyLinkedListLinkImpl<Iterator> {
public:
        virtual void                            NodeChanged(Node* node, uint32 statFields,
                                                                        const OldNodeAttributes& oldAttributes);
};


// #pragma mark - LastModifiedIndex


LastModifiedIndex::LastModifiedIndex()
        :
        Index(),
        fNodes(NULL),
        fIteratorsToUpdate(NULL)
{
}


LastModifiedIndex::~LastModifiedIndex()
{
        if (IsListening())
                fVolume->RemoveNodeListener(this);

        ASSERT(fIteratorsToUpdate->IsEmpty());

        delete fIteratorsToUpdate;
        delete fNodes;
}


status_t
LastModifiedIndex::Init(Volume* volume)
{
        status_t error = Index::Init(volume, "last_modified", B_INT32_TYPE, true,
                sizeof(time_t));
        if (error != B_OK)
                return error;

        fVolume->AddNodeListener(this, NULL);

        fNodes = new(std::nothrow) NodeTree;
        fIteratorsToUpdate = new(std::nothrow) IteratorList;
        if (fNodes == NULL || fIteratorsToUpdate == NULL)
                return B_NO_MEMORY;

        return B_OK;
}


int32
LastModifiedIndex::CountEntries() const
{
        return fNodes->CountItems();
}


void
LastModifiedIndex::NodeAdded(Node* node)
{
        fNodes->Insert(node);
}


void
LastModifiedIndex::NodeRemoved(Node* node)
{
        fNodes->Remove(node, node);
}


void
LastModifiedIndex::NodeChanged(Node* node, uint32 statFields,
        const OldNodeAttributes& oldAttributes)
{
        IteratorList iterators;
        iterators.TakeFrom(fIteratorsToUpdate);

        time_t oldLastModified = oldAttributes.ModifiedTime().tv_sec;
        time_t newLastModified = node->ModifiedTime().tv_sec;
        if (newLastModified == oldLastModified)
                return;

        NodeTree::Iterator nodeIterator;
        Node** foundNode = fNodes->Find(
                LastModifiedIndexPrimaryKey(node, oldLastModified), node,
                &nodeIterator);

        if (foundNode == NULL || *foundNode != node)
                return;

        // move the iterators that point to the node to the previous node
        for (IteratorList::ConstIterator it = iterators.GetIterator();
                        Iterator* iterator = it.Next();) {
                iterator->NodeChangeBegin(node);
        }

        // remove and re-insert the node
        nodeIterator.Remove();
        if (fNodes->Insert(node) != B_OK) {
                fIteratorsToUpdate->MakeEmpty();
                return;
        }

        // 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.
        for (IteratorList::ConstIterator it = iterators.GetIterator();
                        Iterator* iterator = it.Next();) {
                iterator->NodeChangeEnd(node);
        }

        // update live queries
        fVolume->UpdateLiveQueries(node, Name(), Type(),
                (const uint8*)&oldLastModified, sizeof(oldLastModified),
                (const uint8*)&newLastModified, sizeof(newLastModified));
}


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


AbstractIndexIterator*
LastModifiedIndex::InternalFind(const void* key, size_t length)
{
        if (length != sizeof(time_t))
                return NULL;
        Iterator* iterator = new(std::nothrow) Iterator;
        if (iterator != NULL) {
                if (!iterator->SetTo(this, *(const time_t*)key)) {
                        delete iterator;
                        iterator = NULL;
                }
        }
        return iterator;
}


void
LastModifiedIndex::_AddIteratorToUpdate(Iterator* iterator)
{
        fIteratorsToUpdate->Add(iterator);
}


// #pragma mark - Iterator


void
LastModifiedIndex::Iterator::NodeChanged(Node* node, uint32 statFields,
        const OldNodeAttributes& oldAttributes)
{
        fIndex->_AddIteratorToUpdate(this);
}