Chromium Code Reviews| 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 |