| // Copyright 2017 The BoringSSL Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #ifndef OPENSSL_HEADER_SSL_SPAN_H | |
| #define OPENSSL_HEADER_SSL_SPAN_H | |
| #include <openssl/base.h> // IWYU pragma: export | |
| #if !defined(BORINGSSL_NO_CXX) | |
| // Work around consumers including our headers under extern "C". | |
| extern "C++" { | |
| #include <stdlib.h> | |
| #include <algorithm> | |
| #include <array> | |
| #include <limits> | |
| #include <string_view> | |
| #include <type_traits> | |
| #if __has_include(<version>) | |
| #include <version> | |
| #endif | |
| #if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L | |
| #include <ranges> | |
| #endif | |
| BSSL_NAMESPACE_BEGIN | |
| inline constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max(); | |
| template <typename T, size_t N = dynamic_extent> | |
| class Span; | |
| BSSL_NAMESPACE_END | |
| #if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L | |
| // Mark `Span` as satisfying the `view` and `borrowed_range` concepts. This | |
| // should be done before the definition of `Span`, so that any inlined calls to | |
| // range functionality use the correct specializations. | |
| template <typename T, size_t N> | |
| inline constexpr bool std::ranges::enable_view<bssl::Span<T, N>> = true; | |
| template <typename T, size_t N> | |
| inline constexpr bool std::ranges::enable_borrowed_range<bssl::Span<T, N>> = | |
| true; | |
| #endif | |
| BSSL_NAMESPACE_BEGIN | |
| namespace internal { | |
| template <typename T> | |
| class SpanBase { | |
| // Put comparison operator implementations into a base class with const T, so | |
| // they can be used with any type that implicitly converts into a Span. | |
| static_assert(std::is_const_v<T>, | |
| "Span<T> must be derived from SpanBase<const T>"); | |
| friend bool operator==(Span<T> lhs, Span<T> rhs) { | |
| return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); | |
| } | |
| friend bool operator!=(Span<T> lhs, Span<T> rhs) { return !(lhs == rhs); } | |
| }; | |
| // Container class to store the size of a span at runtime or compile time. | |
| template <typename T, size_t N> | |
| class SpanStorage : private SpanBase<const T> { | |
| public: | |
| constexpr SpanStorage(T *data, size_t size) : data_(data) { | |
| BSSL_CHECK(size == N); | |
| } | |
| constexpr T *data() const { return data_; } | |
| constexpr size_t size() const { return N; } | |
| private: | |
| T *data_; | |
| }; | |
| template <typename T> | |
| class SpanStorage<T, dynamic_extent> : private SpanBase<const T> { | |
| public: | |
| constexpr SpanStorage(T *data, size_t size) : data_(data), size_(size) {} | |
| constexpr T *data() const { return data_; } | |
| constexpr size_t size() const { return size_; } | |
| private: | |
| T *data_; | |
| size_t size_; | |
| }; | |
| // Heuristically test whether C is a container type that can be converted into | |
| // a Span<T> by checking for data() and size() member functions. | |
| template <typename C, typename T> | |
| using EnableIfContainer = std::enable_if_t< | |
| std::is_convertible_v<decltype(std::declval<C>().data()), T *> && | |
| std::is_integral_v<decltype(std::declval<C>().size())>>; | |
| // A fake type used to be able to SFINAE between two different container | |
| // constructors - by giving one this as a second default argument, and one not. | |
| struct AllowRedeclaringConstructor {}; | |
| } // namespace internal | |
| // A Span<T> is a non-owning reference to a contiguous array of objects of type | |
| // `T`. Conceptually, a Span is a simple a pointer to `T` and a count of | |
| // elements accessible via that pointer. The elements referenced by the Span can | |
| // be mutated if `T` is mutable. | |
| // | |
| // A Span can be constructed from container types implementing `data()` and | |
| // `size()` methods. If `T` is constant, construction from a container type is | |
| // implicit. This allows writing methods that accept data from some unspecified | |
| // container type: | |
| // | |
| // // Foo views data referenced by v. | |
| // void Foo(bssl::Span<const uint8_t> v) { ... } | |
| // | |
| // std::vector<uint8_t> vec; | |
| // Foo(vec); | |
| // | |
| // For mutable Spans, conversion is explicit: | |
| // | |
| // // FooMutate mutates data referenced by v. | |
| // void FooMutate(bssl::Span<uint8_t> v) { ... } | |
| // | |
| // FooMutate(bssl::Span<uint8_t>(vec)); | |
| // | |
| // You can also use C++17 class template argument deduction to construct Spans | |
| // in order to deduce the type of the Span automatically. | |
| // | |
| // FooMutate(bssl::Span(vec)); | |
| // | |
| // Note that Spans have value type semantics. They are cheap to construct and | |
| // copy, and should be passed by value whenever a method would otherwise accept | |
| // a reference or pointer to a container or array. | |
| template <typename T, size_t N> | |
| class Span : public internal::SpanStorage<T, N> { | |
| public: | |
| using element_type = T; | |
| using value_type = std::remove_cv_t<T>; | |
| using size_type = size_t; | |
| using difference_type = ptrdiff_t; | |
| using pointer = T *; | |
| using const_pointer = const T *; | |
| using reference = T &; | |
| using const_reference = const T &; | |
| using iterator = T *; | |
| using const_iterator = const T *; | |
| template <typename U = T, | |
| typename = std::enable_if_t<N == 0 || N == dynamic_extent, U>> | |
| constexpr Span() : internal::SpanStorage<T, N>(nullptr, 0) {} | |
| // NOTE: This constructor may abort() at runtime if len differs from the | |
| // compile-time size, if any. | |
| constexpr Span(T *ptr, size_t len) : internal::SpanStorage<T, N>(ptr, len) {} | |
| template <size_t NA, | |
| typename = std::enable_if_t<N == NA || N == dynamic_extent>> | |
| // NOLINTNEXTLINE(google-explicit-constructor): same as std::span. | |
| constexpr Span(T (&array)[NA]) : internal::SpanStorage<T, N>(array, NA) {} | |
| // TODO(crbug.com/457351017): Add tests for these c'tors. | |
| template <size_t NA, typename U, | |
| typename = internal::EnableIfContainer<std::array<U, NA>, T>, | |
| typename = std::enable_if_t<N == NA || N == dynamic_extent>> | |
| // NOLINTNEXTLINE(google-explicit-constructor): same as std::span. | |
| constexpr Span(std::array<U, NA> &array) | |
| : internal::SpanStorage<T, N>(array.data(), NA) {} | |
| template <size_t NA, typename U, | |
| typename = internal::EnableIfContainer<const std::array<U, NA>, T>, | |
| typename = std::enable_if_t<N == NA || N == dynamic_extent>> | |
| // NOLINTNEXTLINE(google-explicit-constructor): same as std::span. | |
| constexpr Span(const std::array<U, NA> &array) | |
| : internal::SpanStorage<T, N>(array.data(), NA) {} | |
| template < | |
| size_t NA, typename U, | |
| typename = std::enable_if_t<std::is_convertible_v<U (*)[], T (*)[]>>, | |
| typename = std::enable_if_t<N == dynamic_extent || N == NA>> | |
| // NOLINTNEXTLINE(google-explicit-constructor): same as std::span. | |
| constexpr Span(Span<U, NA> other) | |
| : internal::SpanStorage<T, N>(other.data(), other.size()) {} | |
| template <typename C, typename = internal::EnableIfContainer<C, T>, | |
| typename = std::enable_if_t<std::is_const_v<T>, C>, | |
| typename = std::enable_if_t<N == dynamic_extent, C>> | |
| // NOLINTNEXTLINE(google-explicit-constructor): same as std::span. | |
| constexpr Span(const C &container) | |
| : internal::SpanStorage<T, N>(container.data(), container.size()) {} | |
| // NOTE: This constructor may abort() at runtime if the container's length | |
| // differs from the compile-time size, if any. | |
| template <typename C, typename = internal::EnableIfContainer<C, T>, | |
| typename = std::enable_if_t<std::is_const_v<T>, C>, | |
| typename = std::enable_if_t<N != dynamic_extent, C>> | |
| constexpr explicit Span(const C &container, | |
| internal::AllowRedeclaringConstructor = {}) | |
| : internal::SpanStorage<T, N>(container.data(), container.size()) {} | |
| // NOTE: This constructor may abort() at runtime if the container's length | |
| // differs from the compile-time size, if any. | |
| template <typename C, typename = internal::EnableIfContainer<C, T>, | |
| typename = std::enable_if_t<!std::is_const_v<T>, C>> | |
| constexpr explicit Span(C &container) | |
| : internal::SpanStorage<T, N>(container.data(), container.size()) {} | |
| using internal::SpanStorage<T, N>::data; | |
| using internal::SpanStorage<T, N>::size; | |
| constexpr bool empty() const { return size() == 0; } | |
| constexpr iterator begin() const { return data(); } | |
| constexpr const_iterator cbegin() const { return data(); } | |
| constexpr iterator end() const { return data() + size(); } | |
| constexpr const_iterator cend() const { return end(); } | |
| constexpr T &front() const { | |
| BSSL_CHECK(size() != 0); | |
| return data()[0]; | |
| } | |
| constexpr T &back() const { | |
| BSSL_CHECK(size() != 0); | |
| return data()[size() - 1]; | |
| } | |
| constexpr T &operator[](size_t i) const { | |
| BSSL_CHECK(i < size()); | |
| return data()[i]; | |
| } | |
| T &at(size_t i) const { return (*this)[i]; } | |
| private: | |
| static constexpr size_t SubspanOutLen(size_t size, size_t pos, size_t len) { | |
| return len != dynamic_extent ? len : size - pos; | |
| } | |
| static constexpr size_t SubspanTypeOutLen(size_t size, size_t pos, | |
| size_t len) { | |
| // This differs from SubspanOutLen in that if both size and len are | |
| // dynamic_extent, dynamic_extent will be returned. | |
| return len != dynamic_extent | |
| ? len | |
| : (size != dynamic_extent ? size - pos : dynamic_extent); | |
| } | |
| public: | |
| // NOTE: This method may abort() at runtime if pos or len are out of range. | |
| // NOTE: As opposed to std::span, the `dynamic_extent` value of `len` is not | |
| // magical here. This gets rid of a lot of runtime checks. | |
| constexpr Span<T> subspan(size_t pos, size_t len) const { | |
| // absl::Span throws an exception here. Note std::span and Chromium | |
| // base::span forbid pos + len being out of range, with a special case at | |
| // npos/dynamic_extent, whereas absl::Span::subspan clips the span. This | |
| // implements the std::span behavior which is more strict. | |
| BSSL_CHECK(pos <= size()); | |
| BSSL_CHECK(len <= size() - pos); | |
| return Span<T>(data() + pos, len); | |
| } | |
| // NOTE: This method may abort() at runtime if pos is out of range. | |
| constexpr Span<T> subspan(size_t pos) const { | |
| // absl::Span throws an exception here. | |
| BSSL_CHECK(pos <= size()); | |
| return Span<T>(data() + pos, size() - pos); | |
| } | |
| // NOTE: This method may abort() at runtime if len is out of range. | |
| template <size_t pos, size_t len = dynamic_extent> | |
| constexpr Span<T, SubspanTypeOutLen(N, pos, len)> subspan() const { | |
| // absl::Span throws an exception here. Note std::span and Chromium | |
| // base::span forbid pos + len being out of range, with a special case at | |
| // npos/dynamic_extent, whereas absl::Span::subspan clips the span. This | |
| // implements the std::span behavior which is more strict. | |
| BSSL_CHECK(pos <= size()); | |
| BSSL_CHECK(len == dynamic_extent || len <= size() - pos); | |
| return Span<T, SubspanTypeOutLen(N, pos, len)>( | |
| data() + pos, SubspanOutLen(size(), pos, len)); | |
| } | |
| // NOTE: This method may abort() at runtime if len is out of range. | |
| constexpr Span<T> first(size_t len) const { | |
| BSSL_CHECK(len <= size()); | |
| return Span<T>(data(), len); | |
| } | |
| // NOTE: This method may abort() at runtime if len is out of range. | |
| template <size_t len> | |
| constexpr Span<T, len> first() const { | |
| BSSL_CHECK(len <= size()); | |
| return Span<T, len>(data(), len); | |
| } | |
| // NOTE: This method may abort() at runtime if len is out of range. | |
| constexpr Span<T> last(size_t len) const { | |
| BSSL_CHECK(len <= size()); | |
| return Span<T>(data() + size() - len, len); | |
| } | |
| // NOTE: This method may abort() at runtime if len is out of range. | |
| template <size_t len> | |
| constexpr Span<T, len> last() const { | |
| BSSL_CHECK(len <= size()); | |
| return Span<T, len>(data() + size() - len, len); | |
| } | |
| }; | |
| template <typename T> | |
| Span(T *, size_t) -> Span<T>; | |
| template <typename T, size_t size> | |
| Span(T (&array)[size]) -> Span<T, size>; | |
| template <typename T, size_t size> | |
| Span(std::array<T, size> &array) -> Span<T, size>; | |
| template <typename T, size_t size> | |
| Span(const std::array<T, size> &array) -> Span<const T, size>; | |
| template < | |
| typename C, | |
| typename T = std::remove_pointer_t<decltype(std::declval<C>().data())>, | |
| typename = internal::EnableIfContainer<C, T>> | |
| Span(C &) -> Span<T>; | |
| template <typename T> | |
| constexpr Span<T> MakeSpan(T *ptr, size_t size) { | |
| return Span<T>(ptr, size); | |
| } | |
| template <typename C> | |
| constexpr auto MakeSpan(C &c) -> decltype(MakeSpan(c.data(), c.size())) { | |
| return MakeSpan(c.data(), c.size()); | |
| } | |
| template <typename T, size_t N> | |
| constexpr Span<T, N> MakeSpan(T (&array)[N]) { | |
| return array; | |
| } | |
| template <typename T> | |
| constexpr Span<const T> MakeConstSpan(T *ptr, size_t size) { | |
| return Span<const T>(ptr, size); | |
| } | |
| template <typename C> | |
| constexpr auto MakeConstSpan(const C &c) | |
| -> decltype(MakeConstSpan(c.data(), c.size())) { | |
| return MakeConstSpan(c.data(), c.size()); | |
| } | |
| template <typename T, size_t size> | |
| constexpr Span<const T, size> MakeConstSpan(T (&array)[size]) { | |
| return array; | |
| } | |
| inline Span<const uint8_t> StringAsBytes(std::string_view s) { | |
| return MakeConstSpan(reinterpret_cast<const uint8_t *>(s.data()), s.size()); | |
| } | |
| inline std::string_view BytesAsStringView(bssl::Span<const uint8_t> b) { | |
| return std::string_view(reinterpret_cast<const char *>(b.data()), b.size()); | |
| } | |
| BSSL_NAMESPACE_END | |
| } // extern C++ | |
| #endif // !defined(BORINGSSL_NO_CXX) | |
| #endif // OPENSSL_HEADER_SSL_SPAN_H |