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 "mozilla/Attributes.h"
8 
9 #include "nsArrayEnumerator.h"
10 
11 #include "nsIArray.h"
12 #include "nsISimpleEnumerator.h"
13 
14 #include "nsCOMArray.h"
15 #include "nsCOMPtr.h"
16 #include "mozilla/RefPtr.h"
17 
18 class nsSimpleArrayEnumerator final : public nsISimpleEnumerator
19 {
20 public:
21   // nsISupports interface
22   NS_DECL_ISUPPORTS
23 
24   // nsISimpleEnumerator interface
25   NS_DECL_NSISIMPLEENUMERATOR
26 
27   // nsSimpleArrayEnumerator methods
nsSimpleArrayEnumerator(nsIArray * aValueArray)28   explicit nsSimpleArrayEnumerator(nsIArray* aValueArray)
29     : mValueArray(aValueArray)
30     , mIndex(0)
31   {
32   }
33 
34 private:
~nsSimpleArrayEnumerator()35   ~nsSimpleArrayEnumerator() {}
36 
37 protected:
38   nsCOMPtr<nsIArray> mValueArray;
39   uint32_t mIndex;
40 };
41 
NS_IMPL_ISUPPORTS(nsSimpleArrayEnumerator,nsISimpleEnumerator)42 NS_IMPL_ISUPPORTS(nsSimpleArrayEnumerator, nsISimpleEnumerator)
43 
44 NS_IMETHODIMP
45 nsSimpleArrayEnumerator::HasMoreElements(bool* aResult)
46 {
47   NS_PRECONDITION(aResult != 0, "null ptr");
48   if (!aResult) {
49     return NS_ERROR_NULL_POINTER;
50   }
51 
52   if (!mValueArray) {
53     *aResult = false;
54     return NS_OK;
55   }
56 
57   uint32_t cnt;
58   nsresult rv = mValueArray->GetLength(&cnt);
59   if (NS_FAILED(rv)) {
60     return rv;
61   }
62   *aResult = (mIndex < cnt);
63   return NS_OK;
64 }
65 
66 NS_IMETHODIMP
GetNext(nsISupports ** aResult)67 nsSimpleArrayEnumerator::GetNext(nsISupports** aResult)
68 {
69   NS_PRECONDITION(aResult != 0, "null ptr");
70   if (!aResult) {
71     return NS_ERROR_NULL_POINTER;
72   }
73 
74   if (!mValueArray) {
75     *aResult = nullptr;
76     return NS_OK;
77   }
78 
79   uint32_t cnt;
80   nsresult rv = mValueArray->GetLength(&cnt);
81   if (NS_FAILED(rv)) {
82     return rv;
83   }
84   if (mIndex >= cnt) {
85     return NS_ERROR_UNEXPECTED;
86   }
87 
88   return mValueArray->QueryElementAt(mIndex++, NS_GET_IID(nsISupports),
89                                      (void**)aResult);
90 }
91 
92 nsresult
NS_NewArrayEnumerator(nsISimpleEnumerator ** aResult,nsIArray * aArray)93 NS_NewArrayEnumerator(nsISimpleEnumerator** aResult, nsIArray* aArray)
94 {
95   RefPtr<nsSimpleArrayEnumerator> enumer = new nsSimpleArrayEnumerator(aArray);
96   enumer.forget(aResult);
97   return NS_OK;
98 }
99 
100 ////////////////////////////////////////////////////////////////////////////////
101 
102 // enumerator implementation for nsCOMArray
103 // creates a snapshot of the array in question
104 // you MUST use NS_NewArrayEnumerator to create this, so that
105 // allocation is done correctly
106 class nsCOMArrayEnumerator final : public nsISimpleEnumerator
107 {
108 public:
109   // nsISupports interface
110   NS_DECL_ISUPPORTS
111 
112   // nsISimpleEnumerator interface
113   NS_DECL_NSISIMPLEENUMERATOR
114 
115   // nsSimpleArrayEnumerator methods
nsCOMArrayEnumerator()116   nsCOMArrayEnumerator() : mIndex(0) {}
117 
118   // specialized operator to make sure we make room for mValues
119   void* operator new(size_t aSize, const nsCOMArray_base& aArray) CPP_THROW_NEW;
operator delete(void * aPtr)120   void operator delete(void* aPtr) { ::operator delete(aPtr); }
121 
122 private:
123   ~nsCOMArrayEnumerator(void);
124 
125 protected:
126   uint32_t mIndex;            // current position
127   uint32_t mArraySize;        // size of the array
128 
129   // this is actually bigger
130   nsISupports* mValueArray[1];
131 };
132 
NS_IMPL_ISUPPORTS(nsCOMArrayEnumerator,nsISimpleEnumerator)133 NS_IMPL_ISUPPORTS(nsCOMArrayEnumerator, nsISimpleEnumerator)
134 
135 nsCOMArrayEnumerator::~nsCOMArrayEnumerator()
136 {
137   // only release the entries that we haven't visited yet
138   for (; mIndex < mArraySize; ++mIndex) {
139     NS_IF_RELEASE(mValueArray[mIndex]);
140   }
141 }
142 
143 NS_IMETHODIMP
HasMoreElements(bool * aResult)144 nsCOMArrayEnumerator::HasMoreElements(bool* aResult)
145 {
146   NS_PRECONDITION(aResult != 0, "null ptr");
147   if (!aResult) {
148     return NS_ERROR_NULL_POINTER;
149   }
150 
151   *aResult = (mIndex < mArraySize);
152   return NS_OK;
153 }
154 
155 NS_IMETHODIMP
GetNext(nsISupports ** aResult)156 nsCOMArrayEnumerator::GetNext(nsISupports** aResult)
157 {
158   NS_PRECONDITION(aResult != 0, "null ptr");
159   if (!aResult) {
160     return NS_ERROR_NULL_POINTER;
161   }
162 
163   if (mIndex >= mArraySize) {
164     return NS_ERROR_UNEXPECTED;
165   }
166 
167   // pass the ownership of the reference to the caller. Since
168   // we AddRef'ed during creation of |this|, there is no need
169   // to AddRef here
170   *aResult = mValueArray[mIndex++];
171 
172   // this really isn't necessary. just pretend this happens, since
173   // we'll never visit this value again!
174   // mValueArray[(mIndex-1)] = nullptr;
175 
176   return NS_OK;
177 }
178 
179 void*
operator new(size_t aSize,const nsCOMArray_base & aArray)180 nsCOMArrayEnumerator::operator new(size_t aSize,
181                                    const nsCOMArray_base& aArray) CPP_THROW_NEW
182 {
183   // create enough space such that mValueArray points to a large
184   // enough value. Note that the initial value of aSize gives us
185   // space for mValueArray[0], so we must subtract
186   aSize += (aArray.Count() - 1) * sizeof(aArray[0]);
187 
188   // do the actual allocation
189   nsCOMArrayEnumerator* result =
190     static_cast<nsCOMArrayEnumerator*>(::operator new(aSize));
191 
192   // now need to copy over the values, and addref each one
193   // now this might seem like a lot of work, but we're actually just
194   // doing all our AddRef's ahead of time since GetNext() doesn't
195   // need to AddRef() on the way out
196   uint32_t i;
197   uint32_t max = result->mArraySize = aArray.Count();
198   for (i = 0; i < max; ++i) {
199     result->mValueArray[i] = aArray[i];
200     NS_IF_ADDREF(result->mValueArray[i]);
201   }
202 
203   return result;
204 }
205 
206 nsresult
NS_NewArrayEnumerator(nsISimpleEnumerator ** aResult,const nsCOMArray_base & aArray)207 NS_NewArrayEnumerator(nsISimpleEnumerator** aResult,
208                       const nsCOMArray_base& aArray)
209 {
210   RefPtr<nsCOMArrayEnumerator> enumerator = new (aArray) nsCOMArrayEnumerator();
211   enumerator.forget(aResult);
212   return NS_OK;
213 }
214