Chromium Code Reviews| Index: third_party/WebKit/Source/wtf/FlatTable.h |
| diff --git a/third_party/WebKit/Source/wtf/FlatTable.h b/third_party/WebKit/Source/wtf/FlatTable.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d9175002648b1ab2061afd84604a389c42e37794 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/wtf/FlatTable.h |
| @@ -0,0 +1,349 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#ifndef WTF_FlatTable_h |
| +#define WTF_FlatTable_h |
| + |
| +#include "base/macros.h" |
| +#include "wtf/Vector.h" |
| +#include <vector> |
| + |
| +namespace WTF { |
| + |
| +// Iterator customization. |
| +template <typename Delegate> |
| +struct ForwardIteratorDelegate : public Delegate { |
| + using FlatTableDelegate = Delegate; |
| + static constexpr bool is_const = false; |
| + static inline size_t next(size_t index) { return index + 1; } |
| + static inline size_t prev(size_t index) { return index - 1; } |
| +}; |
| + |
| +template <typename Delegate> |
| +struct ConstForwardIteratorDelegate : public Delegate { |
| + using FlatTableDelegate = Delegate; |
| + static constexpr bool is_const = true; |
| + static inline size_t next(size_t index) { return index + 1; } |
| + static inline size_t prev(size_t index) { return index - 1; } |
| +}; |
| + |
| +template <typename Delegate> |
| +struct ReverseIteratorDelegate : public Delegate { |
| + using FlatTableDelegate = Delegate; |
| + static constexpr bool is_const = false; |
| + static inline size_t next(size_t index) { return index - 1; } |
| + static inline size_t prev(size_t index) { return index + 1; } |
| +}; |
| + |
| +template <typename Delegate> |
| +struct ConstReverseIteratorDelegate : public Delegate { |
| + using FlatTableDelegate = Delegate; |
| + static constexpr bool is_const = true; |
| + static inline size_t next(size_t index) { return index - 1; } |
| + static inline size_t prev(size_t index) { return index + 1; } |
| +}; |
| + |
| +// A FlatTable containing common code for FlatMap and FlatSet. Typicall it |
| +// shouldn't be used directly. |
| +template <typename Delegate> |
| +class FlatTable { |
| + public: |
| + FlatTable() : ring_buffer_(4), front_(0), back_(0), search_step_size_(1) { |
| + ComputeModulusMask(); |
| + } |
| + |
| + using key_type = typename Delegate::key_type; |
| + using value_type = typename Delegate::value_type; |
| + |
| + bool empty() const { return front_ == back_; } |
| + |
| + void clear() { |
| + ring_buffer_.clear(); |
| + ring_buffer_.resize(4); |
|
Sami
2016/10/07 09:49:11
Should we make this a named constant?
|
| + front_ = 0; |
| + back_ = 0; |
| + search_step_size_ = 1; |
| + ComputeModulusMask(); |
| + } |
| + |
| + size_t size() const { return (back_ - front_) & modulus_mask_; } |
| + |
| + template <typename IteratorDelegate> |
| + class FlatTableIterator { |
| + public: |
| + using ValueType = |
| + typename std::conditional<IteratorDelegate::is_const, |
| + const typename IteratorDelegate::value_type, |
| + typename IteratorDelegate::value_type>::type; |
| + using FlatTableDelegate = typename IteratorDelegate::FlatTableDelegate; |
| + using FlatTableType = |
| + typename std::conditional<IteratorDelegate::is_const, |
| + const FlatTable<FlatTableDelegate>, |
| + FlatTable<FlatTableDelegate>>::type; |
| + |
| + FlatTableIterator& operator++() { |
| + m_pos = IteratorDelegate::next(m_pos); |
| + return *this; |
| + } |
| + |
| + FlatTableIterator& operator--() { |
| + m_pos = IteratorDelegate::prev(m_pos); |
| + return *this; |
| + } |
| + |
| + FlatTableIterator operator++(int /*unused*/) { |
| + FlatTableIterator result(*this); |
| + m_pos = IteratorDelegate::next(m_pos); |
| + return result; |
| + } |
| + |
| + FlatTableIterator operator--(int /*unused*/) { |
| + FlatTableIterator result(*this); |
| + m_pos = IteratorDelegate::prev(m_pos); |
| + return result; |
| + } |
| + |
| + ValueType* operator->() const { return &m_flatTable->Element(m_pos); } |
| + |
| + ValueType& operator*() const { return m_flatTable->Element(m_pos); } |
| + |
| + template <typename OtherT> |
| + bool operator==(const OtherT& other) const { |
| + return m_flatTable == other.m_flatTable && m_pos == other.m_pos; |
| + } |
| + |
| + template <typename OtherT> |
| + bool operator!=(const OtherT& other) const { |
| + return m_flatTable != other.m_flatTable || m_pos != other.m_pos; |
| + } |
| + |
| + private: |
| + friend class FlatTable<FlatTableDelegate>; |
| + |
| + FlatTableIterator(size_t pos, FlatTableType* flatTable) |
| + : m_pos(pos), m_flatTable(flatTable) { |
| + DCHECK(m_flatTable); |
| + } |
| + |
| + size_t m_pos; |
| + FlatTableType* m_flatTable; |
| + }; |
| + |
| + using iterator = FlatTableIterator<ForwardIteratorDelegate<Delegate>>; |
| + using const_iterator = |
| + FlatTableIterator<ConstForwardIteratorDelegate<Delegate>>; |
| + using reverse_iterator = FlatTableIterator<ReverseIteratorDelegate<Delegate>>; |
| + using const_reverse_iterator = |
| + FlatTableIterator<ConstReverseIteratorDelegate<Delegate>>; |
| + |
| + // Inserts |value| into the map, invalidating any iterators. |
| + // 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.
|
| + std::pair<iterator, bool> insert(value_type value) { |
| + if (empty()) { |
| + Element(back_++) = std::move(value); |
| + search_step_size_ = 1; |
| + return std::make_pair(iterator(front_, this), true); |
| + } |
| + |
| + size_t index; |
| + if (BinarySearch(Delegate::ToKey(value), &index)) { |
| + // Replace existing value. |
| + Element(index) = std::move(value); |
| + return std::make_pair(iterator(index, this), false); |
| + } |
| + |
| + return std::make_pair(iterator(InsertUniqueImpl(std::move(value)), this), |
| + true); |
| + } |
| + |
| + // Inserts |value| into the map, whose key must be unique, invalidating any |
| + // iterators. O(n / 2) |
| + void insertUnique(value_type value) { |
| + 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?
|
| + Element(back_++) = std::move(value); |
| + search_step_size_ = 1; |
| + return; |
| + } |
| + |
| + InsertUniqueImpl(value); |
| + } |
| + |
| + // Erases an entry corresponding to |key|, if any, from the map. |
| + // Invalidates any iterators. O(log n + n / 2) |
| + size_t erase(const key_type& key) { |
| + if (empty()) |
| + return 0; |
| + |
| + size_t keyIndex; |
| + if (!BinarySearch(key, &keyIndex)) |
| + return 0; |
| + |
| + erase(iterator(keyIndex, this)); |
| + return 1u; |
| + } |
| + |
| + // Erases |it| from the map, invalidating any iterators. O(n / 2) |
| + template <typename Iterator> |
| + void erase(const Iterator& it) { |
| + DCHECK_EQ(it.m_flatTable, this); |
| + size_t eraseFrontCost = it.m_pos - front_; |
| + size_t eraseBackCost = back_ - it.m_pos; |
| + if (eraseFrontCost >= eraseBackCost) { |
| + EraseByMovingBack(it.m_pos); |
| + } else { |
| + EraseByMovingFront(it.m_pos); |
| + } |
| + |
| + if (size() <= search_step_size_) |
| + search_step_size_ /= 2; |
| + } |
| + |
| + // O(log n) |
| + iterator find(const key_type& key) { |
| + size_t keyIndex; |
| + if (!BinarySearch(key, &keyIndex)) |
| + return end(); |
| + |
| + return iterator(keyIndex, this); |
| + } |
| + |
| + // O(log n) |
| + const_iterator find(const key_type& key) const { |
| + size_t keyIndex; |
| + if (!BinarySearch(key, &keyIndex)) |
| + return end(); |
| + |
| + return iterator(keyIndex, this); |
| + } |
| + |
| + iterator begin() { return iterator(front_, this); } |
| + iterator end() { return iterator(back_, this); } |
| + const_iterator begin() const { return const_iterator(front_, this); } |
| + const_iterator end() const { return const_iterator(back_, this); } |
| + |
| + reverse_iterator rbegin() { return reverse_iterator(back_ - 1, this); } |
| + const_reverse_iterator rbegin() const { |
| + return const_reverse_iterator(back_ - 1, this); |
| + } |
| + reverse_iterator rend() { return reverse_iterator(front_ - 1, this); } |
| + const_reverse_iterator rend() const { |
| + return const_reverse_iterator(front_ - 1, this); |
| + } |
| + |
| + protected: |
| + inline value_type& Element(size_t pos) { |
| + return ring_buffer_[pos & modulus_mask_]; |
| + } |
| + |
| + const inline value_type& Element(size_t pos) const { |
| + return ring_buffer_[pos & modulus_mask_]; |
| + } |
| + |
| + template <typename Iterator> |
| + bool iteratorBelongsToThisTable(const Iterator& it) const { |
| + return it.m_flatTable == this; |
| + } |
| + |
| + template <typename Iterator> |
| + size_t iteratorPosition(const Iterator& it) const { |
| + return it.m_pos; |
| + } |
| + |
| + 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.
|
| + // Grow the vector if needed. |
| + if (((back_ + 1) & modulus_mask_) == (front_ & modulus_mask_)) |
| + Grow(); |
| + |
| + // Shuffle Elements to make way for |value|. |
| + size_t mid = front_ + size() / 2; |
| + size_t i; |
| + if (value < Element(mid)) { |
| + front_--; |
| + for (i = front_; Element(i + 1) < value; i++) { |
| + Element(i) = std::move(Element(i + 1)); |
| + } |
| + } else { |
| + DCHECK(Element(mid) < value); |
| + for (i = back_++; value < Element(i - 1); i--) { |
| + Element(i) = std::move(Element(i - 1)); |
| + } |
| + } |
| + DCHECK(Element(i) != value); |
| + Element(i) = std::move(value); |
| + if ((size() / 2) >= search_step_size_) |
| + search_step_size_ *= 2; |
| + return i; |
| + } |
| + |
| + void Grow() { |
| + std::vector<value_type> new_buffer(ring_buffer_.size() * 2); |
| + size_t old_back = back_; |
| + back_ = 0; |
| + for (size_t i = front_; i != old_back; i++) { |
| + new_buffer[back_++] = std::move(Element(i)); |
| + } |
| + front_ = 0; |
| + std::swap(ring_buffer_, new_buffer); |
| + ComputeModulusMask(); |
| + } |
| + |
| + bool BinarySearch(const key_type& key, size_t* index) const { |
| + DCHECK(!empty()); |
| + size_t low = front_; |
| + for (size_t step_size = search_step_size_; step_size; step_size /= 2) { |
| + size_t mid = low + step_size; |
| + if (mid < back_ && !(key < Delegate::ToKey(Element(mid)))) |
| + low = mid; |
| + } |
| + *index = low; |
| + return key == Delegate::ToKey(Element(low)); |
| + } |
| + |
| + void EraseByMovingFront(size_t index_to_erase) { |
| + if (index_to_erase == front_) { |
| + Element(index_to_erase) = value_type(); |
| + } |
| + for (size_t i = index_to_erase; i != front_; i--) { |
| + Element(i) = std::move(Element(i - 1)); |
| + } |
| + front_++; |
| + } |
| + |
| + void EraseByMovingBack(size_t index_to_erase) { |
| + back_--; |
| + if (index_to_erase == back_) { |
| + Element(index_to_erase) = value_type(); |
| + } |
| + for (size_t i = index_to_erase; i != (back_ + 1); i++) { |
| + Element(i) = std::move(Element(i + 1)); |
| + } |
| + } |
| + |
| + 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.
|
| + |
| + // The ring buffer is always a power of two in size, because computing |
| + // modulus for powers of two is super fast. |
| + std::vector<value_type> ring_buffer_; |
| + |
| + // Since |ring_buffer_| is a power of two in size, we can compute the modulus |
| + // using a bitwise and with |modulus_mask_|. |
| + size_t modulus_mask_; |
| + |
| + // The index of the first element. |
| + // NOTE only |front_| mod size() is a valid array index. |
| + size_t front_; |
| + |
| + // The index of the last element + 1. |
| + // NOTE only |back_| mod size() is a valid array index. |
| + size_t back_; |
| + |
| + // Equivalent to 2 ^ ceil(log2(size()) - 1). |
| + size_t search_step_size_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(FlatTable); |
| +}; |
| + |
| +} // namespace WTF |
| + |
| +#endif // WTF_FlatTable_h |