michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsDeque.h" michael@0: #include "nsISupportsImpl.h" michael@0: #include michael@0: #ifdef DEBUG_rickg michael@0: #include michael@0: #endif michael@0: michael@0: /** michael@0: * 07/02/2001 09:17p 509,104 clangref.pdf from openwatcom's site michael@0: * Watcom C Language Reference Edition 11.0c michael@0: * page 118 of 297 michael@0: * michael@0: * The % symbol yields the remainder from the division of the first operand michael@0: * by the second operand. The operands of % must have integral type. michael@0: * michael@0: * When both operands of % are positive, the result is a positive value michael@0: * smaller than the second operand. When one or both operands is negative, michael@0: * whether the result is positive or negative is implementation-defined. michael@0: * michael@0: */ michael@0: /* Ok, so first of all, C is underspecified. joy. michael@0: * The following functions do not provide a correct implementation of modulus michael@0: * They provide functionality for x>-y. michael@0: * There are risks of 2*y being greater than max int, which is part of the michael@0: * reason no multiplication is used and other operations are avoided. michael@0: * michael@0: * modasgn michael@0: * @param x variable michael@0: * @param y expression michael@0: * approximately equivalent to x %= y michael@0: * michael@0: * modulus michael@0: * @param x expression michael@0: * @param y expression michael@0: * approximately equivalent to x % y michael@0: */ michael@0: #define modasgn(x,y) if (x<0) x+=y; x%=y michael@0: #define modulus(x,y) ((x<0)?(x+y)%(y):(x)%(y)) michael@0: michael@0: /** michael@0: * Standard constructor michael@0: * @param deallocator, called by Erase and ~nsDeque michael@0: */ michael@0: nsDeque::nsDeque(nsDequeFunctor* aDeallocator) { michael@0: MOZ_COUNT_CTOR(nsDeque); michael@0: mDeallocator=aDeallocator; michael@0: mOrigin=mSize=0; michael@0: mData=mBuffer; // don't allocate space until you must michael@0: mCapacity=sizeof(mBuffer)/sizeof(mBuffer[0]); michael@0: memset(mData, 0, mCapacity*sizeof(mBuffer[0])); michael@0: } michael@0: michael@0: /** michael@0: * Destructor michael@0: */ michael@0: nsDeque::~nsDeque() { michael@0: MOZ_COUNT_DTOR(nsDeque); michael@0: michael@0: #ifdef DEBUG_rickg michael@0: char buffer[30]; michael@0: printf("Capacity: %i\n", mCapacity); michael@0: michael@0: static int mCaps[15] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; michael@0: switch(mCapacity) { michael@0: case 4: mCaps[0]++; break; michael@0: case 8: mCaps[1]++; break; michael@0: case 16: mCaps[2]++; break; michael@0: case 32: mCaps[3]++; break; michael@0: case 64: mCaps[4]++; break; michael@0: case 128: mCaps[5]++; break; michael@0: case 256: mCaps[6]++; break; michael@0: case 512: mCaps[7]++; break; michael@0: case 1024: mCaps[8]++; break; michael@0: case 2048: mCaps[9]++; break; michael@0: case 4096: mCaps[10]++; break; michael@0: default: michael@0: break; michael@0: } michael@0: #endif michael@0: michael@0: Erase(); michael@0: if (mData && (mData!=mBuffer)) { michael@0: free(mData); michael@0: } michael@0: mData=0; michael@0: SetDeallocator(0); michael@0: } michael@0: michael@0: size_t nsDeque::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { michael@0: size_t size = 0; michael@0: if (mData != mBuffer) { michael@0: size += aMallocSizeOf(mData); michael@0: } michael@0: michael@0: if (mDeallocator) { michael@0: size += aMallocSizeOf(mDeallocator); michael@0: } michael@0: michael@0: return size; michael@0: } michael@0: michael@0: size_t nsDeque::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: /** michael@0: * Set the functor to be called by Erase() michael@0: * The deque owns the functor. michael@0: * michael@0: * @param aDeallocator functor object for use by Erase() michael@0: */ michael@0: void nsDeque::SetDeallocator(nsDequeFunctor* aDeallocator){ michael@0: delete mDeallocator; michael@0: mDeallocator=aDeallocator; michael@0: } michael@0: michael@0: /** michael@0: * Remove all items from container without destroying them. michael@0: */ michael@0: void nsDeque::Empty() { michael@0: if (mSize && mData) { michael@0: memset(mData, 0, mCapacity*sizeof(mData)); michael@0: } michael@0: mSize=0; michael@0: mOrigin=0; michael@0: } michael@0: michael@0: /** michael@0: * Remove and delete all items from container michael@0: */ michael@0: void nsDeque::Erase() { michael@0: if (mDeallocator && mSize) { michael@0: ForEach(*mDeallocator); michael@0: } michael@0: Empty(); michael@0: } michael@0: michael@0: /** michael@0: * This method quadruples the size of the deque michael@0: * Elements in the deque are resequenced so that elements michael@0: * in the deque are stored sequentially michael@0: * michael@0: * @return whether growing succeeded michael@0: */ michael@0: bool nsDeque::GrowCapacity() { michael@0: int32_t theNewSize=mCapacity<<2; michael@0: NS_ASSERTION(theNewSize>mCapacity, "Overflow"); michael@0: if (theNewSize<=mCapacity) michael@0: return false; michael@0: void** temp=(void**)malloc(theNewSize * sizeof(void*)); michael@0: if (!temp) michael@0: return false; michael@0: michael@0: //Here's the interesting part: You can't just move the elements michael@0: //directly (in situ) from the old buffer to the new one. michael@0: //Since capacity has changed, the old origin doesn't make michael@0: //sense anymore. It's better to resequence the elements now. michael@0: michael@0: memcpy(temp, mData + mOrigin, sizeof(void*) * (mCapacity - mOrigin)); michael@0: memcpy(temp + (mCapacity - mOrigin), mData, sizeof(void*) * mOrigin); michael@0: michael@0: if (mData != mBuffer) { michael@0: free(mData); michael@0: } michael@0: michael@0: mCapacity=theNewSize; michael@0: mOrigin=0; //now realign the origin... michael@0: mData=temp; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * This method adds an item to the end of the deque. michael@0: * This operation has the potential to cause the michael@0: * underlying buffer to resize. michael@0: * michael@0: * @param aItem: new item to be added to deque michael@0: */ michael@0: bool nsDeque::Push(void* aItem, const fallible_t&) { michael@0: if (mSize==mCapacity && !GrowCapacity()) { michael@0: return false; michael@0: } michael@0: mData[modulus(mOrigin + mSize, mCapacity)]=aItem; michael@0: mSize++; michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * This method adds an item to the front of the deque. michael@0: * This operation has the potential to cause the michael@0: * underlying buffer to resize. michael@0: * michael@0: * --Commments for GrowCapacity() case michael@0: * We've grown and shifted which means that the old michael@0: * final element in the deque is now the first element michael@0: * in the deque. This is temporary. michael@0: * We haven't inserted the new element at the front. michael@0: * michael@0: * To continue with the idea of having the front at zero michael@0: * after a grow, we move the old final item (which through michael@0: * the voodoo of mOrigin-- is now the first) to its final michael@0: * position which is conveniently the old length. michael@0: * michael@0: * Note that this case only happens when the deque is full. michael@0: * [And that pieces of this magic only work if the deque is full.] michael@0: * picture: michael@0: * [ABCDEFGH] @[mOrigin:3]:D. michael@0: * Task: PushFront("Z") michael@0: * shift mOrigin so, @[mOrigin:2]:C michael@0: * stretch and rearrange: (mOrigin:0) michael@0: * [CDEFGHAB ________ ________ ________] michael@0: * copy: (The second C is currently out of bounds) michael@0: * [CDEFGHAB C_______ ________ ________] michael@0: * later we will insert Z: michael@0: * [ZDEFGHAB C_______ ________ ________] michael@0: * and increment size: 9. (C is no longer out of bounds) michael@0: * -- michael@0: * @param aItem: new item to be added to deque michael@0: */ michael@0: bool nsDeque::PushFront(void* aItem, const fallible_t&) { michael@0: mOrigin--; michael@0: modasgn(mOrigin,mCapacity); michael@0: if (mSize==mCapacity) { michael@0: if (!GrowCapacity()) { michael@0: return false; michael@0: } michael@0: /* Comments explaining this are above*/ michael@0: mData[mSize]=mData[mOrigin]; michael@0: } michael@0: mData[mOrigin]=aItem; michael@0: mSize++; michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Remove and return the last item in the container. michael@0: * michael@0: * @return ptr to last item in container michael@0: */ michael@0: void* nsDeque::Pop() { michael@0: void* result=0; michael@0: if (mSize>0) { michael@0: --mSize; michael@0: int32_t offset=modulus(mSize + mOrigin, mCapacity); michael@0: result=mData[offset]; michael@0: mData[offset]=0; michael@0: if (!mSize) { michael@0: mOrigin=0; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * This method gets called you want to remove and return michael@0: * the first member in the container. michael@0: * michael@0: * @return last item in container michael@0: */ michael@0: void* nsDeque::PopFront() { michael@0: void* result=0; michael@0: if (mSize>0) { michael@0: NS_ASSERTION(mOrigin < mCapacity, "Error: Bad origin"); michael@0: result=mData[mOrigin]; michael@0: mData[mOrigin++]=0; //zero it out for debugging purposes. michael@0: mSize--; michael@0: // Cycle around if we pop off the end michael@0: // and reset origin if when we pop the last element michael@0: if (mCapacity==mOrigin || !mSize) { michael@0: mOrigin=0; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * This method gets called you want to peek at the bottom michael@0: * member without removing it. michael@0: * michael@0: * @return last item in container michael@0: */ michael@0: void* nsDeque::Peek() { michael@0: void* result=0; michael@0: if (mSize>0) { michael@0: result = mData[modulus(mSize - 1 + mOrigin, mCapacity)]; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * This method gets called you want to peek at the topmost michael@0: * member without removing it. michael@0: * michael@0: * @return last item in container michael@0: */ michael@0: void* nsDeque::PeekFront() { michael@0: void* result=0; michael@0: if (mSize>0) { michael@0: result=mData[mOrigin]; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * Call this to retrieve the ith element from this container. michael@0: * Keep in mind that accessing the underlying elements is michael@0: * done in a relative fashion. Object 0 is not necessarily michael@0: * the first element (the first element is at mOrigin). michael@0: * michael@0: * @param aIndex : 0 relative offset of item you want michael@0: * @return void* or null michael@0: */ michael@0: void* nsDeque::ObjectAt(int32_t aIndex) const { michael@0: void* result=0; michael@0: if ((aIndex>=0) && (aIndex=mSize)) { michael@0: return 0; michael@0: } michael@0: void* result=mData[modulus(mOrigin + aIndex, mCapacity)]; michael@0: michael@0: // "Shuffle down" all elements in the array by 1, overwritting the element michael@0: // being removed. michael@0: for (int32_t i=aIndex; ioperator==(aIter)); michael@0: } michael@0: michael@0: /** michael@0: * Compare two iterators for increasing order. michael@0: * michael@0: * @param aIter is the other iterator to be compared to michael@0: * @return TRUE if this object points to an element before michael@0: * the element pointed to by aIter. michael@0: * FALSE if this and aIter are not iterating over the same deque. michael@0: */ michael@0: bool nsDequeIterator::operator<(nsDequeIterator& aIter) { michael@0: return bool(((mIndex=(nsDequeIterator& aIter) { michael@0: return bool(((mIndex>=aIter.mIndex) && (&mDeque==&aIter.mDeque))); michael@0: } michael@0: michael@0: /** michael@0: * Pre-increment operator michael@0: * michael@0: * @return object at post-incremented index michael@0: */ michael@0: void* nsDequeIterator::operator++() { michael@0: NS_ASSERTION(mIndex=mDeque.mSize) return 0; michael@0: #endif michael@0: return mDeque.ObjectAt(++mIndex); michael@0: } michael@0: michael@0: /** michael@0: * Post-increment operator michael@0: * michael@0: * @param param is ignored michael@0: * @return object at pre-incremented index michael@0: */ michael@0: void* nsDequeIterator::operator++(int) { michael@0: NS_ASSERTION(mIndex<=mDeque.mSize, michael@0: "You have already reached the end of the Internet."\ michael@0: "You have seen everything there is to see. Please go back. Now." michael@0: ); michael@0: #ifndef TIMELESS_LIGHTWEIGHT michael@0: if (mIndex>mDeque.mSize) return 0; michael@0: #endif michael@0: return mDeque.ObjectAt(mIndex++); michael@0: } michael@0: michael@0: /** michael@0: * Pre-decrement operator michael@0: * michael@0: * @return object at pre-decremented index michael@0: */ michael@0: void* nsDequeIterator::operator--() { michael@0: NS_ASSERTION(mIndex>=0, michael@0: "You have reached the beginning of the Internet."\ michael@0: "You have seen everything there is to see. Please go forward. Now." michael@0: ); michael@0: #ifndef TIMELESS_LIGHTWEIGHT michael@0: if (mIndex<0) return 0; michael@0: #endif michael@0: return mDeque.ObjectAt(--mIndex); michael@0: } michael@0: michael@0: /** michael@0: * Post-decrement operator michael@0: * michael@0: * @param param is ignored michael@0: * @return object at post-decremented index michael@0: */ michael@0: void* nsDequeIterator::operator--(int) { michael@0: NS_ASSERTION(mIndex>=0, michael@0: "You have already reached the beginning of the Internet."\ michael@0: "You have seen everything there is to see. Please go forward. Now." michael@0: ); michael@0: #ifndef TIMELESS_LIGHTWEIGHT michael@0: if (mIndex<0) return 0; michael@0: #endif michael@0: return mDeque.ObjectAt(mIndex--); michael@0: } michael@0: michael@0: /** michael@0: * Dereference operator michael@0: * Note that the iterator floats, so you don't need to do: michael@0: * ++iter; aDeque.PopFront(); michael@0: * Unless you actually want your iterator to jump 2 spaces. michael@0: * michael@0: * Picture: [1 2I 3 4] michael@0: * PopFront() michael@0: * Picture: [2 3I 4] michael@0: * Note that I still happily points to object at the second index michael@0: * michael@0: * @return object at ith index michael@0: */ michael@0: void* nsDequeIterator::GetCurrent() { michael@0: NS_ASSERTION(mIndex=0,"Current is out of bounds"); michael@0: #ifndef TIMELESS_LIGHTWEIGHT michael@0: if (mIndex>=mDeque.mSize||mIndex<0) return 0; michael@0: #endif michael@0: return mDeque.ObjectAt(mIndex); michael@0: } michael@0: michael@0: /** michael@0: * Call this method when you want to iterate all the michael@0: * members of the container, passing a functor along michael@0: * to call your code. michael@0: * michael@0: * @param aFunctor object to call for each member michael@0: * @return *this michael@0: */ michael@0: void nsDequeIterator::ForEach(nsDequeFunctor& aFunctor) const{ michael@0: mDeque.ForEach(aFunctor); michael@0: } michael@0: michael@0: /** michael@0: * Call this method when you want to iterate all the michael@0: * members of the container, calling the functor you michael@0: * passed with each member. This process will interrupt michael@0: * if your function returns non 0 to this method. michael@0: * michael@0: * @param aFunctor object to call for each member michael@0: * @return first nonzero result of aFunctor or 0. michael@0: */ michael@0: const void* nsDequeIterator::FirstThat(nsDequeFunctor& aFunctor) const{ michael@0: return mDeque.FirstThat(aFunctor); michael@0: }