Chat-O-Matic/libs/libdownload/Queue.h

1055 lines
35 KiB
C
Raw Normal View History

2010-07-10 08:37:58 -05:00
/* This file is Copyright 2005 Level Control Systems. See the included LICENSE.txt file for details. */
#ifndef MuscleQueue_h
#define MuscleQueue_h
//#include "support/MuscleSupport.h"
#define ARRAYITEMS(x) (sizeof(x)/sizeof(x[0])) /* returns # of items in array */
template<typename T> inline const T& muscleMax(const T& p1, const T& p2)
{
return (p1 < p2) ? p2 : p1;
}
template<typename T> inline const T& muscleMin(const T& p1, const T& p2)
{
return (p1 < p2) ? p1 : p2;
}
# define newnothrow new
# define newnothrow_array(T, count) newnothrow T[count]
#ifndef SMALL_QUEUE_SIZE
# define SMALL_QUEUE_SIZE 3
#endif
/** This class implements a templated double-ended queue data structure.
* Adding or removing items from the head or tail of a Queue is (on average)
* an O(1) operation. A Queue also makes for a nice Vector, if that's all you need.
*/
template <class ItemType> class Queue
{
public:
/** Constructor.
* @param initialSlots Specifies how many slots to pre-allocate. Defaults to (SMALL_QUEUE_SIZE).
*/
Queue(uint32 initialSlots = SMALL_QUEUE_SIZE);
/** Copy constructor. */
Queue(const Queue& copyMe);
/** Destructor. */
virtual ~Queue();
/** Assigment operator. */
Queue& operator=(const Queue& from);
/** Equality operator. Queues are equal if they are the same length, and
* every nth item in this queue is == to the corresponding item in (rhs). */
bool operator==(const Queue& rhs) const;
/** Returns the negation of the equality operator */
bool operator!=(const Queue& rhs) const {
return !(*this == rhs);
}
/** Appends (item) to the end of the queue. Queue size grows by one.
* @param item The item to append.
* @return B_NO_ERROR on success, B_ERROR on failure (out of memory)
*/
status_t AddTail(const ItemType& item = ItemType());
/** Appends some or all items in (queue) to the end of our queue. Queue size
* grows by (queue.GetNumItems()).
* For example:
* Queue a; // contains 1, 2, 3, 4
* Queue b; // contains 5, 6, 7, 8
* a.AddTail(b); // a now contains 1, 2, 3, 4, 5, 6, 7, 8
* @param queue The queue to append to our queue.
* @param startIndex Index in (queue) to start adding at. Default to zero.
* @param numItems Number of items to add. If this number is too large, it will be capped appropriately. Default is to add all items.
* @return B_NO_ERROR on success, B_ERROR on failure (out of memory)
*/
status_t AddTail(const Queue<ItemType> & queue, uint32 startIndex = 0, uint32 numItems = (uint32) - 1);
/** Adds the given array of items to the tail of the Queue. Equivalent
* to adding them to the tail of the Queue one at a time, but somewhat
* more efficient. On success, the queue size grows by (numItems).
* @param items Pointer to an array of items to add to the Queue.
* @param numItems Number of items in the array
* @return B_NO_ERROR on success, or B_ERROR on failure (out of memory)
*/
status_t AddTail(const ItemType* items, uint32 numItems);
/** Prepends (item) to the head of the queue. Queue size grows by one.
* @param item The item to prepend.
* @return B_NO_ERROR on success, B_ERROR on failure (out of memory)
*/
status_t AddHead(const ItemType& item = ItemType());
/** Concatenates (queue) to the head of our queue.
* Our queue size grows by (queue.GetNumItems()).
* For example:
* Queue a; // contains 1, 2, 3, 4
* Queue b; // contains 5, 6, 7, 8
* a.AddHead(b); // a now contains 5, 6, 7, 8, 1, 2, 3, 4
* @param queue The queue to prepend to our queue.
* @param startIndex Index in (queue) to start adding at. Default to zero.
* @param numItems Number of items to add. If this number is too large, it will be capped appropriately. Default is to add all items.
* @return B_NO_ERROR on success, B_ERROR on failure (out of memory)
*/
status_t AddHead(const Queue<ItemType> & queue, uint32 startIndex = 0, uint32 numItems = (uint32) - 1);
/** Concatenates the given array of items to the head of the Queue.
* The semantics are the same as for AddHead(const Queue<ItemType> &).
* @param items Pointer to an array of items to add to the Queue.
* @param numItems Number of items in the array
* @return B_NO_ERROR on success, or B_ERROR on failure (out of memory)
*/
status_t AddHead(const ItemType* items, uint32 numItems);
/** Removes the item at the head of the queue.
* @return B_NO_ERROR on success, B_ERROR if the queue was empty.
*/
status_t RemoveHead();
/** Removes the item at the head of the queue and places it into (returnItem).
* @param returnItem On success, the removed item is copied into this object.
* @return B_NO_ERROR on success, B_ERROR if the queue was empty
*/
status_t RemoveHead(ItemType& returnItem);
/** Removes the item at the tail of the queue.
* @return B_NO_ERROR on success, B_ERROR if the queue was empty.
*/
status_t RemoveTail();
/** Removes the item at the tail of the queue and places it into (returnItem).
* @param returnItem On success, the removed item is copied into this object.
* @return B_NO_ERROR on success, B_ERROR if the queue was empty
*/
status_t RemoveTail(ItemType& returnItem);
/** Removes the item at the (index)'th position in the queue.
* @param index Which item to remove--can range from zero
* (head of the queue) to GetNumItems()-1 (tail of the queue).
* @return B_NO_ERROR on success, B_ERROR on failure (i.e. bad index)
* Note that this method is somewhat inefficient for indices that
* aren't at the head or tail of the queue (i.e. O(n) time)
*/
status_t RemoveItemAt(uint32 index);
/** Removes the item at the (index)'th position in the queue, and copies it into (returnItem).
* @param index Which item to remove--can range from zero
* (head of the queue) to (GetNumItems()-1) (tail of the queue).
* @param returnItem On success, the removed item is copied into this object.
* @return B_NO_ERROR on success, B_ERROR on failure (i.e. bad index)
*/
status_t RemoveItemAt(uint32 index, ItemType& returnItem);
/** Copies the (index)'th item into (returnItem).
* @param index Which item to get--can range from zero
* (head of the queue) to (GetNumItems()-1) (tail of the queue).
* @param returnItem On success, the retrieved item is copied into this object.
* @return B_NO_ERROR on success, B_ERROR on failure (e.g. bad index)
*/
status_t GetItemAt(uint32 index, ItemType& returnItem) const;
/** Returns a pointer to an item in the array (i.e. no copying of the item is done).
* Included for efficiency; be careful with this: modifying the queue can invalidate
* the returned pointer!
* @param index Index of the item to return a pointer to.
* @return a pointer to the internally held item, or NULL if (index) was invalid.
*/
ItemType* GetItemAt(uint32 index) const;
/** Replaces the (index)'th item in the queue with (newItem).
* @param index Which item to replace--can range from zero
* (head of the queue) to (GetNumItems()-1) (tail of the queue).
* @param newItem The item to place into the queue at the (index)'th position.
* @return B_NO_ERROR on success, B_ERROR on failure (e.g. bad index)
*/
status_t ReplaceItemAt(uint32 index, const ItemType& newItem = ItemType());
/** Inserts (item) into the (nth) slot in the array. InsertItemAt(0)
* is the same as AddHead(item), InsertItemAt(GetNumItems()) is the same
* as AddTail(item). Other positions will involve an O(n) shifting of contents.
* @param index The position at which to insert the new item.
* @param newItem The item to insert into the queue.
* @return B_NO_ERROR on success, B_ERROR on failure (i.e. bad index).
*/
status_t InsertItemAt(uint32 index, const ItemType& newItem = ItemType());
/** Removes all items from the queue.
* @param releaseCachedBuffers If true, we will immediately free any buffers that we may be holding. Otherwise
* we will keep them around so that they can be re-used in the future.
*/
void Clear(bool releaseCachedBuffers = false);
/** Returns the number of items in the queue. (This number does not include pre-allocated space) */
uint32 GetNumItems() const {
return _itemCount;
}
/** Returns the number of item-slots we have allocated space for. Note that this is NOT
* the same as the value returned by GetNumItems() -- it is generally larger, since we pre-allocate
* additional slots in advance to cut down on the number of re-allocations we need to do.
*/
uint32 GetNumAllocatedItemSlots() const {
return _queueSize;
}
/** Returns true iff their are no items in the queue. */
bool IsEmpty() const {
return (_itemCount == 0);
}
/** Returns a read-only reference the head item in the queue. You must not call this when the queue is empty! */
const ItemType& Head() const {
return *GetItemAt(0);
}
/** Returns a read-only reference the tail item in the queue. You must not call this when the queue is empty! */
const ItemType& Tail() const {
return *GetItemAt(_itemCount - 1);
}
/** Returns a writable reference the head item in the queue. You must not call this when the queue is empty! */
ItemType& Head() {
return *GetItemAt(0);
}
/** Returns a writable reference the tail item in the queue. You must not call this when the queue is empty! */
ItemType& Tail() {
return *GetItemAt(_itemCount - 1);
}
/** Returns a pointer to the first item in the queue, or NULL if the queue is empty */
ItemType* HeadPointer() const {
return (_itemCount > 0) ? GetItemAt(0) : NULL;
}
/** Returns a pointer to the last item in the queue, or NULL if the queue is empty */
ItemType* TailPointer() const {
return (_itemCount > 0) ? GetItemAt(_itemCount - 1) : NULL;
}
/** Convenient read-only array-style operator (be sure to only use valid indices!) */
const ItemType& operator [](uint32 Index) const;
/** Convenient read-write array-style operator (be sure to only use valid indices!) */
ItemType& operator [](uint32 Index);
/** Deprecated synonym for GetItemAt(). Don't call this method in new code, call GetItemAt() instead!
* @deprecated
*/
ItemType* GetItemPointer(uint32 index) const {
return GetItemAt(index);
}
/** Makes sure there is enough space allocated for at least (numSlots) items.
* You only need to call this if you wish to minimize the number of data re-allocations done,
* or wish to add or remove a large number of default items at once (by specifying setNumItems=true).
* @param numSlots the minimum amount of items to pre-allocate space for in the Queue.
* @param setNumItems If true, the length of the Queue will be altered by adding or removing
* items to (from) the tail of the Queue until the Queue is the specified size.
* If false (the default), more slots may be pre-allocated, but the
* number of items officially in the Queue remains the same as before.
* @param extraReallocItems If we have to do an array reallocation, this many extra slots will be
* added to the newly allocated array, above and beyond what is strictly
* necessary. That way the likelihood of another reallocation being necessary
* in the near future is reduced. Default value is zero, indicating that
* no extra slots will be allocated. This argument is ignored if (setNumItems) is true.
* @returns B_NO_ERROR on success, or B_ERROR on failure (out of memory)
*/
status_t EnsureSize(uint32 numSlots, bool setNumItems = false, uint32 extraReallocItems = 0);
/** Returns the last index of the given (item), or -1 if (item) is
* not found in the list. O(n) search time.
* @param item The item to look for.
* @return The index of (item), or -1 if no such item is present.
*/
int32 IndexOf(const ItemType& item) const;
/**
* Swaps the values of the two items at the given indices. This operation
* will involve three copies of the held items.
* @param fromIndex First index to swap.
* @param toIndex Second index to swap.
*/
void Swap(uint32 fromIndex, uint32 toIndex);
/**
* Reverses the ordering of the items in the given subrange.
* (e.g. if the items were A,B,C,D,E, this would change them to E,D,C,B,A)
* @param from Index of the start of the subrange. Defaults to zero.
* @param to Index of the next item after the end of the subrange. If greater than
* the number of items currently in the queue, this value will be clipped
* to be equal to that number. Defaults to the largest possible uint32.
*/
void ReverseItemOrdering(uint32 from = 0, uint32 to = ((uint32) - 1));
/**
* This is the signature of the type of callback function that you must pass
* into the Sort() method. This function should work like strcmp(), returning
* a negative value if (item1) is less than item2, or zero if the items are
* equal, or a positive value if (item1) is greater than item2.
* @param item1 The first item
* @param item2 The second item
* @param cookie A user-defined value that was passed in to the Sort() method.
* @return A value indicating which item is "larger", as defined above.
*/
typedef int (*ItemCompareFunc)(const ItemType& item1, const ItemType& item2, void* cookie);
/**
* Does an in-place, stable sort on a subrange of the contents of this Queue.
* (The sort algorithm is a smart, in-place merge sort).
* @param compareFunc A function that compares two items in this queue and returns
* a value indicating which is "larger". (negative indicates
* that the second parameter is larger, positive indicate that the
* first is larger, and zero indicates that the two parameters are equal)
* @param from Index of the start of the subrange. Defaults to zero.
* @param to Index of the next item after the end of the subrange.
* If greater than the number of items currently in the queue,
* the subrange will extend to the last item. Defaults to the largest possible uint32.
* @param optCookie A user-defined value that will be passed to the (compareFunc).
*/
void Sort(ItemCompareFunc compareFunc, uint32 from = 0, uint32 to = ((uint32) - 1), void* optCookie = NULL);
/**
* Swaps our contents with the contents of (that), in an efficient manner.
* @param that The queue whose contents are to be swapped with our own.
*/
void SwapContents(Queue<ItemType> & that);
/**
* Goes through the array and removes every item that is equal to (val).
* @param val the item to look for and remove
* @return The number of instances of (val) that were found and removed during this operation.
*/
uint32 RemoveAllInstancesOf(const ItemType& val);
/**
* Goes through the array and removes the first item that is equal to (val).
* @param val the item to look for and remove
* @return B_NO_ERROR if a matching item was found and removed, or B_ERROR if it wasn't found.
*/
status_t RemoveFirstInstanceOf(const ItemType& val);
/**
* Goes through the array and removes the last item that is equal to (val).
* @param val the item to look for and remove
* @return B_NO_ERROR if a matching item was found and removed, or B_ERROR if it wasn't found.
*/
status_t RemoveLastInstanceOf(const ItemType& val);
/** Returns true iff the first item in our queue is equal to (prefix). */
bool StartsWith(const ItemType& prefix) const {
return ((GetNumItems() > 0) && (Head() == prefix));
}
/** Returns true iff the (prefixQueue) is a prefix of this queue. */
bool StartsWith(const Queue<ItemType> & prefixQueue) const;
/** Returns true iff the last item in our queue is equal to (suffix). */
bool EndsWith(const ItemType& suffix) const {
return ((GetNumItems() > 0) && (Tail() == suffix));
}
/** Returns true iff the (suffixQueue) is a suffix of this queue. */
bool EndsWith(const Queue<ItemType> & suffixQueue) const;
/**
* Returns a pointer to the nth internally-held contiguous-Item-sub-array, to allow efficient
* array-style access to groups of items in this Queue. In the current implementation
* there may be as many as two such sub-arrays present, depending on the internal state of the Queue.
* @param whichArray Index of the internal array to return a pointer to. Typically zero or one.
* @param retLength On success, the number of items in the returned sub-array will be written here.
* @return Pointer to the first item in the sub-array on success, or NULL on failure.
* Note that this array is only guaranteed valid as long as no items are
* added or removed from the Queue.
*/
ItemType* GetArrayPointer(uint32 whichArray, uint32& retLength) {
return const_cast<ItemType*>(GetArrayPointerAux(whichArray, retLength));
}
/** Read-only version of the above */
const ItemType* GetArrayPointer(uint32 whichArray, uint32& retLength) const {
return GetArrayPointerAux(whichArray, retLength);
}
private:
const ItemType* GetArrayPointerAux(uint32 whichArray, uint32& retLength) const;
void SwapContentsAux(Queue<ItemType> & that);
inline uint32 NextIndex(uint32 idx) const {
return (idx >= _queueSize - 1) ? 0 : idx + 1;
}
inline uint32 PrevIndex(uint32 idx) const {
return (idx == 0) ? _queueSize - 1 : idx - 1;
}
// Translates a user-index into an index into the _queue array.
inline uint32 InternalizeIndex(uint32 idx) const {
return (_headIndex + idx) % _queueSize;
}
// Helper methods, used for sorting (stolen from http://www-ihm.lri.fr/~thomas/VisuTri/inplacestablesort.html)
void Merge(ItemCompareFunc compareFunc, uint32 from, uint32 pivot, uint32 to, uint32 len1, uint32 len2, void* cookie);
uint32 Lower(ItemCompareFunc compareFunc, uint32 from, uint32 to, const ItemType& val, void* cookie) const;
uint32 Upper(ItemCompareFunc compareFunc, uint32 from, uint32 to, const ItemType& val, void* cookie) const;
ItemType _smallQueue[SMALL_QUEUE_SIZE]; // small queues can be stored inline in this array
ItemType* _queue; // points to _smallQueue, or to a dynamically alloc'd array
uint32 _queueSize; // number of slots in the _queue array
uint32 _itemCount; // number of valid items in the array
uint32 _headIndex; // index of the first filled slot (meaningless if _itemCount is zero)
uint32 _tailIndex; // index of the last filled slot (meaningless if _itemCount is zero)
const uint32 _initialSize; // as specified in ctor
};
template <class ItemType>
Queue<ItemType>::Queue(uint32 initialSize)
: _queue(NULL), _queueSize(0), _itemCount(0), _initialSize(initialSize)
{
// empty
}
template <class ItemType>
Queue<ItemType>::Queue(const Queue& rhs)
: _queue(NULL), _queueSize(0), _itemCount(0), _initialSize(rhs._initialSize)
{
*this = rhs;
}
template <class ItemType>
bool
Queue<ItemType>::operator ==(const Queue& rhs) const
{
if (this == &rhs) return true;
if (GetNumItems() != rhs.GetNumItems()) return false;
for (int i = GetNumItems() - 1; i >= 0; i--) if (((*this)[i] == rhs[i]) == false) return false;
return true;
}
template <class ItemType>
Queue<ItemType> &
Queue<ItemType>::operator =(const Queue& rhs)
{
if (this != &rhs) {
uint32 numItems = rhs.GetNumItems();
if (EnsureSize(numItems, true) == B_NO_ERROR) for (uint32 i = 0; i < numItems; i++) (*this)[i] = rhs[i];
}
return *this;
}
template <class ItemType>
ItemType &
Queue<ItemType>::operator[](uint32 i)
{
// MASSERT(i<_itemCount, "Invalid index to Queue::[]");
return _queue[InternalizeIndex(i)];
}
template <class ItemType>
const ItemType &
Queue<ItemType>::operator[](uint32 i) const
{
// MASSERT(i<_itemCount, "Invalid index to Queue::[]");
return _queue[InternalizeIndex(i)];
}
template <class ItemType>
ItemType *
Queue<ItemType>::GetItemAt(uint32 i) const
{
return &_queue[InternalizeIndex(i)];
}
template <class ItemType>
Queue<ItemType>::~Queue()
{
if (_queue != _smallQueue) delete [] _queue;
}
template <class ItemType>
status_t
Queue<ItemType>::
AddTail(const ItemType& item)
{
if (EnsureSize(_itemCount + 1, false, _itemCount + 1) == B_ERROR) return B_ERROR;
if (_itemCount == 0) _headIndex = _tailIndex = 0;
else _tailIndex = NextIndex(_tailIndex);
_queue[_tailIndex] = item;
_itemCount++;
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
AddTail(const Queue<ItemType> &queue, uint32 startIndex, uint32 numNewItems)
{
uint32 hisSize = queue.GetNumItems();
numNewItems = muscleMin(numNewItems, (startIndex < hisSize) ? (hisSize - startIndex) : 0);
uint32 mySize = GetNumItems();
uint32 newSize = mySize + numNewItems;
if (EnsureSize(newSize, true) != B_NO_ERROR) return B_ERROR;
for (uint32 i = mySize; i < newSize; i++) (*this)[i] = queue[startIndex++];
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
AddTail(const ItemType* items, uint32 numItems)
{
uint32 mySize = GetNumItems();
uint32 newSize = mySize + numItems;
uint32 rhs = 0;
if (EnsureSize(newSize, true) != B_NO_ERROR) return B_ERROR;
for (uint32 i = mySize; i < newSize; i++) (*this)[i] = items[rhs++];
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
AddHead(const ItemType& item)
{
if (EnsureSize(_itemCount + 1, false, _itemCount + 1) == B_ERROR) return B_ERROR;
if (_itemCount == 0) _headIndex = _tailIndex = 0;
else _headIndex = PrevIndex(_headIndex);
_queue[_headIndex] = item;
_itemCount++;
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
AddHead(const Queue<ItemType> &queue, uint32 startIndex, uint32 numNewItems)
{
uint32 hisSize = queue.GetNumItems();
numNewItems = muscleMin(numNewItems, (startIndex < hisSize) ? (hisSize - startIndex) : 0);
if (EnsureSize(numNewItems + GetNumItems()) != B_NO_ERROR) return B_ERROR;
for (int i = ((int)startIndex + numNewItems) - 1; i >= (int32)startIndex; i--) if (AddHead(queue[i]) == B_ERROR) return B_ERROR;
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
AddHead(const ItemType* items, uint32 numItems)
{
if (EnsureSize(_itemCount + numItems) != B_NO_ERROR) return B_ERROR;
for (int i = ((int)numItems) - 1; i >= 0; i--) if (AddHead(items[i]) == B_ERROR) return B_ERROR;
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
RemoveHead(ItemType& returnItem)
{
if (_itemCount == 0) return B_ERROR;
returnItem = _queue[_headIndex];
return RemoveHead();
}
template <class ItemType>
status_t
Queue<ItemType>::
RemoveHead()
{
if (_itemCount == 0) return B_ERROR;
int oldHeadIndex = _headIndex;
_headIndex = NextIndex(_headIndex);
_itemCount--;
_queue[oldHeadIndex] = ItemType(); // this must be done last, as queue state must be coherent when we do this
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
RemoveTail(ItemType& returnItem)
{
if (_itemCount == 0) return B_ERROR;
returnItem = _queue[_tailIndex];
return RemoveTail();
}
template <class ItemType>
status_t
Queue<ItemType>::
RemoveTail()
{
if (_itemCount == 0) return B_ERROR;
int removedItemIndex = _tailIndex;
_tailIndex = PrevIndex(_tailIndex);
_itemCount--;
_queue[removedItemIndex] = ItemType(); // this must be done last, as queue state must be coherent when we do this
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
GetItemAt(uint32 index, ItemType& returnItem) const
{
if (index >= _itemCount) return B_ERROR;
returnItem = _queue[InternalizeIndex(index)];
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
RemoveItemAt(uint32 index, ItemType& returnItem)
{
if (index >= _itemCount) return B_ERROR;
returnItem = _queue[InternalizeIndex(index)];
return RemoveItemAt(index);
}
template <class ItemType>
status_t
Queue<ItemType>::
RemoveItemAt(uint32 index)
{
if (index >= _itemCount) return B_ERROR;
uint32 internalizedIndex = InternalizeIndex(index);
uint32 indexToClear;
if (index < _itemCount / 2) {
// item is closer to the head: shift everything forward one, ending at the head
while (internalizedIndex != _headIndex) {
uint32 prev = PrevIndex(internalizedIndex);
_queue[internalizedIndex] = _queue[prev];
internalizedIndex = prev;
}
indexToClear = _headIndex;
_headIndex = NextIndex(_headIndex);
} else {
// item is closer to the tail: shift everything back one, ending at the tail
while (internalizedIndex != _tailIndex) {
uint32 next = NextIndex(internalizedIndex);
_queue[internalizedIndex] = _queue[next];
internalizedIndex = next;
}
indexToClear = _tailIndex;
_tailIndex = PrevIndex(_tailIndex);
}
_itemCount--;
_queue[indexToClear] = ItemType(); // this must be done last, as queue state must be coherent when we do this
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
ReplaceItemAt(uint32 index, const ItemType& newItem)
{
if (index >= _itemCount) return B_ERROR;
_queue[InternalizeIndex(index)] = newItem;
return B_NO_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
InsertItemAt(uint32 index, const ItemType& newItem)
{
// Simple cases
if (index > _itemCount) return B_ERROR;
if (index == _itemCount) return AddTail(newItem);
if (index == 0) return AddHead(newItem);
// Harder case: inserting into the middle of the array
if (index < _itemCount / 2) {
// Add a space at the front, and shift things back
if (AddHead() != B_NO_ERROR) return B_ERROR; // allocate an extra slot
for (uint32 i = 0; i < index; i++) ReplaceItemAt(i, *GetItemAt(i + 1));
} else {
// Add a space at the rear, and shift things forward
if (AddTail() != B_NO_ERROR) return B_ERROR; // allocate an extra slot
for (int32 i = ((int32)_itemCount) - 1; i > ((int32)index); i--) ReplaceItemAt(i, *GetItemAt(i - 1));
}
return ReplaceItemAt(index, newItem);
}
template <class ItemType>
void
Queue<ItemType>::
Clear(bool releaseCachedBuffers)
{
if ((releaseCachedBuffers) && (_queue != _smallQueue)) {
delete [] _queue;
_queue = NULL;
_queueSize = 0;
_itemCount = 0;
} else while (RemoveTail() == B_NO_ERROR) {
/* empty */
}
}
template <class ItemType>
status_t
Queue<ItemType>::
EnsureSize(uint32 size, bool setNumItems, uint32 extraPreallocs)
{
if ((_queue == NULL) || (_queueSize < size)) {
const uint32 sqLen = ARRAYITEMS(_smallQueue);
uint32 temp = size + extraPreallocs;
uint32 newQLen = muscleMax(_initialSize, ((setNumItems) || (temp <= sqLen)) ? muscleMax(sqLen, temp) : temp);
ItemType* newQueue = ((_queue == _smallQueue) || (newQLen > sqLen)) ? newnothrow_array(ItemType, newQLen) : _smallQueue;
if (newQueue == NULL) {
return B_ERROR;
}
if (newQueue == _smallQueue) newQLen = sqLen;
for (uint32 i = 0; i < _itemCount; i++) (void) GetItemAt(i, newQueue[i]); // we know that (_itemCount < size)
if (setNumItems) _itemCount = size;
_headIndex = 0;
_tailIndex = _itemCount - 1;
if (_queue == _smallQueue) {
ItemType blank = ItemType();
for (uint32 i = 0; i < sqLen; i++) _smallQueue[i] = blank;
} else delete [] _queue;
_queue = newQueue;
_queueSize = newQLen;
}
if (setNumItems) {
// Force ourselves to contain exactly the required number of items
if (_itemCount < size) {
// We can do this quickly because the "new" items are already initialized properly
_tailIndex = (_tailIndex + (size - _itemCount)) % _queueSize;
_itemCount = size;
} else while (_itemCount > size) (void) RemoveTail(); // Gotta overwrite the "removed" items, so this is a bit slower
}
return B_NO_ERROR;
}
template <class ItemType>
int32
Queue<ItemType>::
IndexOf(const ItemType& item) const
{
if (_queue) for (int i = ((int)GetNumItems()) - 1; i >= 0; i--) if (*GetItemAt(i) == item) return i;
return -1;
}
template <class ItemType>
void
Queue<ItemType>::
Swap(uint32 fromIndex, uint32 toIndex)
{
ItemType temp = *(GetItemAt(fromIndex));
ReplaceItemAt(fromIndex, *(GetItemAt(toIndex)));
ReplaceItemAt(toIndex, temp);
}
template <class ItemType>
void
Queue<ItemType>::
Sort(ItemCompareFunc compareFunc, uint32 from, uint32 to, void* cookie)
{
uint32 size = GetNumItems();
if (to > size) to = size;
if (to > from) {
if (to < from + 12) {
// too easy, just do a bubble sort (base case)
if (to > from + 1) {
for (uint32 i = from + 1; i < to; i++) {
for (uint32 j = i; j > from; j--) {
int ret = compareFunc(*(GetItemAt(j)), *(GetItemAt(j - 1)), cookie);
if (ret < 0) Swap(j, j - 1);
else break;
}
}
}
} else {
// Okay, do the real thing
uint32 middle = (from + to) / 2;
Sort(compareFunc, from, middle, cookie);
Sort(compareFunc, middle, to, cookie);
Merge(compareFunc, from, middle, to, middle - from, to - middle, cookie);
}
}
}
template <class ItemType>
void
Queue<ItemType>::
ReverseItemOrdering(uint32 from, uint32 to)
{
uint32 size = GetNumItems();
if (size > 0) {
to--; // make it inclusive
if (to >= size) to = size - 1;
while (from < to) Swap(from++, to--);
}
}
template <class ItemType>
status_t
Queue<ItemType>::
RemoveFirstInstanceOf(const ItemType& val)
{
uint32 ni = GetNumItems();
for (uint32 i = 0; i < ni; i++) if ((*this)[i] == val) return RemoveItemAt(i);
return B_ERROR;
}
template <class ItemType>
status_t
Queue<ItemType>::
RemoveLastInstanceOf(const ItemType& val)
{
for (int32 i = ((int32)GetNumItems()) - 1; i >= 0; i--) if ((*this)[i] == val) return RemoveItemAt(i);
return B_ERROR;
}
template <class ItemType>
uint32
Queue<ItemType>::
RemoveAllInstancesOf(const ItemType& val)
{
// Efficiently collapse all non-matching slots up to the top of the list
uint32 ret = 0;
uint32 writeTo = 0;
uint32 origSize = GetNumItems();
for (uint32 readFrom = 0; readFrom < origSize; readFrom++) {
const ItemType& nextRead = (*this)[readFrom];
if (nextRead == val) ret++;
else {
if (readFrom > writeTo) (*this)[writeTo] = nextRead;
writeTo++;
}
}
// Now get rid of any now-surplus slots
for (; writeTo < origSize; writeTo++) RemoveTail();
return ret;
}
template <class ItemType>
void
Queue<ItemType>::
Merge(ItemCompareFunc compareFunc, uint32 from, uint32 pivot, uint32 to, uint32 len1, uint32 len2, void* cookie)
{
if ((len1) && (len2)) {
if (len1 + len2 == 2) {
if (compareFunc(*(GetItemAt(pivot)), *(GetItemAt(from)), cookie) < 0) Swap(pivot, from);
} else {
uint32 first_cut, second_cut;
uint32 len11, len22;
if (len1 > len2) {
len11 = len1 / 2;
first_cut = from + len11;
second_cut = Lower(compareFunc, pivot, to, *GetItemAt(first_cut), cookie);
len22 = second_cut - pivot;
} else {
len22 = len2 / 2;
second_cut = pivot + len22;
first_cut = Upper(compareFunc, from, pivot, *GetItemAt(second_cut), cookie);
len11 = first_cut - from;
}
// do a rotation
if ((pivot != first_cut) && (pivot != second_cut)) {
// find the greatest common denominator of (pivot-first_cut) and (second_cut-first_cut)
uint32 n = pivot - first_cut;
{
uint32 m = second_cut - first_cut;
while (n != 0) {
uint32 t = m % n;
m = n;
n = t;
}
n = m;
}
while (n--) {
const ItemType val = *GetItemAt(first_cut + n);
uint32 shift = pivot - first_cut;
uint32 p1 = first_cut + n;
uint32 p2 = p1 + shift;
while (p2 != first_cut + n) {
ReplaceItemAt(p1, *GetItemAt(p2));
p1 = p2;
if (second_cut - p2 > shift) p2 += shift;
else p2 = first_cut + (shift - (second_cut - p2));
}
ReplaceItemAt(p1, val);
}
}
uint32 new_mid = first_cut + len22;
Merge(compareFunc, from, first_cut, new_mid, len11, len22, cookie);
Merge(compareFunc, new_mid, second_cut, to, len1 - len11, len2 - len22, cookie);
}
}
}
template <class ItemType>
uint32
Queue<ItemType>::
Lower(ItemCompareFunc compareFunc, uint32 from, uint32 to, const ItemType& val, void* cookie) const
{
if (to > from) {
uint32 len = to - from;
while (len > 0) {
uint32 half = len / 2;
uint32 mid = from + half;
if (compareFunc(*(GetItemAt(mid)), val, cookie) < 0) {
from = mid + 1;
len = len - half - 1;
} else len = half;
}
}
return from;
}
template <class ItemType>
uint32
Queue<ItemType>::
Upper(ItemCompareFunc compareFunc, uint32 from, uint32 to, const ItemType& val, void* cookie) const
{
if (to > from) {
uint32 len = to - from;
while (len > 0) {
uint32 half = len / 2;
uint32 mid = from + half;
if (compareFunc(val, *(GetItemAt(mid)), cookie) < 0) len = half;
else {
from = mid + 1;
len = len - half - 1;
}
}
}
return from;
}
template <class ItemType>
const ItemType *
Queue<ItemType> :: GetArrayPointerAux(uint32 whichArray, uint32& retLength) const
{
if (_itemCount > 0) {
switch (whichArray) {
case 0:
retLength = (_headIndex <= _tailIndex) ? (_tailIndex - _headIndex) + 1 : (_queueSize - _headIndex);
return &_queue[_headIndex];
break;
case 1:
if (_headIndex > _tailIndex) {
retLength = _tailIndex + 1;
return &_queue[0];
}
break;
}
}
return NULL;
}
template <class ItemType>
void
Queue<ItemType>::SwapContents(Queue<ItemType> & that)
{
bool thisSmall = (_queue == _smallQueue);
bool thatSmall = (that._queue == that._smallQueue);
if ((thisSmall) && (thatSmall)) {
// First, move any extra items from the longer queue to the shorter one...
uint32 commonSize = muscleMin(GetNumItems(), that.GetNumItems());
int32 sizeDiff = ((int32)GetNumItems()) - ((int32)that.GetNumItems());
Queue<ItemType> & copyTo = (sizeDiff > 0) ? that : *this;
Queue<ItemType> & copyFrom = (sizeDiff > 0) ? *this : that;
(void) copyTo.AddTail(copyFrom, commonSize); // guaranteed not to fail
(void) copyFrom.EnsureSize(commonSize, true); // remove the copied items from (copyFrom)
// Then just swap the elements that are present in both arrays
for (uint32 i = 0; i < commonSize; i++) muscleSwap((*this)[i], that[i]);
} else if (thisSmall) SwapContentsAux(that);
else if (thatSmall) that.SwapContentsAux(*this);
else {
// this case is easy, we can just swizzle the pointers around
muscleSwap(_queue, that._queue);
muscleSwap(_queueSize, that._queueSize);
muscleSwap(_headIndex, that._headIndex);
muscleSwap(_tailIndex, that._tailIndex);
muscleSwap(_itemCount, that._itemCount);
}
}
template <class ItemType>
void
Queue<ItemType>::SwapContentsAux(Queue<ItemType> & largeThat)
{
// First, copy over our (small) contents to his static buffer
uint32 ni = GetNumItems();
for (uint32 i = 0; i < ni; i++) largeThat._smallQueue[i] = (*this)[i];
// Now adopt his dynamic buffer
_queue = largeThat._queue;
_queueSize = largeThat._queueSize;
_headIndex = largeThat._headIndex;
_tailIndex = largeThat._tailIndex;
// And point him back at his static buffer
if (ni > 0) {
largeThat._queue = largeThat._smallQueue;
largeThat._queueSize = ARRAYITEMS(largeThat._smallQueue);
largeThat._headIndex = 0;
largeThat._tailIndex = ni - 1;
} else {
largeThat._queue = NULL;
largeThat._queueSize = 0;
// headIndex and tailIndex are undefined in this case anyway
}
muscleSwap(_itemCount, largeThat._itemCount);
}
template <class ItemType>
bool
Queue<ItemType>::StartsWith(const Queue<ItemType> & prefixQueue) const
{
if (prefixQueue.GetNumItems() > GetNumItems()) return false;
uint32 prefixQueueLen = prefixQueue.GetNumItems();
for (uint32 i = 0; i < prefixQueueLen; i++) if (!(prefixQueue[i] == (*this)[i])) return false;
return true;
}
template <class ItemType>
bool
Queue<ItemType>::EndsWith(const Queue<ItemType> & suffixQueue) const
{
if (suffixQueue.GetNumItems() > GetNumItems()) return false;
int32 lastToCheck = GetNumItems() - suffixQueue.GetNumItems();
for (int32 i = GetNumItems() - 1; i >= lastToCheck; i--) if (!(suffixQueue[i] == (*this)[i])) return false;
return true;
}
#endif