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> { |