// Copyright 2025 The ANGLE Project Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This is Chromium's base/containers/span.h, modified to support C++17 // as part of the PDFium project, stubbed to be self-contained, and then // modified to conform to ANGLE: // -- fixed missing constexpr as exercised by test. // -- added operator==(). #ifndef COMMON_SPAN_H_ #define COMMON_SPAN_H_ #include #include #include #include #include #include #include #include "common/base/anglebase/logging.h" #include "common/unsafe_buffers.h" namespace angle { constexpr size_t dynamic_extent = static_cast(-1); template using DefaultSpanInternalPtr = T *; template > class Span; namespace internal { template struct IsSpanImpl : std::false_type {}; template struct IsSpanImpl> : std::true_type {}; template using IsSpan = IsSpanImpl::type>; template struct IsStdArrayImpl : std::false_type {}; template struct IsStdArrayImpl> : std::true_type {}; template using IsStdArray = IsStdArrayImpl::type>; template using IsLegalSpanConversion = std::is_convertible; template using ContainerHasConvertibleData = IsLegalSpanConversion< typename std::remove_pointer().data())>::type, T>; template using ContainerHasIntegralSize = std::is_integral().size())>; template using EnableIfLegalSpanConversion = typename std::enable_if::value>::type; // SFINAE check if Container can be converted to a Span. Note that the // implementation details of this check differ slightly from the requirements in // the working group proposal: in particular, the proposal also requires that // the container conversion constructor participate in overload resolution only // if two additional conditions are true: // // 1. Container implements operator[]. // 2. Container::value_type matches remove_const_t. // // The requirements are relaxed slightly here: in particular, not requiring (2) // means that an immutable Span can be easily constructed from a mutable // container. template using EnableIfSpanCompatibleContainer = typename std::enable_if::value && !internal::IsStdArray::value && ContainerHasConvertibleData::value && ContainerHasIntegralSize::value>::type; template using EnableIfConstSpanCompatibleContainer = typename std::enable_if::value && !internal::IsSpan::value && !internal::IsStdArray::value && ContainerHasConvertibleData::value && ContainerHasIntegralSize::value>::type; } // namespace internal // A Span is a value type that represents an array of elements of type T. Since // it only consists of a pointer to memory with an associated size, it is very // light-weight. It is cheap to construct, copy, move and use spans, so that // users are encouraged to use it as a pass-by-value parameter. A Span does not // own the underlying memory, so care must be taken to ensure that a Span does // not outlive the backing store. // // Differences from the working group proposal // ------------------------------------------- // // https://wg21.link/P0122 is the latest working group proposal, Chromium // currently implements R6. // // Differences in constants and types: // - Span has a capital "S". // - Allows custom smart pointer types in internal representation, if desired. // - no element_type type alias // - no index_type type alias // - no different_type type alias // - no extent constant // // Differences from [span.cons]: // - no constructor from a pointer range // // Differences from [span.sub]: // - using size_t instead of ptrdiff_t for indexing // // Differences from [span.obs]: // - using size_t instead of ptrdiff_t to represent size() // // Differences from [span.elem]: // - no operator ()() // - using size_t instead of ptrdiff_t for indexing // // Additions beyond the C++ standard draft // - as_chars() function. // - as_writable_chars() function. // - as_byte_span() function. // - as_writable_byte_span() function. // - span_from_ref() function. // - byte_span_from_ref() function. // [span], class template span template class Span { public: using value_type = typename std::remove_cv::type; using pointer = T *; using reference = T &; using iterator = T *; using const_iterator = const T *; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; // [span.cons], span constructors, copy, assignment, and destructor constexpr Span() noexcept = default; ANGLE_UNSAFE_BUFFER_USAGE constexpr Span(T *data, size_t size) noexcept : data_(data), size_(size) { DCHECK(data_ || size_ == 0); } // TODO(dcheng): Implement construction from a |begin| and |end| pointer. template constexpr Span(T (&array)[N]) noexcept // SAFETY: The type signature guarantees `array` contains `N` elements. : ANGLE_UNSAFE_BUFFERS(Span(array, N)) { static_assert(Extent == dynamic_extent || Extent == N); } template constexpr Span(std::array &array) noexcept // SAFETY: The type signature guarantees `array` contains `N` elements. : ANGLE_UNSAFE_BUFFERS(Span(array.data(), N)) { static_assert(Extent == dynamic_extent || Extent == N); } template >> constexpr Span(const std::array &array) noexcept // SAFETY: The type signature guarantees `array` contains `N` elements. : ANGLE_UNSAFE_BUFFERS(Span(array.data(), N)) { static_assert(Extent == dynamic_extent || Extent == N); } // Conversion from a container that provides |T* data()| and |integral_type // size()|. Note that |data()| may not return nullptr for some empty // containers, which can lead to container overflow errors when probing // raw ptrs. template > constexpr Span(Container &container) // SAFETY: `size()` is the number of elements that can be safely accessed // at `data()`. : ANGLE_UNSAFE_BUFFERS(Span(container.data(), container.size())) {} template > constexpr Span(const Container &container) // SAFETY: `size()` is exactly the number of elements in the initializer // list, so accessing that many will be safe. : ANGLE_UNSAFE_BUFFERS(Span(container.data(), container.size())) {} constexpr Span(const Span &other) noexcept = default; // Conversions from spans of compatible types: this allows a Span to be // seamlessly used as a Span, but not the other way around. template > constexpr Span(const Span &other) // SAFETY: `size()` is the number of elements that can be safely accessed // at `data()`. : ANGLE_UNSAFE_BUFFERS(Span(other.data(), other.size())) { static_assert(Extent == dynamic_extent || Extent == M, "Assigning to fixed span from incompatible span"); } Span &operator=(const Span &other) noexcept = default; Span &operator=(Span &&other) noexcept = default; ~Span() noexcept = default; template bool operator==(const Span &other) const { return std::equal(begin(), end(), other.begin(), other.end()); } template bool operator!=(const Span &other) const { return !std::equal(begin(), end(), other.begin(), other.end()); } // [span.sub], span subviews template constexpr Span first() const { // TODO(tsepez): The following assert isn't yet good enough to replace // the runtime check since we are still allowing unchecked conversions // to arbitrary non-dynamic_extent spans. static_assert(Extent == dynamic_extent || Count <= Extent); CHECK(Count <= size_); // SAFETY: CHECK() on line above. return ANGLE_UNSAFE_BUFFERS(Span(data(), Count)); } constexpr Span first(size_t count) const { CHECK(count <= size_); // SAFETY: CHECK() on line above. return ANGLE_UNSAFE_BUFFERS(Span(static_cast(data_), count)); } template constexpr Span last() const { // TODO(tsepez): The following assert isn't yet good enough to replace // the runtime check since we are still allowing unchecked conversions // to arbitrary non-dynamic_extent spans. static_assert(Extent == dynamic_extent || Count <= Extent); CHECK(Count <= size_); // SAFETY: CHECK() on line above. return ANGLE_UNSAFE_BUFFERS(Span(data() + (size_ - Count), Count)); } constexpr Span last(size_t count) const { CHECK(count <= size_); // SAFETY: CHECK() on line above. return ANGLE_UNSAFE_BUFFERS(Span(static_cast(data_) + (size_ - count), count)); } template constexpr Span subspan() const { // TODO(tsepez): The following check isn't yet good enough to replace // the runtime check since we are still allowing unchecked conversions // to arbitrary non-dynamic_extent spans. static_assert(Extent == dynamic_extent || Count == dynamic_extent || Offset + Count <= Extent); return subspan(Offset, Count); } constexpr Span subspan(size_t pos, size_t count = dynamic_extent) const { CHECK(pos <= size_); CHECK(count == dynamic_extent || count <= size_ - pos); // SAFETY: CHECK()s on lines above. return ANGLE_UNSAFE_BUFFERS( Span(static_cast(data_) + pos, count == dynamic_extent ? size_ - pos : count)); } // [span.obs], span observers constexpr size_t size() const noexcept { return size_; } constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); } constexpr bool empty() const noexcept { return size_ == 0; } // [span.elem], span element access T &operator[](size_t index) const noexcept { CHECK(index < size_); return ANGLE_UNSAFE_BUFFERS(static_cast(data_)[index]); } constexpr T &front() const noexcept { CHECK(!empty()); return *data(); } constexpr T &back() const noexcept { CHECK(!empty()); return ANGLE_UNSAFE_BUFFERS(*(data() + size() - 1)); } constexpr T *data() const noexcept { return static_cast(data_); } // [span.iter], span iterator support constexpr iterator begin() const noexcept { return static_cast(data_); } constexpr iterator end() const noexcept { return ANGLE_UNSAFE_BUFFERS(begin() + 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 const_reverse_iterator(cend()); } constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } private: InternalPtr data_ = nullptr; size_t size_ = 0; }; // Deduction guides. template Span(T (&)[N]) -> Span; template Span(const T (&)[N]) -> Span; template Span(std::array &) -> Span; template Span(const std::array &) -> Span; template ().data())>>> Span(Container &&) -> Span().data())>>; // [span.objectrep], views of object representation template Span as_bytes(Span s) noexcept { // SAFETY: from size_bytes() method. return ANGLE_UNSAFE_BUFFERS( Span(reinterpret_cast(s.data()), s.size_bytes())); } template ::value>::type> Span as_writable_bytes(Span s) noexcept { // SAFETY: from size_bytes() method. return ANGLE_UNSAFE_BUFFERS( Span(reinterpret_cast(s.data()), s.size_bytes())); } template Span as_chars(Span s) noexcept { // SAFETY: from size_bytes() method. return ANGLE_UNSAFE_BUFFERS( Span(reinterpret_cast(s.data()), s.size_bytes())); } template ::value>::type> Span as_writable_chars(Span s) noexcept { // SAFETY: from size_bytes() method. return ANGLE_UNSAFE_BUFFERS(Span(reinterpret_cast(s.data()), s.size_bytes())); } // `span_from_ref` converts a reference to T into a span of length 1. This is a // non-std helper that is inspired by the `std::slice::from_ref()` function from // Rust. template static constexpr Span span_from_ref(T &single_object) noexcept { // SAFETY: single object passed by reference. return ANGLE_UNSAFE_BUFFERS(Span(&single_object, 1u)); } // `byte_span_from_ref` converts a reference to T into a span of uint8_t of // length sizeof(T). This is a non-std helper that is a sugar for // `as_writable_bytes(span_from_ref(x))`. template static constexpr Span byte_span_from_ref(const T &single_object) noexcept { return as_bytes(span_from_ref(single_object)); } template static constexpr Span byte_span_from_ref(T &single_object) noexcept { return as_writable_bytes(span_from_ref(single_object)); } // Convenience function for converting an object which is itself convertible // to span into a span of bytes (i.e. span of const uint8_t). Typically used // to convert std::string or string-objects holding chars, or std::vector // or vector-like objects holding other scalar types, prior to passing them // into an API that requires byte spans. template Span as_byte_span(const T &arg) { return as_bytes(Span(arg)); } template Span as_byte_span(T &&arg) { return as_bytes(Span(arg)); } // Convenience function for converting an object which is itself convertible // to span into a span of mutable bytes (i.e. span of uint8_t). Typically used // to convert std::string or string-objects holding chars, or std::vector // or vector-like objects holding other scalar types, prior to passing them // into an API that requires mutable byte spans. template constexpr Span as_writable_byte_span(T &&arg) { return as_writable_bytes(Span(arg)); } } // namespace angle #endif // COMMON_SPAN_H_