nsPropertyTable.cpp (9422B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /** 8 * nsPropertyTable allows a set of arbitrary key/value pairs to be stored 9 * for any number of nodes, in a global hashtable rather than on the nodes 10 * themselves. Nodes can be any type of object; the hashtable keys are 11 * nsAtom pointers, and the values are void pointers. 12 */ 13 14 #include "nsPropertyTable.h" 15 16 #include "PLDHashTable.h" 17 #include "mozilla/MemoryReporting.h" 18 #include "nsAtom.h" 19 #include "nsError.h" 20 21 struct PropertyListMapEntry : public PLDHashEntryHdr { 22 const void* key; 23 void* value; 24 }; 25 26 //---------------------------------------------------------------------- 27 28 class nsPropertyTable::PropertyList { 29 public: 30 PropertyList(nsAtom* aName, NSPropertyDtorFunc aDtorFunc, void* aDtorData, 31 bool aTransfer); 32 ~PropertyList(); 33 34 // Removes the property associated with the given object, and destroys 35 // the property value 36 bool RemovePropertyFor(nsPropertyOwner aObject); 37 38 // Destroy all remaining properties (without removing them) 39 void Destroy(); 40 41 bool Equals(const nsAtom* aPropertyName) { return mName == aPropertyName; } 42 43 size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); 44 45 RefPtr<nsAtom> mName; // property name 46 PLDHashTable mObjectValueMap; // map of object/value pairs 47 NSPropertyDtorFunc mDtorFunc; // property specific value dtor function 48 void* mDtorData; // pointer to pass to dtor 49 bool mTransfer; // whether to transfer in 50 // TransferOrRemoveAllPropertiesFor 51 52 PropertyList* mNext; 53 }; 54 55 void nsPropertyTable::RemoveAllProperties() { 56 while (mPropertyList) { 57 PropertyList* tmp = mPropertyList; 58 59 mPropertyList = mPropertyList->mNext; 60 tmp->Destroy(); 61 delete tmp; 62 } 63 } 64 65 void nsPropertyTable::RemoveAllPropertiesFor(nsPropertyOwner aObject) { 66 for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) { 67 prop->RemovePropertyFor(aObject); 68 } 69 } 70 71 nsresult nsPropertyTable::TransferOrRemoveAllPropertiesFor( 72 nsPropertyOwner aObject, nsPropertyTable& aOtherTable) { 73 nsresult rv = NS_OK; 74 for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) { 75 if (prop->mTransfer) { 76 auto entry = static_cast<PropertyListMapEntry*>( 77 prop->mObjectValueMap.Search(aObject)); 78 if (entry) { 79 rv = aOtherTable.SetProperty(aObject, prop->mName, entry->value, 80 prop->mDtorFunc, prop->mDtorData, 81 prop->mTransfer); 82 if (NS_FAILED(rv)) { 83 RemoveAllPropertiesFor(aObject); 84 aOtherTable.RemoveAllPropertiesFor(aObject); 85 break; 86 } 87 88 prop->mObjectValueMap.RemoveEntry(entry); 89 } 90 } else { 91 prop->RemovePropertyFor(aObject); 92 } 93 } 94 95 return rv; 96 } 97 98 void nsPropertyTable::Enumerate(nsPropertyOwner aObject, 99 NSPropertyFunc aCallback, void* aData) { 100 PropertyList* prop; 101 for (prop = mPropertyList; prop; prop = prop->mNext) { 102 auto entry = static_cast<PropertyListMapEntry*>( 103 prop->mObjectValueMap.Search(aObject)); 104 if (entry) { 105 aCallback(const_cast<void*>(aObject.get()), prop->mName, entry->value, 106 aData); 107 } 108 } 109 } 110 111 void nsPropertyTable::EnumerateAll(NSPropertyFunc aCallBack, void* aData) { 112 for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) { 113 for (auto iter = prop->mObjectValueMap.ConstIter(); !iter.Done(); 114 iter.Next()) { 115 auto entry = static_cast<PropertyListMapEntry*>(iter.Get()); 116 aCallBack(const_cast<void*>(entry->key), prop->mName, entry->value, 117 aData); 118 } 119 } 120 } 121 122 void* nsPropertyTable::GetPropertyInternal(nsPropertyOwner aObject, 123 const nsAtom* aPropertyName, 124 bool aRemove, nsresult* aResult) { 125 MOZ_ASSERT(aPropertyName && aObject, "unexpected null param"); 126 nsresult rv = NS_PROPTABLE_PROP_NOT_THERE; 127 void* propValue = nullptr; 128 129 PropertyList* propertyList = GetPropertyListFor(aPropertyName); 130 if (propertyList) { 131 auto entry = static_cast<PropertyListMapEntry*>( 132 propertyList->mObjectValueMap.Search(aObject)); 133 if (entry) { 134 propValue = entry->value; 135 if (aRemove) { 136 // don't call propertyList->mDtorFunc. That's the caller's job now. 137 propertyList->mObjectValueMap.RemoveEntry(entry); 138 } 139 rv = NS_OK; 140 } 141 } 142 143 if (aResult) *aResult = rv; 144 145 return propValue; 146 } 147 148 nsresult nsPropertyTable::SetPropertyInternal( 149 nsPropertyOwner aObject, nsAtom* aPropertyName, void* aPropertyValue, 150 NSPropertyDtorFunc aPropDtorFunc, void* aPropDtorData, bool aTransfer) { 151 MOZ_ASSERT(aPropertyName && aObject, "unexpected null param"); 152 153 PropertyList* propertyList = GetPropertyListFor(aPropertyName); 154 155 if (propertyList) { 156 // Make sure the dtor function and data and the transfer flag match 157 if (aPropDtorFunc != propertyList->mDtorFunc || 158 aPropDtorData != propertyList->mDtorData || 159 aTransfer != propertyList->mTransfer) { 160 NS_WARNING("Destructor/data mismatch while setting property"); 161 return NS_ERROR_INVALID_ARG; 162 } 163 164 } else { 165 propertyList = new PropertyList(aPropertyName, aPropDtorFunc, aPropDtorData, 166 aTransfer); 167 propertyList->mNext = mPropertyList; 168 mPropertyList = propertyList; 169 } 170 171 // The current property value (if there is one) is replaced and the current 172 // value is destroyed 173 nsresult result = NS_OK; 174 auto entry = static_cast<PropertyListMapEntry*>( 175 propertyList->mObjectValueMap.Add(aObject, mozilla::fallible)); 176 if (!entry) return NS_ERROR_OUT_OF_MEMORY; 177 // A nullptr entry->key is the sign that the entry has just been allocated 178 // for us. If it's non-nullptr then we have an existing entry. 179 if (entry->key) { 180 if (propertyList->mDtorFunc) { 181 propertyList->mDtorFunc(const_cast<void*>(entry->key), aPropertyName, 182 entry->value, propertyList->mDtorData); 183 } 184 result = NS_PROPTABLE_PROP_OVERWRITTEN; 185 } 186 entry->key = aObject; 187 entry->value = aPropertyValue; 188 189 return result; 190 } 191 192 nsresult nsPropertyTable::RemoveProperty(nsPropertyOwner aObject, 193 const nsAtom* aPropertyName) { 194 MOZ_ASSERT(aPropertyName && aObject, "unexpected null param"); 195 196 PropertyList* propertyList = GetPropertyListFor(aPropertyName); 197 if (propertyList) { 198 if (propertyList->RemovePropertyFor(aObject)) { 199 return NS_OK; 200 } 201 } 202 203 return NS_PROPTABLE_PROP_NOT_THERE; 204 } 205 206 nsPropertyTable::PropertyList* nsPropertyTable::GetPropertyListFor( 207 const nsAtom* aPropertyName) const { 208 PropertyList* result; 209 210 for (result = mPropertyList; result; result = result->mNext) { 211 if (result->Equals(aPropertyName)) { 212 break; 213 } 214 } 215 216 return result; 217 } 218 219 //---------------------------------------------------------------------- 220 221 nsPropertyTable::PropertyList::PropertyList(nsAtom* aName, 222 NSPropertyDtorFunc aDtorFunc, 223 void* aDtorData, bool aTransfer) 224 : mName(aName), 225 mObjectValueMap(PLDHashTable::StubOps(), sizeof(PropertyListMapEntry)), 226 mDtorFunc(aDtorFunc), 227 mDtorData(aDtorData), 228 mTransfer(aTransfer), 229 mNext(nullptr) {} 230 231 nsPropertyTable::PropertyList::~PropertyList() = default; 232 233 void nsPropertyTable::PropertyList::Destroy() { 234 // Enumerate any remaining object/value pairs and destroy the value object. 235 if (mDtorFunc) { 236 for (auto iter = mObjectValueMap.ConstIter(); !iter.Done(); iter.Next()) { 237 auto entry = static_cast<PropertyListMapEntry*>(iter.Get()); 238 mDtorFunc(const_cast<void*>(entry->key), mName, entry->value, mDtorData); 239 } 240 } 241 } 242 243 bool nsPropertyTable::PropertyList::RemovePropertyFor(nsPropertyOwner aObject) { 244 auto entry = 245 static_cast<PropertyListMapEntry*>(mObjectValueMap.Search(aObject)); 246 if (!entry) return false; 247 248 void* value = entry->value; 249 mObjectValueMap.RemoveEntry(entry); 250 251 if (mDtorFunc) 252 mDtorFunc(const_cast<void*>(aObject.get()), mName, value, mDtorData); 253 254 return true; 255 } 256 257 size_t nsPropertyTable::PropertyList::SizeOfIncludingThis( 258 mozilla::MallocSizeOf aMallocSizeOf) { 259 size_t n = aMallocSizeOf(this); 260 n += mObjectValueMap.ShallowSizeOfExcludingThis(aMallocSizeOf); 261 return n; 262 } 263 264 size_t nsPropertyTable::SizeOfExcludingThis( 265 mozilla::MallocSizeOf aMallocSizeOf) const { 266 size_t n = 0; 267 268 for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) { 269 n += prop->SizeOfIncludingThis(aMallocSizeOf); 270 } 271 272 return n; 273 } 274 275 size_t nsPropertyTable::SizeOfIncludingThis( 276 mozilla::MallocSizeOf aMallocSizeOf) const { 277 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 278 } 279 280 /* static */ 281 void nsPropertyTable::SupportsDtorFunc(void* aObject, nsAtom* aPropertyName, 282 void* aPropertyValue, void* aData) { 283 nsISupports* propertyValue = static_cast<nsISupports*>(aPropertyValue); 284 NS_IF_RELEASE(propertyValue); 285 }