/* 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 inline const T& muscleMax(const T& p1, const T& p2) { return (p1 < p2) ? p2 : p1; } template 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 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 & 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 & 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 &). * @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 & 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 & 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 & 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(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 & 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 Queue::Queue(uint32 initialSize) : _queue(NULL), _queueSize(0), _itemCount(0), _initialSize(initialSize) { // empty } template Queue::Queue(const Queue& rhs) : _queue(NULL), _queueSize(0), _itemCount(0), _initialSize(rhs._initialSize) { *this = rhs; } template bool Queue::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 Queue & Queue::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 ItemType & Queue::operator[](uint32 i) { // MASSERT(i<_itemCount, "Invalid index to Queue::[]"); return _queue[InternalizeIndex(i)]; } template const ItemType & Queue::operator[](uint32 i) const { // MASSERT(i<_itemCount, "Invalid index to Queue::[]"); return _queue[InternalizeIndex(i)]; } template ItemType * Queue::GetItemAt(uint32 i) const { return &_queue[InternalizeIndex(i)]; } template Queue::~Queue() { if (_queue != _smallQueue) delete [] _queue; } template status_t Queue:: 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 status_t Queue:: AddTail(const Queue &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 status_t Queue:: 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 status_t Queue:: 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 status_t Queue:: AddHead(const Queue &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 status_t Queue:: 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 status_t Queue:: RemoveHead(ItemType& returnItem) { if (_itemCount == 0) return B_ERROR; returnItem = _queue[_headIndex]; return RemoveHead(); } template status_t Queue:: 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 status_t Queue:: RemoveTail(ItemType& returnItem) { if (_itemCount == 0) return B_ERROR; returnItem = _queue[_tailIndex]; return RemoveTail(); } template status_t Queue:: 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 status_t Queue:: GetItemAt(uint32 index, ItemType& returnItem) const { if (index >= _itemCount) return B_ERROR; returnItem = _queue[InternalizeIndex(index)]; return B_NO_ERROR; } template status_t Queue:: RemoveItemAt(uint32 index, ItemType& returnItem) { if (index >= _itemCount) return B_ERROR; returnItem = _queue[InternalizeIndex(index)]; return RemoveItemAt(index); } template status_t Queue:: 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 status_t Queue:: ReplaceItemAt(uint32 index, const ItemType& newItem) { if (index >= _itemCount) return B_ERROR; _queue[InternalizeIndex(index)] = newItem; return B_NO_ERROR; } template status_t Queue:: 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 void Queue:: Clear(bool releaseCachedBuffers) { if ((releaseCachedBuffers) && (_queue != _smallQueue)) { delete [] _queue; _queue = NULL; _queueSize = 0; _itemCount = 0; } else while (RemoveTail() == B_NO_ERROR) { /* empty */ } } template status_t Queue:: 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 int32 Queue:: 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 void Queue:: Swap(uint32 fromIndex, uint32 toIndex) { ItemType temp = *(GetItemAt(fromIndex)); ReplaceItemAt(fromIndex, *(GetItemAt(toIndex))); ReplaceItemAt(toIndex, temp); } template void Queue:: 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 void Queue:: 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 status_t Queue:: 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 status_t Queue:: RemoveLastInstanceOf(const ItemType& val) { for (int32 i = ((int32)GetNumItems()) - 1; i >= 0; i--) if ((*this)[i] == val) return RemoveItemAt(i); return B_ERROR; } template uint32 Queue:: 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 void Queue:: 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 uint32 Queue:: 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 uint32 Queue:: 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 const ItemType * Queue :: 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 void Queue::SwapContents(Queue & 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 & copyTo = (sizeDiff > 0) ? that : *this; Queue & 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 void Queue::SwapContentsAux(Queue & 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 bool Queue::StartsWith(const Queue & 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 bool Queue::EndsWith(const Queue & 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