|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsDeque.h" |
|
7 #include "nsISupportsImpl.h" |
|
8 #include <string.h> |
|
9 #ifdef DEBUG_rickg |
|
10 #include <stdio.h> |
|
11 #endif |
|
12 |
|
13 /** |
|
14 * 07/02/2001 09:17p 509,104 clangref.pdf from openwatcom's site |
|
15 * Watcom C Language Reference Edition 11.0c |
|
16 * page 118 of 297 |
|
17 * |
|
18 * The % symbol yields the remainder from the division of the first operand |
|
19 * by the second operand. The operands of % must have integral type. |
|
20 * |
|
21 * When both operands of % are positive, the result is a positive value |
|
22 * smaller than the second operand. When one or both operands is negative, |
|
23 * whether the result is positive or negative is implementation-defined. |
|
24 * |
|
25 */ |
|
26 /* Ok, so first of all, C is underspecified. joy. |
|
27 * The following functions do not provide a correct implementation of modulus |
|
28 * They provide functionality for x>-y. |
|
29 * There are risks of 2*y being greater than max int, which is part of the |
|
30 * reason no multiplication is used and other operations are avoided. |
|
31 * |
|
32 * modasgn |
|
33 * @param x variable |
|
34 * @param y expression |
|
35 * approximately equivalent to x %= y |
|
36 * |
|
37 * modulus |
|
38 * @param x expression |
|
39 * @param y expression |
|
40 * approximately equivalent to x % y |
|
41 */ |
|
42 #define modasgn(x,y) if (x<0) x+=y; x%=y |
|
43 #define modulus(x,y) ((x<0)?(x+y)%(y):(x)%(y)) |
|
44 |
|
45 /** |
|
46 * Standard constructor |
|
47 * @param deallocator, called by Erase and ~nsDeque |
|
48 */ |
|
49 nsDeque::nsDeque(nsDequeFunctor* aDeallocator) { |
|
50 MOZ_COUNT_CTOR(nsDeque); |
|
51 mDeallocator=aDeallocator; |
|
52 mOrigin=mSize=0; |
|
53 mData=mBuffer; // don't allocate space until you must |
|
54 mCapacity=sizeof(mBuffer)/sizeof(mBuffer[0]); |
|
55 memset(mData, 0, mCapacity*sizeof(mBuffer[0])); |
|
56 } |
|
57 |
|
58 /** |
|
59 * Destructor |
|
60 */ |
|
61 nsDeque::~nsDeque() { |
|
62 MOZ_COUNT_DTOR(nsDeque); |
|
63 |
|
64 #ifdef DEBUG_rickg |
|
65 char buffer[30]; |
|
66 printf("Capacity: %i\n", mCapacity); |
|
67 |
|
68 static int mCaps[15] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; |
|
69 switch(mCapacity) { |
|
70 case 4: mCaps[0]++; break; |
|
71 case 8: mCaps[1]++; break; |
|
72 case 16: mCaps[2]++; break; |
|
73 case 32: mCaps[3]++; break; |
|
74 case 64: mCaps[4]++; break; |
|
75 case 128: mCaps[5]++; break; |
|
76 case 256: mCaps[6]++; break; |
|
77 case 512: mCaps[7]++; break; |
|
78 case 1024: mCaps[8]++; break; |
|
79 case 2048: mCaps[9]++; break; |
|
80 case 4096: mCaps[10]++; break; |
|
81 default: |
|
82 break; |
|
83 } |
|
84 #endif |
|
85 |
|
86 Erase(); |
|
87 if (mData && (mData!=mBuffer)) { |
|
88 free(mData); |
|
89 } |
|
90 mData=0; |
|
91 SetDeallocator(0); |
|
92 } |
|
93 |
|
94 size_t nsDeque::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { |
|
95 size_t size = 0; |
|
96 if (mData != mBuffer) { |
|
97 size += aMallocSizeOf(mData); |
|
98 } |
|
99 |
|
100 if (mDeallocator) { |
|
101 size += aMallocSizeOf(mDeallocator); |
|
102 } |
|
103 |
|
104 return size; |
|
105 } |
|
106 |
|
107 size_t nsDeque::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { |
|
108 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
109 } |
|
110 |
|
111 /** |
|
112 * Set the functor to be called by Erase() |
|
113 * The deque owns the functor. |
|
114 * |
|
115 * @param aDeallocator functor object for use by Erase() |
|
116 */ |
|
117 void nsDeque::SetDeallocator(nsDequeFunctor* aDeallocator){ |
|
118 delete mDeallocator; |
|
119 mDeallocator=aDeallocator; |
|
120 } |
|
121 |
|
122 /** |
|
123 * Remove all items from container without destroying them. |
|
124 */ |
|
125 void nsDeque::Empty() { |
|
126 if (mSize && mData) { |
|
127 memset(mData, 0, mCapacity*sizeof(mData)); |
|
128 } |
|
129 mSize=0; |
|
130 mOrigin=0; |
|
131 } |
|
132 |
|
133 /** |
|
134 * Remove and delete all items from container |
|
135 */ |
|
136 void nsDeque::Erase() { |
|
137 if (mDeallocator && mSize) { |
|
138 ForEach(*mDeallocator); |
|
139 } |
|
140 Empty(); |
|
141 } |
|
142 |
|
143 /** |
|
144 * This method quadruples the size of the deque |
|
145 * Elements in the deque are resequenced so that elements |
|
146 * in the deque are stored sequentially |
|
147 * |
|
148 * @return whether growing succeeded |
|
149 */ |
|
150 bool nsDeque::GrowCapacity() { |
|
151 int32_t theNewSize=mCapacity<<2; |
|
152 NS_ASSERTION(theNewSize>mCapacity, "Overflow"); |
|
153 if (theNewSize<=mCapacity) |
|
154 return false; |
|
155 void** temp=(void**)malloc(theNewSize * sizeof(void*)); |
|
156 if (!temp) |
|
157 return false; |
|
158 |
|
159 //Here's the interesting part: You can't just move the elements |
|
160 //directly (in situ) from the old buffer to the new one. |
|
161 //Since capacity has changed, the old origin doesn't make |
|
162 //sense anymore. It's better to resequence the elements now. |
|
163 |
|
164 memcpy(temp, mData + mOrigin, sizeof(void*) * (mCapacity - mOrigin)); |
|
165 memcpy(temp + (mCapacity - mOrigin), mData, sizeof(void*) * mOrigin); |
|
166 |
|
167 if (mData != mBuffer) { |
|
168 free(mData); |
|
169 } |
|
170 |
|
171 mCapacity=theNewSize; |
|
172 mOrigin=0; //now realign the origin... |
|
173 mData=temp; |
|
174 |
|
175 return true; |
|
176 } |
|
177 |
|
178 /** |
|
179 * This method adds an item to the end of the deque. |
|
180 * This operation has the potential to cause the |
|
181 * underlying buffer to resize. |
|
182 * |
|
183 * @param aItem: new item to be added to deque |
|
184 */ |
|
185 bool nsDeque::Push(void* aItem, const fallible_t&) { |
|
186 if (mSize==mCapacity && !GrowCapacity()) { |
|
187 return false; |
|
188 } |
|
189 mData[modulus(mOrigin + mSize, mCapacity)]=aItem; |
|
190 mSize++; |
|
191 return true; |
|
192 } |
|
193 |
|
194 /** |
|
195 * This method adds an item to the front of the deque. |
|
196 * This operation has the potential to cause the |
|
197 * underlying buffer to resize. |
|
198 * |
|
199 * --Commments for GrowCapacity() case |
|
200 * We've grown and shifted which means that the old |
|
201 * final element in the deque is now the first element |
|
202 * in the deque. This is temporary. |
|
203 * We haven't inserted the new element at the front. |
|
204 * |
|
205 * To continue with the idea of having the front at zero |
|
206 * after a grow, we move the old final item (which through |
|
207 * the voodoo of mOrigin-- is now the first) to its final |
|
208 * position which is conveniently the old length. |
|
209 * |
|
210 * Note that this case only happens when the deque is full. |
|
211 * [And that pieces of this magic only work if the deque is full.] |
|
212 * picture: |
|
213 * [ABCDEFGH] @[mOrigin:3]:D. |
|
214 * Task: PushFront("Z") |
|
215 * shift mOrigin so, @[mOrigin:2]:C |
|
216 * stretch and rearrange: (mOrigin:0) |
|
217 * [CDEFGHAB ________ ________ ________] |
|
218 * copy: (The second C is currently out of bounds) |
|
219 * [CDEFGHAB C_______ ________ ________] |
|
220 * later we will insert Z: |
|
221 * [ZDEFGHAB C_______ ________ ________] |
|
222 * and increment size: 9. (C is no longer out of bounds) |
|
223 * -- |
|
224 * @param aItem: new item to be added to deque |
|
225 */ |
|
226 bool nsDeque::PushFront(void* aItem, const fallible_t&) { |
|
227 mOrigin--; |
|
228 modasgn(mOrigin,mCapacity); |
|
229 if (mSize==mCapacity) { |
|
230 if (!GrowCapacity()) { |
|
231 return false; |
|
232 } |
|
233 /* Comments explaining this are above*/ |
|
234 mData[mSize]=mData[mOrigin]; |
|
235 } |
|
236 mData[mOrigin]=aItem; |
|
237 mSize++; |
|
238 return true; |
|
239 } |
|
240 |
|
241 /** |
|
242 * Remove and return the last item in the container. |
|
243 * |
|
244 * @return ptr to last item in container |
|
245 */ |
|
246 void* nsDeque::Pop() { |
|
247 void* result=0; |
|
248 if (mSize>0) { |
|
249 --mSize; |
|
250 int32_t offset=modulus(mSize + mOrigin, mCapacity); |
|
251 result=mData[offset]; |
|
252 mData[offset]=0; |
|
253 if (!mSize) { |
|
254 mOrigin=0; |
|
255 } |
|
256 } |
|
257 return result; |
|
258 } |
|
259 |
|
260 /** |
|
261 * This method gets called you want to remove and return |
|
262 * the first member in the container. |
|
263 * |
|
264 * @return last item in container |
|
265 */ |
|
266 void* nsDeque::PopFront() { |
|
267 void* result=0; |
|
268 if (mSize>0) { |
|
269 NS_ASSERTION(mOrigin < mCapacity, "Error: Bad origin"); |
|
270 result=mData[mOrigin]; |
|
271 mData[mOrigin++]=0; //zero it out for debugging purposes. |
|
272 mSize--; |
|
273 // Cycle around if we pop off the end |
|
274 // and reset origin if when we pop the last element |
|
275 if (mCapacity==mOrigin || !mSize) { |
|
276 mOrigin=0; |
|
277 } |
|
278 } |
|
279 return result; |
|
280 } |
|
281 |
|
282 /** |
|
283 * This method gets called you want to peek at the bottom |
|
284 * member without removing it. |
|
285 * |
|
286 * @return last item in container |
|
287 */ |
|
288 void* nsDeque::Peek() { |
|
289 void* result=0; |
|
290 if (mSize>0) { |
|
291 result = mData[modulus(mSize - 1 + mOrigin, mCapacity)]; |
|
292 } |
|
293 return result; |
|
294 } |
|
295 |
|
296 /** |
|
297 * This method gets called you want to peek at the topmost |
|
298 * member without removing it. |
|
299 * |
|
300 * @return last item in container |
|
301 */ |
|
302 void* nsDeque::PeekFront() { |
|
303 void* result=0; |
|
304 if (mSize>0) { |
|
305 result=mData[mOrigin]; |
|
306 } |
|
307 return result; |
|
308 } |
|
309 |
|
310 /** |
|
311 * Call this to retrieve the ith element from this container. |
|
312 * Keep in mind that accessing the underlying elements is |
|
313 * done in a relative fashion. Object 0 is not necessarily |
|
314 * the first element (the first element is at mOrigin). |
|
315 * |
|
316 * @param aIndex : 0 relative offset of item you want |
|
317 * @return void* or null |
|
318 */ |
|
319 void* nsDeque::ObjectAt(int32_t aIndex) const { |
|
320 void* result=0; |
|
321 if ((aIndex>=0) && (aIndex<mSize)) { |
|
322 result=mData[modulus(mOrigin + aIndex, mCapacity)]; |
|
323 } |
|
324 return result; |
|
325 } |
|
326 |
|
327 void* nsDeque::RemoveObjectAt(int32_t aIndex) { |
|
328 if ((aIndex<0) || (aIndex>=mSize)) { |
|
329 return 0; |
|
330 } |
|
331 void* result=mData[modulus(mOrigin + aIndex, mCapacity)]; |
|
332 |
|
333 // "Shuffle down" all elements in the array by 1, overwritting the element |
|
334 // being removed. |
|
335 for (int32_t i=aIndex; i<mSize; i++) { |
|
336 mData[modulus(mOrigin + i, mCapacity)] = mData[modulus(mOrigin + i + 1, mCapacity)]; |
|
337 } |
|
338 mSize--; |
|
339 |
|
340 return result; |
|
341 } |
|
342 |
|
343 /** |
|
344 * Create and return an iterator pointing to |
|
345 * the beginning of the queue. Note that this |
|
346 * takes the circular buffer semantics into account. |
|
347 * |
|
348 * @return new deque iterator, init'ed to 1st item |
|
349 */ |
|
350 nsDequeIterator nsDeque::Begin() const{ |
|
351 return nsDequeIterator(*this, 0); |
|
352 } |
|
353 |
|
354 /** |
|
355 * Create and return an iterator pointing to |
|
356 * the last item in the deque. |
|
357 * Note that this takes the circular buffer semantics |
|
358 * into account. |
|
359 * |
|
360 * @return new deque iterator, init'ed to the last item |
|
361 */ |
|
362 nsDequeIterator nsDeque::End() const{ |
|
363 return nsDequeIterator(*this, mSize - 1); |
|
364 } |
|
365 |
|
366 void* nsDeque::Last() const { |
|
367 return End().GetCurrent(); |
|
368 } |
|
369 |
|
370 /** |
|
371 * Call this method when you want to iterate all the |
|
372 * members of the container, passing a functor along |
|
373 * to call your code. |
|
374 * |
|
375 * @param aFunctor object to call for each member |
|
376 * @return *this |
|
377 */ |
|
378 void nsDeque::ForEach(nsDequeFunctor& aFunctor) const{ |
|
379 for (int32_t i=0; i<mSize; i++) { |
|
380 aFunctor(ObjectAt(i)); |
|
381 } |
|
382 } |
|
383 |
|
384 /** |
|
385 * Call this method when you want to iterate all the |
|
386 * members of the container, calling the functor you |
|
387 * passed with each member. This process will interrupt |
|
388 * if your function returns non 0 to this method. |
|
389 * |
|
390 * @param aFunctor object to call for each member |
|
391 * @return first nonzero result of aFunctor or 0. |
|
392 */ |
|
393 const void* nsDeque::FirstThat(nsDequeFunctor& aFunctor) const{ |
|
394 for (int32_t i=0; i<mSize; i++) { |
|
395 void* obj=aFunctor(ObjectAt(i)); |
|
396 if (obj) { |
|
397 return obj; |
|
398 } |
|
399 } |
|
400 return 0; |
|
401 } |
|
402 |
|
403 /****************************************************** |
|
404 * Here comes the nsDequeIterator class... |
|
405 ******************************************************/ |
|
406 |
|
407 /** |
|
408 * DequeIterator is an object that knows how to iterate (forward and backward) |
|
409 * through a Deque. Normally, you don't need to do this, but there are some special |
|
410 * cases where it is pretty handy, so here you go. |
|
411 * |
|
412 * This is a standard dequeiterator constructor |
|
413 * |
|
414 * @param aQueue is the deque object to be iterated |
|
415 * @param aIndex is the starting position for your iteration |
|
416 */ |
|
417 nsDequeIterator::nsDequeIterator(const nsDeque& aQueue, int aIndex) |
|
418 : mIndex(aIndex), |
|
419 mDeque(aQueue) |
|
420 { |
|
421 } |
|
422 |
|
423 /** |
|
424 * Create a copy of a DequeIterator |
|
425 * |
|
426 * @param aCopy is another iterator to copy from |
|
427 */ |
|
428 nsDequeIterator::nsDequeIterator(const nsDequeIterator& aCopy) |
|
429 : mIndex(aCopy.mIndex), |
|
430 mDeque(aCopy.mDeque) |
|
431 { |
|
432 } |
|
433 |
|
434 /** |
|
435 * Moves iterator to first element in deque |
|
436 * @return *this |
|
437 */ |
|
438 nsDequeIterator& nsDequeIterator::First(){ |
|
439 mIndex=0; |
|
440 return *this; |
|
441 } |
|
442 |
|
443 /** |
|
444 * Standard assignment operator for dequeiterator |
|
445 * |
|
446 * @param aCopy is an iterator to be copied from |
|
447 * @return *this |
|
448 */ |
|
449 nsDequeIterator& nsDequeIterator::operator=(const nsDequeIterator& aCopy) { |
|
450 NS_ASSERTION(&mDeque==&aCopy.mDeque,"you can't change the deque that an interator is iterating over, sorry."); |
|
451 mIndex=aCopy.mIndex; |
|
452 return *this; |
|
453 } |
|
454 |
|
455 /** |
|
456 * preform ! operation against to iterators to test for equivalence |
|
457 * (or lack thereof)! |
|
458 * |
|
459 * @param aIter is the object to be compared to |
|
460 * @return TRUE if NOT equal. |
|
461 */ |
|
462 bool nsDequeIterator::operator!=(nsDequeIterator& aIter) { |
|
463 return bool(!this->operator==(aIter)); |
|
464 } |
|
465 |
|
466 /** |
|
467 * Compare two iterators for increasing order. |
|
468 * |
|
469 * @param aIter is the other iterator to be compared to |
|
470 * @return TRUE if this object points to an element before |
|
471 * the element pointed to by aIter. |
|
472 * FALSE if this and aIter are not iterating over the same deque. |
|
473 */ |
|
474 bool nsDequeIterator::operator<(nsDequeIterator& aIter) { |
|
475 return bool(((mIndex<aIter.mIndex) && (&mDeque==&aIter.mDeque))); |
|
476 } |
|
477 |
|
478 /** |
|
479 * Compare two iterators for equivalence. |
|
480 * |
|
481 * @param aIter is the other iterator to be compared to |
|
482 * @return TRUE if EQUAL |
|
483 */ |
|
484 bool nsDequeIterator::operator==(nsDequeIterator& aIter) { |
|
485 return bool(((mIndex==aIter.mIndex) && (&mDeque==&aIter.mDeque))); |
|
486 } |
|
487 |
|
488 /** |
|
489 * Compare two iterators for non strict decreasing order. |
|
490 * |
|
491 * @param aIter is the other iterator to be compared to |
|
492 * @return TRUE if this object points to the same element, or |
|
493 * an element after the element pointed to by aIter. |
|
494 * FALSE if this and aIter are not iterating over the same deque. |
|
495 */ |
|
496 bool nsDequeIterator::operator>=(nsDequeIterator& aIter) { |
|
497 return bool(((mIndex>=aIter.mIndex) && (&mDeque==&aIter.mDeque))); |
|
498 } |
|
499 |
|
500 /** |
|
501 * Pre-increment operator |
|
502 * |
|
503 * @return object at post-incremented index |
|
504 */ |
|
505 void* nsDequeIterator::operator++() { |
|
506 NS_ASSERTION(mIndex<mDeque.mSize, |
|
507 "You have reached the end of the Internet."\ |
|
508 "You have seen everything there is to see. Please go back. Now." |
|
509 ); |
|
510 #ifndef TIMELESS_LIGHTWEIGHT |
|
511 if (mIndex>=mDeque.mSize) return 0; |
|
512 #endif |
|
513 return mDeque.ObjectAt(++mIndex); |
|
514 } |
|
515 |
|
516 /** |
|
517 * Post-increment operator |
|
518 * |
|
519 * @param param is ignored |
|
520 * @return object at pre-incremented index |
|
521 */ |
|
522 void* nsDequeIterator::operator++(int) { |
|
523 NS_ASSERTION(mIndex<=mDeque.mSize, |
|
524 "You have already reached the end of the Internet."\ |
|
525 "You have seen everything there is to see. Please go back. Now." |
|
526 ); |
|
527 #ifndef TIMELESS_LIGHTWEIGHT |
|
528 if (mIndex>mDeque.mSize) return 0; |
|
529 #endif |
|
530 return mDeque.ObjectAt(mIndex++); |
|
531 } |
|
532 |
|
533 /** |
|
534 * Pre-decrement operator |
|
535 * |
|
536 * @return object at pre-decremented index |
|
537 */ |
|
538 void* nsDequeIterator::operator--() { |
|
539 NS_ASSERTION(mIndex>=0, |
|
540 "You have reached the beginning of the Internet."\ |
|
541 "You have seen everything there is to see. Please go forward. Now." |
|
542 ); |
|
543 #ifndef TIMELESS_LIGHTWEIGHT |
|
544 if (mIndex<0) return 0; |
|
545 #endif |
|
546 return mDeque.ObjectAt(--mIndex); |
|
547 } |
|
548 |
|
549 /** |
|
550 * Post-decrement operator |
|
551 * |
|
552 * @param param is ignored |
|
553 * @return object at post-decremented index |
|
554 */ |
|
555 void* nsDequeIterator::operator--(int) { |
|
556 NS_ASSERTION(mIndex>=0, |
|
557 "You have already reached the beginning of the Internet."\ |
|
558 "You have seen everything there is to see. Please go forward. Now." |
|
559 ); |
|
560 #ifndef TIMELESS_LIGHTWEIGHT |
|
561 if (mIndex<0) return 0; |
|
562 #endif |
|
563 return mDeque.ObjectAt(mIndex--); |
|
564 } |
|
565 |
|
566 /** |
|
567 * Dereference operator |
|
568 * Note that the iterator floats, so you don't need to do: |
|
569 * <code>++iter; aDeque.PopFront();</code> |
|
570 * Unless you actually want your iterator to jump 2 spaces. |
|
571 * |
|
572 * Picture: [1 2I 3 4] |
|
573 * PopFront() |
|
574 * Picture: [2 3I 4] |
|
575 * Note that I still happily points to object at the second index |
|
576 * |
|
577 * @return object at ith index |
|
578 */ |
|
579 void* nsDequeIterator::GetCurrent() { |
|
580 NS_ASSERTION(mIndex<mDeque.mSize&&mIndex>=0,"Current is out of bounds"); |
|
581 #ifndef TIMELESS_LIGHTWEIGHT |
|
582 if (mIndex>=mDeque.mSize||mIndex<0) return 0; |
|
583 #endif |
|
584 return mDeque.ObjectAt(mIndex); |
|
585 } |
|
586 |
|
587 /** |
|
588 * Call this method when you want to iterate all the |
|
589 * members of the container, passing a functor along |
|
590 * to call your code. |
|
591 * |
|
592 * @param aFunctor object to call for each member |
|
593 * @return *this |
|
594 */ |
|
595 void nsDequeIterator::ForEach(nsDequeFunctor& aFunctor) const{ |
|
596 mDeque.ForEach(aFunctor); |
|
597 } |
|
598 |
|
599 /** |
|
600 * Call this method when you want to iterate all the |
|
601 * members of the container, calling the functor you |
|
602 * passed with each member. This process will interrupt |
|
603 * if your function returns non 0 to this method. |
|
604 * |
|
605 * @param aFunctor object to call for each member |
|
606 * @return first nonzero result of aFunctor or 0. |
|
607 */ |
|
608 const void* nsDequeIterator::FirstThat(nsDequeFunctor& aFunctor) const{ |
|
609 return mDeque.FirstThat(aFunctor); |
|
610 } |