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

Unified Diff: base/containers/flat_tree.h

Issue 2853333004: Add range insertion for base::flat_tree (Closed)
Patch Set: Another round of comments Created 3 years, 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/containers/flat_set.h ('k') | base/containers/flat_tree_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/containers/flat_tree.h
diff --git a/base/containers/flat_tree.h b/base/containers/flat_tree.h
index 8e85f2377655f954c585be08bea9749edd79240c..afbbf95c4fdda48db3065cb86c202c9ef169e1f6 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,72 @@ 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 and an iterator to the next bigger element in [first,
+ // last) is returned.
+ template <class V>
+ std::pair<iterator, bool> append_or_assign(iterator first,
+ iterator last,
+ V&& val) {
+ auto position = std::lower_bound(first, last, val, value_comp());
+
+ if (position == last || value_comp()(val, *position)) {
+ // emplace_back might invalidate position, which is why distance needs to
+ // be cached.
+ const difference_type distance = std::distance(begin(), position);
+ impl_.body_.emplace_back(std::forward<V>(val));
+ return {std::next(begin(), distance), 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 and an iterator to the next bigger element in [first,
+ // last) is returned.
+ 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)) {
+ // emplace_back might invalidate position, which is why distance needs to
+ // be cached.
+ const difference_type distance = std::distance(begin(), position);
+ impl_.body_.emplace_back(std::forward<V>(val));
+ return {std::next(begin(), distance), 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 +409,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 +457,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 +474,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 +502,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 +636,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 +684,70 @@ auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
}
template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <class InputIterator>
+void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
+ InputIterator first,
+ InputIterator last,
+ FlatContainerDupes dupes) {
+ 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;
+
+ if (insert_inplace) {
+ if (overwrite_existing) {
+ for (; first != last; ++first)
+ insert_or_assign(*first);
+ } else
+ std::copy(first, last, std::inserter(*this, end()));
+ return;
+ }
+
+ // Provide a convenience lambda to obtain an iterator pointing past the last
+ // old element. This needs to be dymanic due to possible re-allocations.
+ const size_type original_size = size();
+ auto middle = [this, original_size]() {
+ return std::next(begin(), original_size);
+ };
+
+ // For batch updates initialize the first insertion point.
+ difference_type pos_first_new = original_size;
+
+ // Loop over the input range while appending new values and overwriting
+ // existing ones, if applicable. Keep track of the first insertion point.
+ if (overwrite_existing) {
+ for (; first != last; ++first) {
+ std::pair<iterator, bool> result =
+ append_or_assign(begin(), middle(), *first);
+ if (result.second) {
+ pos_first_new =
+ std::min(pos_first_new, std::distance(begin(), result.first));
+ }
+ }
+ } else {
+ for (; first != last; ++first) {
+ std::pair<iterator, bool> result =
+ append_unique(begin(), middle(), *first);
+ if (result.second) {
+ pos_first_new =
+ std::min(pos_first_new, std::distance(begin(), result.first));
+ }
+ }
+ }
+
+ // The new elements might be unordered and contain duplicates, so post-process
+ // the just inserted elements and merge them with the rest, inserting them at
+ // the previously found spot.
+ sort_and_unique(middle(), end(), dupes);
+ std::inplace_merge(std::next(begin(), pos_first_new), middle(), 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> {
« no previous file with comments | « base/containers/flat_set.h ('k') | base/containers/flat_tree_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698