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 #ifndef XPCOM_DS_NSREFCOUNTEDHASHTABLE_H_
8 #define XPCOM_DS_NSREFCOUNTEDHASHTABLE_H_
9 
10 #include "nsBaseHashtable.h"
11 #include "nsHashKeys.h"
12 
13 /**
14  * templated hashtable class maps keys to reference pointers.
15  * See nsBaseHashtable for complete declaration.
16  * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
17  *   for a complete specification.
18  * @param PtrType the reference-type being wrapped
19  * @see nsClassHashtable, nsTHashMap
20  */
21 template <class KeyClass, class PtrType>
22 class nsRefCountedHashtable
23     : public nsBaseHashtable<
24           KeyClass, PtrType,
25           typename mozilla::detail::SmartPtrTraits<PtrType>::RawPointerType> {
26  public:
27   using KeyType = typename KeyClass::KeyType;
28   using SmartPtrTraits = mozilla::detail::SmartPtrTraits<PtrType>;
29   using PointeeType = typename SmartPtrTraits::PointeeType;
30   using RawPointerType = typename SmartPtrTraits::RawPointerType;
31   using base_type = nsBaseHashtable<KeyClass, PtrType, RawPointerType>;
32 
33   using base_type::base_type;
34 
35   static_assert(SmartPtrTraits::IsRefCounted);
36 
37   /**
38    * @copydoc nsBaseHashtable::Get
39    * @param aData This is an XPCOM getter, so aData is already_addrefed.
40    *   If the key doesn't exist, *aData will be set to nullptr.
41    */
42   bool Get(KeyType aKey, RawPointerType* aData) const;
43 
44   /**
45    * @copydoc nsBaseHashtable::Get
46    */
47   [[nodiscard]] already_AddRefed<PointeeType> Get(KeyType aKey) const;
48 
49   /**
50    * Gets a weak reference to the hashtable entry.
51    * @param aFound If not nullptr, will be set to true if the entry is found,
52    *               to false otherwise.
53    * @return The entry, or nullptr if not found. Do not release this pointer!
54    */
55   [[nodiscard]] RawPointerType GetWeak(KeyType aKey,
56                                        bool* aFound = nullptr) const;
57 
58   using base_type::InsertOrUpdate;
59 
60   template <typename U,
61             typename = std::enable_if_t<std::is_base_of_v<PointeeType, U>>>
62   void InsertOrUpdate(
63       KeyType aKey,
64       typename SmartPtrTraits::template OtherSmartPtrType<U>&& aData);
65 
66   template <typename U,
67             typename = std::enable_if_t<std::is_base_of_v<PointeeType, U>>>
68   [[nodiscard]] bool InsertOrUpdate(
69       KeyType aKey,
70       typename SmartPtrTraits::template OtherSmartPtrType<U>&& aData,
71       const mozilla::fallible_t&);
72 
73   template <typename U,
74             typename = std::enable_if_t<std::is_base_of_v<PointeeType, U>>>
75   void InsertOrUpdate(KeyType aKey, already_AddRefed<U>&& aData);
76 
77   template <typename U,
78             typename = std::enable_if_t<std::is_base_of_v<PointeeType, U>>>
79   [[nodiscard]] bool InsertOrUpdate(KeyType aKey, already_AddRefed<U>&& aData,
80                                     const mozilla::fallible_t&);
81 
82   /**
83    * Remove the entry associated with aKey (if any), optionally _moving_ its
84    * current value into *aData, thereby avoiding calls to AddRef and Release.
85    * Return true if found.
86    * @param aKey the key to remove from the hashtable
87    * @param aData where to move the value (if non-null).  If an entry is not
88    *              found it will be set to nullptr.
89    * @return true if an entry for aKey was found (and removed)
90    */
91   inline bool Remove(KeyType aKey, RawPointerType* aData = nullptr);
92 
Clone()93   nsRefCountedHashtable Clone() const {
94     return this->template CloneAs<nsRefCountedHashtable>();
95   }
96 };
97 
98 template <typename K, typename T>
ImplCycleCollectionUnlink(nsRefCountedHashtable<K,T> & aField)99 inline void ImplCycleCollectionUnlink(nsRefCountedHashtable<K, T>& aField) {
100   aField.Clear();
101 }
102 
103 template <typename K, typename T>
104 inline void ImplCycleCollectionTraverse(
105     nsCycleCollectionTraversalCallback& aCallback,
106     nsRefCountedHashtable<K, T>& aField, const char* aName,
107     uint32_t aFlags = 0) {
108   for (auto iter = aField.ConstIter(); !iter.Done(); iter.Next()) {
109     CycleCollectionNoteChild(aCallback, iter.UserData(), aName, aFlags);
110   }
111 }
112 
113 //
114 // nsRefCountedHashtable definitions
115 //
116 
117 template <class KeyClass, class PtrType>
Get(KeyType aKey,RawPointerType * aRefPtr)118 bool nsRefCountedHashtable<KeyClass, PtrType>::Get(
119     KeyType aKey, RawPointerType* aRefPtr) const {
120   typename base_type::EntryType* ent = this->GetEntry(aKey);
121 
122   if (ent) {
123     if (aRefPtr) {
124       *aRefPtr = ent->GetData();
125 
126       NS_IF_ADDREF(*aRefPtr);
127     }
128 
129     return true;
130   }
131 
132   // if the key doesn't exist, set *aRefPtr to null
133   // so that it is a valid XPCOM getter
134   if (aRefPtr) {
135     *aRefPtr = nullptr;
136   }
137 
138   return false;
139 }
140 
141 template <class KeyClass, class PtrType>
142 already_AddRefed<typename nsRefCountedHashtable<KeyClass, PtrType>::PointeeType>
Get(KeyType aKey)143 nsRefCountedHashtable<KeyClass, PtrType>::Get(KeyType aKey) const {
144   typename base_type::EntryType* ent = this->GetEntry(aKey);
145   if (!ent) {
146     return nullptr;
147   }
148 
149   PtrType copy = ent->GetData();
150   return copy.forget();
151 }
152 
153 template <class KeyClass, class PtrType>
154 typename nsRefCountedHashtable<KeyClass, PtrType>::RawPointerType
GetWeak(KeyType aKey,bool * aFound)155 nsRefCountedHashtable<KeyClass, PtrType>::GetWeak(KeyType aKey,
156                                                   bool* aFound) const {
157   typename base_type::EntryType* ent = this->GetEntry(aKey);
158 
159   if (ent) {
160     if (aFound) {
161       *aFound = true;
162     }
163 
164     return ent->GetData();
165   }
166 
167   // Key does not exist, return nullptr and set aFound to false
168   if (aFound) {
169     *aFound = false;
170   }
171 
172   return nullptr;
173 }
174 
175 template <class KeyClass, class PtrType>
176 template <typename U, typename>
InsertOrUpdate(KeyType aKey,typename SmartPtrTraits::template OtherSmartPtrType<U> && aData)177 void nsRefCountedHashtable<KeyClass, PtrType>::InsertOrUpdate(
178     KeyType aKey,
179     typename SmartPtrTraits::template OtherSmartPtrType<U>&& aData) {
180   if (!InsertOrUpdate(aKey, std::move(aData), mozilla::fallible)) {
181     NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
182   }
183 }
184 
185 template <class KeyClass, class PtrType>
186 template <typename U, typename>
InsertOrUpdate(KeyType aKey,typename SmartPtrTraits::template OtherSmartPtrType<U> && aData,const mozilla::fallible_t &)187 bool nsRefCountedHashtable<KeyClass, PtrType>::InsertOrUpdate(
188     KeyType aKey,
189     typename SmartPtrTraits::template OtherSmartPtrType<U>&& aData,
190     const mozilla::fallible_t&) {
191   typename base_type::EntryType* ent = this->PutEntry(aKey, mozilla::fallible);
192 
193   if (!ent) {
194     return false;
195   }
196 
197   ent->SetData(std::move(aData));
198 
199   return true;
200 }
201 
202 template <class KeyClass, class PtrType>
203 template <typename U, typename>
InsertOrUpdate(KeyType aKey,already_AddRefed<U> && aData)204 void nsRefCountedHashtable<KeyClass, PtrType>::InsertOrUpdate(
205     KeyType aKey, already_AddRefed<U>&& aData) {
206   if (!InsertOrUpdate(aKey, std::move(aData), mozilla::fallible)) {
207     NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
208   }
209 }
210 
211 template <class KeyClass, class PtrType>
212 template <typename U, typename>
InsertOrUpdate(KeyType aKey,already_AddRefed<U> && aData,const mozilla::fallible_t &)213 bool nsRefCountedHashtable<KeyClass, PtrType>::InsertOrUpdate(
214     KeyType aKey, already_AddRefed<U>&& aData, const mozilla::fallible_t&) {
215   typename base_type::EntryType* ent = this->PutEntry(aKey, mozilla::fallible);
216 
217   if (!ent) {
218     return false;
219   }
220 
221   ent->SetData(std::move(aData));
222 
223   return true;
224 }
225 
226 template <class KeyClass, class PtrType>
Remove(KeyType aKey,RawPointerType * aRefPtr)227 bool nsRefCountedHashtable<KeyClass, PtrType>::Remove(KeyType aKey,
228                                                       RawPointerType* aRefPtr) {
229   typename base_type::EntryType* ent = this->GetEntry(aKey);
230 
231   if (ent) {
232     if (aRefPtr) {
233       ent->GetModifiableData()->forget(aRefPtr);
234     }
235     this->RemoveEntry(ent);
236     return true;
237   }
238 
239   if (aRefPtr) {
240     *aRefPtr = nullptr;
241   }
242   return false;
243 }
244 
245 #endif  // XPCOM_DS_NSREFCOUNTEDHASHTABLE_H_
246