root/src/add-ons/kernel/file_systems/ext2/HashRevokeManager.cpp
/*
 * Copyright 2001-2010, Haiku Inc. All rights reserved.
 * This file may be used under the terms of the MIT License.
 *
 * Authors:
 *              Janito V. Ferreira Filho
 */


#include "HashRevokeManager.h"

#include <new>


//#define TRACE_EXT2
#ifdef TRACE_EXT2
#       define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
#else
#       define TRACE(x...) ;
#endif


HashRevokeManager::HashRevokeManager(bool has64bits)
        : RevokeManager(has64bits),
        fHash(NULL),
        kInitialHashSize(128)
                // TODO: Benchmark and find an optimal value
{
}


HashRevokeManager::~HashRevokeManager()
{
        if (fHash != NULL) {
                if (fRevokeCount != 0) {
                        RevokeElement *element = fHash->Clear(true);

                        while (element != NULL) {
                                RevokeElement* next = element->next;
                                delete element;
                                element = next;
                        }
                }

                delete fHash;
        }
}


status_t
HashRevokeManager::Init()
{
        fHash = new(std::nothrow) RevokeTable();

        if (fHash == NULL || fHash->Init(kInitialHashSize) != B_OK)
                return B_NO_MEMORY;

        return B_OK;
}


status_t
HashRevokeManager::Insert(uint32 block, uint32 commitID)
{
        RevokeElement* element = fHash->Lookup(block);

        if (element != NULL) {
                TRACE("HashRevokeManager::Insert(): Already has an element\n");
                if (element->commitID < commitID) {
                        TRACE("HashRevokeManager::Insert(): Deleting previous element\n");
                        bool retValue = fHash->Remove(element);

                        if (!retValue)
                                return B_ERROR;

                        delete element;
                } else {
                        return B_OK;
                                // We already have a newer version of the block
                }
        }

        return _ForceInsert(block, commitID);
}


status_t
HashRevokeManager::Remove(uint32 block)
{
        RevokeElement* element = fHash->Lookup(block);

        if (element == NULL)
                return B_ERROR; // TODO: Perhaps we should just ignore?

        fHash->Remove(element);
                // Can't fail as we just did a sucessful Lookup()

        delete element;
        return B_OK;
}


bool
HashRevokeManager::Lookup(uint32 block, uint32 commitID)
{
        RevokeElement* element = fHash->Lookup(block);

        if (element == NULL)
                return false;

        return element->commitID >= commitID;
}


/*static*/ int
HashRevokeManager::Compare(void* _revoked, const void *_block)
{
        RevokeElement* revoked = (RevokeElement*)_revoked;
        uint32 block = *(uint32*)_block;

        if (revoked->block == block)
                return 0;

        return (revoked->block > block) ? 1 : -1;
}


/*static*/ uint32
HashRevokeManager::Hash(void* _revoked, const void* _block, uint32 range)
{
        TRACE("HashRevokeManager::Hash(): revoked: %p, block: %p, range: %"
                B_PRIu32 "\n", _revoked, _block, range);
        RevokeElement* revoked = (RevokeElement*)_revoked;

        if (revoked != NULL)
                return revoked->block % range;

        uint32 block = *(uint32*)_block;
        return block % range;
}


status_t
HashRevokeManager::_ForceInsert(uint32 block, uint32 commitID)
{
        RevokeElement* element = new(std::nothrow) RevokeElement;

        if (element == NULL)
                return B_NO_MEMORY;

        element->block = block;
        element->commitID = commitID;

        status_t retValue = fHash->Insert(element);

        if (retValue == B_OK) {
                fRevokeCount++;
                TRACE("HashRevokeManager::_ForceInsert(): revoke count: %" B_PRIu32
                        "\n", fRevokeCount);
        }

        return retValue;
}