/src/mozilla-central/xpcom/ds/nsDeque.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsDeque.h" |
8 | | #include "nsISupportsImpl.h" |
9 | | #include <string.h> |
10 | | #ifdef DEBUG_rickg |
11 | | #include <stdio.h> |
12 | | #endif |
13 | | |
14 | | #include "mozilla/CheckedInt.h" |
15 | | |
16 | 0 | #define modulus(x,y) ((x)%(y)) |
17 | | |
18 | | /** |
19 | | * Standard constructor |
20 | | * @param deallocator, called by Erase and ~nsDeque |
21 | | */ |
22 | | nsDeque::nsDeque(nsDequeFunctor* aDeallocator) |
23 | 7 | { |
24 | 7 | MOZ_COUNT_CTOR(nsDeque); |
25 | 7 | mDeallocator = aDeallocator; |
26 | 7 | mOrigin = mSize = 0; |
27 | 7 | mData = mBuffer; // don't allocate space until you must |
28 | 7 | mCapacity = sizeof(mBuffer) / sizeof(mBuffer[0]); |
29 | 7 | memset(mData, 0, mCapacity * sizeof(mBuffer[0])); |
30 | 7 | } |
31 | | |
32 | | /** |
33 | | * Destructor |
34 | | */ |
35 | | nsDeque::~nsDeque() |
36 | 0 | { |
37 | 0 | MOZ_COUNT_DTOR(nsDeque); |
38 | 0 |
|
39 | | #ifdef DEBUG_rickg |
40 | | char buffer[30]; |
41 | | printf("Capacity: %i\n", mCapacity); |
42 | | |
43 | | static int mCaps[15] = {0}; |
44 | | switch (mCapacity) { |
45 | | case 4: mCaps[0]++; break; |
46 | | case 8: mCaps[1]++; break; |
47 | | case 16: mCaps[2]++; break; |
48 | | case 32: mCaps[3]++; break; |
49 | | case 64: mCaps[4]++; break; |
50 | | case 128: mCaps[5]++; break; |
51 | | case 256: mCaps[6]++; break; |
52 | | case 512: mCaps[7]++; break; |
53 | | case 1024: mCaps[8]++; break; |
54 | | case 2048: mCaps[9]++; break; |
55 | | case 4096: mCaps[10]++; break; |
56 | | default: |
57 | | break; |
58 | | } |
59 | | #endif |
60 | |
|
61 | 0 | Erase(); |
62 | 0 | if (mData && mData != mBuffer) { |
63 | 0 | free(mData); |
64 | 0 | } |
65 | 0 | mData = 0; |
66 | 0 | SetDeallocator(0); |
67 | 0 | } |
68 | | |
69 | | size_t |
70 | | nsDeque::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
71 | 0 | { |
72 | 0 | size_t size = 0; |
73 | 0 | if (mData != mBuffer) { |
74 | 0 | size += aMallocSizeOf(mData); |
75 | 0 | } |
76 | 0 |
|
77 | 0 | if (mDeallocator) { |
78 | 0 | size += aMallocSizeOf(mDeallocator); |
79 | 0 | } |
80 | 0 |
|
81 | 0 | return size; |
82 | 0 | } |
83 | | |
84 | | size_t |
85 | | nsDeque::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
86 | 0 | { |
87 | 0 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
88 | 0 | } |
89 | | |
90 | | /** |
91 | | * Set the functor to be called by Erase() |
92 | | * The deque owns the functor. |
93 | | * |
94 | | * @param aDeallocator functor object for use by Erase() |
95 | | */ |
96 | | void |
97 | | nsDeque::SetDeallocator(nsDequeFunctor* aDeallocator) |
98 | 0 | { |
99 | 0 | delete mDeallocator; |
100 | 0 | mDeallocator = aDeallocator; |
101 | 0 | } |
102 | | |
103 | | /** |
104 | | * Remove all items from container without destroying them. |
105 | | */ |
106 | | void |
107 | | nsDeque::Empty() |
108 | 0 | { |
109 | 0 | if (mSize && mData) { |
110 | 0 | memset(mData, 0, mCapacity * sizeof(*mData)); |
111 | 0 | } |
112 | 0 | mSize = 0; |
113 | 0 | mOrigin = 0; |
114 | 0 | } |
115 | | |
116 | | /** |
117 | | * Remove and delete all items from container |
118 | | */ |
119 | | void |
120 | | nsDeque::Erase() |
121 | 0 | { |
122 | 0 | if (mDeallocator && mSize) { |
123 | 0 | ForEach(*mDeallocator); |
124 | 0 | } |
125 | 0 | Empty(); |
126 | 0 | } |
127 | | |
128 | | /** |
129 | | * This method quadruples the size of the deque |
130 | | * Elements in the deque are resequenced so that elements |
131 | | * in the deque are stored sequentially |
132 | | * |
133 | | * @return whether growing succeeded |
134 | | */ |
135 | | bool |
136 | | nsDeque::GrowCapacity() |
137 | 0 | { |
138 | 0 | mozilla::CheckedInt<size_t> newCapacity = mCapacity; |
139 | 0 | newCapacity *= 4; |
140 | 0 |
|
141 | 0 | NS_ASSERTION(newCapacity.isValid(), "Overflow"); |
142 | 0 | if (!newCapacity.isValid()) { |
143 | 0 | return false; |
144 | 0 | } |
145 | 0 | |
146 | 0 | // Sanity check the new byte size. |
147 | 0 | mozilla::CheckedInt<size_t> newByteSize = newCapacity; |
148 | 0 | newByteSize *= sizeof(void*); |
149 | 0 |
|
150 | 0 | NS_ASSERTION(newByteSize.isValid(), "Overflow"); |
151 | 0 | if (!newByteSize.isValid()) { |
152 | 0 | return false; |
153 | 0 | } |
154 | 0 | |
155 | 0 | void** temp = (void**)malloc(newByteSize.value()); |
156 | 0 | if (!temp) { |
157 | 0 | return false; |
158 | 0 | } |
159 | 0 | |
160 | 0 | //Here's the interesting part: You can't just move the elements |
161 | 0 | //directly (in situ) from the old buffer to the new one. |
162 | 0 | //Since capacity has changed, the old origin doesn't make |
163 | 0 | //sense anymore. It's better to resequence the elements now. |
164 | 0 | |
165 | 0 | memcpy(temp, mData + mOrigin, sizeof(void*) * (mCapacity - mOrigin)); |
166 | 0 | memcpy(temp + (mCapacity - mOrigin), mData, sizeof(void*) * mOrigin); |
167 | 0 |
|
168 | 0 | if (mData != mBuffer) { |
169 | 0 | free(mData); |
170 | 0 | } |
171 | 0 |
|
172 | 0 | mCapacity = newCapacity.value(); |
173 | 0 | mOrigin = 0; //now realign the origin... |
174 | 0 | mData = temp; |
175 | 0 |
|
176 | 0 | return true; |
177 | 0 | } |
178 | | |
179 | | /** |
180 | | * This method adds an item to the end of the deque. |
181 | | * This operation has the potential to cause the |
182 | | * underlying buffer to resize. |
183 | | * |
184 | | * @param aItem: new item to be added to deque |
185 | | */ |
186 | | bool |
187 | | nsDeque::Push(void* aItem, const fallible_t&) |
188 | 0 | { |
189 | 0 | if (mSize == mCapacity && !GrowCapacity()) { |
190 | 0 | return false; |
191 | 0 | } |
192 | 0 | mData[modulus(mOrigin + mSize, mCapacity)] = aItem; |
193 | 0 | mSize++; |
194 | 0 | return true; |
195 | 0 | } |
196 | | |
197 | | /** |
198 | | * This method adds an item to the front of the deque. |
199 | | * This operation has the potential to cause the |
200 | | * underlying buffer to resize. |
201 | | * |
202 | | * --Commments for GrowCapacity() case |
203 | | * We've grown and shifted which means that the old |
204 | | * final element in the deque is now the first element |
205 | | * in the deque. This is temporary. |
206 | | * We haven't inserted the new element at the front. |
207 | | * |
208 | | * To continue with the idea of having the front at zero |
209 | | * after a grow, we move the old final item (which through |
210 | | * the voodoo of mOrigin-- is now the first) to its final |
211 | | * position which is conveniently the old length. |
212 | | * |
213 | | * Note that this case only happens when the deque is full. |
214 | | * [And that pieces of this magic only work if the deque is full.] |
215 | | * picture: |
216 | | * [ABCDEFGH] @[mOrigin:3]:D. |
217 | | * Task: PushFront("Z") |
218 | | * shift mOrigin so, @[mOrigin:2]:C |
219 | | * stretch and rearrange: (mOrigin:0) |
220 | | * [CDEFGHAB ________ ________ ________] |
221 | | * copy: (The second C is currently out of bounds) |
222 | | * [CDEFGHAB C_______ ________ ________] |
223 | | * later we will insert Z: |
224 | | * [ZDEFGHAB C_______ ________ ________] |
225 | | * and increment size: 9. (C is no longer out of bounds) |
226 | | * -- |
227 | | * @param aItem: new item to be added to deque |
228 | | */ |
229 | | bool |
230 | | nsDeque::PushFront(void* aItem, const fallible_t&) |
231 | 0 | { |
232 | 0 |
|
233 | 0 | if (mOrigin == 0) { |
234 | 0 | mOrigin = mCapacity - 1; |
235 | 0 | } else { |
236 | 0 | mOrigin--; |
237 | 0 | } |
238 | 0 |
|
239 | 0 | if (mSize == mCapacity) { |
240 | 0 | if (!GrowCapacity()) { |
241 | 0 | return false; |
242 | 0 | } |
243 | 0 | /* Comments explaining this are above*/ |
244 | 0 | mData[mSize] = mData[mOrigin]; |
245 | 0 | } |
246 | 0 | mData[mOrigin] = aItem; |
247 | 0 | mSize++; |
248 | 0 | return true; |
249 | 0 | } |
250 | | |
251 | | /** |
252 | | * Remove and return the last item in the container. |
253 | | * |
254 | | * @return ptr to last item in container |
255 | | */ |
256 | | void* |
257 | | nsDeque::Pop() |
258 | 0 | { |
259 | 0 | void* result = 0; |
260 | 0 | if (mSize > 0) { |
261 | 0 | --mSize; |
262 | 0 | size_t offset = modulus(mSize + mOrigin, mCapacity); |
263 | 0 | result = mData[offset]; |
264 | 0 | mData[offset] = 0; |
265 | 0 | if (!mSize) { |
266 | 0 | mOrigin = 0; |
267 | 0 | } |
268 | 0 | } |
269 | 0 | return result; |
270 | 0 | } |
271 | | |
272 | | /** |
273 | | * This method gets called you want to remove and return |
274 | | * the first member in the container. |
275 | | * |
276 | | * @return last item in container |
277 | | */ |
278 | | void* |
279 | | nsDeque::PopFront() |
280 | 0 | { |
281 | 0 | void* result = 0; |
282 | 0 | if (mSize > 0) { |
283 | 0 | NS_ASSERTION(mOrigin < mCapacity, "Error: Bad origin"); |
284 | 0 | result = mData[mOrigin]; |
285 | 0 | mData[mOrigin++] = 0; //zero it out for debugging purposes. |
286 | 0 | mSize--; |
287 | 0 | // Cycle around if we pop off the end |
288 | 0 | // and reset origin if when we pop the last element |
289 | 0 | if (mCapacity == mOrigin || !mSize) { |
290 | 0 | mOrigin = 0; |
291 | 0 | } |
292 | 0 | } |
293 | 0 | return result; |
294 | 0 | } |
295 | | |
296 | | /** |
297 | | * This method gets called you want to peek at the bottom |
298 | | * member without removing it. |
299 | | * |
300 | | * @return last item in container |
301 | | */ |
302 | | void* |
303 | | nsDeque::Peek() const |
304 | 0 | { |
305 | 0 | void* result = 0; |
306 | 0 | if (mSize > 0) { |
307 | 0 | result = mData[modulus(mSize - 1 + mOrigin, mCapacity)]; |
308 | 0 | } |
309 | 0 | return result; |
310 | 0 | } |
311 | | |
312 | | /** |
313 | | * This method gets called you want to peek at the topmost |
314 | | * member without removing it. |
315 | | * |
316 | | * @return last item in container |
317 | | */ |
318 | | void* |
319 | | nsDeque::PeekFront() const |
320 | 0 | { |
321 | 0 | void* result = 0; |
322 | 0 | if (mSize > 0) { |
323 | 0 | result = mData[mOrigin]; |
324 | 0 | } |
325 | 0 | return result; |
326 | 0 | } |
327 | | |
328 | | /** |
329 | | * Call this to retrieve the ith element from this container. |
330 | | * Keep in mind that accessing the underlying elements is |
331 | | * done in a relative fashion. Object 0 is not necessarily |
332 | | * the first element (the first element is at mOrigin). |
333 | | * |
334 | | * @param aIndex : 0 relative offset of item you want |
335 | | * @return void* or null |
336 | | */ |
337 | | void* |
338 | | nsDeque::ObjectAt(size_t aIndex) const |
339 | 0 | { |
340 | 0 | void* result = 0; |
341 | 0 | if (aIndex < mSize) { |
342 | 0 | result = mData[modulus(mOrigin + aIndex, mCapacity)]; |
343 | 0 | } |
344 | 0 | return result; |
345 | 0 | } |
346 | | |
347 | | /** |
348 | | * Call this method when you want to iterate all the |
349 | | * members of the container, passing a functor along |
350 | | * to call your code. |
351 | | * |
352 | | * @param aFunctor object to call for each member |
353 | | * @return *this |
354 | | */ |
355 | | void |
356 | | nsDeque::ForEach(nsDequeFunctor& aFunctor) const |
357 | 0 | { |
358 | 0 | for (size_t i = 0; i < mSize; ++i) { |
359 | 0 | aFunctor(ObjectAt(i)); |
360 | 0 | } |
361 | 0 | } |