Reland "Reland "[base] Introduce base::CheckedContiguousRange""
This is a reland of 84fcf8bbfaf9ccd3c3dea716c11e233481209c77
Original change's description:
> Reland "[base] Introduce base::CheckedContiguousRange"
>
> This is a reland of 83ced150300e54539bf7f5547a534eec68824476
>
> Original change's description:
> > [base] Introduce base::CheckedContiguousRange
> >
> > This change introduces base::CheckedContiguousRange, which similarly to
> > base::span is a light-weight wrapper around a contiguous container
> > performing bound CHECKs.
> >
> > However, in contrast to base::span this class keeps a pointer to the
> > underlying container, and thus is able to respond to changes to data()
> > and size(), which base::span can't do.
> >
> > Furthermore, this change provides a constexpr overload of base::data()
> > for std::array and fixes a bug in CheckedContiguousIterator::operator-=.
> >
> > Bug: 990059
> >
> > Change-Id: I3fef91c7ef7874bf495ac2ab6dbaf3a8b02dab35
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1893858
> > Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
> > Reviewed-by: Chris Palmer <palmer@chromium.org>
> > Reviewed-by: Daniel Cheng <dcheng@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#712120}
>
> Bug: 990059
> Change-Id: If7cfd16ca526930643e9fac7567f58ba9ef60b1f
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1893069
> Reviewed-by: Chris Palmer <palmer@chromium.org>
> Reviewed-by: Daniel Cheng <dcheng@chromium.org>
> Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#712493}
Bug: 990059
Change-Id: I728ab7d229d931919f928b4786a19c8ca46920f9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1906352
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Reviewed-by: Chris Palmer <palmer@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714801}
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 8b41a14..5107f61 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -194,6 +194,7 @@
"containers/adapters.h",
"containers/buffer_iterator.h",
"containers/checked_iterators.h",
+ "containers/checked_range.h",
"containers/circular_deque.h",
"containers/flat_map.h",
"containers/flat_set.h",
@@ -2531,6 +2532,7 @@
"component_export_unittest.cc",
"containers/adapters_unittest.cc",
"containers/buffer_iterator_unittest.cc",
+ "containers/checked_range_unittest.cc",
"containers/circular_deque_unittest.cc",
"containers/flat_map_unittest.cc",
"containers/flat_set_unittest.cc",
@@ -3178,6 +3180,7 @@
"callback_list_unittest.nc",
"callback_unittest.nc",
"containers/buffer_iterator_unittest.nc",
+ "containers/checked_iterators_unittest.nc",
"containers/span_unittest.nc",
"memory/ref_counted_unittest.nc",
"memory/weak_ptr_unittest.nc",
diff --git a/base/containers/checked_iterators.h b/base/containers/checked_iterators.h
index bf3d6949..cdfd290 100644
--- a/base/containers/checked_iterators.h
+++ b/base/containers/checked_iterators.h
@@ -38,9 +38,9 @@
constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) =
default;
- // Converting constructor allowing conversions like CRAI<T> to CRAI<const T>,
- // but disallowing CRAI<const T> to CRAI<T> or CRAI<Derived> to CRAI<Base>,
- // which are unsafe. Furthermore, this is the same condition as used by the
+ // Converting constructor allowing conversions like CCI<T> to CCI<const T>,
+ // but disallowing CCI<const T> to CCI<T> or CCI<Derived> to CCI<Base>, which
+ // are unsafe. Furthermore, this is the same condition as used by the
// converting constructors of std::span<T> and std::unique_ptr<T[]>.
// See https://wg21.link/n4042 for details.
template <
@@ -108,7 +108,7 @@
return *this;
}
- constexpr CheckedContiguousIterator& operator--(int) {
+ constexpr CheckedContiguousIterator operator--(int) {
CheckedContiguousIterator old = *this;
--*this;
return old;
@@ -132,9 +132,9 @@
constexpr CheckedContiguousIterator& operator-=(difference_type rhs) {
if (rhs < 0) {
- CHECK_LE(rhs, end_ - current_);
+ CHECK_LE(-rhs, end_ - current_);
} else {
- CHECK_LE(-rhs, current_ - start_);
+ CHECK_LE(rhs, current_ - start_);
}
current_ -= rhs;
return *this;
diff --git a/base/containers/checked_iterators_unittest.nc b/base/containers/checked_iterators_unittest.nc
new file mode 100644
index 0000000..a8c50539
--- /dev/null
+++ b/base/containers/checked_iterators_unittest.nc
@@ -0,0 +1,325 @@
+// Copyright 2019 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.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/containers/checked_iterators.h"
+
+namespace base {
+
+#if defined(NCTEST_CHECKED_ITERATORS_CONSTRUCTOR_START_END) // [r"constexpr variable 'iter' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // start can't be larger than end
+ constexpr CheckedContiguousIterator<const int> iter(kArray + 1, kArray);
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_CONSTRUCTOR_START_CURRENT) // [r"constexpr variable 'iter' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // current can't be larger than start
+ constexpr CheckedContiguousIterator<const int> iter(kArray + 1, kArray, kArray + 5);
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_CONSTRUCTOR_CURRENT_END) // [r"constexpr variable 'iter' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // current can't be larger than end
+ constexpr CheckedContiguousIterator<const int> iter(kArray, kArray + 2, kArray + 1);
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_EQ_DIFFERENT_ITER) // [r"constexpr variable 'equal' must be initialized by a constant expression"]
+
+constexpr int kArray1[] = {1, 2, 3, 4, 5};
+constexpr int kArray2[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't compare iterators into different containers
+ constexpr CheckedContiguousIterator<const int> iter1(kArray1, kArray1 + 5);
+ constexpr CheckedContiguousIterator<const int> iter2(kArray2, kArray2 + 5);
+ constexpr bool equal = iter1 == iter2;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_NE_DIFFERENT_ITER) // [r"constexpr variable 'not_equal' must be initialized by a constant expression"]
+
+constexpr int kArray1[] = {1, 2, 3, 4, 5};
+constexpr int kArray2[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't compare iterators into different containers
+ constexpr CheckedContiguousIterator<const int> iter1(kArray1, kArray1 + 5);
+ constexpr CheckedContiguousIterator<const int> iter2(kArray2, kArray2 + 5);
+ constexpr bool not_equal = iter1 != iter2;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_LT_DIFFERENT_ITER) // [r"constexpr variable 'less_than' must be initialized by a constant expression"]
+
+constexpr int kArray1[] = {1, 2, 3, 4, 5};
+constexpr int kArray2[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't compare iterators into different containers
+ constexpr CheckedContiguousIterator<const int> iter1(kArray1, kArray1 + 5);
+ constexpr CheckedContiguousIterator<const int> iter2(kArray2, kArray2 + 5);
+ constexpr bool less_than = iter1 < iter2;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_LE_DIFFERENT_ITER) // [r"constexpr variable 'less_equal' must be initialized by a constant expression"]
+
+constexpr int kArray1[] = {1, 2, 3, 4, 5};
+constexpr int kArray2[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't compare iterators into different containers
+ constexpr CheckedContiguousIterator<const int> iter1(kArray1, kArray1 + 5);
+ constexpr CheckedContiguousIterator<const int> iter2(kArray2, kArray2 + 5);
+ constexpr bool less_equal = iter1 <= iter2;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_GT_DIFFERENT_ITER) // [r"constexpr variable 'greater_than' must be initialized by a constant expression"]
+
+constexpr int kArray1[] = {1, 2, 3, 4, 5};
+constexpr int kArray2[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't compare iterators into different containers
+ constexpr CheckedContiguousIterator<const int> iter1(kArray1, kArray1 + 5);
+ constexpr CheckedContiguousIterator<const int> iter2(kArray2, kArray2 + 5);
+ constexpr bool greater_than = iter1 > iter2;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_GE_DIFFERENT_ITER) // [r"constexpr variable 'greater_equal' must be initialized by a constant expression"]
+
+constexpr int kArray1[] = {1, 2, 3, 4, 5};
+constexpr int kArray2[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't compare iterators into different containers
+ constexpr CheckedContiguousIterator<const int> iter1(kArray1, kArray1 + 5);
+ constexpr CheckedContiguousIterator<const int> iter2(kArray2, kArray2 + 5);
+ constexpr bool greater_equal = iter1 >= iter2;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_PRE_INCR_END) // [r"constexpr variable 'pre_incr' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+constexpr int PreIncr() {
+ // Can't pre-increment the end iterator.
+ CheckedContiguousIterator<const int> end_iter(kArray, kArray + 5, kArray + 5);
+ ++end_iter;
+ return 0;
+}
+
+void WontCompile() {
+ constexpr int pre_incr = PreIncr();
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_POST_INCR_END) // [r"constexpr variable 'post_incr' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+constexpr int PostIncr() {
+ // Can't post-increment the end iterator.
+ CheckedContiguousIterator<const int> end_iter(kArray, kArray + 5, kArray + 5);
+ end_iter++;
+ return 0;
+}
+
+void WontCompile() {
+ constexpr int post_incr = PostIncr();
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_PRE_DECR_BEGIN) // [r"constexpr variable 'pre_decr' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+constexpr int PreDecr() {
+ // Can't pre-decrement the begin iterator.
+ CheckedContiguousIterator<const int> begin_iter(kArray, kArray + 5);
+ --begin_iter;
+ return 0;
+}
+
+void WontCompile() {
+ constexpr int pre_decr = PreDecr();
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_POST_DECR_BEGIN) // [r"constexpr variable 'post_decr' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+constexpr int PostDecr() {
+ // Can't post-decrement the begin iterator.
+ CheckedContiguousIterator<const int> begin_iter(kArray, kArray + 5);
+ begin_iter--;
+ return 0;
+}
+
+void WontCompile() {
+ constexpr int post_decr = PostDecr();
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_INCR_PAST_END) // [r"constexpr variable 'incr_past_end' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+constexpr int IncrPastEnd() {
+ // Can't increment iterator past the end.
+ CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ iter += 6;
+ return 0;
+}
+
+void WontCompile() {
+ constexpr int incr_past_end = IncrPastEnd();
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_DECR_PAST_BEGIN) // [r"constexpr variable 'decr_past_begin' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+constexpr int DecrPastBegin() {
+ // Can't decrement iterator past the begin.
+ CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ iter += -1;
+ return 0;
+}
+
+void WontCompile() {
+ constexpr int decr_past_begin = DecrPastBegin();
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_INCR_PAST_END_2) // [r"constexpr variable 'iter_past_end' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't increment iterator past the end.
+ constexpr CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ constexpr auto iter_past_end = iter + 6;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_DECR_PAST_BEGIN_2) // [r"constexpr variable 'iter_past_begin' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't decrement iterator past the begin.
+ constexpr CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ constexpr auto iter_past_begin = iter + (-1);
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_DECR_PAST_BEGIN_3) // [r"constexpr variable 'decr_past_begin' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+constexpr int DecrPastBegin() {
+ // Can't decrement iterator past the begin.
+ CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ iter -= 1;
+ return 0;
+}
+
+void WontCompile() {
+ constexpr int decr_past_begin = DecrPastBegin();
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_INCR_PAST_END_3) // [r"constexpr variable 'incr_past_end' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+constexpr int IncrPastEnd() {
+ // Can't increment iterator past the end.
+ CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ iter -= (-6);
+ return 0;
+}
+
+void WontCompile() {
+ constexpr int incr_past_end = IncrPastEnd();
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_DECR_PAST_BEGIN_4) // [r"constexpr variable 'iter_past_begin' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't decrement iterator past the begin.
+ constexpr CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ constexpr auto iter_past_begin = iter - 1;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_INCR_PAST_END_4) // [r"constexpr variable 'iter_past_end' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't increment iterator past the end.
+ constexpr CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ constexpr auto iter_past_end = iter - (-6);
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_DIFFERENCE_DIFFERENT_ITER) // [r"constexpr variable 'difference' must be initialized by a constant expression"]
+
+constexpr int kArray1[] = {1, 2, 3, 4, 5};
+constexpr int kArray2[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't compare iterators into different containers
+ constexpr CheckedContiguousIterator<const int> iter1(kArray1, kArray1 + 5);
+ constexpr CheckedContiguousIterator<const int> iter2(kArray2, kArray2 + 5);
+ constexpr auto difference = iter1 - iter2;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_STAR_END) // [r"constexpr variable 'ref' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't dereference the end iterator by star.
+ constexpr CheckedContiguousIterator<const int> end_iter(kArray, kArray + 5, kArray + 5);
+ constexpr auto& ref = *end_iter;
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_ARROW_END) // [r"constexpr variable 'ptr' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't dereference the end iterator by arrow.
+ constexpr CheckedContiguousIterator<const int> end_iter(kArray, kArray + 5, kArray + 5);
+ constexpr auto* ptr = end_iter.operator->();
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_NEGATIVE_OPERATOR_AT) // [r"constexpr variable 'ref' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't use a negative index in operator[].
+ constexpr CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ constexpr auto& ref = iter[-1];
+}
+
+#elif defined(NCTEST_CHECKED_ITERATORS_OPERATOR_AT_END) // [r"constexpr variable 'ref' must be initialized by a constant expression"]
+
+constexpr int kArray[] = {1, 2, 3, 4, 5};
+
+void WontCompile() {
+ // Can't use a operator[] to deref the end.
+ constexpr CheckedContiguousIterator<const int> iter(kArray, kArray + 5);
+ constexpr auto& ref = iter[5];
+}
+
+#endif
+
+} // namespace base
diff --git a/base/containers/checked_range.h b/base/containers/checked_range.h
new file mode 100644
index 0000000..bbca4d70
--- /dev/null
+++ b/base/containers/checked_range.h
@@ -0,0 +1,162 @@
+// Copyright 2019 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.
+
+#ifndef BASE_CONTAINERS_CHECKED_RANGE_H_
+#define BASE_CONTAINERS_CHECKED_RANGE_H_
+
+#include <stddef.h>
+
+#include <iterator>
+#include <type_traits>
+
+#include "base/containers/checked_iterators.h"
+#include "base/stl_util.h"
+
+namespace base {
+
+// CheckedContiguousRange is a light-weight wrapper around a container modeling
+// the ContiguousContainer requirement [1, 2]. Effectively this means that the
+// container stores its elements contiguous in memory. Furthermore, it is
+// expected that base::data(container) and base::size(container) are valid
+// expressions, and that data() + idx is dereferenceable for all idx in the
+// range [0, size()). In the standard library this includes the containers
+// std::string, std::vector and std::array, but other containers like
+// std::initializer_list and C arrays are supported as well.
+//
+// In general this class is in nature quite similar to base::span, and its API
+// is inspired by it. However, one important difference is that this class
+// stores a pointer to the underlying container (as opposed to just storing its
+// data() and size()), and thus is able to deal with changes to the container,
+// such as removing or adding elements.
+//
+// Note however that this class still does not extend the life-time of the
+// underlying container, and thus callers need to make sure that the container
+// outlives the view to avoid dangling pointers and references.
+//
+// Lastly, this class leverages base::CheckedContiguousIterator to perform
+// bounds CHECKs, causing program termination when e.g. dereferencing the end
+// iterator.
+//
+// [1] https://en.cppreference.com/w/cpp/named_req/ContiguousContainer
+// [2]
+// https://eel.is/c++draft/container.requirements.general#def:contiguous_container
+template <typename ContiguousContainer>
+class CheckedContiguousRange {
+ public:
+ using element_type = std::remove_pointer_t<decltype(
+ base::data(std::declval<ContiguousContainer&>()))>;
+ using value_type = std::remove_cv_t<element_type>;
+ using reference = element_type&;
+ using const_reference = const element_type&;
+ using pointer = element_type*;
+ using const_pointer = const element_type*;
+ using iterator = CheckedContiguousIterator<element_type>;
+ using const_iterator = CheckedContiguousConstIterator<element_type>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using difference_type = typename iterator::difference_type;
+ using size_type = size_t;
+
+ static_assert(!std::is_reference<ContiguousContainer>::value,
+ "Error: ContiguousContainer can not be a reference.");
+
+ // Required for converting constructor below.
+ template <typename Container>
+ friend class CheckedContiguousRange;
+
+ constexpr CheckedContiguousRange() noexcept = default;
+
+ // Templated constructor restricted to possibly rvref qualified versions of
+ // ContiguousContainer. This makes sure it does not shadow the auto generated
+ // copy and move constructors.
+ template <int&... ExplicitArgumentBarrier,
+ typename Container,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_cv_t<std::remove_reference_t<ContiguousContainer>>,
+ std::remove_cv_t<std::remove_reference_t<Container>>>::value>>
+ constexpr CheckedContiguousRange(Container&& container) noexcept
+ : container_(&container) {}
+
+ // Converting constructor allowing conversions like CCR<C> to CCR<const C>,
+ // but disallowing CCR<const C> to CCR<C> or CCR<Derived[]> to CCR<Base[]>,
+ // which are unsafe. Furthermore, this is the same condition as used by the
+ // converting constructors of std::span<T> and std::unique_ptr<T[]>.
+ // See https://wg21.link/n4042 for details.
+ template <int&... ExplicitArgumentBarrier,
+ typename Container,
+ typename = std::enable_if_t<std::is_convertible<
+ typename CheckedContiguousRange<Container>::element_type (*)[],
+ element_type (*)[]>::value>>
+ constexpr CheckedContiguousRange(
+ CheckedContiguousRange<Container> range) noexcept
+ : container_(range.container_) {}
+
+ constexpr iterator begin() const noexcept {
+ return iterator(data(), data(), data() + size());
+ }
+
+ constexpr iterator end() const noexcept {
+ return iterator(data(), data() + size(), data() + size());
+ }
+
+ constexpr const_iterator cbegin() const noexcept { return begin(); }
+
+ constexpr const_iterator cend() const noexcept { return end(); }
+
+ constexpr reverse_iterator rbegin() const noexcept {
+ return reverse_iterator(end());
+ }
+
+ constexpr reverse_iterator rend() const noexcept {
+ return reverse_iterator(begin());
+ }
+
+ constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+
+ constexpr const_reverse_iterator crend() const noexcept { return rend(); }
+
+ constexpr reference front() const noexcept { return *begin(); }
+
+ constexpr reference back() const noexcept { return *(end() - 1); }
+
+ constexpr reference operator[](size_type idx) const noexcept {
+ return *(begin() + idx);
+ }
+
+ constexpr pointer data() const noexcept { return base::data(*container_); }
+
+ constexpr const_pointer cdata() const noexcept { return data(); }
+
+ constexpr size_type size() const noexcept { return base::size(*container_); }
+
+ constexpr bool empty() const noexcept { return base::empty(*container_); }
+
+ private:
+ ContiguousContainer* container_ = nullptr;
+};
+
+// Utility functions helping to create const ranges and performing automatic
+// type deduction.
+template <typename ContiguousContainer>
+using CheckedContiguousConstRange =
+ CheckedContiguousRange<const ContiguousContainer>;
+
+template <int&... ExplicitArgumentBarrier, typename ContiguousContainer>
+constexpr auto MakeCheckedContiguousRange(
+ ContiguousContainer&& container) noexcept {
+ return CheckedContiguousRange<std::remove_reference_t<ContiguousContainer>>(
+ std::forward<ContiguousContainer>(container));
+}
+
+template <int&... ExplicitArgumentBarrier, typename ContiguousContainer>
+constexpr auto MakeCheckedContiguousConstRange(
+ ContiguousContainer&& container) noexcept {
+ return CheckedContiguousConstRange<
+ std::remove_reference_t<ContiguousContainer>>(
+ std::forward<ContiguousContainer>(container));
+}
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_CHECKED_RANGE_H_
diff --git a/base/containers/checked_range_unittest.cc b/base/containers/checked_range_unittest.cc
new file mode 100644
index 0000000..f8040ea4
--- /dev/null
+++ b/base/containers/checked_range_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright 2019 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/checked_range.h"
+#include <algorithm>
+#include <array>
+#include <initializer_list>
+#include <type_traits>
+
+#include "base/strings/string_piece.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(CheckedContiguousRange, Constructor_Vector) {
+ std::vector<int> vector = {1, 2, 3, 4, 5};
+ CheckedContiguousRange<std::vector<int>> range(vector);
+ EXPECT_EQ(vector.data(), range.data());
+ EXPECT_EQ(vector.size(), range.size());
+}
+
+TEST(CheckedContiguousRange, Constructor_String) {
+ std::string str = "Hello World";
+ CheckedContiguousRange<std::string> range(str);
+ EXPECT_EQ(str.data(), range.data());
+ EXPECT_EQ(str.size(), range.size());
+}
+
+TEST(CheckedContiguousRange, Constructor_Array) {
+ static constexpr int array[] = {1, 2, 3, 4, 5};
+ constexpr CheckedContiguousRange<const int[5]> range(array);
+ static_assert(data(array) == range.data(), "");
+ static_assert(size(array) == range.size(), "");
+}
+
+TEST(CheckedContiguousRange, Constructor_StdArray) {
+ static constexpr std::array<int, 5> array = {1, 2, 3, 4, 5};
+ constexpr CheckedContiguousRange<const std::array<int, 5>> range(array);
+ static_assert(data(array) == range.data(), "");
+ static_assert(size(array) == range.size(), "");
+}
+
+TEST(CheckedContiguousRange, Constructor_StringPiece) {
+ static constexpr base::StringPiece str = "Hello World";
+ constexpr CheckedContiguousRange<const base::StringPiece> range(str);
+ static_assert(str.data() == range.data(), "");
+ static_assert(str.size() == range.size(), "");
+}
+
+TEST(CheckedContiguousRange, Constructor_InitializerList) {
+ static constexpr std::initializer_list<int> il = {1, 2, 3, 4, 5};
+ constexpr CheckedContiguousRange<const std::initializer_list<int>> range(il);
+ static_assert(data(il) == range.data(), "");
+ static_assert(size(il) == range.size(), "");
+}
+
+TEST(CheckedContiguousRange, Constructor_Copy) {
+ std::vector<int> vector = {1, 2, 3, 4, 5};
+ CheckedContiguousRange<std::vector<int>> range(vector);
+ CheckedContiguousRange<std::vector<int>> copy(range);
+ EXPECT_EQ(vector.data(), copy.data());
+ EXPECT_EQ(vector.size(), copy.size());
+}
+
+TEST(CheckedContiguousRange, Constructor_Move) {
+ std::vector<int> vector = {1, 2, 3, 4, 5};
+ CheckedContiguousRange<std::vector<int>> range(vector);
+ CheckedContiguousRange<std::vector<int>> move(std::move(range));
+ EXPECT_EQ(vector.data(), move.data());
+ EXPECT_EQ(vector.size(), move.size());
+}
+
+TEST(CheckedContiguousRange, Copy_Assign) {
+ std::vector<int> vector = {1, 2, 3, 4, 5};
+ CheckedContiguousRange<std::vector<int>> range(vector);
+ CheckedContiguousRange<std::vector<int>> copy;
+ copy = range;
+ EXPECT_EQ(vector.data(), copy.data());
+ EXPECT_EQ(vector.size(), copy.size());
+}
+
+TEST(CheckedContiguousRange, Move_Assign) {
+ std::vector<int> vector = {1, 2, 3, 4, 5};
+ CheckedContiguousRange<std::vector<int>> range(vector);
+ CheckedContiguousRange<std::vector<int>> move;
+ move = std::move(range);
+ EXPECT_EQ(vector.data(), move.data());
+ EXPECT_EQ(vector.size(), move.size());
+}
+
+TEST(CheckedContiguousRange, Iterators) {
+ std::vector<int> vector;
+ CheckedContiguousRange<const std::vector<int>> range(vector);
+
+ // Check that all ranges by the iterators compare equal, even when elements
+ // are added.
+ for (size_t i = 0; i < 5; ++i) {
+ vector.push_back(i);
+ EXPECT_TRUE(
+ std::equal(vector.begin(), vector.end(), range.begin(), range.end()));
+ EXPECT_TRUE(std::equal(vector.cbegin(), vector.cend(), range.cbegin(),
+ range.cend()));
+ EXPECT_TRUE(std::equal(vector.rbegin(), vector.rend(), range.rbegin(),
+ range.rend()));
+ EXPECT_TRUE(std::equal(vector.crbegin(), vector.crend(), range.crbegin(),
+ range.crend()));
+ }
+}
+
+TEST(CheckedContiguousRange, Front) {
+ static constexpr std::array<int, 5> array = {1, 2, 3, 4, 5};
+ constexpr CheckedContiguousRange<const std::array<int, 5>> range(array);
+ static_assert(array.front() == range.front(), "");
+}
+
+TEST(CheckedContiguousRange, Back) {
+ static constexpr std::array<int, 5> array = {1, 2, 3, 4, 5};
+ constexpr CheckedContiguousRange<const std::array<int, 5>> range(array);
+ static_assert(array.back() == range.back(), "");
+}
+
+TEST(CheckedContiguousRange, OperatorAt_Constexpr) {
+ static constexpr std::array<int, 5> array = {1, 2, 3, 4, 5};
+ constexpr CheckedContiguousRange<const std::array<int, 5>> range(array);
+ static_assert(array[0] == range[0], "");
+ static_assert(array[1] == range[1], "");
+ static_assert(array[2] == range[2], "");
+ static_assert(array[3] == range[3], "");
+ static_assert(array[4] == range[4], "");
+}
+
+TEST(CheckedContiguousRange, Mutable_OperatorAt) {
+ std::vector<int> vector = {1, 2, 3, 4, 5};
+ CheckedContiguousRange<std::vector<int>> range(vector);
+
+ EXPECT_EQ(vector[0], range[0]);
+ range[0] = 2;
+ EXPECT_EQ(vector[0], 2);
+}
+
+TEST(CheckedContiguousRange, Mutable_Data) {
+ std::vector<int> vector = {3, 1, 4, 2, 5};
+ CheckedContiguousRange<std::vector<int>> range(vector);
+
+ EXPECT_FALSE(std::is_sorted(vector.begin(), vector.end()));
+ std::sort(range.data(), range.data() + range.size());
+ EXPECT_TRUE(std::is_sorted(vector.begin(), vector.end()));
+}
+
+TEST(CheckedContiguousRange, DataSizeEmpty_Constexpr) {
+ static constexpr std::array<int, 0> array = {};
+ constexpr CheckedContiguousRange<const std::array<int, 0>> range(array);
+ static_assert(data(array) == range.data(), "");
+ static_assert(data(array) == range.cdata(), "");
+ static_assert(size(array) == range.size(), "");
+ static_assert(range.empty(), "");
+}
+
+TEST(CheckedContiguousRange, MakeCheckedContiguousRange) {
+ using Vec = std::vector<int>;
+
+ static_assert(std::is_same<CheckedContiguousRange<Vec>,
+ decltype(MakeCheckedContiguousRange(
+ std::declval<Vec&>()))>::value,
+ "");
+
+ static_assert(std::is_same<CheckedContiguousRange<const Vec>,
+ decltype(MakeCheckedContiguousRange(
+ std::declval<const Vec&>()))>::value,
+ "");
+
+ static_assert(std::is_same<CheckedContiguousRange<Vec>,
+ decltype(MakeCheckedContiguousRange(
+ std::declval<Vec&&>()))>::value,
+ "");
+
+ static_assert(std::is_same<CheckedContiguousRange<const Vec>,
+ decltype(MakeCheckedContiguousRange(
+ std::declval<const Vec&&>()))>::value,
+ "");
+}
+
+TEST(CheckedContiguousRange, MakeCheckedContiguousConstRange) {
+ using Vec = std::vector<int>;
+
+ static_assert(std::is_same<CheckedContiguousRange<const Vec>,
+ decltype(MakeCheckedContiguousConstRange(
+ std::declval<Vec&>()))>::value,
+ "");
+
+ static_assert(std::is_same<CheckedContiguousRange<const Vec>,
+ decltype(MakeCheckedContiguousConstRange(
+ std::declval<const Vec&>()))>::value,
+ "");
+
+ static_assert(std::is_same<CheckedContiguousRange<const Vec>,
+ decltype(MakeCheckedContiguousConstRange(
+ std::declval<Vec&&>()))>::value,
+ "");
+
+ static_assert(std::is_same<CheckedContiguousRange<const Vec>,
+ decltype(MakeCheckedContiguousConstRange(
+ std::declval<const Vec&&>()))>::value,
+ "");
+}
+
+TEST(CheckedContiguousRange, Conversions) {
+ using T = std::vector<int>;
+ // Check that adding const is allowed, but removing const is not.
+ static_assert(std::is_convertible<CheckedContiguousRange<T>,
+ CheckedContiguousRange<const T>>::value,
+ "");
+ static_assert(!std::is_constructible<CheckedContiguousRange<T>,
+ CheckedContiguousRange<const T>>::value,
+ "");
+
+ struct Base {};
+ struct Derived : Base {};
+ // Check that trying to assign arrays from a derived class fails.
+ static_assert(
+ !std::is_constructible<CheckedContiguousRange<Base[5]>,
+ CheckedContiguousRange<Derived[5]>>::value,
+ "");
+}
+
+TEST(CheckedContiguousRange, OutOfBoundsDeath) {
+ std::vector<int> empty_vector;
+ CheckedContiguousRange<std::vector<int>> empty_range(empty_vector);
+ ASSERT_DEATH_IF_SUPPORTED(empty_range[0], "");
+ ASSERT_DEATH_IF_SUPPORTED(empty_range.front(), "");
+ ASSERT_DEATH_IF_SUPPORTED(empty_range.back(), "");
+
+ static constexpr int array[] = {0, 1, 2};
+ constexpr CheckedContiguousRange<const int[3]> range(array);
+ ASSERT_DEATH_IF_SUPPORTED(range[3], "");
+}
+
+} // namespace base
diff --git a/base/containers/span_unittest.cc b/base/containers/span_unittest.cc
index de05bdb..6e2d9be 100644
--- a/base/containers/span_unittest.cc
+++ b/base/containers/span_unittest.cc
@@ -1302,16 +1302,12 @@
TEST(SpanTest, OutOfBoundsDeath) {
constexpr span<int, 0> kEmptySpan;
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan[0], "");
- ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin()[0], "");
- ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end()[0], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.first(1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.last(1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.subspan(1), "");
constexpr span<int> kEmptyDynamicSpan;
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan[0], "");
- ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin()[0], "");
- ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end()[0], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.front(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.first(1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.last(1), "");
@@ -1322,10 +1318,8 @@
constexpr span<const int> kNonEmptyDynamicSpan(kArray);
EXPECT_EQ(3U, kNonEmptyDynamicSpan.size());
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan[4], "");
- ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[-1], "");
- ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[3], "");
- ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(10), "");
- ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(1, 7), "");
+ ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.subspan(10), "");
+ ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.subspan(1, 7), "");
}
TEST(SpanTest, IteratorIsRangeMoveSafe) {
diff --git a/base/stl_util.h b/base/stl_util.h
index e22b029..4edd1fb 100644
--- a/base/stl_util.h
+++ b/base/stl_util.h
@@ -144,6 +144,20 @@
return il.begin();
}
+// std::array::data() was not constexpr prior to C++17 [1].
+// Hence these overloads are provided.
+//
+// [1] https://en.cppreference.com/w/cpp/container/array/data
+template <typename T, size_t N>
+constexpr T* data(std::array<T, N>& array) noexcept {
+ return !array.empty() ? &array[0] : nullptr;
+}
+
+template <typename T, size_t N>
+constexpr const T* data(const std::array<T, N>& array) noexcept {
+ return !array.empty() ? &array[0] : nullptr;
+}
+
// Returns a const reference to the underlying container of a container adapter.
// Works for std::priority_queue, std::queue, and std::stack.
template <class A>