// 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 #include #include #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. Similarly to base::span (and other view-like containers // such as base::StringPiece) callers are encouraged to pass checked ranges by // value. // // 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 class CheckedContiguousRange { public: using element_type = std::remove_pointer_t()))>; using value_type = std::remove_cv_t; using reference = element_type&; using const_reference = const element_type&; using pointer = element_type*; using const_pointer = const element_type*; using iterator = CheckedContiguousIterator; using const_iterator = CheckedContiguousConstIterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using difference_type = typename iterator::difference_type; using size_type = size_t; static_assert(!std::is_reference::value, "Error: ContiguousContainer can not be a reference."); // Required for converting constructor below. template friend class CheckedContiguousRange; // Default constructor. Behaves as if the underlying container was empty. constexpr CheckedContiguousRange() noexcept = default; // Templated constructor restricted to possibly cvref qualified versions of // ContiguousContainer. This makes sure it does not shadow the auto generated // copy and move constructors. template >, std::remove_cv_t>>::value>> constexpr CheckedContiguousRange(Container&& container) noexcept : container_(&container) {} // Converting constructor allowing conversions like CCR to CCR, // but disallowing CCR to CCR or CCR to CCR, // which are unsafe. Furthermore, this is the same condition as used by the // converting constructors of std::span and std::unique_ptr. // See https://wg21.link/n4042 for details. template ::element_type (*)[], element_type (*)[]>::value>> constexpr CheckedContiguousRange( CheckedContiguousRange 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 container_ ? base::data(*container_) : nullptr; } constexpr const_pointer cdata() const noexcept { return data(); } constexpr size_type size() const noexcept { return container_ ? base::size(*container_) : 0; } constexpr bool empty() const noexcept { return container_ ? base::empty(*container_) : true; } private: ContiguousContainer* container_ = nullptr; }; // Utility functions helping to create const ranges and performing automatic // type deduction. template using CheckedContiguousConstRange = CheckedContiguousRange; template constexpr auto MakeCheckedContiguousRange( ContiguousContainer&& container) noexcept { return CheckedContiguousRange>( std::forward(container)); } template constexpr auto MakeCheckedContiguousConstRange( ContiguousContainer&& container) noexcept { return CheckedContiguousConstRange< std::remove_reference_t>( std::forward(container)); } } // namespace base #endif // BASE_CONTAINERS_CHECKED_RANGE_H_