checked_range.h 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef BASE_CONTAINERS_CHECKED_RANGE_H_
  5. #define BASE_CONTAINERS_CHECKED_RANGE_H_
  6. #include <stddef.h>
  7. #include <iterator>
  8. #include <type_traits>
  9. #include "base/containers/checked_iterators.h"
  10. #include "base/stl_util.h"
  11. namespace base {
  12. // CheckedContiguousRange is a light-weight wrapper around a container modeling
  13. // the ContiguousContainer requirement [1, 2]. Effectively this means that the
  14. // container stores its elements contiguous in memory. Furthermore, it is
  15. // expected that base::data(container) and base::size(container) are valid
  16. // expressions, and that data() + idx is dereferenceable for all idx in the
  17. // range [0, size()). In the standard library this includes the containers
  18. // std::string, std::vector and std::array, but other containers like
  19. // std::initializer_list and C arrays are supported as well.
  20. //
  21. // In general this class is in nature quite similar to base::span, and its API
  22. // is inspired by it. Similarly to base::span (and other view-like containers
  23. // such as base::StringPiece) callers are encouraged to pass checked ranges by
  24. // value.
  25. //
  26. // However, one important difference is that this class stores a pointer to the
  27. // underlying container (as opposed to just storing its data() and size()), and
  28. // thus is able to deal with changes to the container, such as removing or
  29. // adding elements.
  30. //
  31. // Note however that this class still does not extend the life-time of the
  32. // underlying container, and thus callers need to make sure that the container
  33. // outlives the view to avoid dangling pointers and references.
  34. //
  35. // Lastly, this class leverages base::CheckedContiguousIterator to perform
  36. // bounds CHECKs, causing program termination when e.g. dereferencing the end
  37. // iterator.
  38. //
  39. // [1] https://en.cppreference.com/w/cpp/named_req/ContiguousContainer
  40. // [2]
  41. // https://eel.is/c++draft/container.requirements.general#def:contiguous_container
  42. template <typename ContiguousContainer>
  43. class CheckedContiguousRange {
  44. public:
  45. using element_type = std::remove_pointer_t<decltype(
  46. base::data(std::declval<ContiguousContainer&>()))>;
  47. using value_type = std::remove_cv_t<element_type>;
  48. using reference = element_type&;
  49. using const_reference = const element_type&;
  50. using pointer = element_type*;
  51. using const_pointer = const element_type*;
  52. using iterator = CheckedContiguousIterator<element_type>;
  53. using const_iterator = CheckedContiguousConstIterator<element_type>;
  54. using reverse_iterator = std::reverse_iterator<iterator>;
  55. using const_reverse_iterator = std::reverse_iterator<const_iterator>;
  56. using difference_type = typename iterator::difference_type;
  57. using size_type = size_t;
  58. static_assert(!std::is_reference<ContiguousContainer>::value,
  59. "Error: ContiguousContainer can not be a reference.");
  60. // Required for converting constructor below.
  61. template <typename Container>
  62. friend class CheckedContiguousRange;
  63. // Default constructor. Behaves as if the underlying container was empty.
  64. constexpr CheckedContiguousRange() noexcept = default;
  65. // Templated constructor restricted to possibly cvref qualified versions of
  66. // ContiguousContainer. This makes sure it does not shadow the auto generated
  67. // copy and move constructors.
  68. template <int&... ExplicitArgumentBarrier,
  69. typename Container,
  70. typename = std::enable_if_t<std::is_same<
  71. std::remove_cv_t<std::remove_reference_t<ContiguousContainer>>,
  72. std::remove_cv_t<std::remove_reference_t<Container>>>::value>>
  73. constexpr CheckedContiguousRange(Container&& container) noexcept
  74. : container_(&container) {}
  75. // Converting constructor allowing conversions like CCR<C> to CCR<const C>,
  76. // but disallowing CCR<const C> to CCR<C> or CCR<Derived[]> to CCR<Base[]>,
  77. // which are unsafe. Furthermore, this is the same condition as used by the
  78. // converting constructors of std::span<T> and std::unique_ptr<T[]>.
  79. // See https://wg21.link/n4042 for details.
  80. template <int&... ExplicitArgumentBarrier,
  81. typename Container,
  82. typename = std::enable_if_t<std::is_convertible<
  83. typename CheckedContiguousRange<Container>::element_type (*)[],
  84. element_type (*)[]>::value>>
  85. constexpr CheckedContiguousRange(
  86. CheckedContiguousRange<Container> range) noexcept
  87. : container_(range.container_) {}
  88. constexpr iterator begin() const noexcept {
  89. return iterator(data(), data(), data() + size());
  90. }
  91. constexpr iterator end() const noexcept {
  92. return iterator(data(), data() + size(), data() + size());
  93. }
  94. constexpr const_iterator cbegin() const noexcept { return begin(); }
  95. constexpr const_iterator cend() const noexcept { return end(); }
  96. constexpr reverse_iterator rbegin() const noexcept {
  97. return reverse_iterator(end());
  98. }
  99. constexpr reverse_iterator rend() const noexcept {
  100. return reverse_iterator(begin());
  101. }
  102. constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
  103. constexpr const_reverse_iterator crend() const noexcept { return rend(); }
  104. constexpr reference front() const noexcept { return *begin(); }
  105. constexpr reference back() const noexcept { return *(end() - 1); }
  106. constexpr reference operator[](size_type idx) const noexcept {
  107. return *(begin() + idx);
  108. }
  109. constexpr pointer data() const noexcept {
  110. return container_ ? base::data(*container_) : nullptr;
  111. }
  112. constexpr const_pointer cdata() const noexcept { return data(); }
  113. constexpr size_type size() const noexcept {
  114. return container_ ? base::size(*container_) : 0;
  115. }
  116. constexpr bool empty() const noexcept {
  117. return container_ ? base::empty(*container_) : true;
  118. }
  119. private:
  120. ContiguousContainer* container_ = nullptr;
  121. };
  122. // Utility functions helping to create const ranges and performing automatic
  123. // type deduction.
  124. template <typename ContiguousContainer>
  125. using CheckedContiguousConstRange =
  126. CheckedContiguousRange<const ContiguousContainer>;
  127. template <int&... ExplicitArgumentBarrier, typename ContiguousContainer>
  128. constexpr auto MakeCheckedContiguousRange(
  129. ContiguousContainer&& container) noexcept {
  130. return CheckedContiguousRange<std::remove_reference_t<ContiguousContainer>>(
  131. std::forward<ContiguousContainer>(container));
  132. }
  133. template <int&... ExplicitArgumentBarrier, typename ContiguousContainer>
  134. constexpr auto MakeCheckedContiguousConstRange(
  135. ContiguousContainer&& container) noexcept {
  136. return CheckedContiguousConstRange<
  137. std::remove_reference_t<ContiguousContainer>>(
  138. std::forward<ContiguousContainer>(container));
  139. }
  140. } // namespace base
  141. #endif // BASE_CONTAINERS_CHECKED_RANGE_H_