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