Chromium Code Reviews| Index: base/containers/flat_tree.h |
| diff --git a/base/containers/flat_tree.h b/base/containers/flat_tree.h |
| index 8e85f2377655f954c585be08bea9749edd79240c..c8060ca21af5e38624e929b3f9eda0501fd596ef 100644 |
| --- a/base/containers/flat_tree.h |
| +++ b/base/containers/flat_tree.h |
| @@ -6,6 +6,7 @@ |
| #define BASE_CONTAINERS_FLAT_TREE_H_ |
| #include <algorithm> |
| +#include <iterator> |
| #include <vector> |
| namespace base { |
| @@ -17,6 +18,15 @@ enum FlatContainerDupes { |
| namespace internal { |
| +// This is a convenience method returning true if Iterator is at least a |
| +// ForwardIterator and thus supports multiple passes over a range. |
| +template <class Iterator> |
| +constexpr bool is_multipass() { |
| + return std::is_base_of< |
| + std::forward_iterator_tag, |
| + typename std::iterator_traits<Iterator>::iterator_category>::value; |
| +} |
| + |
| // This algorithm is like unique() from the standard library except it |
| // selects only the last of consecutive values instead of the first. |
| template <class Iterator, class BinaryPredicate> |
| @@ -187,9 +197,8 @@ class flat_tree { |
| // Insert operations. |
| // |
| // Assume that every operation invalidates iterators and references. |
| - // Insertion of one element can take O(size). See the Notes section in the |
| - // class comments on why we do not currently implement range insertion. |
| - // Capacity of flat_tree grows in an implementation-defined manner. |
| + // Insertion of one element can take O(size). Capacity of flat_tree grows in |
| + // an implementation-defined manner. |
| // |
| // NOTE: Prefer to build a new flat_tree from a std::vector (or similar) |
| // instead of calling insert() repeatedly. |
| @@ -200,6 +209,14 @@ class flat_tree { |
| iterator insert(const_iterator position_hint, const value_type& x); |
| iterator insert(const_iterator position_hint, value_type&& x); |
| + // This method inserts the values from the range [first, last) into the |
| + // current tree. In case of KEEP_LAST_OF_DUPES newly added elements can |
| + // overwrite existing values. |
| + template <class InputIterator> |
| + void insert(InputIterator first, |
| + InputIterator last, |
| + FlatContainerDupes dupes); |
| + |
| template <class... Args> |
| std::pair<iterator, bool> emplace(Args&&... args); |
| @@ -316,9 +333,64 @@ class flat_tree { |
| return std::next(begin(), distance); |
| } |
| - void sort_and_unique(FlatContainerDupes dupes) { |
| + // This method is inspired by both std::map::insert(P&&) and |
| + // std::map::insert_or_assign(const K&, V&&). It inserts val if an equivalent |
| + // element is not present yet, otherwise it overwrites. It returns an iterator |
| + // to the modified element and a flag indicating whether insertion or |
| + // assignment happened. |
| + template <class V> |
| + std::pair<iterator, bool> insert_or_assign(V&& val) { |
| + auto position = lower_bound(GetKeyFromValue()(val)); |
| + |
| + if (position == end() || value_comp()(val, *position)) |
| + return {impl_.body_.emplace(position, std::forward<V>(val)), true}; |
| + |
| + *position = std::forward<V>(val); |
| + return {position, false}; |
| + } |
| + |
| + // This method is similar to insert_or_assign, with the following differences: |
| + // - Instead of searching [begin(), end()) it only searches [first, last). |
| + // - In case no equivalent element is found, val is appended to the end of the |
| + // underlying body. |
| + template <class V> |
| + std::pair<iterator, bool> append_or_assign(iterator first, |
|
dyaroshev
2017/05/17 10:15:19
I don't actually believe, that in these methods re
jdoerrie
2017/05/17 11:32:05
Done.
|
| + iterator last, |
| + V&& val) { |
| + auto position = std::lower_bound(first, last, val, value_comp()); |
| + |
| + if (position == last || value_comp()(val, *position)) { |
| + impl_.body_.emplace_back(std::forward<V>(val)); |
| + return {std::prev(end()), true}; |
| + } |
| + |
| + *position = std::forward<V>(val); |
| + return {position, false}; |
| + } |
| + |
| + // This method is similar to insert, with the following differences: |
| + // - Instead of searching [begin(), end()) it only searches [first, last). |
| + // - In case no equivalent element is found, val is appended to the end of the |
| + // underlying body. |
| + template <class V> |
| + std::pair<iterator, bool> append_unique(iterator first, |
| + iterator last, |
| + V&& val) { |
| + auto position = std::lower_bound(first, last, val, value_comp()); |
| + |
| + if (position == last || value_comp()(val, *position)) { |
| + impl_.body_.emplace_back(std::forward<V>(val)); |
| + return {std::prev(end()), true}; |
| + } |
| + |
| + return {position, false}; |
| + } |
| + |
| + void sort_and_unique(iterator first, |
| + iterator last, |
| + FlatContainerDupes dupes) { |
| // Preserve stability for the unique code below. |
| - std::stable_sort(begin(), end(), impl_.get_value_comp()); |
| + std::stable_sort(first, last, impl_.get_value_comp()); |
| auto comparator = [this](const value_type& lhs, const value_type& rhs) { |
| // lhs is already <= rhs due to sort, therefore |
| @@ -329,13 +401,13 @@ class flat_tree { |
| iterator erase_after; |
| switch (dupes) { |
| case KEEP_FIRST_OF_DUPES: |
| - erase_after = std::unique(begin(), end(), comparator); |
| + erase_after = std::unique(first, last, comparator); |
| break; |
| case KEEP_LAST_OF_DUPES: |
| - erase_after = LastUnique(begin(), end(), comparator); |
| + erase_after = LastUnique(first, last, comparator); |
| break; |
| } |
| - erase(erase_after, end()); |
| + erase(erase_after, last); |
| } |
| // To support comparators that may not be possible to default-construct, we |
| @@ -377,7 +449,7 @@ flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( |
| FlatContainerDupes dupe_handling, |
| const KeyCompare& comp) |
| : impl_(comp, first, last) { |
| - sort_and_unique(dupe_handling); |
| + sort_and_unique(begin(), end(), dupe_handling); |
| } |
| template <class Key, class Value, class GetKeyFromValue, class KeyCompare> |
| @@ -394,7 +466,7 @@ flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( |
| FlatContainerDupes dupe_handling, |
| const KeyCompare& comp) |
| : impl_(comp, std::move(items)) { |
| - sort_and_unique(dupe_handling); |
| + sort_and_unique(begin(), end(), dupe_handling); |
| } |
| template <class Key, class Value, class GetKeyFromValue, class KeyCompare> |
| @@ -422,7 +494,7 @@ template <class Key, class Value, class GetKeyFromValue, class KeyCompare> |
| auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=( |
| std::initializer_list<value_type> ilist) -> flat_tree& { |
| impl_.body_ = ilist; |
| - sort_and_unique(KEEP_FIRST_OF_DUPES); |
| + sort_and_unique(begin(), end(), KEEP_FIRST_OF_DUPES); |
| return *this; |
| } |
| @@ -556,7 +628,8 @@ auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::crend() const |
| template <class Key, class Value, class GetKeyFromValue, class KeyCompare> |
| auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert( |
| const value_type& val) -> std::pair<iterator, bool> { |
| - auto position = lower_bound(val); |
| + GetKeyFromValue extractor; |
| + auto position = lower_bound(extractor(val)); |
| if (position == end() || impl_.get_value_comp()(val, *position)) |
| return {impl_.body_.insert(position, val), true}; |
| @@ -603,6 +676,62 @@ auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert( |
| } |
| template <class Key, class Value, class GetKeyFromValue, class KeyCompare> |
| +template <class InputIterator> |
| +auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert( |
| + InputIterator first, |
| + InputIterator last, |
| + FlatContainerDupes dupes) -> void { |
|
dyaroshev
2017/05/17 10:15:19
Nit: we just wrote void, cause the type doesn't de
jdoerrie
2017/05/17 11:32:05
Done.
|
| + if (first == last) |
| + return; |
| + |
| + // Cache results whether existing elements should be overwritten and whether |
| + // inserting new elements happens immediately or will be done in a batch. |
| + const bool overwrite_existing = dupes == KEEP_LAST_OF_DUPES; |
| + const bool insert_inplace = |
| + is_multipass<InputIterator>() && std::next(first) == last; |
|
jdoerrie
2017/05/17 06:41:16
You were right with your earlier comment that this
dyaroshev
2017/05/17 10:15:19
I suspect it depends on two numbers: size() and di
jdoerrie
2017/05/17 11:32:05
Acknowledged.
|
| + |
| + if (insert_inplace) { |
| + if (overwrite_existing) { |
| + for (; first != last; ++first) |
| + insert_or_assign(*first); |
| + } else { |
| + for (; first != last; ++first) |
| + insert(*first); |
|
dyaroshev
2017/05/17 10:15:19
Nit: std::copy(first, last, std::inserter(*this, e
|
| + } |
| + return; |
| + } |
| + |
| + // For batch updates initialize the first insertion point. |
| + const size_type original_size = size(); |
| + |
| + // Provide a convenience lambda to obtain an iterator pointing past the last |
| + // old element. This needs to be dymanic due to possible re-allocations. |
| + auto middle = [this, original_size]() { |
| + return std::next(begin(), original_size); |
| + }; |
| + |
| + // Loop over the input range while appending new values and overwriting |
| + // existing ones, if applicable. |
| + if (overwrite_existing) { |
| + for (; first != last; ++first) |
| + append_or_assign(begin(), middle(), *first); |
| + } else { |
| + for (; first != last; ++first) |
| + append_unique(begin(), middle(), *first); |
| + } |
| + |
| + // The new elements might be unordered and contain duplicates, so post-process |
| + // the just inserted elements and merge them with the rest. Binary search for |
| + // the first insertion spot. |
| + sort_and_unique(middle(), end(), dupes); |
| + if (middle() != end()) { |
| + std::inplace_merge( |
| + std::lower_bound(begin(), middle(), *middle(), value_comp()), middle(), |
|
dyaroshev
2017/05/17 10:15:19
I really think you ought to cache binary search.
jdoerrie
2017/05/17 11:32:05
Done.
|
| + end(), value_comp()); |
| + } |
| +} |
| + |
| +template <class Key, class Value, class GetKeyFromValue, class KeyCompare> |
| template <class... Args> |
| auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace(Args&&... args) |
| -> std::pair<iterator, bool> { |