michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim:cindent:ts=2:et:sw=2: michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: * michael@0: * This Original Code has been modified by IBM Corporation. Modifications made by IBM michael@0: * described herein are Copyright (c) International Business Machines Corporation, 2000. michael@0: * Modifications to Mozilla code or documentation identified per MPL Section 3.3 michael@0: * michael@0: * Date Modified by Description of modification michael@0: * 04/20/2000 IBM Corp. OS/2 VisualAge build. michael@0: */ michael@0: michael@0: /** michael@0: * nsPropertyTable allows a set of arbitrary key/value pairs to be stored michael@0: * for any number of nodes, in a global hashtable rather than on the nodes michael@0: * themselves. Nodes can be any type of object; the hashtable keys are michael@0: * nsIAtom pointers, and the values are void pointers. michael@0: */ michael@0: michael@0: #include "nsPropertyTable.h" michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "pldhash.h" michael@0: #include "nsError.h" michael@0: #include "nsIAtom.h" michael@0: michael@0: struct PropertyListMapEntry : public PLDHashEntryHdr { michael@0: const void *key; michael@0: void *value; michael@0: }; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: class nsPropertyTable::PropertyList { michael@0: public: michael@0: PropertyList(nsIAtom* aName, michael@0: NSPropertyDtorFunc aDtorFunc, michael@0: void* aDtorData, michael@0: bool aTransfer) NS_HIDDEN; michael@0: ~PropertyList() NS_HIDDEN; michael@0: michael@0: // Removes the property associated with the given object, and destroys michael@0: // the property value michael@0: NS_HIDDEN_(bool) DeletePropertyFor(nsPropertyOwner aObject); michael@0: michael@0: // Destroy all remaining properties (without removing them) michael@0: NS_HIDDEN_(void) Destroy(); michael@0: michael@0: NS_HIDDEN_(bool) Equals(nsIAtom *aPropertyName) michael@0: { michael@0: return mName == aPropertyName; michael@0: } michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); michael@0: michael@0: nsCOMPtr mName; // property name michael@0: PLDHashTable mObjectValueMap; // map of object/value pairs michael@0: NSPropertyDtorFunc mDtorFunc; // property specific value dtor function michael@0: void* mDtorData; // pointer to pass to dtor michael@0: bool mTransfer; // whether to transfer in michael@0: // TransferOrDeleteAllPropertiesFor michael@0: michael@0: PropertyList* mNext; michael@0: }; michael@0: michael@0: void michael@0: nsPropertyTable::DeleteAllProperties() michael@0: { michael@0: while (mPropertyList) { michael@0: PropertyList* tmp = mPropertyList; michael@0: michael@0: mPropertyList = mPropertyList->mNext; michael@0: tmp->Destroy(); michael@0: delete tmp; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPropertyTable::DeleteAllPropertiesFor(nsPropertyOwner aObject) michael@0: { michael@0: for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) { michael@0: prop->DeletePropertyFor(aObject); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsPropertyTable::TransferOrDeleteAllPropertiesFor(nsPropertyOwner aObject, michael@0: nsPropertyTable *aOtherTable) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) { michael@0: if (prop->mTransfer) { michael@0: PropertyListMapEntry *entry = static_cast michael@0: (PL_DHashTableOperate(&prop->mObjectValueMap, aObject, michael@0: PL_DHASH_LOOKUP)); michael@0: if (PL_DHASH_ENTRY_IS_BUSY(entry)) { michael@0: rv = aOtherTable->SetProperty(aObject, prop->mName, michael@0: entry->value, prop->mDtorFunc, michael@0: prop->mDtorData, prop->mTransfer); michael@0: if (NS_FAILED(rv)) { michael@0: DeleteAllPropertiesFor(aObject); michael@0: aOtherTable->DeleteAllPropertiesFor(aObject); michael@0: michael@0: break; michael@0: } michael@0: michael@0: PL_DHashTableRawRemove(&prop->mObjectValueMap, entry); michael@0: } michael@0: } michael@0: else { michael@0: prop->DeletePropertyFor(aObject); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsPropertyTable::Enumerate(nsPropertyOwner aObject, michael@0: NSPropertyFunc aCallback, void *aData) michael@0: { michael@0: PropertyList* prop; michael@0: for (prop = mPropertyList; prop; prop = prop->mNext) { michael@0: PropertyListMapEntry *entry = static_cast michael@0: (PL_DHashTableOperate(&prop->mObjectValueMap, aObject, PL_DHASH_LOOKUP)); michael@0: if (PL_DHASH_ENTRY_IS_BUSY(entry)) { michael@0: aCallback(const_cast(aObject.get()), prop->mName, entry->value, michael@0: aData); michael@0: } michael@0: } michael@0: } michael@0: michael@0: struct PropertyEnumeratorData michael@0: { michael@0: nsIAtom* mName; michael@0: NSPropertyFunc mCallBack; michael@0: void* mData; michael@0: }; michael@0: michael@0: static PLDHashOperator michael@0: PropertyEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr, michael@0: uint32_t aNumber, void* aArg) michael@0: { michael@0: PropertyListMapEntry* entry = static_cast(aHdr); michael@0: PropertyEnumeratorData* data = static_cast(aArg); michael@0: data->mCallBack(const_cast(entry->key), data->mName, entry->value, michael@0: data->mData); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: nsPropertyTable::EnumerateAll(NSPropertyFunc aCallBack, void* aData) michael@0: { michael@0: for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) { michael@0: PropertyEnumeratorData data = { prop->mName, aCallBack, aData }; michael@0: PL_DHashTableEnumerate(&prop->mObjectValueMap, PropertyEnumerator, &data); michael@0: } michael@0: } michael@0: michael@0: void* michael@0: nsPropertyTable::GetPropertyInternal(nsPropertyOwner aObject, michael@0: nsIAtom *aPropertyName, michael@0: bool aRemove, michael@0: nsresult *aResult) michael@0: { michael@0: NS_PRECONDITION(aPropertyName && aObject, "unexpected null param"); michael@0: nsresult rv = NS_PROPTABLE_PROP_NOT_THERE; michael@0: void *propValue = nullptr; michael@0: michael@0: PropertyList* propertyList = GetPropertyListFor(aPropertyName); michael@0: if (propertyList) { michael@0: PropertyListMapEntry *entry = static_cast michael@0: (PL_DHashTableOperate(&propertyList->mObjectValueMap, aObject, michael@0: PL_DHASH_LOOKUP)); michael@0: if (PL_DHASH_ENTRY_IS_BUSY(entry)) { michael@0: propValue = entry->value; michael@0: if (aRemove) { michael@0: // don't call propertyList->mDtorFunc. That's the caller's job now. michael@0: PL_DHashTableRawRemove(&propertyList->mObjectValueMap, entry); michael@0: } michael@0: rv = NS_OK; michael@0: } michael@0: } michael@0: michael@0: if (aResult) michael@0: *aResult = rv; michael@0: michael@0: return propValue; michael@0: } michael@0: michael@0: nsresult michael@0: nsPropertyTable::SetPropertyInternal(nsPropertyOwner aObject, michael@0: nsIAtom *aPropertyName, michael@0: void *aPropertyValue, michael@0: NSPropertyDtorFunc aPropDtorFunc, michael@0: void *aPropDtorData, michael@0: bool aTransfer, michael@0: void **aOldValue) michael@0: { michael@0: NS_PRECONDITION(aPropertyName && aObject, "unexpected null param"); michael@0: michael@0: PropertyList* propertyList = GetPropertyListFor(aPropertyName); michael@0: michael@0: if (propertyList) { michael@0: // Make sure the dtor function and data and the transfer flag match michael@0: if (aPropDtorFunc != propertyList->mDtorFunc || michael@0: aPropDtorData != propertyList->mDtorData || michael@0: aTransfer != propertyList->mTransfer) { michael@0: NS_WARNING("Destructor/data mismatch while setting property"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: } else { michael@0: propertyList = new PropertyList(aPropertyName, aPropDtorFunc, michael@0: aPropDtorData, aTransfer); michael@0: if (!propertyList || !propertyList->mObjectValueMap.ops) { michael@0: delete propertyList; michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: propertyList->mNext = mPropertyList; michael@0: mPropertyList = propertyList; michael@0: } michael@0: michael@0: // The current property value (if there is one) is replaced and the current michael@0: // value is destroyed michael@0: nsresult result = NS_OK; michael@0: PropertyListMapEntry *entry = static_cast michael@0: (PL_DHashTableOperate(&propertyList->mObjectValueMap, aObject, PL_DHASH_ADD)); michael@0: if (!entry) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: // A nullptr entry->key is the sign that the entry has just been allocated michael@0: // for us. If it's non-nullptr then we have an existing entry. michael@0: if (entry->key) { michael@0: if (aOldValue) michael@0: *aOldValue = entry->value; michael@0: else if (propertyList->mDtorFunc) michael@0: propertyList->mDtorFunc(const_cast(entry->key), aPropertyName, michael@0: entry->value, propertyList->mDtorData); michael@0: result = NS_PROPTABLE_PROP_OVERWRITTEN; michael@0: } michael@0: else if (aOldValue) { michael@0: *aOldValue = nullptr; michael@0: } michael@0: entry->key = aObject; michael@0: entry->value = aPropertyValue; michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: nsPropertyTable::DeleteProperty(nsPropertyOwner aObject, michael@0: nsIAtom *aPropertyName) michael@0: { michael@0: NS_PRECONDITION(aPropertyName && aObject, "unexpected null param"); michael@0: michael@0: PropertyList* propertyList = GetPropertyListFor(aPropertyName); michael@0: if (propertyList) { michael@0: if (propertyList->DeletePropertyFor(aObject)) michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_PROPTABLE_PROP_NOT_THERE; michael@0: } michael@0: michael@0: nsPropertyTable::PropertyList* michael@0: nsPropertyTable::GetPropertyListFor(nsIAtom* aPropertyName) const michael@0: { michael@0: PropertyList* result; michael@0: michael@0: for (result = mPropertyList; result; result = result->mNext) { michael@0: if (result->Equals(aPropertyName)) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: nsPropertyTable::PropertyList::PropertyList(nsIAtom *aName, michael@0: NSPropertyDtorFunc aDtorFunc, michael@0: void *aDtorData, michael@0: bool aTransfer) michael@0: : mName(aName), michael@0: mDtorFunc(aDtorFunc), michael@0: mDtorData(aDtorData), michael@0: mTransfer(aTransfer), michael@0: mNext(nullptr) michael@0: { michael@0: PL_DHashTableInit(&mObjectValueMap, PL_DHashGetStubOps(), this, michael@0: sizeof(PropertyListMapEntry), 16); michael@0: } michael@0: michael@0: nsPropertyTable::PropertyList::~PropertyList() michael@0: { michael@0: PL_DHashTableFinish(&mObjectValueMap); michael@0: } michael@0: michael@0: michael@0: static PLDHashOperator michael@0: DestroyPropertyEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: nsPropertyTable::PropertyList *propList = michael@0: static_cast(table->data); michael@0: PropertyListMapEntry* entry = static_cast(hdr); michael@0: michael@0: propList->mDtorFunc(const_cast(entry->key), propList->mName, michael@0: entry->value, propList->mDtorData); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: nsPropertyTable::PropertyList::Destroy() michael@0: { michael@0: // Enumerate any remaining object/value pairs and destroy the value object michael@0: if (mDtorFunc) michael@0: PL_DHashTableEnumerate(&mObjectValueMap, DestroyPropertyEnumerator, michael@0: nullptr); michael@0: } michael@0: michael@0: bool michael@0: nsPropertyTable::PropertyList::DeletePropertyFor(nsPropertyOwner aObject) michael@0: { michael@0: PropertyListMapEntry *entry = static_cast michael@0: (PL_DHashTableOperate(&mObjectValueMap, aObject, PL_DHASH_LOOKUP)); michael@0: if (!PL_DHASH_ENTRY_IS_BUSY(entry)) michael@0: return false; michael@0: michael@0: void* value = entry->value; michael@0: PL_DHashTableRawRemove(&mObjectValueMap, entry); michael@0: michael@0: if (mDtorFunc) michael@0: mDtorFunc(const_cast(aObject.get()), mName, value, mDtorData); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: size_t michael@0: nsPropertyTable::PropertyList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: n += PL_DHashTableSizeOfExcludingThis(&mObjectValueMap, nullptr, aMallocSizeOf); michael@0: return n; michael@0: } michael@0: michael@0: size_t michael@0: nsPropertyTable::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = 0; michael@0: michael@0: for (PropertyList *prop = mPropertyList; prop; prop = prop->mNext) { michael@0: n += prop->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: return n; michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: nsPropertyTable::SupportsDtorFunc(void *aObject, nsIAtom *aPropertyName, michael@0: void *aPropertyValue, void *aData) michael@0: { michael@0: nsISupports *propertyValue = static_cast(aPropertyValue); michael@0: NS_IF_RELEASE(propertyValue); michael@0: }