Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Side by Side Diff: third_party/WebKit/Source/wtf/FlatTable.h

Issue 2396533004: Introduce a FlatMap and FlatSet into WTF (Closed)
Patch Set: Fix compile Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef WTF_FlatTable_h
6 #define WTF_FlatTable_h
7
8 #include "base/macros.h"
9 #include "wtf/Vector.h"
10 #include <vector>
11
12 namespace WTF {
13
14 // Iterator customization.
15 template <typename Delegate>
16 struct ForwardIteratorDelegate : public Delegate {
17 using FlatTableDelegate = Delegate;
18 static constexpr bool is_const = false;
19 static inline size_t next(size_t index) { return index + 1; }
20 static inline size_t prev(size_t index) { return index - 1; }
21 };
22
23 template <typename Delegate>
24 struct ConstForwardIteratorDelegate : public Delegate {
25 using FlatTableDelegate = Delegate;
26 static constexpr bool is_const = true;
27 static inline size_t next(size_t index) { return index + 1; }
28 static inline size_t prev(size_t index) { return index - 1; }
29 };
30
31 template <typename Delegate>
32 struct ReverseIteratorDelegate : public Delegate {
33 using FlatTableDelegate = Delegate;
34 static constexpr bool is_const = false;
35 static inline size_t next(size_t index) { return index - 1; }
36 static inline size_t prev(size_t index) { return index + 1; }
37 };
38
39 template <typename Delegate>
40 struct ConstReverseIteratorDelegate : public Delegate {
41 using FlatTableDelegate = Delegate;
42 static constexpr bool is_const = true;
43 static inline size_t next(size_t index) { return index - 1; }
44 static inline size_t prev(size_t index) { return index + 1; }
45 };
46
47 // A FlatTable containing common code for FlatMap and FlatSet. Typicall it
48 // shouldn't be used directly.
49 template <typename Delegate>
50 class FlatTable {
51 public:
52 FlatTable() : ring_buffer_(4), front_(0), back_(0), search_step_size_(1) {
53 ComputeModulusMask();
54 }
55
56 using key_type = typename Delegate::key_type;
57 using value_type = typename Delegate::value_type;
58
59 bool empty() const { return front_ == back_; }
60
61 void clear() {
62 ring_buffer_.clear();
63 ring_buffer_.resize(4);
Sami 2016/10/07 09:49:11 Should we make this a named constant?
64 front_ = 0;
65 back_ = 0;
66 search_step_size_ = 1;
67 ComputeModulusMask();
68 }
69
70 size_t size() const { return (back_ - front_) & modulus_mask_; }
71
72 template <typename IteratorDelegate>
73 class FlatTableIterator {
74 public:
75 using ValueType =
76 typename std::conditional<IteratorDelegate::is_const,
77 const typename IteratorDelegate::value_type,
78 typename IteratorDelegate::value_type>::type;
79 using FlatTableDelegate = typename IteratorDelegate::FlatTableDelegate;
80 using FlatTableType =
81 typename std::conditional<IteratorDelegate::is_const,
82 const FlatTable<FlatTableDelegate>,
83 FlatTable<FlatTableDelegate>>::type;
84
85 FlatTableIterator& operator++() {
86 m_pos = IteratorDelegate::next(m_pos);
87 return *this;
88 }
89
90 FlatTableIterator& operator--() {
91 m_pos = IteratorDelegate::prev(m_pos);
92 return *this;
93 }
94
95 FlatTableIterator operator++(int /*unused*/) {
96 FlatTableIterator result(*this);
97 m_pos = IteratorDelegate::next(m_pos);
98 return result;
99 }
100
101 FlatTableIterator operator--(int /*unused*/) {
102 FlatTableIterator result(*this);
103 m_pos = IteratorDelegate::prev(m_pos);
104 return result;
105 }
106
107 ValueType* operator->() const { return &m_flatTable->Element(m_pos); }
108
109 ValueType& operator*() const { return m_flatTable->Element(m_pos); }
110
111 template <typename OtherT>
112 bool operator==(const OtherT& other) const {
113 return m_flatTable == other.m_flatTable && m_pos == other.m_pos;
114 }
115
116 template <typename OtherT>
117 bool operator!=(const OtherT& other) const {
118 return m_flatTable != other.m_flatTable || m_pos != other.m_pos;
119 }
120
121 private:
122 friend class FlatTable<FlatTableDelegate>;
123
124 FlatTableIterator(size_t pos, FlatTableType* flatTable)
125 : m_pos(pos), m_flatTable(flatTable) {
126 DCHECK(m_flatTable);
127 }
128
129 size_t m_pos;
130 FlatTableType* m_flatTable;
131 };
132
133 using iterator = FlatTableIterator<ForwardIteratorDelegate<Delegate>>;
134 using const_iterator =
135 FlatTableIterator<ConstForwardIteratorDelegate<Delegate>>;
136 using reverse_iterator = FlatTableIterator<ReverseIteratorDelegate<Delegate>>;
137 using const_reverse_iterator =
138 FlatTableIterator<ConstReverseIteratorDelegate<Delegate>>;
139
140 // Inserts |value| into the map, invalidating any iterators.
141 // O(log n + n / 2)
Sami 2016/10/07 09:49:11 Maybe document the return value?
alex clarke (OOO till 29th) 2016/10/13 14:16:32 Done.
142 std::pair<iterator, bool> insert(value_type value) {
143 if (empty()) {
144 Element(back_++) = std::move(value);
145 search_step_size_ = 1;
146 return std::make_pair(iterator(front_, this), true);
147 }
148
149 size_t index;
150 if (BinarySearch(Delegate::ToKey(value), &index)) {
151 // Replace existing value.
152 Element(index) = std::move(value);
153 return std::make_pair(iterator(index, this), false);
154 }
155
156 return std::make_pair(iterator(InsertUniqueImpl(std::move(value)), this),
157 true);
158 }
159
160 // Inserts |value| into the map, whose key must be unique, invalidating any
161 // iterators. O(n / 2)
162 void insertUnique(value_type value) {
163 if (empty()) {
Sami 2016/10/07 09:49:11 DCHECK that value is actually unique? Edit: I gue
alex clarke (OOO till 29th) 2016/10/13 14:16:32 Right, that should be enough?
164 Element(back_++) = std::move(value);
165 search_step_size_ = 1;
166 return;
167 }
168
169 InsertUniqueImpl(value);
170 }
171
172 // Erases an entry corresponding to |key|, if any, from the map.
173 // Invalidates any iterators. O(log n + n / 2)
174 size_t erase(const key_type& key) {
175 if (empty())
176 return 0;
177
178 size_t keyIndex;
179 if (!BinarySearch(key, &keyIndex))
180 return 0;
181
182 erase(iterator(keyIndex, this));
183 return 1u;
184 }
185
186 // Erases |it| from the map, invalidating any iterators. O(n / 2)
187 template <typename Iterator>
188 void erase(const Iterator& it) {
189 DCHECK_EQ(it.m_flatTable, this);
190 size_t eraseFrontCost = it.m_pos - front_;
191 size_t eraseBackCost = back_ - it.m_pos;
192 if (eraseFrontCost >= eraseBackCost) {
193 EraseByMovingBack(it.m_pos);
194 } else {
195 EraseByMovingFront(it.m_pos);
196 }
197
198 if (size() <= search_step_size_)
199 search_step_size_ /= 2;
200 }
201
202 // O(log n)
203 iterator find(const key_type& key) {
204 size_t keyIndex;
205 if (!BinarySearch(key, &keyIndex))
206 return end();
207
208 return iterator(keyIndex, this);
209 }
210
211 // O(log n)
212 const_iterator find(const key_type& key) const {
213 size_t keyIndex;
214 if (!BinarySearch(key, &keyIndex))
215 return end();
216
217 return iterator(keyIndex, this);
218 }
219
220 iterator begin() { return iterator(front_, this); }
221 iterator end() { return iterator(back_, this); }
222 const_iterator begin() const { return const_iterator(front_, this); }
223 const_iterator end() const { return const_iterator(back_, this); }
224
225 reverse_iterator rbegin() { return reverse_iterator(back_ - 1, this); }
226 const_reverse_iterator rbegin() const {
227 return const_reverse_iterator(back_ - 1, this);
228 }
229 reverse_iterator rend() { return reverse_iterator(front_ - 1, this); }
230 const_reverse_iterator rend() const {
231 return const_reverse_iterator(front_ - 1, this);
232 }
233
234 protected:
235 inline value_type& Element(size_t pos) {
236 return ring_buffer_[pos & modulus_mask_];
237 }
238
239 const inline value_type& Element(size_t pos) const {
240 return ring_buffer_[pos & modulus_mask_];
241 }
242
243 template <typename Iterator>
244 bool iteratorBelongsToThisTable(const Iterator& it) const {
245 return it.m_flatTable == this;
246 }
247
248 template <typename Iterator>
249 size_t iteratorPosition(const Iterator& it) const {
250 return it.m_pos;
251 }
252
253 size_t InsertUniqueImpl(value_type value) {
Sami 2016/10/07 09:49:11 Maybe the great renaming will fix this but I find
alex clarke (OOO till 29th) 2016/10/13 14:16:32 Done.
254 // Grow the vector if needed.
255 if (((back_ + 1) & modulus_mask_) == (front_ & modulus_mask_))
256 Grow();
257
258 // Shuffle Elements to make way for |value|.
259 size_t mid = front_ + size() / 2;
260 size_t i;
261 if (value < Element(mid)) {
262 front_--;
263 for (i = front_; Element(i + 1) < value; i++) {
264 Element(i) = std::move(Element(i + 1));
265 }
266 } else {
267 DCHECK(Element(mid) < value);
268 for (i = back_++; value < Element(i - 1); i--) {
269 Element(i) = std::move(Element(i - 1));
270 }
271 }
272 DCHECK(Element(i) != value);
273 Element(i) = std::move(value);
274 if ((size() / 2) >= search_step_size_)
275 search_step_size_ *= 2;
276 return i;
277 }
278
279 void Grow() {
280 std::vector<value_type> new_buffer(ring_buffer_.size() * 2);
281 size_t old_back = back_;
282 back_ = 0;
283 for (size_t i = front_; i != old_back; i++) {
284 new_buffer[back_++] = std::move(Element(i));
285 }
286 front_ = 0;
287 std::swap(ring_buffer_, new_buffer);
288 ComputeModulusMask();
289 }
290
291 bool BinarySearch(const key_type& key, size_t* index) const {
292 DCHECK(!empty());
293 size_t low = front_;
294 for (size_t step_size = search_step_size_; step_size; step_size /= 2) {
295 size_t mid = low + step_size;
296 if (mid < back_ && !(key < Delegate::ToKey(Element(mid))))
297 low = mid;
298 }
299 *index = low;
300 return key == Delegate::ToKey(Element(low));
301 }
302
303 void EraseByMovingFront(size_t index_to_erase) {
304 if (index_to_erase == front_) {
305 Element(index_to_erase) = value_type();
306 }
307 for (size_t i = index_to_erase; i != front_; i--) {
308 Element(i) = std::move(Element(i - 1));
309 }
310 front_++;
311 }
312
313 void EraseByMovingBack(size_t index_to_erase) {
314 back_--;
315 if (index_to_erase == back_) {
316 Element(index_to_erase) = value_type();
317 }
318 for (size_t i = index_to_erase; i != (back_ + 1); i++) {
319 Element(i) = std::move(Element(i + 1));
320 }
321 }
322
323 void ComputeModulusMask() { modulus_mask_ = ring_buffer_.size() - 1; }
Sami 2016/10/07 09:49:11 DCHECK that this is 2^n-1?
alex clarke (OOO till 29th) 2016/10/13 14:16:32 Done something similar.
324
325 // The ring buffer is always a power of two in size, because computing
326 // modulus for powers of two is super fast.
327 std::vector<value_type> ring_buffer_;
328
329 // Since |ring_buffer_| is a power of two in size, we can compute the modulus
330 // using a bitwise and with |modulus_mask_|.
331 size_t modulus_mask_;
332
333 // The index of the first element.
334 // NOTE only |front_| mod size() is a valid array index.
335 size_t front_;
336
337 // The index of the last element + 1.
338 // NOTE only |back_| mod size() is a valid array index.
339 size_t back_;
340
341 // Equivalent to 2 ^ ceil(log2(size()) - 1).
342 size_t search_step_size_;
343
344 DISALLOW_COPY_AND_ASSIGN(FlatTable);
345 };
346
347 } // namespace WTF
348
349 #endif // WTF_FlatTable_h
OLDNEW
« third_party/WebKit/Source/wtf/FlatSet.h ('K') | « third_party/WebKit/Source/wtf/FlatSetTest.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698