root/src/kits/debugger/debug_info/GlobalTypeLookup.cpp
/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "GlobalTypeLookup.h"

#include <new>

#include <String.h>

#include <AutoLocker.h>

#include "Type.h"
#include "TypeLookupConstraints.h"


struct GlobalTypeCache::TypeEntry {
        Type*           type;
        TypeEntry*      fNextByName;
        TypeEntry*      fNextByID;

        TypeEntry(Type* type)
                :
                type(type)
        {
                type->AcquireReference();
        }

        ~TypeEntry()
        {
                type->ReleaseReference();
        }
};


struct GlobalTypeCache::TypeEntryHashDefinitionByName {
        typedef const BString   KeyType;
        typedef TypeEntry               ValueType;

        size_t HashKey(const BString& key) const
        {
                return key.HashValue();
        }

        size_t Hash(const TypeEntry* value) const
        {
                return HashKey(value->type->Name());
        }

        bool Compare(const BString& key, const TypeEntry* value) const
        {
                return key == value->type->Name();
        }

        TypeEntry*& GetLink(TypeEntry* value) const
        {
                return value->fNextByName;
        }
};


struct GlobalTypeCache::TypeEntryHashDefinitionByID {
        typedef const BString   KeyType;
        typedef TypeEntry               ValueType;

        size_t HashKey(const BString& key) const
        {
                return key.HashValue();
        }

        size_t Hash(const TypeEntry* value) const
        {
                return HashKey(value->type->ID());
        }

        bool Compare(const BString& key, const TypeEntry* value) const
        {
                return key == value->type->ID();
        }

        TypeEntry*& GetLink(TypeEntry* value) const
        {
                return value->fNextByID;
        }
};


// #pragma mark - GlobalTypeCache


GlobalTypeCache::GlobalTypeCache()
        :
        fLock("global type cache"),
        fTypesByName(NULL),
        fTypesByID(NULL)
{
}


GlobalTypeCache::~GlobalTypeCache()
{
        if (fTypesByName != NULL)
                fTypesByName->Clear();

        // release all cached type references
        if (fTypesByID != NULL) {
                TypeEntry* entry = fTypesByID->Clear(true);
                while (entry != NULL) {
                        TypeEntry* nextEntry = entry->fNextByID;
                        delete entry;
                        entry = nextEntry;
                }
        }

        delete fTypesByName;
        delete fTypesByID;
}


status_t
GlobalTypeCache::Init()
{
        // check lock
        status_t error = fLock.InitCheck();
        if (error != B_OK)
                return error;

        // create name table
        fTypesByName = new(std::nothrow) NameTable;
        if (fTypesByName == NULL)
                return B_NO_MEMORY;

        error = fTypesByName->Init();
        if (error != B_OK)
                return error;

        // create ID table
        fTypesByID = new(std::nothrow) IDTable;
        if (fTypesByID == NULL)
                return B_NO_MEMORY;

        error = fTypesByID->Init();
        if (error != B_OK)
                return error;

        return B_OK;
}


Type*
GlobalTypeCache::GetType(const BString& name,
        const TypeLookupConstraints &constraints) const
{
        TypeEntry* typeEntry = fTypesByName->Lookup(name);
        if (typeEntry != NULL) {
                if (constraints.HasTypeKind()
                        && typeEntry->type->Kind() != constraints.TypeKind())
                        typeEntry = NULL;
                else if (constraints.HasSubtypeKind()) {
                        if (typeEntry->type->Kind() == TYPE_ADDRESS) {
                                        AddressType* type = dynamic_cast<AddressType*>(
                                                typeEntry->type);
                                        if (type == NULL)
                                                typeEntry = NULL;
                                        else if (type->AddressKind() != constraints.SubtypeKind())
                                                typeEntry = NULL;
                        } else if (typeEntry->type->Kind() == TYPE_COMPOUND) {
                                        CompoundType* type = dynamic_cast<CompoundType*>(
                                                typeEntry->type);
                                        if (type == NULL)
                                                typeEntry = NULL;
                                        else if (type->CompoundKind() != constraints.SubtypeKind())
                                                typeEntry = NULL;
                        }
                }
        }
        return typeEntry != NULL ? typeEntry->type : NULL;
}


Type*
GlobalTypeCache::GetTypeByID(const BString& id) const
{
        TypeEntry* typeEntry = fTypesByID->Lookup(id);
        return typeEntry != NULL ? typeEntry->type : NULL;
}


status_t
GlobalTypeCache::AddType(Type* type)
{
        const BString& id = type->ID();
        const BString& name = type->Name();

        if (fTypesByID->Lookup(id) != NULL
                || (name.Length() > 0 && fTypesByID->Lookup(name) != NULL)) {
                return B_BAD_VALUE;
        }

        TypeEntry* typeEntry = new(std::nothrow) TypeEntry(type);
        if (typeEntry == NULL)
                return B_NO_MEMORY;

        fTypesByID->Insert(typeEntry);

        if (name.Length() > 0)
                fTypesByName->Insert(typeEntry);

        return B_OK;
}


void
GlobalTypeCache::RemoveType(Type* type)
{
        if (TypeEntry* typeEntry = fTypesByID->Lookup(type->ID())) {
                if (typeEntry->type == type) {
                        fTypesByID->Remove(typeEntry);

                        if (type->Name().Length() > 0)
                                fTypesByName->Remove(typeEntry);

                        delete typeEntry;
                }
        }
}


void
GlobalTypeCache::RemoveTypes(image_id imageID)
{
        AutoLocker<GlobalTypeCache> locker(this);

        for (IDTable::Iterator it = fTypesByID->GetIterator();
                        TypeEntry* typeEntry = it.Next();) {
                if (typeEntry->type->ImageID() == imageID) {
                        fTypesByID->RemoveUnchecked(typeEntry);

                        if (typeEntry->type->Name().Length() > 0)
                                fTypesByName->Remove(typeEntry);

                        delete typeEntry;
                }
        }
}


// #pragma mark - GlobalTypeLookup


GlobalTypeLookup::~GlobalTypeLookup()
{
}