home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
tlx501.zip
/
SRC
/
VPQUEUE.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-08
|
19KB
|
638 lines
/****************************************************************************
$Id: vpqueue.cpp 501.0 1995/03/07 12:26:28 RON Exp $
Copyright (c) 1991-95 Tarma Software Research. All rights reserved.
Project: Tarma Library for C++ V5.0
Author: Ron van der Wal
Implementation of class TLVPQueue.
$Log: vpqueue.cpp $
Revision 501.0 1995/03/07 12:26:28 RON
Updated for TLX 5.01
Revision 1.10 1995/01/31 16:30:34 RON
Update for release 012
Added partial support for SunPro C++ compiler
Revision 1.9 1995/01/06 15:58:51 ron
Corrected Revision keyword
Revision 1.8 1994/11/16 15:45:55 ron
Added module info; rearranged #include directives
Revision 1.7 1994/10/05 18:46:06 ron
Renamed TLx...() functions to tl...()
Revision 1.6 1994/09/28 14:47:35 ron
Removed Macintosh-style #include references
Revision 1.5 1994/09/27 20:23:27 ron
Changed path separator from / to \
Revision 1.4 1994/09/26 15:51:16 ron
Renamed SetSize() to Resize()
Revision 1.3 1994/09/07 15:45:25 ron
Corrected 3 subtle, but horrible mistakes in the assumptions about
mLeft and mRight w.r.t. insertion and extraction
Revision 1.2 1994/09/06 20:28:18 ron
Corrected assertions in Count()
Removed references to Count() in other asserts where the class invariant
was temporarily violated
Revision 1.1 1994/08/16 18:13:22 ron
Initial revision
****************************************************************************/
#include <tlx\501\_build.h>
TLX_MODULE_INFO("$Revision: 501.0 $");
#include <string.h> // For mem... routines
#include <tlx\501\except.h> // Exception handling
#include <tlx\501\vparrays.h> // Class declaration
/*-------------------------------------------------------------------------*/
TLVPQueue::TLVPQueue(size_t aSize, size_t aDelta)
/* Constructor, also doubles as default constructor. Creates an empty
queue with a given initial capacity and expansion factor.
---------------------------------------------------------------------------*/
: mVector(aSize), mDelta(aDelta), mLeft(0), mRight(0), mCount(0)
{
TLX_ASSERT(Size() == aSize);
}
/*-------------------------------------------------------------------------*/
TLVPQueue::TLVPQueue(void *vp)
/* Constructor creating a queue with a single element in it. The queue
is not expandable initially.
---------------------------------------------------------------------------*/
: mVector(vp), mDelta(0), mLeft(0), mRight(0), mCount(1)
{
TLX_ASSERT(Size() == 1);
}
/*-------------------------------------------------------------------------*/
TLVPQueue::TLVPQueue(void **vpp, size_t sz)
/* Constructor creating a queue from a C-style vector of pointers. The
queue is not expandable initially.
---------------------------------------------------------------------------*/
: mVector(vpp, sz), mDelta(0), mLeft(0), mRight(0), mCount(sz)
{
TLX_ASSERT(Size() == sz);
}
/*-------------------------------------------------------------------------*/
TLVPQueue::TLVPQueue(const TLVPQueue &q)
/* Copy constructor.
---------------------------------------------------------------------------*/
: mVector(q.mVector), mDelta(q.mDelta), mLeft(q.mLeft), mRight(q.mRight),
mCount(q.mCount)
{
TLX_ASSERT(Size() == q.Size());
}
/*-------------------------------------------------------------------------*/
void TLVPQueue::AddLeft(void *vp)
/* Adds a single element to the left end of the queue, expanding the queue
if necessary and allowed.
---------------------------------------------------------------------------*/
{
TLX_ASSERT_PTR(vp);
EnsureAvailable(1);
if (--mLeft < 0)
mLeft = Size() - 1;
mVector[mLeft] = vp;
mCount++;
}
#if defined(_MSC_VER) && _MSC_VER < 1011
// MSVC++ 4.1 internal compiler error
#pragma optimize( "g", off)
#endif
/*-------------------------------------------------------------------------*/
void TLVPQueue::AddRight(void *vp)
/* Adds a single element to the right end of the queue, expanding the
queue if necessary and allowed.
---------------------------------------------------------------------------*/
{
TLX_ASSERT_PTR(vp);
EnsureAvailable(1);
mVector[mRight] = vp;
if ((tSize)++mRight >= Size())
mRight = 0;
mCount++;
}
#if defined(_MSC_VER) && _MSC_VER < 1011
#pragma optimize( "", on)
#endif
/*-------------------------------------------------------------------------*/
size_t TLVPQueue::Available() const
/* Returns the number of free slots in the storage for the queue.
---------------------------------------------------------------------------*/
{
TLX_ASSERT(Size() >= Count());
return Size() - Count();
}
/*-------------------------------------------------------------------------*/
bool TLVPQueue::Contains(void *vp) const
/* Checks if the given pointer appears in the queue.
---------------------------------------------------------------------------*/
{
if (Count() < 1)
return false;
for (index_t i = mLeft;;)
{
if (mVector[i] == vp)
return true;
if ((tSize)++i >= Size())
i = 0;
if (i == mRight)
break;
}
return false;
}
/*-------------------------------------------------------------------------*/
size_t TLVPQueue::Count() const
/* Returns the number of elements in the queue.
---------------------------------------------------------------------------*/
{
#ifdef _TLXDBG
// Perform some sanity checks
TLX_ASSERT(mCount <= Size());
if (mCount == Size()) // Queue full
TLX_ASSERT(mLeft == mRight);
else if (mLeft <= mRight) // No wrap around
TLX_ASSERT(mRight - mLeft == mCount);
else // Wrap around
TLX_ASSERT(Size() - mLeft + mRight == mCount);
#endif
return mCount;
}
/*-------------------------------------------------------------------------*/
void TLVPQueue::EnqueueUnique(void *vp)
/* Adds the pointer to the queue, but only if it does not yet appear
in the queue.
---------------------------------------------------------------------------*/
{
if (!Contains(vp))
Enqueue(vp);
}
/*-------------------------------------------------------------------------*/
void TLVPQueue::EnsureAvailable(size_t aSize)
/* Ensures that there are at least 'aSize' positions available in the
physical storage for the queue. If necessary and allowed, the queue
storage is resized to make room.
---------------------------------------------------------------------------*/
{
size_t avail = Available();
if (avail >= aSize)
return;
if (IsFrozen())
THROW(TLXFull(LOCUS));
size_t expansion = ((aSize - avail) % mDelta + 1) * mDelta;
Resize(Size() + expansion);
TLX_ASSERT(Available() >= aSize);
}
/*-------------------------------------------------------------------------*/
void *TLVPQueue::Extract(void *vp)
/* Removes the given pointer from the queue and returns it.
---------------------------------------------------------------------------*/
{
if (IsEmpty())
THROW(TLXEmpty(LOCUS));
// Find index of item to be removed
index_t i;
for (i = mLeft; mVector[i] != vp; )
{
if ((tSize)++i >= Size()) i = 0;
if (i >= mRight)
THROW(TLXNotFound(LOCUS));
}
// Fill the gap by either moving left part to the right, or right
// part to the left, depending on the easier operation.
if (i >= mLeft) // Move left part to the right
{
TLX_ASSERT(mLeft < Size() - 1 || mLeft == i);
memmove(&mVector[mLeft + 1], &mVector[mLeft],
(i - mLeft) * sizeof(void *));
mVector[mLeft] = 0;
if ((tSize)++mLeft >= Size()) mLeft = 0;
}
else // Move right wrap-around to the left
{
TLX_ASSERT(i < mRight);
TLX_ASSERT(mRight <= mLeft);
TLX_ASSERT(mRight > 0);
mRight--;
memmove(&mVector[mRight - 1], &mVector[mRight],
(mRight - i) * sizeof(void *));
mVector[mRight] = 0;
}
TLX_ASSERT(mCount > 0);
mCount--;
return vp;
}
/*-------------------------------------------------------------------------*/
void *TLVPQueue::ExtractLeft()
/* Removes the leftmost element from the queue and returns it.
---------------------------------------------------------------------------*/
{
if (IsEmpty())
THROW(TLXEmpty(LOCUS));
void *tmp = mVector[mLeft];
mVector[mLeft] = 0;
if ((tSize)++mLeft >= Size())
mLeft = 0;
TLX_ASSERT(mCount > 0);
mCount--;
return tmp;
}
/*-------------------------------------------------------------------------*/
void *TLVPQueue::ExtractRight()
/* Removes the rightmost element from the queue and returns it.
---------------------------------------------------------------------------*/
{
if (IsEmpty())
THROW(TLXEmpty(LOCUS));
if (--mRight < 0)
mRight = Size() - 1;
void *tmp = mVector[mRight];
mVector[mRight] = 0;
TLX_ASSERT(mCount > 0);
mCount--;
return tmp;
}
#if 0
/*-------------------------------------------------------------------------*/
bool TLVPQueue::IterFirst(index_t &i) const
/* Initializes the iterator to point at the first (leftmost) element of
the queue. Returns nonzero if the iterator is valid, else zero.
---------------------------------------------------------------------------*/
{
i = mLeft;
return !IsEmpty();
}
/*-------------------------------------------------------------------------*/
bool TLVPQueue::IterLast(index_t &i) const
/* Initializes the iterator to point at the last (rightmost) element of
the queue. Returns nonzero if the iterator is valid, else zero.
---------------------------------------------------------------------------*/
{
i = mRight - 1;
if (i < 0)
i = Size() - 1;
return !IsEmpty();
}
/*-------------------------------------------------------------------------*/
bool TLVPQueue::IterNext(index_t &i) const
/* Advances the iterator one position from left to right, returning nonzero
if the iterator is still valid after the operation.
---------------------------------------------------------------------------*/
{
if (++i >= Size())
i = 0;
// Note: we use the '> mLeft' test to ensure termination
if (mRight >= mLeft)
return i < mRight;
else
return i > mLeft || i < mRight;
}
/*-------------------------------------------------------------------------*/
bool TLVPQueue::IterPrev(index_t &i) const
/* Advances the iterator one position from right to left, returning nonzero
if the iterator is still valid after the operation.
---------------------------------------------------------------------------*/
{
if (--i < 0)
i = Size() - 1;
if (mRight >= mLeft)
return i >= mLeft;
else
return i >= mLeft || i < mRight;
}
#endif
/*-------------------------------------------------------------------------*/
TLVPQueue &TLVPQueue::operator =(void *vp)
/* Overloading of assignment operator which allows assignment of a single
pointer to the queue. The previous contents of the queue are deleted.
---------------------------------------------------------------------------*/
{
mVector.operator =(vp);
mLeft = 0;
mRight = 0;
mCount = 1;
return *this;
}
/*-------------------------------------------------------------------------*/
TLVPQueue &TLVPQueue::operator =(const TLVPQueue &q)
/* Overloading of assignment operator.
---------------------------------------------------------------------------*/
{
if (this != &q)
{
mVector.operator =(q.mVector);
mDelta = q.mDelta;
mLeft = q.mLeft;
mRight = q.mRight;
mCount = q.mCount;
}
return *this;
}
#if 0
/*-------------------------------------------------------------------------*/
void *&TLVPQueue::PeekIter(index_t i)
/* Returns a reference to the element that is being addressed by the
iterator.
---------------------------------------------------------------------------*/
{
TLX_ASSERT(Count() > 0);
if (mLeft < mRight)
TLX_ASSERT(i >= mLeft && i < mRight);
else
TLX_ASSERT((i >= mLeft && i < Size()) || (i > 0 && i < mRight));
return mVector[i];
}
/*-------------------------------------------------------------------------*/
void *TLVPQueue::PeekIter(index_t i) const
/* Returns the value of the element that is being addressed by the
iterator.
---------------------------------------------------------------------------*/
{
TLX_ASSERT(Count() > 0);
if (mLeft < mRight)
TLX_ASSERT(i >= mLeft && i < mRight);
else
TLX_ASSERT((i >= mLeft && i < Size()) || (i > 0 && i < mRight));
return mVector[i];
}
#endif
/*-------------------------------------------------------------------------*/
void *&TLVPQueue::PeekLeft()
/* Returns a reference to the leftmost element in the queue.
---------------------------------------------------------------------------*/
{
if (IsEmpty())
THROW(TLXEmpty(LOCUS));
return mVector[mLeft];
}
/*-------------------------------------------------------------------------*/
void *TLVPQueue::PeekLeft() const
/* Returns the value of the leftmost element in the queue.
---------------------------------------------------------------------------*/
{
if (IsEmpty())
THROW(TLXEmpty(LOCUS));
return mVector[mLeft];
}
/*-------------------------------------------------------------------------*/
void *&TLVPQueue::PeekRight()
/* Returns a reference to the rightmost element in the queue.
---------------------------------------------------------------------------*/
{
if (IsEmpty())
THROW(TLXEmpty(LOCUS));
return mVector[mRight > 0 ? mRight - 1 : Size() - 1];
}
/*-------------------------------------------------------------------------*/
void *TLVPQueue::PeekRight() const
/* Returns the value of the rightmost element in the queue.
---------------------------------------------------------------------------*/
{
if (IsEmpty())
THROW(TLXEmpty(LOCUS));
return mVector[mRight > 0 ? mRight - 1 : Size() - 1];
}
#if defined(_MSC_VER) && _MSC_VER < 1011
// MSVC++ 4.1 internal compiler error
#pragma optimize( "g", off)
#endif
/*-------------------------------------------------------------------------*/
void TLVPQueue::Remove(void *vp)
/* Removes a single element from the queue, deleting the pointed to object
if we own it.
---------------------------------------------------------------------------*/
{
void *ptr = Extract(vp);
TLX_ASSERT(ptr == vp);
if (IsOwner())
DeletePtr(ptr);
}
#if defined(_MSC_VER) && _MSC_VER < 1011
#pragma optimize( "", on)
#endif
/*-------------------------------------------------------------------------*/
void TLVPQueue::RemoveAll()
/* Removes all pointers in the queue, deleting the pointed to objects
if we are the owner of them.
---------------------------------------------------------------------------*/
{
mVector.RemoveAll();
mLeft = mRight = mCount = 0;
}
/*-------------------------------------------------------------------------*/
void TLVPQueue::RemoveLeft()
/* Removes the leftmost element from the queue, deleting the pointed to
object if we are the owner.
---------------------------------------------------------------------------*/
{
void *ptr = ExtractLeft();
if (IsOwner())
DeletePtr(ptr);
}
/*-------------------------------------------------------------------------*/
void TLVPQueue::RemoveRight()
/* Removes the rightmost element from the queue, deleting the pointed to
object if we are the owner.
---------------------------------------------------------------------------*/
{
void *ptr = ExtractRight();
if (IsOwner())
DeletePtr(ptr);
}
/*-------------------------------------------------------------------------*/
size_t TLVPQueue::SetDelta(size_t aDelta)
/* Sets a new delta, returning the old one.
---------------------------------------------------------------------------*/
{
size_t olddelta = mDelta;
mDelta = aDelta;
return olddelta;
}
/*-------------------------------------------------------------------------*/
void TLVPQueue::Resize(size_t aSize)
/* Changes the physical size of the queue, without disturbing the items in
the queue. It is an error to reduce the size of the queue below the
current logical length.
---------------------------------------------------------------------------*/
{
if (aSize < Count())
THROW(TLXResize(LOCUS, aSize, Count()));
if (aSize == Size()) // Trivial
return;
// Resizing the queue is complicated by the fact that the elements in
// the queue may be wrapped around. If we simply use the mVector.Size()
// operation, this may result in a malformed queue, as follows:
//
// - If the size of the queue is reduced, elements at the right end
// of the queue will be lost;
// - If the size of the queue is increased, a gap at the right end
// will open.
//
// To prevent this from happening, we take some precautions:
//
// - If the size of the queue is reduced, we first move down the elements
// at the right;
// - If the size of the queue is enlarged, we move the elements at the
// right up after the enlargement.
size_t oldsize = Size();
if (aSize < oldsize)
{
// Move endangered items to a safe place
if (mLeft < mRight) // No wrap around; just move down
{
if (mLeft > 0)
{
memmove(&mVector[0], &mVector[mLeft], mCount * sizeof(void *));
memset(&mVector[mRight], 0, (Size()-mRight+1) * sizeof(void *));
mLeft = 0;
mRight = mCount;
}
}
else // Wrap around; reduce the gap
{
// Elements are moved to the left
size_t reduction = oldsize - aSize;
memmove(&mVector[mLeft-reduction], &mVector[mLeft],
(Size() - mLeft) * sizeof(void *));
mLeft -= reduction;
// We now have a gap at the far right that will vanish as the
// vector is reduced in size.
}
}
// Perform the resize operation
mVector.Resize(aSize);
TLX_ASSERT(Size() == aSize);
TLX_ASSERT(Size() >= mCount);
// See if we must adjust the queue items any further
if (aSize > oldsize)
{
// If there was a wrap around to start with, we now have a gap in
// the middle of the queue (located at the end of the underlying
// vector). Move up the right part of the vector to close it.
if (mLeft >= mRight && mCount > 0) // Wrap around
{
size_t expansion = Size() - oldsize;
size_t bytesToMove = oldsize - mLeft;
memmove(&mVector[mLeft + expansion], &mVector[mLeft],
bytesToMove * sizeof(void *));
memset(&mVector[mLeft], 0, bytesToMove * sizeof(void *));
mLeft += expansion;
}
}
TLX_ASSERT(Count() == mCount);
}