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

Side by Side Diff: base/containers/flat_tree.h

Issue 2853333004: Add range insertion for base::flat_tree (Closed)
Patch Set: Next 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef BASE_CONTAINERS_FLAT_TREE_H_ 5 #ifndef BASE_CONTAINERS_FLAT_TREE_H_
6 #define BASE_CONTAINERS_FLAT_TREE_H_ 6 #define BASE_CONTAINERS_FLAT_TREE_H_
7 7
8 #include <algorithm> 8 #include <algorithm>
9 #include <iterator>
9 #include <vector> 10 #include <vector>
10 11
11 namespace base { 12 namespace base {
12 13
13 enum FlatContainerDupes { 14 enum FlatContainerDupes {
14 KEEP_FIRST_OF_DUPES, 15 KEEP_FIRST_OF_DUPES,
15 KEEP_LAST_OF_DUPES, 16 KEEP_LAST_OF_DUPES,
16 }; 17 };
17 18
18 namespace internal { 19 namespace internal {
19 20
21 // This is a convenience method returning true if Iterator is at least a
22 // ForwardIterator and thus supports multiple passes over a range.
23 template <class Iterator>
24 constexpr bool is_multipass() {
25 return std::is_base_of<
26 std::forward_iterator_tag,
27 typename std::iterator_traits<Iterator>::iterator_category>::value;
28 }
29
20 // This algorithm is like unique() from the standard library except it 30 // This algorithm is like unique() from the standard library except it
21 // selects only the last of consecutive values instead of the first. 31 // selects only the last of consecutive values instead of the first.
22 template <class Iterator, class BinaryPredicate> 32 template <class Iterator, class BinaryPredicate>
23 Iterator LastUnique(Iterator first, Iterator last, BinaryPredicate compare) { 33 Iterator LastUnique(Iterator first, Iterator last, BinaryPredicate compare) {
24 Iterator replacable = std::adjacent_find(first, last, compare); 34 Iterator replacable = std::adjacent_find(first, last, compare);
25 35
26 // No duplicate elements found. 36 // No duplicate elements found.
27 if (replacable == last) 37 if (replacable == last)
28 return last; 38 return last;
29 39
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
180 const_reverse_iterator crbegin() const; 190 const_reverse_iterator crbegin() const;
181 191
182 reverse_iterator rend(); 192 reverse_iterator rend();
183 const_reverse_iterator rend() const; 193 const_reverse_iterator rend() const;
184 const_reverse_iterator crend() const; 194 const_reverse_iterator crend() const;
185 195
186 // -------------------------------------------------------------------------- 196 // --------------------------------------------------------------------------
187 // Insert operations. 197 // Insert operations.
188 // 198 //
189 // Assume that every operation invalidates iterators and references. 199 // Assume that every operation invalidates iterators and references.
190 // Insertion of one element can take O(size). See the Notes section in the 200 // Insertion of one element can take O(size). Capacity of flat_tree grows in
191 // class comments on why we do not currently implement range insertion. 201 // an implementation-defined manner.
192 // Capacity of flat_tree grows in an implementation-defined manner.
193 // 202 //
194 // NOTE: Prefer to build a new flat_tree from a std::vector (or similar) 203 // NOTE: Prefer to build a new flat_tree from a std::vector (or similar)
195 // instead of calling insert() repeatedly. 204 // instead of calling insert() repeatedly.
196 205
197 std::pair<iterator, bool> insert(const value_type& val); 206 std::pair<iterator, bool> insert(const value_type& val);
198 std::pair<iterator, bool> insert(value_type&& val); 207 std::pair<iterator, bool> insert(value_type&& val);
199 208
200 iterator insert(const_iterator position_hint, const value_type& x); 209 iterator insert(const_iterator position_hint, const value_type& x);
201 iterator insert(const_iterator position_hint, value_type&& x); 210 iterator insert(const_iterator position_hint, value_type&& x);
202 211
212 // This method inserts the values from the range [first, last) into the
213 // current tree. In case of KEEP_LAST_OF_DUPES newly added elements can
214 // overwrite existing values.
215 template <class InputIterator>
216 void insert(InputIterator first,
217 InputIterator last,
218 FlatContainerDupes dupes);
219
203 template <class... Args> 220 template <class... Args>
204 std::pair<iterator, bool> emplace(Args&&... args); 221 std::pair<iterator, bool> emplace(Args&&... args);
205 222
206 template <class... Args> 223 template <class... Args>
207 iterator emplace_hint(const_iterator position_hint, Args&&... args); 224 iterator emplace_hint(const_iterator position_hint, Args&&... args);
208 225
209 // -------------------------------------------------------------------------- 226 // --------------------------------------------------------------------------
210 // Erase operations. 227 // Erase operations.
211 // 228 //
212 // Assume that every operation invalidates iterators and references. 229 // Assume that every operation invalidates iterators and references.
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
309 const key_compare& key_comp_; 326 const key_compare& key_comp_;
310 }; 327 };
311 328
312 const flat_tree& as_const() { return *this; } 329 const flat_tree& as_const() { return *this; }
313 330
314 iterator const_cast_it(const_iterator c_it) { 331 iterator const_cast_it(const_iterator c_it) {
315 auto distance = std::distance(cbegin(), c_it); 332 auto distance = std::distance(cbegin(), c_it);
316 return std::next(begin(), distance); 333 return std::next(begin(), distance);
317 } 334 }
318 335
319 void sort_and_unique(FlatContainerDupes dupes) { 336 // This method is inspired by both std::map::insert(P&&) and
337 // std::map::insert_or_assign(const K&, V&&). It inserts val if an equivalent
338 // element is not present yet, otherwise it overwrites. It returns an iterator
339 // to the modified element and a flag indicating whether insertion or
340 // assignment happened.
341 template <class V>
342 std::pair<iterator, bool> insert_or_assign(V&& val) {
343 auto position = lower_bound(GetKeyFromValue()(val));
344
345 if (position == end() || value_comp()(val, *position))
346 return {impl_.body_.emplace(position, std::forward<V>(val)), true};
347
348 *position = std::forward<V>(val);
349 return {position, false};
350 }
351
352 // This method is similar to insert_or_assign, with the following differences:
353 // - Instead of searching [begin(), end()) it only searches [first, last).
354 // - In case no equivalent element is found, val is appended to the end of the
355 // underlying body.
356 template <class V>
357 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.
358 iterator last,
359 V&& val) {
360 auto position = std::lower_bound(first, last, val, value_comp());
361
362 if (position == last || value_comp()(val, *position)) {
363 impl_.body_.emplace_back(std::forward<V>(val));
364 return {std::prev(end()), true};
365 }
366
367 *position = std::forward<V>(val);
368 return {position, false};
369 }
370
371 // This method is similar to insert, with the following differences:
372 // - Instead of searching [begin(), end()) it only searches [first, last).
373 // - In case no equivalent element is found, val is appended to the end of the
374 // underlying body.
375 template <class V>
376 std::pair<iterator, bool> append_unique(iterator first,
377 iterator last,
378 V&& val) {
379 auto position = std::lower_bound(first, last, val, value_comp());
380
381 if (position == last || value_comp()(val, *position)) {
382 impl_.body_.emplace_back(std::forward<V>(val));
383 return {std::prev(end()), true};
384 }
385
386 return {position, false};
387 }
388
389 void sort_and_unique(iterator first,
390 iterator last,
391 FlatContainerDupes dupes) {
320 // Preserve stability for the unique code below. 392 // Preserve stability for the unique code below.
321 std::stable_sort(begin(), end(), impl_.get_value_comp()); 393 std::stable_sort(first, last, impl_.get_value_comp());
322 394
323 auto comparator = [this](const value_type& lhs, const value_type& rhs) { 395 auto comparator = [this](const value_type& lhs, const value_type& rhs) {
324 // lhs is already <= rhs due to sort, therefore 396 // lhs is already <= rhs due to sort, therefore
325 // !(lhs < rhs) <=> lhs == rhs. 397 // !(lhs < rhs) <=> lhs == rhs.
326 return !impl_.get_value_comp()(lhs, rhs); 398 return !impl_.get_value_comp()(lhs, rhs);
327 }; 399 };
328 400
329 iterator erase_after; 401 iterator erase_after;
330 switch (dupes) { 402 switch (dupes) {
331 case KEEP_FIRST_OF_DUPES: 403 case KEEP_FIRST_OF_DUPES:
332 erase_after = std::unique(begin(), end(), comparator); 404 erase_after = std::unique(first, last, comparator);
333 break; 405 break;
334 case KEEP_LAST_OF_DUPES: 406 case KEEP_LAST_OF_DUPES:
335 erase_after = LastUnique(begin(), end(), comparator); 407 erase_after = LastUnique(first, last, comparator);
336 break; 408 break;
337 } 409 }
338 erase(erase_after, end()); 410 erase(erase_after, last);
339 } 411 }
340 412
341 // To support comparators that may not be possible to default-construct, we 413 // To support comparators that may not be possible to default-construct, we
342 // have to store an instance of Compare. Using this to store all internal 414 // have to store an instance of Compare. Using this to store all internal
343 // state of flat_tree and using private inheritance to store compare lets us 415 // state of flat_tree and using private inheritance to store compare lets us
344 // take advantage of an empty base class optimization to avoid extra space in 416 // take advantage of an empty base class optimization to avoid extra space in
345 // the common case when Compare has no state. 417 // the common case when Compare has no state.
346 struct Impl : private value_compare { 418 struct Impl : private value_compare {
347 Impl() = default; 419 Impl() = default;
348 420
(...skipping 21 matching lines...) Expand all
370 : impl_(comp) {} 442 : impl_(comp) {}
371 443
372 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 444 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
373 template <class InputIterator> 445 template <class InputIterator>
374 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( 446 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
375 InputIterator first, 447 InputIterator first,
376 InputIterator last, 448 InputIterator last,
377 FlatContainerDupes dupe_handling, 449 FlatContainerDupes dupe_handling,
378 const KeyCompare& comp) 450 const KeyCompare& comp)
379 : impl_(comp, first, last) { 451 : impl_(comp, first, last) {
380 sort_and_unique(dupe_handling); 452 sort_and_unique(begin(), end(), dupe_handling);
381 } 453 }
382 454
383 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 455 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
384 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( 456 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
385 const flat_tree&) = default; 457 const flat_tree&) = default;
386 458
387 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 459 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
388 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(flat_tree&&) = 460 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(flat_tree&&) =
389 default; 461 default;
390 462
391 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 463 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
392 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( 464 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
393 std::vector<value_type> items, 465 std::vector<value_type> items,
394 FlatContainerDupes dupe_handling, 466 FlatContainerDupes dupe_handling,
395 const KeyCompare& comp) 467 const KeyCompare& comp)
396 : impl_(comp, std::move(items)) { 468 : impl_(comp, std::move(items)) {
397 sort_and_unique(dupe_handling); 469 sort_and_unique(begin(), end(), dupe_handling);
398 } 470 }
399 471
400 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 472 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
401 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree( 473 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
402 std::initializer_list<value_type> ilist, 474 std::initializer_list<value_type> ilist,
403 FlatContainerDupes dupe_handling, 475 FlatContainerDupes dupe_handling,
404 const KeyCompare& comp) 476 const KeyCompare& comp)
405 : flat_tree(std::begin(ilist), std::end(ilist), dupe_handling, comp) {} 477 : flat_tree(std::begin(ilist), std::end(ilist), dupe_handling, comp) {}
406 478
407 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 479 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
408 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::~flat_tree() = default; 480 flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::~flat_tree() = default;
409 481
410 // ---------------------------------------------------------------------------- 482 // ----------------------------------------------------------------------------
411 // Assignments. 483 // Assignments.
412 484
413 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 485 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
414 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=( 486 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(
415 const flat_tree&) -> flat_tree& = default; 487 const flat_tree&) -> flat_tree& = default;
416 488
417 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 489 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
418 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(flat_tree &&) 490 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(flat_tree &&)
419 -> flat_tree& = default; 491 -> flat_tree& = default;
420 492
421 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 493 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
422 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=( 494 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(
423 std::initializer_list<value_type> ilist) -> flat_tree& { 495 std::initializer_list<value_type> ilist) -> flat_tree& {
424 impl_.body_ = ilist; 496 impl_.body_ = ilist;
425 sort_and_unique(KEEP_FIRST_OF_DUPES); 497 sort_and_unique(begin(), end(), KEEP_FIRST_OF_DUPES);
426 return *this; 498 return *this;
427 } 499 }
428 500
429 // ---------------------------------------------------------------------------- 501 // ----------------------------------------------------------------------------
430 // Memory management. 502 // Memory management.
431 503
432 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 504 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
433 void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::reserve( 505 void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::reserve(
434 size_type new_capacity) { 506 size_type new_capacity) {
435 impl_.body_.reserve(new_capacity); 507 impl_.body_.reserve(new_capacity);
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 // 621 //
550 // Currently we use position_hint the same way as eastl or boost: 622 // Currently we use position_hint the same way as eastl or boost:
551 // https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set. h#L493 623 // https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set. h#L493
552 // 624 //
553 // We duplicate code between copy and move version so that we can avoid 625 // We duplicate code between copy and move version so that we can avoid
554 // creating a temporary value. 626 // creating a temporary value.
555 627
556 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 628 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
557 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert( 629 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
558 const value_type& val) -> std::pair<iterator, bool> { 630 const value_type& val) -> std::pair<iterator, bool> {
559 auto position = lower_bound(val); 631 GetKeyFromValue extractor;
632 auto position = lower_bound(extractor(val));
560 633
561 if (position == end() || impl_.get_value_comp()(val, *position)) 634 if (position == end() || impl_.get_value_comp()(val, *position))
562 return {impl_.body_.insert(position, val), true}; 635 return {impl_.body_.insert(position, val), true};
563 636
564 return {position, false}; 637 return {position, false};
565 } 638 }
566 639
567 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 640 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
568 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert( 641 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
569 value_type&& val) -> std::pair<iterator, bool> { 642 value_type&& val) -> std::pair<iterator, bool> {
(...skipping 26 matching lines...) Expand all
596 if (position_hint == end() || impl_.get_value_comp()(val, *position_hint)) { 669 if (position_hint == end() || impl_.get_value_comp()(val, *position_hint)) {
597 if (position_hint == begin() || 670 if (position_hint == begin() ||
598 impl_.get_value_comp()(*(position_hint - 1), val)) 671 impl_.get_value_comp()(*(position_hint - 1), val))
599 // We have to cast away const because of crbug.com/677044. 672 // We have to cast away const because of crbug.com/677044.
600 return impl_.body_.insert(const_cast_it(position_hint), std::move(val)); 673 return impl_.body_.insert(const_cast_it(position_hint), std::move(val));
601 } 674 }
602 return insert(std::move(val)).first; 675 return insert(std::move(val)).first;
603 } 676 }
604 677
605 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 678 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
679 template <class InputIterator>
680 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
681 InputIterator first,
682 InputIterator last,
683 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.
684 if (first == last)
685 return;
686
687 // Cache results whether existing elements should be overwritten and whether
688 // inserting new elements happens immediately or will be done in a batch.
689 const bool overwrite_existing = dupes == KEEP_LAST_OF_DUPES;
690 const bool insert_inplace =
691 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.
692
693 if (insert_inplace) {
694 if (overwrite_existing) {
695 for (; first != last; ++first)
696 insert_or_assign(*first);
697 } else {
698 for (; first != last; ++first)
699 insert(*first);
dyaroshev 2017/05/17 10:15:19 Nit: std::copy(first, last, std::inserter(*this, e
700 }
701 return;
702 }
703
704 // For batch updates initialize the first insertion point.
705 const size_type original_size = size();
706
707 // Provide a convenience lambda to obtain an iterator pointing past the last
708 // old element. This needs to be dymanic due to possible re-allocations.
709 auto middle = [this, original_size]() {
710 return std::next(begin(), original_size);
711 };
712
713 // Loop over the input range while appending new values and overwriting
714 // existing ones, if applicable.
715 if (overwrite_existing) {
716 for (; first != last; ++first)
717 append_or_assign(begin(), middle(), *first);
718 } else {
719 for (; first != last; ++first)
720 append_unique(begin(), middle(), *first);
721 }
722
723 // The new elements might be unordered and contain duplicates, so post-process
724 // the just inserted elements and merge them with the rest. Binary search for
725 // the first insertion spot.
726 sort_and_unique(middle(), end(), dupes);
727 if (middle() != end()) {
728 std::inplace_merge(
729 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.
730 end(), value_comp());
731 }
732 }
733
734 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
606 template <class... Args> 735 template <class... Args>
607 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace(Args&&... args) 736 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace(Args&&... args)
608 -> std::pair<iterator, bool> { 737 -> std::pair<iterator, bool> {
609 return insert(value_type(std::forward<Args>(args)...)); 738 return insert(value_type(std::forward<Args>(args)...));
610 } 739 }
611 740
612 template <class Key, class Value, class GetKeyFromValue, class KeyCompare> 741 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
613 template <class... Args> 742 template <class... Args>
614 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace_hint( 743 auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace_hint(
615 const_iterator position_hint, 744 const_iterator position_hint,
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
768 void EraseIf(base::internal::flat_tree<Key, Value, GetKeyFromValue, KeyCompare>& 897 void EraseIf(base::internal::flat_tree<Key, Value, GetKeyFromValue, KeyCompare>&
769 container, 898 container,
770 Predicate pred) { 899 Predicate pred) {
771 container.erase(std::remove_if(container.begin(), container.end(), pred), 900 container.erase(std::remove_if(container.begin(), container.end(), pred),
772 container.end()); 901 container.end());
773 } 902 }
774 903
775 } // namespace base 904 } // namespace base
776 905
777 #endif // BASE_CONTAINERS_FLAT_TREE_H_ 906 #endif // BASE_CONTAINERS_FLAT_TREE_H_
OLDNEW
« 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