xpcom/glue/nsDeque.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:270dc7c0bbdc
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 }

mercurial