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 #include "nsCOMArray.h"
8 
9 #include "mozilla/MemoryReporting.h"
10 #include "mozilla/OperatorNewExtensions.h"
11 #include "nsQuickSort.h"
12 
13 #include "nsCOMPtr.h"
14 
15 // This specialization is private to nsCOMArray.
16 // It exists solely to automatically zero-out newly created array elements.
17 template <>
18 class nsTArrayElementTraits<nsISupports*> {
19   typedef nsISupports* E;
20 
21  public:
22   // Zero out the value
Construct(E * aE)23   static inline void Construct(E* aE) {
24     new (mozilla::KnownNotNull, static_cast<void*>(aE)) E();
25   }
26   // Invoke the copy-constructor in place.
27   template <class A>
Construct(E * aE,const A & aArg)28   static inline void Construct(E* aE, const A& aArg) {
29     new (mozilla::KnownNotNull, static_cast<void*>(aE)) E(aArg);
30   }
31   // Construct in place.
32   template <class... Args>
Emplace(E * aE,Args &&...aArgs)33   static inline void Emplace(E* aE, Args&&... aArgs) {
34     new (mozilla::KnownNotNull, static_cast<void*>(aE))
35         E(std::forward<Args>(aArgs)...);
36   }
37   // Invoke the destructor in place.
Destruct(E * aE)38   static inline void Destruct(E* aE) { aE->~E(); }
39 };
40 
41 static void ReleaseObjects(nsTArray<nsISupports*>& aArray);
42 
43 // implementations of non-trivial methods in nsCOMArray_base
44 
nsCOMArray_base(const nsCOMArray_base & aOther)45 nsCOMArray_base::nsCOMArray_base(const nsCOMArray_base& aOther) {
46   // make sure we do only one allocation
47   mArray.SetCapacity(aOther.Count());
48   AppendObjects(aOther);
49 }
50 
~nsCOMArray_base()51 nsCOMArray_base::~nsCOMArray_base() { Clear(); }
52 
IndexOf(nsISupports * aObject,uint32_t aStartIndex) const53 int32_t nsCOMArray_base::IndexOf(nsISupports* aObject,
54                                  uint32_t aStartIndex) const {
55   return mArray.IndexOf(aObject, aStartIndex);
56 }
57 
IndexOfObject(nsISupports * aObject) const58 int32_t nsCOMArray_base::IndexOfObject(nsISupports* aObject) const {
59   nsCOMPtr<nsISupports> supports = do_QueryInterface(aObject);
60   if (NS_WARN_IF(!supports)) {
61     return -1;
62   }
63 
64   uint32_t i, count;
65   int32_t retval = -1;
66   count = mArray.Length();
67   for (i = 0; i < count; ++i) {
68     nsCOMPtr<nsISupports> arrayItem = do_QueryInterface(mArray[i]);
69     if (arrayItem == supports) {
70       retval = i;
71       break;
72     }
73   }
74   return retval;
75 }
76 
EnumerateForwards(nsBaseArrayEnumFunc aFunc,void * aData) const77 bool nsCOMArray_base::EnumerateForwards(nsBaseArrayEnumFunc aFunc,
78                                         void* aData) const {
79   for (uint32_t index = 0; index < mArray.Length(); ++index) {
80     if (!(*aFunc)(mArray[index], aData)) {
81       return false;
82     }
83   }
84 
85   return true;
86 }
87 
EnumerateBackwards(nsBaseArrayEnumFunc aFunc,void * aData) const88 bool nsCOMArray_base::EnumerateBackwards(nsBaseArrayEnumFunc aFunc,
89                                          void* aData) const {
90   for (uint32_t index = mArray.Length(); index--;) {
91     if (!(*aFunc)(mArray[index], aData)) {
92       return false;
93     }
94   }
95 
96   return true;
97 }
98 
VoidStarComparator(const void * aElement1,const void * aElement2,void * aData)99 int nsCOMArray_base::VoidStarComparator(const void* aElement1,
100                                         const void* aElement2, void* aData) {
101   auto ctx = static_cast<nsISupportsComparatorContext*>(aData);
102   return (*ctx->mComparatorFunc)(*static_cast<nsISupports* const*>(aElement1),
103                                  *static_cast<nsISupports* const*>(aElement2),
104                                  ctx->mData);
105 }
106 
Sort(nsISupportsComparatorFunc aFunc,void * aData)107 void nsCOMArray_base::Sort(nsISupportsComparatorFunc aFunc, void* aData) {
108   if (mArray.Length() > 1) {
109     nsISupportsComparatorContext ctx = {aFunc, aData};
110     NS_QuickSort(mArray.Elements(), mArray.Length(), sizeof(nsISupports*),
111                  VoidStarComparator, &ctx);
112   }
113 }
114 
InsertObjectAt(nsISupports * aObject,int32_t aIndex)115 bool nsCOMArray_base::InsertObjectAt(nsISupports* aObject, int32_t aIndex) {
116   if ((uint32_t)aIndex > mArray.Length()) {
117     return false;
118   }
119 
120   mArray.InsertElementAt(aIndex, aObject);
121 
122   NS_IF_ADDREF(aObject);
123   return true;
124 }
125 
InsertElementAt(uint32_t aIndex,nsISupports * aElement)126 void nsCOMArray_base::InsertElementAt(uint32_t aIndex, nsISupports* aElement) {
127   mArray.InsertElementAt(aIndex, aElement);
128   NS_IF_ADDREF(aElement);
129 }
130 
InsertElementAt(uint32_t aIndex,already_AddRefed<nsISupports> aElement)131 void nsCOMArray_base::InsertElementAt(uint32_t aIndex,
132                                       already_AddRefed<nsISupports> aElement) {
133   mArray.InsertElementAt(aIndex, aElement.take());
134 }
135 
InsertObjectsAt(const nsCOMArray_base & aObjects,int32_t aIndex)136 bool nsCOMArray_base::InsertObjectsAt(const nsCOMArray_base& aObjects,
137                                       int32_t aIndex) {
138   if ((uint32_t)aIndex > mArray.Length()) {
139     return false;
140   }
141 
142   mArray.InsertElementsAt(aIndex, aObjects.mArray);
143 
144   // need to addref all these
145   uint32_t count = aObjects.Length();
146   for (uint32_t i = 0; i < count; ++i) {
147     NS_IF_ADDREF(aObjects[i]);
148   }
149 
150   return true;
151 }
152 
InsertElementsAt(uint32_t aIndex,const nsCOMArray_base & aElements)153 void nsCOMArray_base::InsertElementsAt(uint32_t aIndex,
154                                        const nsCOMArray_base& aElements) {
155   mArray.InsertElementsAt(aIndex, aElements.mArray);
156 
157   // need to addref all these
158   uint32_t count = aElements.Length();
159   for (uint32_t i = 0; i < count; ++i) {
160     NS_IF_ADDREF(aElements[i]);
161   }
162 }
163 
InsertElementsAt(uint32_t aIndex,nsISupports * const * aElements,uint32_t aCount)164 void nsCOMArray_base::InsertElementsAt(uint32_t aIndex,
165                                        nsISupports* const* aElements,
166                                        uint32_t aCount) {
167   mArray.InsertElementsAt(aIndex, aElements, aCount);
168 
169   // need to addref all these
170   for (uint32_t i = 0; i < aCount; ++i) {
171     NS_IF_ADDREF(aElements[i]);
172   }
173 }
174 
ReplaceObjectAt(nsISupports * aObject,int32_t aIndex)175 void nsCOMArray_base::ReplaceObjectAt(nsISupports* aObject, int32_t aIndex) {
176   mArray.EnsureLengthAtLeast(aIndex + 1);
177   nsISupports* oldObject = mArray[aIndex];
178   // Make sure to addref first, in case aObject == oldObject
179   NS_IF_ADDREF(mArray[aIndex] = aObject);
180   NS_IF_RELEASE(oldObject);
181 }
182 
RemoveObject(nsISupports * aObject)183 bool nsCOMArray_base::RemoveObject(nsISupports* aObject) {
184   bool result = mArray.RemoveElement(aObject);
185   if (result) {
186     NS_IF_RELEASE(aObject);
187   }
188   return result;
189 }
190 
RemoveObjectAt(int32_t aIndex)191 bool nsCOMArray_base::RemoveObjectAt(int32_t aIndex) {
192   if (uint32_t(aIndex) < mArray.Length()) {
193     nsISupports* element = mArray[aIndex];
194 
195     mArray.RemoveElementAt(aIndex);
196     NS_IF_RELEASE(element);
197     return true;
198   }
199 
200   return false;
201 }
202 
RemoveElementAt(uint32_t aIndex)203 void nsCOMArray_base::RemoveElementAt(uint32_t aIndex) {
204   nsISupports* element = mArray[aIndex];
205   mArray.RemoveElementAt(aIndex);
206   NS_IF_RELEASE(element);
207 }
208 
RemoveObjectsAt(int32_t aIndex,int32_t aCount)209 bool nsCOMArray_base::RemoveObjectsAt(int32_t aIndex, int32_t aCount) {
210   if (uint32_t(aIndex) + uint32_t(aCount) <= mArray.Length()) {
211     nsTArray<nsISupports*> elementsToDestroy(aCount);
212     elementsToDestroy.AppendElements(mArray.Elements() + aIndex, aCount);
213     mArray.RemoveElementsAt(aIndex, aCount);
214     ReleaseObjects(elementsToDestroy);
215     return true;
216   }
217 
218   return false;
219 }
220 
RemoveElementsAt(uint32_t aIndex,uint32_t aCount)221 void nsCOMArray_base::RemoveElementsAt(uint32_t aIndex, uint32_t aCount) {
222   nsTArray<nsISupports*> elementsToDestroy(aCount);
223   elementsToDestroy.AppendElements(mArray.Elements() + aIndex, aCount);
224   mArray.RemoveElementsAt(aIndex, aCount);
225   ReleaseObjects(elementsToDestroy);
226 }
227 
228 // useful for destructors
ReleaseObjects(nsTArray<nsISupports * > & aArray)229 void ReleaseObjects(nsTArray<nsISupports*>& aArray) {
230   for (uint32_t i = 0; i < aArray.Length(); ++i) {
231     NS_IF_RELEASE(aArray[i]);
232   }
233 }
234 
Clear()235 void nsCOMArray_base::Clear() {
236   nsTArray<nsISupports*> objects = std::move(mArray);
237   ReleaseObjects(objects);
238 }
239 
SetCount(int32_t aNewCount)240 bool nsCOMArray_base::SetCount(int32_t aNewCount) {
241   NS_ASSERTION(aNewCount >= 0, "SetCount(negative index)");
242   if (aNewCount < 0) {
243     return false;
244   }
245 
246   int32_t count = mArray.Length();
247   if (count > aNewCount) {
248     RemoveObjectsAt(aNewCount, mArray.Length() - aNewCount);
249   }
250   mArray.SetLength(aNewCount);
251   return true;
252 }
253