OLD | NEW |
---|---|
(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 | |
OLD | NEW |