[go: nahoru, domu]

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>