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 template <typename T, size_t MinInlineCapacity = 0,
29           class AllocPolicy = TempAllocPolicy>
30 class Fifo {
31   static_assert(MinInlineCapacity % 2 == 0, "MinInlineCapacity must be even!");
32 
33  protected:
34   // An element A is "younger" than an element B if B was inserted into the
35   // |Fifo| before A was.
36   //
37   // Invariant 1: Every element within |front_| is older than every element
38   // within |rear_|.
39   // Invariant 2: Entries within |front_| are sorted from younger to older.
40   // Invariant 3: Entries within |rear_| are sorted from older to younger.
41   // Invariant 4: If the |Fifo| is not empty, then |front_| is not empty.
42   Vector<T, MinInlineCapacity / 2, AllocPolicy> front_;
43   Vector<T, MinInlineCapacity / 2, AllocPolicy> rear_;
44 
45  private:
46   // Maintain invariants after adding or removing entries.
fixup()47   void fixup() {
48     if (front_.empty() && !rear_.empty()) {
49       front_.swap(rear_);
50       std::reverse(front_.begin(), front_.end());
51     }
52   }
53 
54  public:
55   explicit Fifo(AllocPolicy alloc = AllocPolicy())
front_(alloc)56       : front_(alloc), rear_(alloc) {}
57 
Fifo(Fifo && rhs)58   Fifo(Fifo&& rhs)
59       : front_(std::move(rhs.front_)), rear_(std::move(rhs.rear_)) {}
60 
61   Fifo& operator=(Fifo&& rhs) {
62     MOZ_ASSERT(&rhs != this, "self-move disallowed");
63     this->~Fifo();
64     new (this) Fifo(std::move(rhs));
65     return *this;
66   }
67 
68   Fifo(const Fifo&) = delete;
69   Fifo& operator=(const Fifo&) = delete;
70 
length()71   size_t length() const {
72     MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0);  // Invariant 4.
73     return front_.length() + rear_.length();
74   }
75 
empty()76   bool empty() const {
77     MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0);  // Invariant 4.
78     return front_.empty();
79   }
80 
81   // Iterator from oldest to yongest element.
82   struct ConstIterator {
83     const Fifo& self_;
84     size_t idx_;
85 
ConstIteratorConstIterator86     ConstIterator(const Fifo& self, size_t idx) : self_(self), idx_(idx) {}
87 
88     ConstIterator& operator++() {
89       ++idx_;
90       return *this;
91     }
92 
93     const T& operator*() const {
94       // Iterate front in reverse, then rear.
95       size_t split = self_.front_.length();
96       return (idx_ < split) ? self_.front_[(split - 1) - idx_]
97                             : self_.rear_[idx_ - split];
98     }
99 
100     bool operator!=(const ConstIterator& other) const {
101       return (&self_ != &other.self_) || (idx_ != other.idx_);
102     }
103   };
104 
begin()105   ConstIterator begin() const { return ConstIterator(*this, 0); }
106 
end()107   ConstIterator end() const { return ConstIterator(*this, length()); }
108 
109   // Push an element to the back of the queue. This method can take either a
110   // |const T&| or a |T&&|.
111   template <typename U>
pushBack(U && u)112   [[nodiscard]] bool pushBack(U&& u) {
113     if (!rear_.append(std::forward<U>(u))) {
114       return false;
115     }
116     fixup();
117     return true;
118   }
119 
120   // Construct a T in-place at the back of the queue.
121   template <typename... Args>
emplaceBack(Args &&...args)122   [[nodiscard]] bool emplaceBack(Args&&... args) {
123     if (!rear_.emplaceBack(std::forward<Args>(args)...)) {
124       return false;
125     }
126     fixup();
127     return true;
128   }
129 
130   // Access the element at the front of the queue.
front()131   T& front() {
132     MOZ_ASSERT(!empty());
133     return front_.back();
134   }
front()135   const T& front() const {
136     MOZ_ASSERT(!empty());
137     return front_.back();
138   }
139 
140   // Remove the front element from the queue.
popFront()141   void popFront() {
142     MOZ_ASSERT(!empty());
143     front_.popBack();
144     fixup();
145   }
146 
147   // Convenience utility.
popCopyFront()148   T popCopyFront() {
149     T ret = front();
150     popFront();
151     return ret;
152   }
153 
154   // Clear all elements from the queue.
clear()155   void clear() {
156     front_.clear();
157     rear_.clear();
158   }
159 
160   // Clear all elements for which the given predicate returns 'true'. Return
161   // the number of elements removed.
162   template <class Pred>
eraseIf(Pred pred)163   size_t eraseIf(Pred pred) {
164     size_t frontLength = front_.length();
165     front_.eraseIf(pred);
166     size_t erased = frontLength - front_.length();
167 
168     size_t rearLength = rear_.length();
169     rear_.eraseIf(pred);
170     erased += rearLength - rear_.length();
171 
172     fixup();
173     return erased;
174   }
175 
sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)176   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
177     return front_.sizeOfExcludingThis(mallocSizeOf) +
178            rear_.sizeOfExcludingThis(mallocSizeOf);
179   }
sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)180   size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
181     return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
182   }
183 };
184 
185 }  // namespace js
186 
187 #endif /* js_Fifo_h */
188