Fifo.h (5688B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef js_Fifo_h 8 #define js_Fifo_h 9 10 #include <algorithm> 11 #include <utility> 12 13 #include "js/Vector.h" 14 15 namespace js { 16 17 // A first-in-first-out queue container type. Fifo calls constructors and 18 // destructors of all elements added so non-PODs may be used safely. |Fifo| 19 // stores the first |MinInlineCapacity| elements in-place before resorting to 20 // dynamic allocation. 21 // 22 // T requirements: 23 // - Either movable or copyable. 24 // MinInlineCapacity requirements: 25 // - Must be even. 26 // AllocPolicy: 27 // - see "Allocation policies" in AllocPolicy.h 28 // 29 // The vector template description is variadic to handle the differences 30 // between GCVector and Vector in template parameters. 31 template <typename T, size_t MinInlineCapacity = 0, 32 class AllocPolicy = TempAllocPolicy, 33 template <typename, size_t, class, typename...> class VectorType = 34 Vector> 35 class Fifo { 36 static_assert(MinInlineCapacity % 2 == 0, "MinInlineCapacity must be even!"); 37 38 protected: 39 // An element A is "younger" than an element B if B was inserted into the 40 // |Fifo| before A was. 41 // 42 // Invariant 1: Every element within |front_| is older than every element 43 // within |rear_|. 44 // Invariant 2: Entries within |front_| are sorted from younger to older. 45 // Invariant 3: Entries within |rear_| are sorted from older to younger. 46 // Invariant 4: If the |Fifo| is not empty, then |front_| is not empty. 47 VectorType<T, MinInlineCapacity / 2, AllocPolicy> front_; 48 VectorType<T, MinInlineCapacity / 2, AllocPolicy> rear_; 49 50 private: 51 // Maintain invariants after adding or removing entries. 52 void fixup() { 53 if (front_.empty() && !rear_.empty()) { 54 front_.swap(rear_); 55 std::reverse(front_.begin(), front_.end()); 56 } 57 } 58 59 public: 60 explicit Fifo(AllocPolicy alloc = AllocPolicy()) 61 : front_(alloc), rear_(alloc) {} 62 63 Fifo(Fifo&& rhs) 64 : front_(std::move(rhs.front_)), rear_(std::move(rhs.rear_)) {} 65 66 Fifo& operator=(Fifo&& rhs) { 67 MOZ_ASSERT(&rhs != this, "self-move disallowed"); 68 this->~Fifo(); 69 new (this) Fifo(std::move(rhs)); 70 return *this; 71 } 72 73 Fifo(const Fifo&) = delete; 74 Fifo& operator=(const Fifo&) = delete; 75 76 size_t length() const { 77 MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0); // Invariant 4. 78 return front_.length() + rear_.length(); 79 } 80 81 bool empty() const { 82 MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0); // Invariant 4. 83 return front_.empty(); 84 } 85 86 // Iterator from oldest to yongest element. 87 struct ConstIterator { 88 const Fifo& self_; 89 size_t idx_; 90 91 ConstIterator(const Fifo& self, size_t idx) : self_(self), idx_(idx) {} 92 93 ConstIterator& operator++() { 94 ++idx_; 95 return *this; 96 } 97 98 const T& operator*() const { 99 // Iterate front in reverse, then rear. 100 size_t split = self_.front_.length(); 101 return (idx_ < split) ? self_.front_[(split - 1) - idx_] 102 : self_.rear_[idx_ - split]; 103 } 104 105 bool operator!=(const ConstIterator& other) const { 106 return (&self_ != &other.self_) || (idx_ != other.idx_); 107 } 108 }; 109 110 ConstIterator begin() const { return ConstIterator(*this, 0); } 111 112 ConstIterator end() const { return ConstIterator(*this, length()); } 113 114 // Push an element to the back of the queue. This method can take either a 115 // |const T&| or a |T&&|. 116 template <typename U> 117 [[nodiscard]] bool pushBack(U&& u) { 118 if (!rear_.append(std::forward<U>(u))) { 119 return false; 120 } 121 fixup(); 122 return true; 123 } 124 125 // Construct a T in-place at the back of the queue. 126 template <typename... Args> 127 [[nodiscard]] bool emplaceBack(Args&&... args) { 128 if (!rear_.emplaceBack(std::forward<Args>(args)...)) { 129 return false; 130 } 131 fixup(); 132 return true; 133 } 134 135 // Construct a T in-place at the front of the queue, 136 // making it the next to be popped off. 137 template <typename... Args> 138 [[nodiscard]] bool emplaceFront(Args&&... args) { 139 return front_.emplaceBack(std::forward<Args>(args)...); 140 } 141 142 // Access the element at the front of the queue. 143 T& front() { 144 MOZ_ASSERT(!empty()); 145 return front_.back(); 146 } 147 const T& front() const { 148 MOZ_ASSERT(!empty()); 149 return front_.back(); 150 } 151 152 // Remove the front element from the queue. 153 void popFront() { 154 MOZ_ASSERT(!empty()); 155 front_.popBack(); 156 fixup(); 157 } 158 159 // Convenience utility. 160 T popCopyFront() { 161 T ret = front(); 162 popFront(); 163 return ret; 164 } 165 166 // Clear all elements from the queue. 167 void clear() { 168 front_.clear(); 169 rear_.clear(); 170 } 171 172 // Clear all elements for which the given predicate returns 'true'. Return 173 // the number of elements removed. 174 template <class Pred> 175 size_t eraseIf(Pred pred) { 176 size_t frontLength = front_.length(); 177 front_.eraseIf(pred); 178 size_t erased = frontLength - front_.length(); 179 180 size_t rearLength = rear_.length(); 181 rear_.eraseIf(pred); 182 erased += rearLength - rear_.length(); 183 184 fixup(); 185 return erased; 186 } 187 188 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 189 return front_.sizeOfExcludingThis(mallocSizeOf) + 190 rear_.sizeOfExcludingThis(mallocSizeOf); 191 } 192 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 193 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 194 } 195 }; 196 197 } // namespace js 198 199 #endif /* js_Fifo_h */