Skip to content

Commit

Permalink
[base] Add sorted_unique tag to flat_tree
Browse files Browse the repository at this point in the history
This change adds a base::sorted_unique tag to the constructors of
base::flat_tee, allowing callers to skip the sort and unique step if
this is not necessary.

Bug: 682254
Change-Id: I7b0c210e8bca56fcbba5840e0aaf214c185b60bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2494941
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822545}
  • Loading branch information
jdoerrie authored and Commit Bot committed Oct 30, 2020
1 parent a1b6965 commit 73f3988
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 58 deletions.
1 change: 1 addition & 0 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ component("base") {
"containers/contiguous_iterator.h",
"containers/flat_map.h",
"containers/flat_set.h",
"containers/flat_tree.cc",
"containers/flat_tree.h",
"containers/id_map.h",
"containers/intrusive_heap.cc",
Expand Down
22 changes: 20 additions & 2 deletions base/containers/flat_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ struct GetKeyFromValuePairFirst {
// flat_map is a container with a std::map-like interface that stores its
// contents in a sorted vector.
//
// Its implementation mostly tracks the corresponding standardization proposal
// https://wg21.link/P0429, except that the storage of keys and values is not
// split.
//
// Please see //base/containers/README.md for an overview of which container
// to select.
//
Expand Down Expand Up @@ -59,17 +63,31 @@ struct GetKeyFromValuePairFirst {
// reference, the functions available are:
//
// Constructors (inputs need not be sorted):
// flat_map(InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_map(const flat_map&);
// flat_map(flat_map&&);
// flat_map(InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_map(const std::vector<value_type>& items,
// const Compare& compare = Compare());
// flat_map(std::vector<value_type>&& items,
// const Compare& compare = Compare()); // Re-use storage.
// flat_map(std::initializer_list<value_type> ilist,
// const Compare& comp = Compare());
//
// Constructors (inputs need to be sorted):
// flat_map(sorted_unique_t,
// InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_map(sorted_unique_t,
// const std::vector<Key>& items,
// const Compare& compare = Compare());
// flat_map(sorted_unique_t,
// std::vector<Key>&& items,
// const Compare& compare = Compare()); // Re-use storage.
// flat_map(sorted_unique_t,
// std::initializer_list<value_type> ilist,
// const Compare& comp = Compare());
//
// Assignment functions:
// flat_map& operator=(const flat_map&);
// flat_map& operator=(flat_map&&);
Expand Down
21 changes: 19 additions & 2 deletions base/containers/flat_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace base {
// flat_set is a container with a std::set-like interface that stores its
// contents in a sorted vector.
//
// Its implementation mostly tracks the corresponding standardization proposal
// https://wg21.link/P1222.
//
// Please see //base/containers/README.md for an overview of which container
// to select.
//
Expand Down Expand Up @@ -45,17 +48,31 @@ namespace base {
// reference, the functions available are:
//
// Constructors (inputs need not be sorted):
// flat_set(InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_set(const flat_set&);
// flat_set(flat_set&&);
// flat_set(InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_set(const std::vector<Key>& items,
// const Compare& compare = Compare());
// flat_set(std::vector<Key>&& items,
// const Compare& compare = Compare()); // Re-use storage.
// flat_set(std::initializer_list<value_type> ilist,
// const Compare& comp = Compare());
//
// Constructors (inputs need to be sorted):
// flat_set(sorted_unique_t,
// InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_set(sorted_unique_t,
// const std::vector<Key>& items,
// const Compare& compare = Compare());
// flat_set(sorted_unique_t,
// std::vector<Key>&& items,
// const Compare& compare = Compare()); // Re-use storage.
// flat_set(sorted_unique_t,
// std::initializer_list<value_type> ilist,
// const Compare& comp = Compare());
//
// Assignment functions:
// flat_set& operator=(const flat_set&);
// flat_set& operator=(flat_set&&);
Expand Down
11 changes: 11 additions & 0 deletions base/containers/flat_tree.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2020 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.

#include "base/containers/flat_tree.h"

namespace base {

sorted_unique_t sorted_unique;

} // namespace base
120 changes: 92 additions & 28 deletions base/containers/flat_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,33 @@
#include <utility>
#include <vector>

#include "base/functional/not_fn.h"
#include "base/ranges/algorithm.h"
#include "base/stl_util.h"
#include "base/template_util.h"

namespace base {

// Tag type that allows skipping the sort_and_unique step when constructing a
// flat_tree in case the underlying container is already sorted and has no
// duplicate elements.
struct sorted_unique_t {
constexpr explicit sorted_unique_t() = default;
};
extern sorted_unique_t sorted_unique;

namespace internal {

// Helper functions used in DCHECKs below to make sure that inputs tagged with
// sorted_unique are indeed sorted and unique.
template <typename Range, typename Comp>
bool is_sorted_and_unique(const Range& range, Comp comp) {
return ranges::is_sorted(range, comp) &&
// Being unique implies that there are no adjacent elements that
// compare equal.
ranges::adjacent_find(range, base::not_fn(comp)) == ranges::end(range);
}

// 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>
Expand Down Expand Up @@ -99,26 +118,49 @@ class flat_tree {
//
// The constructors that take ranges, lists, and vectors do not require that
// the input be sorted.
//
// When passing the base::sorted_unique tag as the first argument no sort and
// unique step takes places. This is useful if the underlying container
// already has the required properties.

flat_tree() = default;
flat_tree(const flat_tree&) = default;
flat_tree(flat_tree&&) noexcept = default;

flat_tree();
explicit flat_tree(const key_compare& comp);

template <class InputIterator>
flat_tree(InputIterator first,
InputIterator last,
const key_compare& comp = key_compare());

flat_tree(const flat_tree&);
flat_tree(flat_tree&&) noexcept = default;

flat_tree(const underlying_type& items,
const key_compare& comp = key_compare());

flat_tree(underlying_type&& items, const key_compare& comp = key_compare());

flat_tree(std::initializer_list<value_type> ilist,
const key_compare& comp = key_compare());

~flat_tree();
template <class InputIterator>
flat_tree(sorted_unique_t,
InputIterator first,
InputIterator last,
const key_compare& comp = key_compare());

flat_tree(sorted_unique_t,
const underlying_type& items,
const key_compare& comp = key_compare());

flat_tree(sorted_unique_t,
underlying_type&& items,
const key_compare& comp = key_compare());

flat_tree(sorted_unique_t,
std::initializer_list<value_type> ilist,
const key_compare& comp = key_compare());

~flat_tree() = default;

// --------------------------------------------------------------------------
// Assignments.
Expand Down Expand Up @@ -431,15 +473,13 @@ class flat_tree {
// Preserve stability for the unique code below.
std::stable_sort(first, last, value_comp());

auto equal_comp = [this](const value_type& lhs, const value_type& rhs) {
// lhs is already <= rhs due to sort, therefore
// !(lhs < rhs) <=> lhs == rhs.
return !value_comp()(lhs, rhs);
};

// lhs is already <= rhs due to sort, therefore !(lhs < rhs) <=> lhs == rhs.
auto equal_comp = base::not_fn(value_comp());
erase(std::unique(first, last, equal_comp), last);
}

void sort_and_unique() { sort_and_unique(begin(), end()); }

// To support comparators that may not be possible to default-construct, we
// have to store an instance of Compare. Using this to store all internal
// state of flat_tree and using private inheritance to store compare lets us
Expand Down Expand Up @@ -468,9 +508,6 @@ class flat_tree {
// ----------------------------------------------------------------------------
// Lifetime.

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree() = default;

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
const KeyCompare& comp)
Expand All @@ -483,27 +520,23 @@ flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
InputIterator last,
const KeyCompare& comp)
: impl_(comp, first, last) {
sort_and_unique(begin(), end());
sort_and_unique();
}

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
const flat_tree&) = default;

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
const underlying_type& items,
const KeyCompare& comp)
: impl_(comp, items) {
sort_and_unique(begin(), end());
sort_and_unique();
}

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
underlying_type&& items,
const KeyCompare& comp)
: impl_(comp, std::move(items)) {
sort_and_unique(begin(), end());
sort_and_unique();
}

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
Expand All @@ -513,7 +546,40 @@ flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
: flat_tree(std::begin(ilist), std::end(ilist), comp) {}

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::~flat_tree() = default;
template <class InputIterator>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
sorted_unique_t,
InputIterator first,
InputIterator last,
const KeyCompare& comp)
: impl_(comp, first, last) {
DCHECK(is_sorted_and_unique(*this, comp));
}

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
sorted_unique_t,
const underlying_type& items,
const KeyCompare& comp)
: impl_(comp, items) {
DCHECK(is_sorted_and_unique(*this, comp));
}

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
sorted_unique_t,
underlying_type&& items,
const KeyCompare& comp)
: impl_(comp, std::move(items)) {
DCHECK(is_sorted_and_unique(*this, comp));
}

template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
sorted_unique_t,
std::initializer_list<value_type> ilist,
const KeyCompare& comp)
: flat_tree(sorted_unique, std::begin(ilist), std::end(ilist), comp) {}

// ----------------------------------------------------------------------------
// Assignments.
Expand All @@ -532,7 +598,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(begin(), end());
sort_and_unique();
return *this;
}

Expand Down Expand Up @@ -756,11 +822,9 @@ auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::
template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::replace(
underlying_type&& body) {
// Ensure that |body| is sorted and has no repeated elements.
DCHECK(ranges::is_sorted(body, value_comp()));
DCHECK(ranges::adjacent_find(body, [this](const auto& lhs, const auto& rhs) {
return !value_comp()(lhs, rhs);
}) == body.end());
// Ensure that `body` is sorted and has no repeated elements according to
// `value_comp()`.
DCHECK(is_sorted_and_unique(body, value_comp()));
impl_.body_ = std::move(body);
}

Expand Down
Loading

0 comments on commit 73f3988

Please sign in to comment.