pw_span: Begin transitioning to std::span - Provide an implementation of std::span accessible from the <span> header. - Add tests to confirm that std::span and pw::span implicitly convert to one another. Change-Id: I02e8ffc289975810b51ef32ffbc3b59a6e037caf Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12760 Commit-Queue: Wyatt Hepler <hepler@google.com> Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_span/BUILD b/pw_span/BUILD index 47129f2..528106a 100644 --- a/pw_span/BUILD +++ b/pw_span/BUILD
@@ -24,7 +24,11 @@ pw_cc_library( name = "pw_span", - hdrs = ["public/pw_span/span.h"], + srcs = ["public/pw_span/internal/span.h"], + hdrs = [ + "public/pw_span/span.h", + "public_overrides/span", + ], includes = ["public"], deps = ["//pw_polyfill"], ) @@ -37,3 +41,12 @@ "//pw_unit_test", ], ) + +pw_cc_test( + name = "pw_span_test", + srcs = ["pw_span_test.cc"], + deps = [ + ":pw_span", + "//pw_unit_test", + ], +)
diff --git a/pw_span/BUILD.gn b/pw_span/BUILD.gn index 737f6f5..cd2c881 100644 --- a/pw_span/BUILD.gn +++ b/pw_span/BUILD.gn
@@ -19,18 +19,27 @@ import("$dir_pw_docgen/docs.gni") import("$dir_pw_unit_test/test.gni") config("default_config") { - include_dirs = [ "public" ] + include_dirs = [ + "public", + "public_overrides", + ] } pw_source_set("pw_span") { public_configs = [ ":default_config" ] public_deps = [ "$dir_pw_polyfill" ] - public = [ "public/pw_span/span.h" ] - sources = public + public = [ + "public/pw_span/span.h", + "public_overrides/span", + ] + sources = [ "public/pw_span/internal/span.h" ] } pw_test_group("tests") { - tests = [ ":test" ] + tests = [ + ":test", + ":pw_span_test", + ] } pw_test("test") { @@ -38,6 +47,11 @@ sources = [ "span_test.cc" ] } +pw_test("pw_span_test") { + deps = [ ":pw_span" ] + sources = [ "pw_span_test.cc" ] +} + pw_doc_group("docs") { sources = [ "docs.rst" ] }
diff --git a/pw_span/CMakeLists.txt b/pw_span/CMakeLists.txt index 4b848e2..65644a1 100644 --- a/pw_span/CMakeLists.txt +++ b/pw_span/CMakeLists.txt
@@ -13,3 +13,4 @@ # the License. pw_auto_add_simple_module(pw_span PUBLIC_DEPS pw_polyfill) +target_include_directories(pw_span PUBLIC public_overrides)
diff --git a/pw_span/docs.rst b/pw_span/docs.rst index 9fff2f3..b44ba1e 100644 --- a/pw_span/docs.rst +++ b/pw_span/docs.rst
@@ -9,11 +9,30 @@ ------- The ``pw_span`` module provides an implementation of C++20's `std::span <https://en.cppreference.com/w/cpp/container/span>`_, which is a -non-owning view of an array of values. The intent is for ``pw::span``'s -interface to exactly match ``std::span``. +non-owning view of an array of values. The intent is for this implementation of +``std::span`` is to exactly match the C++20 standard. -``pw::span`` is a convenient abstraction that wraps a pointer and a size. -``pw::span`` is especially useful in APIs. Spans support implicit conversions +The only header provided by the ``pw_span`` namespace is ``<span>``. It is +included as if it were coming from the C++ Standard Library. If the C++ library +provides ``<span>``, the library's version of ``std::span`` is used in place of +``pw_span``'s. + +``pw_span`` requires two include paths -- ``public/`` and ``public_overrides/``. +The internal implementation header is in ``public/``, and the ``<span>`` header +that mimics the C++ Standard Library is in ``public_overrides/``. + +.. warning:: + + Currently, there is a ``pw_span/span.h`` header that provides a ``pw::span`` + class. ``pw::span`` is deprecated. Do NOT use it in new code. Instead, include + ``<span>`` and use ``std::span``. ``pw::span`` will be removed as soon as + projects have migrated to ``std::span``. + + +Using std::span +=============== +``std::span`` is a convenient abstraction that wraps a pointer and a size. +``std::span`` is especially useful in APIs. Spans support implicit conversions from C arrays, ``std::array``, or any STL-style container, such as ``std::string_view``. @@ -30,33 +49,35 @@ ProcessBuffer(data_pointer, data_size); } -Pointer and size arguments can be replaced with a ``pw::span``: +Pointer and size arguments can be replaced with a ``std::span``: .. code-block:: cpp - // With pw::span, the buffer is passed as a single argument. - bool ProcessBuffer(const pw::span<uint8_t>& buffer); + #include <span> + + // With std::span, the buffer is passed as a single argument. + bool ProcessBuffer(std::span<uint8_t> buffer); bool DoStuff() { ProcessBuffer(c_array); ProcessBuffer(array_object); - ProcessBuffer(pw::span(data_pointer, data_size)); + ProcessBuffer(std::span(data_pointer, data_size)); } .. tip:: - Use ``pw::span<std::byte>`` or ``pw::span<const std::byte>`` to represent - spans of binary data. Use ``pw::as_bytes`` or ``pw::as_writeable_bytes`` + Use ``std::span<std::byte>`` or ``std::span<const std::byte>`` to represent + spans of binary data. Use ``std::as_bytes`` or ``std::as_writeable_bytes`` to convert any span to a byte span. .. code-block:: cpp - void ProcessData(pw::span<const std::byte> data); + void ProcessData(std::span<const std::byte> data); void DoStuff() { std::array<AnyType, 7> data = { ... }; - ProcessData(pw::as_bytes(pw::span(data))); + ProcessData(std::as_bytes(std::span(data))); } Compatibility ============= -C++17 +Works with C++11, but some features require C++17.
diff --git a/pw_span/public/pw_span/internal/span.h b/pw_span/public/pw_span/internal/span.h new file mode 100644 index 0000000..f3ac9be --- /dev/null +++ b/pw_span/public/pw_span/internal/span.h
@@ -0,0 +1,471 @@ +// Copyright 2020 The Pigweed 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. + +// std::span is a stand-in for C++20's std::span. Do NOT include this header +// directly; instead, include it as <span>. +// +// A span is a non-owning array view class. It refers to an external array by +// storing a pointer and length. Unlike std::array, the size does not have to be +// a template parameter, so this class can be used to without stating its size. +// +// This file a modified version of base::span from Chromium: +// https://chromium.googlesource.com/chromium/src/+/d93ae920e4309682deb9352a4637cfc2941c1d1f/base/containers/span.h +// +// In order to minimize changes from the original, this file does NOT fully +// adhere to Pigweed's style guide. +// +// A few changes were made to the Chromium version of span. These include: +// - Use std::data and std::size instead of base::* versions. +// - Rename base namespace to std. +// - Rename internal namespace to pw_span_internal. +// - Remove uses of checked_iterators.h and CHECK. +// - Replace make_span functions with C++17 class template deduction guides. +// - Use std::byte instead of uint8_t for compatibility with std::span. +// +#pragma once + +#include <algorithm> +#include <array> +#include <cstddef> +#include <iterator> +#include <limits> +#include <type_traits> +#include <utility> + +#include "pw_polyfill/language_features.h" + +// Pigweed: Disable the asserts from Chromium for now. +#define _PW_SPAN_ASSERT(arg) + +namespace std { + +// [views.constants] +constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max(); + +template <typename T, size_t Extent = dynamic_extent> +class span; + +namespace pw_span_internal { + +template <typename T> +struct ExtentImpl : std::integral_constant<size_t, dynamic_extent> {}; + +template <typename T, size_t N> +struct ExtentImpl<T[N]> : std::integral_constant<size_t, N> {}; + +template <typename T, size_t N> +struct ExtentImpl<std::array<T, N>> : std::integral_constant<size_t, N> {}; + +template <typename T, size_t N> +struct ExtentImpl<std::span<T, N>> : std::integral_constant<size_t, N> {}; + +template <typename T> +using Extent = ExtentImpl<std::remove_cv_t<std::remove_reference_t<T>>>; + +template <typename T> +struct IsSpanImpl : std::false_type {}; + +template <typename T, size_t Extent> +struct IsSpanImpl<span<T, Extent>> : std::true_type {}; + +template <typename T> +using IsSpan = IsSpanImpl<std::decay_t<T>>; + +template <typename T> +struct IsStdArrayImpl : std::false_type {}; + +template <typename T, size_t N> +struct IsStdArrayImpl<std::array<T, N>> : std::true_type {}; + +template <typename T> +using IsStdArray = IsStdArrayImpl<std::decay_t<T>>; + +template <typename T> +using IsCArray = std::is_array<std::remove_reference_t<T>>; + +template <typename From, typename To> +using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>; + +template <typename Container, typename T> +using ContainerHasConvertibleData = IsLegalDataConversion< + std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>, + T>; + +template <typename Container> +using ContainerHasIntegralSize = + std::is_integral<decltype(std::size(std::declval<Container>()))>; + +template <typename From, size_t FromExtent, typename To, size_t ToExtent> +using EnableIfLegalSpanConversion = + std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) && + IsLegalDataConversion<From, To>::value>; + +// SFINAE check if Array can be converted to a span<T>. +template <typename Array, typename T, size_t Extent> +using EnableIfSpanCompatibleArray = + std::enable_if_t<(Extent == dynamic_extent || + Extent == pw_span_internal::Extent<Array>::value) && + ContainerHasConvertibleData<Array, T>::value>; + +// SFINAE check if Container can be converted to a span<T>. +template <typename Container, typename T> +using IsSpanCompatibleContainer = + std::conditional_t<!IsSpan<Container>::value && + !IsStdArray<Container>::value && + !IsCArray<Container>::value && + ContainerHasConvertibleData<Container, T>::value && + ContainerHasIntegralSize<Container>::value, + std::true_type, + std::false_type>; + +template <typename Container, typename T> +using EnableIfSpanCompatibleContainer = + std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value>; + +template <typename Container, typename T, size_t Extent> +using EnableIfSpanCompatibleContainerAndSpanIsDynamic = + std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value && + Extent == dynamic_extent>; + +// A helper template for storing the size of a span. Spans with static extents +// don't require additional storage, since the extent itself is specified in the +// template parameter. +template <size_t Extent> +class ExtentStorage { + public: + constexpr explicit ExtentStorage(size_t /* size */) noexcept {} + constexpr size_t size() const noexcept { return Extent; } +}; + +// Specialization of ExtentStorage for dynamic extents, which do require +// explicit storage for the size. +template <> +struct ExtentStorage<dynamic_extent> { + constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {} + constexpr size_t size() const noexcept { return size_; } + + private: + size_t size_; +}; + +} // namespace pw_span_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. +// +// span is somewhat analogous to StringPiece, but with arbitrary element types, +// allowing mutation if T is non-const. +// +// span is implicitly convertible from C++ arrays, as well as most [1] +// container-like types that provide a data() and size() method (such as +// std::vector<T>). A mutable span<T> can also be implicitly converted to an +// immutable span<const T>. +// +// Consider using a span for functions that take a data pointer and size +// parameter: it allows the function to still act on an array-like type, while +// allowing the caller code to be a bit more concise. +// +// For read-only data access pass a span<const T>: the caller can supply either +// a span<const T> or a span<T>, while the callee will have a read-only view. +// For read-write access a mutable span<T> is required. +// +// Without span: +// Read-Only: +// // std::string HexEncode(const uint8_t* data, size_t size); +// std::vector<uint8_t> data_buffer = GenerateData(); +// std::string r = HexEncode(data_buffer.data(), data_buffer.size()); +// +// Mutable: +// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...); +// char str_buffer[100]; +// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14); +// +// With span: +// Read-Only: +// // std::string HexEncode(std::span<const uint8_t> data); +// std::vector<uint8_t> data_buffer = GenerateData(); +// std::string r = HexEncode(data_buffer); +// +// Mutable: +// // ssize_t SafeSNPrintf(std::span<char>, const char* fmt, Args...); +// char str_buffer[100]; +// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14); +// +// Spans with "const" and pointers +// ------------------------------- +// +// Const and pointers can get confusing. Here are vectors of pointers and their +// corresponding spans: +// +// const std::vector<int*> => std::span<int* const> +// std::vector<const int*> => std::span<const int*> +// const std::vector<const int*> => std::span<const int* const> +// +// Differences from the C++20 draft +// -------------------------------- +// +// http://eel.is/c++draft/views contains the latest C++20 draft of std::span. +// Chromium tries to follow the draft as close as possible. Differences between +// the draft and the implementation are documented in subsections below. +// +// Differences from [span.cons]: +// - Constructing a static span (i.e. Extent != dynamic_extent) from a dynamic +// sized container (e.g. std::vector) requires an explicit conversion (in the +// C++20 draft this is simply UB) +// +// Furthermore, all constructors and methods are marked noexcept due to the lack +// of exceptions in Chromium. + +// [span], class template span +template <typename T, size_t Extent> +class span : public pw_span_internal::ExtentStorage<Extent> { + private: + using ExtentStorage = pw_span_internal::ExtentStorage<Extent>; + + 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 reference = T&; + using iterator = T*; + using reverse_iterator = std::reverse_iterator<iterator>; + static constexpr size_t extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + constexpr span() noexcept : ExtentStorage(0), data_(nullptr) { + static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent"); + } + + constexpr span(T* data, size_t size) noexcept + : ExtentStorage(size), data_(data) { + _PW_SPAN_ASSERT(Extent == dynamic_extent || Extent == size); + } + + // Artificially templatized to break ambiguity for span(ptr, 0). + template <typename = void> + constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + _PW_SPAN_ASSERT(begin <= end); + } + + template < + size_t N, + typename = + pw_span_internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>> + constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {} + + template <typename U, + size_t N, + typename = pw_span_internal:: + EnableIfSpanCompatibleArray<std::array<U, N>&, T, Extent>> + constexpr span(std::array<U, N>& array) noexcept + : span(std::data(array), N) {} + + template <typename U, + size_t N, + typename = pw_span_internal:: + EnableIfSpanCompatibleArray<const std::array<U, N>&, T, Extent>> + constexpr span(const std::array<U, N>& array) noexcept + : span(std::data(array), N) {} + + // Conversion from a container that has compatible std::data() and integral + // std::size(). + template <typename Container, + typename = pw_span_internal:: + EnableIfSpanCompatibleContainerAndSpanIsDynamic<Container&, + T, + Extent>> + constexpr span(Container& container) noexcept + : span(std::data(container), std::size(container)) {} + + template < + typename Container, + typename = pw_span_internal:: + EnableIfSpanCompatibleContainerAndSpanIsDynamic<const Container&, + T, + Extent>> + constexpr span(const Container& container) noexcept + : span(std::data(container), std::size(container)) {} + + constexpr span(const span& other) noexcept = default; + + // Conversions from spans of compatible types and extents: this allows a + // span<T> to be seamlessly used as a span<const T>, but not the other way + // around. If extent is not dynamic, OtherExtent has to be equal to Extent. + template <typename U, + size_t OtherExtent, + typename = pw_span_internal:: + EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>> + constexpr span(const span<U, OtherExtent>& other) + : span(other.data(), other.size()) {} + + PW_CONSTEXPR_FUNCTION span& operator=(const span& other) noexcept = default; + ~span() noexcept = default; + + // [span.sub], span subviews + template <size_t Count> + constexpr span<T, Count> first() const noexcept { + static_assert(Count <= Extent, "Count must not exceed Extent"); + _PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size()); + return {data(), Count}; + } + + template <size_t Count> + constexpr span<T, Count> last() const noexcept { + static_assert(Count <= Extent, "Count must not exceed Extent"); + _PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size()); + return {data() + (size() - Count), Count}; + } + + template <size_t Offset, size_t Count = dynamic_extent> + constexpr span<T, + (Count != dynamic_extent + ? Count + : (Extent != dynamic_extent ? Extent - Offset + : dynamic_extent))> + subspan() const noexcept { + static_assert(Offset <= Extent, "Offset must not exceed Extent"); + static_assert(Count == dynamic_extent || Count <= Extent - Offset, + "Count must not exceed Extent - Offset"); + _PW_SPAN_ASSERT(Extent != dynamic_extent || Offset <= size()); + _PW_SPAN_ASSERT(Extent != dynamic_extent || Count == dynamic_extent || + Count <= size() - Offset); + return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset}; + } + + constexpr span<T, dynamic_extent> first(size_t count) const noexcept { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + _PW_SPAN_ASSERT(count <= size()); + return {data(), count}; + } + + constexpr span<T, dynamic_extent> last(size_t count) const noexcept { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + _PW_SPAN_ASSERT(count <= size()); + return {data() + (size() - count), count}; + } + + constexpr span<T, dynamic_extent> subspan( + size_t offset, size_t count = dynamic_extent) const noexcept { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + _PW_SPAN_ASSERT(offset <= size()); + _PW_SPAN_ASSERT(count == dynamic_extent || count <= size() - offset); + return {data() + offset, count != dynamic_extent ? count : size() - offset}; + } + + // [span.obs], span observers + constexpr size_t size() const noexcept { return ExtentStorage::size(); } + constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); } + [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } + + // [span.elem], span element access + constexpr T& operator[](size_t idx) const noexcept { + // Note: CHECK_LT is not constexpr, hence regular CHECK must be used. + _PW_SPAN_ASSERT(idx < size()); + return *(data() + idx); + } + + constexpr T& front() const noexcept { + static_assert(Extent == dynamic_extent || Extent > 0, + "Extent must not be 0"); + _PW_SPAN_ASSERT(Extent != dynamic_extent || !empty()); + return *data(); + } + + constexpr T& back() const noexcept { + static_assert(Extent == dynamic_extent || Extent > 0, + "Extent must not be 0"); + _PW_SPAN_ASSERT(Extent != dynamic_extent || !empty()); + return *(data() + size() - 1); + } + + constexpr T* data() const noexcept { return data_; } + + // [span.iter], span iterator support + constexpr iterator begin() const noexcept { return data_; } + constexpr iterator end() const noexcept { return data_ + size(); } + + constexpr reverse_iterator rbegin() const noexcept { + return reverse_iterator(end()); + } + constexpr reverse_iterator rend() const noexcept { + return reverse_iterator(begin()); + } + + private: + T* data_; +}; + +// span<T, Extent>::extent can not be declared inline prior to C++17, hence this +// definition is required. +// template <class T, size_t Extent> +// constexpr size_t span<T, Extent>::extent; + +// [span.objectrep], views of object representation +template <typename T, size_t X> +span<const std::byte, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)> +as_bytes(span<T, X> s) noexcept { + return {reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()}; +} + +template <typename T, + size_t X, + typename = std::enable_if_t<!std::is_const<T>::value>> +span<std::byte, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)> +as_writable_bytes(span<T, X> s) noexcept { + return {reinterpret_cast<std::byte*>(s.data()), s.size_bytes()}; +} + +// Type-deducing helpers for constructing a span. +// Pigweed: Instead of a make_span function, provide the deduction guides +// specified in the C++20 standard. +#ifdef __cpp_deduction_guides + +template <class T, std::size_t N> +span(T (&)[N]) -> span<T, N>; + +template <class T, std::size_t N> +span(std::array<T, N>&) -> span<T, N>; + +template <class T, std::size_t N> +span(const std::array<T, N>&) -> span<const T, N>; + +namespace pw_span_internal { + +// Containers can be mutable or const and have mutable or const members. Check +// the type of the accessed elements to determine which type of span should be +// created (e.g. span<char> or span<const char>). +template <typename T> +using ValueType = std::remove_reference_t<decltype(std::declval<T>()[0])>; + +} // namespace pw_span_internal + +// This diverges a little from the standard, which uses std::ranges. +template <class Container> +span(Container&) -> span<pw_span_internal::ValueType<Container>>; + +template <class Container> +span(const Container&) -> span<pw_span_internal::ValueType<const Container>>; + +#endif // __cpp_deduction_guides + +} // namespace std + +#undef _PW_SPAN_ASSERT
diff --git a/pw_span/public/pw_span/span.h b/pw_span/public/pw_span/span.h index 8aa270d..98e7acf 100644 --- a/pw_span/public/pw_span/span.h +++ b/pw_span/public/pw_span/span.h
@@ -12,28 +12,13 @@ // License for the specific language governing permissions and limitations under // the License. -// pw::span is a stand-in for C++20's std::span. +// pw::span is DEPRECATED. Instead of using pw::span from pw_span/span.h, use +// std::span from <span>. pw_span/span.h and pw::span will be removed once code +// has been migrated to std::span. // -// A span is a non-owning array view class. It refers to an external array by -// storing a pointer and length. Unlike std::array, the size does not have to be -// a template parameter, so this class can be used to without stating its size. -// -// This file a modified version of base::span from Chromium: -// https://chromium.googlesource.com/chromium/src/+/d93ae920e4309682deb9352a4637cfc2941c1d1f/base/containers/span.h -// -// In order to minimize changes from the original, this file does NOT fully -// adhere to Pigweed's style guide. -// -// A few changes were made to the Chromium version of span. These include: -// - Use std::data and std::size instead of base::* versions. -// - Rename base namespace to pw. -// - Rename internal namespace to span_internal. -// - Remove uses of checked_iterators.h and CHECK. -// - Replace make_span functions with C++17 class template deduction guides. -// - Use std::byte instead of uint8_t for compatibility with std::span. -// -// Eventually, when C++20 is widely supported, this class will be replaced with -// std::span. +// This code is a copy of the std::span code in pw_span/internal/span.h. +// pw::span cannot be an alias of std::span because class template argument +// deduction does not work with aliases. #pragma once #include <algorithm> @@ -234,7 +219,7 @@ // [span], class template span template <typename T, size_t Extent> -class span : public span_internal::ExtentStorage<Extent> { +class /* [[deprecated]] */ span : public span_internal::ExtentStorage<Extent> { private: using ExtentStorage = span_internal::ExtentStorage<Extent>;
diff --git a/pw_span/public_overrides/span b/pw_span/public_overrides/span new file mode 100644 index 0000000..7991e33 --- /dev/null +++ b/pw_span/public_overrides/span
@@ -0,0 +1,28 @@ +// Copyright 2020 The Pigweed 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. +#pragma once + +#if __has_include(<version>) +#include <version> +#endif // __has_include(<version>) + +#ifdef __cpp_lib_span // C++ library feature test macro, provided by <version>. + +#include_next <span> + +#else + +#include "pw_span/internal/span.h" + +#endif // __cpp_lib_span
diff --git a/pw_span/pw_span_test.cc b/pw_span/pw_span_test.cc new file mode 100644 index 0000000..00a1adf --- /dev/null +++ b/pw_span/pw_span_test.cc
@@ -0,0 +1,1681 @@ +// Copyright 2020 The Pigweed 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. + +// This file is a copy of the tests for std::span. It tests pw::span, which is a +// temporary copy of std::span for compatibility purposes. The pw::span class, +// and this test, will be deleted as soon as projects fully migrate to +// std::span. +#include <algorithm> +#include <cstdint> +#include <memory> +#include <span> +#include <string> +#include <type_traits> +#include <vector> + +#include "gtest/gtest.h" +#include "pw_span/span.h" + +// Pigweed: gMock matchers are not yet supported. +#if 0 +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::Pointwise; +#endif // 0 + +namespace pw { + +namespace { + +// constexpr implementation of std::equal's 4 argument overload. +template <class InputIterator1, class InputIterator2> +constexpr bool constexpr_equal(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2) { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) { + if (*first1 != *first2) + return false; + } + + return first1 == last1 && first2 == last2; +} + +} // namespace + +// TODO(hepler): Remove these tests after eliminating pw::span. +TEST(StdSpanCompatibility, ConstructFromPwSpanToStdSpan) { + char array[] = {'a', 'b', 'c', 'd', '\0'}; + + ::pw::span pw_span(array); + std::span std_span(pw_span); + + std_span[0] = '!'; + EXPECT_STREQ(std_span.data(), "!bcd"); + + EXPECT_EQ(pw_span.data(), std_span.data()); + EXPECT_EQ(pw_span.size(), std_span.size()); +} + +TEST(StdSpanCompatibility, ConstructFromStdSpanToPwSpan) { + char array[] = {'a', 'b', 'c', 'd', '\0'}; + + std::span std_span(array); + ::pw::span pw_span(std_span); + + pw_span[0] = '!'; + EXPECT_STREQ(pw_span.data(), "!bcd"); + + EXPECT_EQ(pw_span.data(), std_span.data()); + EXPECT_EQ(pw_span.size(), std_span.size()); +} + +TEST(StdSpanCompatibility, AssignFromPwSpanToStdSpan) { + char array[] = {'a', 'b', 'c', 'd', '\0'}; + + auto pw_span = ::pw::span(array); + std::span std_span = pw_span; + + std_span[0] = '!'; + EXPECT_STREQ(std_span.data(), "!bcd"); + + EXPECT_EQ(pw_span.data(), std_span.data()); + EXPECT_EQ(pw_span.size(), std_span.size()); +} + +TEST(StdSpanCompatibility, AssignFromStdSpanToPwSpan) { + char array[] = {'a', 'b', 'c', 'd', '\0'}; + + auto std_span = std::span(array); + ::pw::span pw_span = std_span; + + pw_span[0] = '!'; + EXPECT_STREQ(pw_span.data(), "!bcd"); + + EXPECT_EQ(pw_span.data(), std_span.data()); + EXPECT_EQ(pw_span.size(), std_span.size()); +} + +TEST(SpanTest, DeductionGuides_MutableArray) { + char array[] = {'a', 'b', 'c', 'd', '\0'}; + + auto the_span = span(array); + static_assert(the_span.extent == 5u); + static_assert(the_span.size() == 5u); + + the_span[0] = '!'; + EXPECT_STREQ(the_span.data(), "!bcd"); +} + +TEST(SpanTest, DeductionGuides_ConstArray) { + static constexpr char array[] = {'a', 'b', 'c', 'd', '\0'}; + + constexpr auto the_span = span(array); + static_assert(the_span.extent == 5u); + static_assert(the_span.size() == 5u); + + EXPECT_STREQ(the_span.data(), "abcd"); +} + +TEST(SpanTest, DeductionGuides_MutableStdArray) { + std::array<char, 5> array{'a', 'b', 'c', 'd'}; + + auto the_span = span(array); + static_assert(the_span.extent == 5u); + static_assert(the_span.size() == 5u); + + the_span[0] = '?'; + EXPECT_STREQ(the_span.data(), "?bcd"); +} + +TEST(SpanTest, DeductionGuides_ConstStdArray) { + static constexpr std::array<char, 5> array{'a', 'b', 'c', 'd'}; + + constexpr auto the_span = span(array); + static_assert(the_span.extent == 5u); + static_assert(the_span.size() == 5u); + + EXPECT_STREQ(the_span.data(), "abcd"); +} + +TEST(SpanTest, DeductionGuides_MutableContainerWithConstElements) { + std::string_view string("Hello"); + auto the_span = span(string); + static_assert(the_span.extent == dynamic_extent); + + EXPECT_STREQ("Hello", the_span.data()); + EXPECT_EQ(5u, the_span.size()); +} + +TEST(SpanTest, DeductionGuides_MutableContainerWithMutableElements) { + std::string string("Hello"); + auto the_span = span(string); + static_assert(the_span.extent == dynamic_extent); + + EXPECT_EQ(5u, the_span.size()); + the_span[1] = 'a'; + EXPECT_STREQ(the_span.data(), string.data()); + EXPECT_STREQ("Hallo", the_span.data()); +} + +class MutableStringView { + public: + using element_type = char; + using value_type = char; + using size_type = size_t; + using difference_type = ptrdiff_t; + using pointer = char*; + using reference = char&; + using iterator = char*; + using const_iterator = const char*; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + MutableStringView(char* str) : data_(str, std::strlen(str)) {} + + char& operator[](size_type index) const { return data_[index]; } + pointer data() const { return data_.data(); } + size_type size() const { return data_.size(); } + iterator begin() const { return data_.begin(); } + iterator end() const { return data_.end(); } + + private: + span<char> data_; +}; + +TEST(SpanTest, DeductionGuides_ConstContainerWithMutableElements) { + char data[] = "54321"; + MutableStringView view(data); + + auto the_span = span(view); + static_assert(the_span.extent == dynamic_extent); + + EXPECT_EQ(5u, the_span.size()); + view[2] = '?'; + EXPECT_STREQ("54?21", the_span.data()); + EXPECT_STREQ("54?21", data); +} + +TEST(SpanTest, DeductionGuides_ConstContainerWithMutableValueType) { + const std::string string("Hello"); + auto the_span = span(string); + static_assert(the_span.extent == dynamic_extent); + + EXPECT_EQ(5u, the_span.size()); + EXPECT_STREQ("Hello", the_span.data()); +} + +TEST(SpanTest, DeductionGuides_ConstContainerWithConstElements) { + const std::string_view string("Hello"); + auto the_span = span(string); + static_assert(the_span.extent == dynamic_extent); + + EXPECT_EQ(5u, the_span.size()); + EXPECT_STREQ("Hello", the_span.data()); +} + +TEST(SpanTest, DeductionGuides_FromTemporary_ContainerWithConstElements) { + auto the_span = span(std::string_view("Hello")); + static_assert(the_span.extent == dynamic_extent); + + EXPECT_EQ(5u, the_span.size()); + EXPECT_STREQ("Hello", the_span.data()); +} + +TEST(SpanTest, DeductionGuides_FromReference) { + std::array<int, 5> array{1, 3, 5, 7, 9}; + std::array<int, 5>& array_ref = array; + + auto the_span = span(array_ref); + static_assert(the_span.extent == 5); + + for (unsigned i = 0; i < array.size(); ++i) { + ASSERT_EQ(array[i], the_span[i]); + } +} + +TEST(SpanTest, DeductionGuides_FromConstReference) { + std::string_view string = "yo!"; + const std::string_view& string_ref = string; + + auto the_span = span(string_ref); + static_assert(the_span.extent == dynamic_extent); + + EXPECT_EQ(string, the_span.data()); +} + +TEST(SpanTest, DefaultConstructor) { + span<int> dynamic_span; + EXPECT_EQ(nullptr, dynamic_span.data()); + EXPECT_EQ(0u, dynamic_span.size()); + + constexpr span<int, 0> static_span; + static_assert(nullptr == static_span.data(), ""); + static_assert(static_span.empty(), ""); +} + +TEST(SpanTest, ConstructFromDataAndSize) { + constexpr span<int> empty_span(nullptr, 0); + EXPECT_TRUE(empty_span.empty()); + EXPECT_EQ(nullptr, empty_span.data()); + + std::vector<int> vector = {1, 1, 2, 3, 5, 8}; + + span<int> dynamic_span(vector.data(), vector.size()); + EXPECT_EQ(vector.data(), dynamic_span.data()); + EXPECT_EQ(vector.size(), dynamic_span.size()); + + for (size_t i = 0; i < dynamic_span.size(); ++i) + EXPECT_EQ(vector[i], dynamic_span[i]); + + span<int, 6> static_span(vector.data(), vector.size()); + EXPECT_EQ(vector.data(), static_span.data()); + EXPECT_EQ(vector.size(), static_span.size()); + + for (size_t i = 0; i < static_span.size(); ++i) + EXPECT_EQ(vector[i], static_span[i]); +} + +TEST(SpanTest, ConstructFromPointerPair) { + constexpr span<int> empty_span(nullptr, nullptr); + EXPECT_TRUE(empty_span.empty()); + EXPECT_EQ(nullptr, empty_span.data()); + + std::vector<int> vector = {1, 1, 2, 3, 5, 8}; + + span<int> dynamic_span(vector.data(), vector.data() + vector.size() / 2); + EXPECT_EQ(vector.data(), dynamic_span.data()); + EXPECT_EQ(vector.size() / 2, dynamic_span.size()); + + for (size_t i = 0; i < dynamic_span.size(); ++i) + EXPECT_EQ(vector[i], dynamic_span[i]); + + span<int, 3> static_span(vector.data(), vector.data() + vector.size() / 2); + EXPECT_EQ(vector.data(), static_span.data()); + EXPECT_EQ(vector.size() / 2, static_span.size()); + + for (size_t i = 0; i < static_span.size(); ++i) + EXPECT_EQ(vector[i], static_span[i]); +} + +TEST(SpanTest, AllowedConversionsFromStdArray) { + // In the following assertions we use std::is_convertible_v<From, To>, which + // for non-void types is equivalent to checking whether the following + // expression is well-formed: + // + // T obj = std::declval<From>(); + // + // In particular we are checking whether From is implicitly convertible to To, + // which also implies that To is explicitly constructible from From. + static_assert( + std::is_convertible<std::array<int, 3>&, span<int>>::value, + "Error: l-value reference to std::array<int> should be convertible to " + "span<int> with dynamic extent."); + static_assert( + std::is_convertible<std::array<int, 3>&, span<int, 3>>::value, + "Error: l-value reference to std::array<int> should be convertible to " + "span<int> with the same static extent."); + static_assert( + std::is_convertible<std::array<int, 3>&, span<const int>>::value, + "Error: l-value reference to std::array<int> should be convertible to " + "span<const int> with dynamic extent."); + static_assert( + std::is_convertible<std::array<int, 3>&, span<const int, 3>>::value, + "Error: l-value reference to std::array<int> should be convertible to " + "span<const int> with the same static extent."); + static_assert( + std::is_convertible<const std::array<int, 3>&, span<const int>>::value, + "Error: const l-value reference to std::array<int> should be " + "convertible to span<const int> with dynamic extent."); + static_assert( + std::is_convertible<const std::array<int, 3>&, span<const int, 3>>::value, + "Error: const l-value reference to std::array<int> should be convertible " + "to span<const int> with the same static extent."); + static_assert( + std::is_convertible<std::array<const int, 3>&, span<const int>>::value, + "Error: l-value reference to std::array<const int> should be " + "convertible to span<const int> with dynamic extent."); + static_assert( + std::is_convertible<std::array<const int, 3>&, span<const int, 3>>::value, + "Error: l-value reference to std::array<const int> should be convertible " + "to span<const int> with the same static extent."); + static_assert( + std::is_convertible<const std::array<const int, 3>&, + span<const int>>::value, + "Error: const l-value reference to std::array<const int> should be " + "convertible to span<const int> with dynamic extent."); + static_assert( + std::is_convertible<const std::array<const int, 3>&, + span<const int, 3>>::value, + "Error: const l-value reference to std::array<const int> should be " + "convertible to span<const int> with the same static extent."); +} + +TEST(SpanTest, DisallowedConstructionsFromStdArray) { + // In the following assertions we use !std::is_constructible_v<T, Args>, which + // is equivalent to checking whether the following expression is malformed: + // + // T obj(std::declval<Args>()...); + // + // In particular we are checking that T is not explicitly constructible from + // Args, which also implies that T is not implicitly constructible from Args + // as well. + static_assert( + !std::is_constructible<span<int>, const std::array<int, 3>&>::value, + "Error: span<int> with dynamic extent should not be constructible " + "from const l-value reference to std::array<int>"); + + static_assert( + !std::is_constructible<span<int>, std::array<const int, 3>&>::value, + "Error: span<int> with dynamic extent should not be constructible " + "from l-value reference to std::array<const int>"); + + static_assert( + !std::is_constructible<span<int>, const std::array<const int, 3>&>::value, + "Error: span<int> with dynamic extent should not be constructible " + "const from l-value reference to std::array<const int>"); + + static_assert( + !std::is_constructible<span<int, 2>, std::array<int, 3>&>::value, + "Error: span<int> with static extent should not be constructible " + "from l-value reference to std::array<int> with different extent"); + + static_assert( + !std::is_constructible<span<int, 4>, std::array<int, 3>&>::value, + "Error: span<int> with dynamic extent should not be constructible " + "from l-value reference to std::array<int> with different extent"); + + static_assert( + !std::is_constructible<span<int>, std::array<bool, 3>&>::value, + "Error: span<int> with dynamic extent should not be constructible " + "from l-value reference to std::array<bool>"); +} + +TEST(SpanTest, ConstructFromConstexprArray) { + static constexpr int kArray[] = {5, 4, 3, 2, 1}; + + constexpr span<const int> dynamic_span(kArray); + static_assert(kArray == dynamic_span.data(), ""); + static_assert(std::size(kArray) == dynamic_span.size(), ""); + + static_assert(kArray[0] == dynamic_span[0], ""); + static_assert(kArray[1] == dynamic_span[1], ""); + static_assert(kArray[2] == dynamic_span[2], ""); + static_assert(kArray[3] == dynamic_span[3], ""); + static_assert(kArray[4] == dynamic_span[4], ""); + + constexpr span<const int, std::size(kArray)> static_span(kArray); + static_assert(kArray == static_span.data(), ""); + static_assert(std::size(kArray) == static_span.size(), ""); + + static_assert(kArray[0] == static_span[0], ""); + static_assert(kArray[1] == static_span[1], ""); + static_assert(kArray[2] == static_span[2], ""); + static_assert(kArray[3] == static_span[3], ""); + static_assert(kArray[4] == static_span[4], ""); +} + +TEST(SpanTest, ConstructFromArray) { + int array[] = {5, 4, 3, 2, 1}; + + span<const int> const_span(array); + EXPECT_EQ(array, const_span.data()); + EXPECT_EQ(std::size(array), const_span.size()); + for (size_t i = 0; i < const_span.size(); ++i) + EXPECT_EQ(array[i], const_span[i]); + + span<int> dynamic_span(array); + EXPECT_EQ(array, dynamic_span.data()); + EXPECT_EQ(std::size(array), dynamic_span.size()); + for (size_t i = 0; i < dynamic_span.size(); ++i) + EXPECT_EQ(array[i], dynamic_span[i]); + + span<int, std::size(array)> static_span(array); + EXPECT_EQ(array, static_span.data()); + EXPECT_EQ(std::size(array), static_span.size()); + for (size_t i = 0; i < static_span.size(); ++i) + EXPECT_EQ(array[i], static_span[i]); +} + +TEST(SpanTest, ConstructFromStdArray) { + // Note: Constructing a constexpr span from a constexpr std::array does not + // work prior to C++17 due to non-constexpr std::array::data. + std::array<int, 5> array = {{5, 4, 3, 2, 1}}; + + span<const int> const_span(array); + EXPECT_EQ(array.data(), const_span.data()); + EXPECT_EQ(array.size(), const_span.size()); + for (size_t i = 0; i < const_span.size(); ++i) + EXPECT_EQ(array[i], const_span[i]); + + span<int> dynamic_span(array); + EXPECT_EQ(array.data(), dynamic_span.data()); + EXPECT_EQ(array.size(), dynamic_span.size()); + for (size_t i = 0; i < dynamic_span.size(); ++i) + EXPECT_EQ(array[i], dynamic_span[i]); + + span<int, std::size(array)> static_span(array); + EXPECT_EQ(array.data(), static_span.data()); + EXPECT_EQ(array.size(), static_span.size()); + for (size_t i = 0; i < static_span.size(); ++i) + EXPECT_EQ(array[i], static_span[i]); +} + +TEST(SpanTest, ConstructFromInitializerList) { + std::initializer_list<int> il = {1, 1, 2, 3, 5, 8}; + + span<const int> const_span(il); + EXPECT_EQ(il.begin(), const_span.data()); + EXPECT_EQ(il.size(), const_span.size()); + + for (size_t i = 0; i < const_span.size(); ++i) + EXPECT_EQ(il.begin()[i], const_span[i]); + + span<const int, 6> static_span(il.begin(), il.end()); + EXPECT_EQ(il.begin(), static_span.data()); + EXPECT_EQ(il.size(), static_span.size()); + + for (size_t i = 0; i < static_span.size(); ++i) + EXPECT_EQ(il.begin()[i], static_span[i]); +} + +TEST(SpanTest, ConstructFromStdString) { + std::string str = "foobar"; + + span<const char> const_span(str); + EXPECT_EQ(str.data(), const_span.data()); + EXPECT_EQ(str.size(), const_span.size()); + + for (size_t i = 0; i < const_span.size(); ++i) + EXPECT_EQ(str[i], const_span[i]); + + span<char> dynamic_span(str); + EXPECT_EQ(str.data(), dynamic_span.data()); + EXPECT_EQ(str.size(), dynamic_span.size()); + + for (size_t i = 0; i < dynamic_span.size(); ++i) + EXPECT_EQ(str[i], dynamic_span[i]); + + span<char, 6> static_span(data(str), str.size()); + EXPECT_EQ(str.data(), static_span.data()); + EXPECT_EQ(str.size(), static_span.size()); + + for (size_t i = 0; i < static_span.size(); ++i) + EXPECT_EQ(str[i], static_span[i]); +} + +TEST(SpanTest, ConstructFromConstContainer) { + const std::vector<int> vector = {1, 1, 2, 3, 5, 8}; + + span<const int> const_span(vector); + EXPECT_EQ(vector.data(), const_span.data()); + EXPECT_EQ(vector.size(), const_span.size()); + + for (size_t i = 0; i < const_span.size(); ++i) + EXPECT_EQ(vector[i], const_span[i]); + + span<const int, 6> static_span(vector.data(), vector.size()); + EXPECT_EQ(vector.data(), static_span.data()); + EXPECT_EQ(vector.size(), static_span.size()); + + for (size_t i = 0; i < static_span.size(); ++i) + EXPECT_EQ(vector[i], static_span[i]); +} + +TEST(SpanTest, ConstructFromContainer) { + std::vector<int> vector = {1, 1, 2, 3, 5, 8}; + + span<const int> const_span(vector); + EXPECT_EQ(vector.data(), const_span.data()); + EXPECT_EQ(vector.size(), const_span.size()); + + for (size_t i = 0; i < const_span.size(); ++i) + EXPECT_EQ(vector[i], const_span[i]); + + span<int> dynamic_span(vector); + EXPECT_EQ(vector.data(), dynamic_span.data()); + EXPECT_EQ(vector.size(), dynamic_span.size()); + + for (size_t i = 0; i < dynamic_span.size(); ++i) + EXPECT_EQ(vector[i], dynamic_span[i]); + + span<int, 6> static_span(vector.data(), vector.size()); + EXPECT_EQ(vector.data(), static_span.data()); + EXPECT_EQ(vector.size(), static_span.size()); + + for (size_t i = 0; i < static_span.size(); ++i) + EXPECT_EQ(vector[i], static_span[i]); +} + +#if 0 + +// Pigweed: gMock matchers are not yet supported. +TEST(SpanTest, ConvertNonConstIntegralToConst) { + std::vector<int> vector = {1, 1, 2, 3, 5, 8}; + + span<int> int_span(vector.data(), vector.size()); + span<const int> const_span(int_span); + EXPECT_EQ(int_span.size(), const_span.size()); + + EXPECT_THAT(const_span, Pointwise(Eq(), int_span)); + + span<int, 6> static_int_span(vector.data(), vector.size()); + span<const int, 6> static_const_span(static_int_span); + EXPECT_THAT(static_const_span, Pointwise(Eq(), static_int_span)); +} + +// Pigweed: gMock matchers are not yet supported. +TEST(SpanTest, ConvertNonConstPointerToConst) { + auto a = std::make_unique<int>(11); + auto b = std::make_unique<int>(22); + auto c = std::make_unique<int>(33); + std::vector<int*> vector = {a.get(), b.get(), c.get()}; + + span<int*> non_const_pointer_span(vector); + EXPECT_THAT(non_const_pointer_span, Pointwise(Eq(), vector)); + span<int* const> const_pointer_span(non_const_pointer_span); + EXPECT_THAT(const_pointer_span, Pointwise(Eq(), non_const_pointer_span)); + // Note: no test for conversion from span<int> to span<const int*>, since that + // would imply a conversion from int** to const int**, which is unsafe. + // + // Note: no test for conversion from span<int*> to span<const int* const>, + // due to CWG Defect 330: + // http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#330 + + span<int*, 3> static_non_const_pointer_span(vector.data(), vector.size()); + EXPECT_THAT(static_non_const_pointer_span, Pointwise(Eq(), vector)); + span<int* const, 3> static_const_pointer_span(static_non_const_pointer_span); + EXPECT_THAT(static_const_pointer_span, + Pointwise(Eq(), static_non_const_pointer_span)); +} + +// Pigweed: This test does not work on platforms where int32_t is long int. +TEST(SpanTest, ConvertBetweenEquivalentTypes) { + std::vector<int32_t> vector = {2, 4, 8, 16, 32}; + + span<int32_t> int32_t_span(vector); + span<int> converted_span(int32_t_span); + EXPECT_EQ(int32_t_span.data(), converted_span.data()); + EXPECT_EQ(int32_t_span.size(), converted_span.size()); + + span<int32_t, 5> static_int32_t_span(vector.data(), vector.size()); + span<int, 5> static_converted_span(static_int32_t_span); + EXPECT_EQ(static_int32_t_span.data(), static_converted_span.data()); + EXPECT_EQ(static_int32_t_span.size(), static_converted_span.size()); +} + +#endif // 0 + +TEST(SpanTest, TemplatedFirst) { + static constexpr int array[] = {1, 2, 3}; + constexpr span<const int, 3> span(array); + + { + constexpr auto subspan = span.first<0>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(subspan.empty(), ""); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + constexpr auto subspan = span.first<1>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(1u == subspan.size(), ""); + static_assert(1u == decltype(subspan)::extent, ""); + static_assert(1 == subspan[0], ""); + } + + { + constexpr auto subspan = span.first<2>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(2u == subspan.size(), ""); + static_assert(2u == decltype(subspan)::extent, ""); + static_assert(1 == subspan[0], ""); + static_assert(2 == subspan[1], ""); + } + + { + constexpr auto subspan = span.first<3>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(3u == subspan.size(), ""); + static_assert(3u == decltype(subspan)::extent, ""); + static_assert(1 == subspan[0], ""); + static_assert(2 == subspan[1], ""); + static_assert(3 == subspan[2], ""); + } +} + +TEST(SpanTest, TemplatedLast) { + static constexpr int array[] = {1, 2, 3}; + constexpr span<const int, 3> span(array); + + { + constexpr auto subspan = span.last<0>(); + static_assert(span.data() + 3 == subspan.data(), ""); + static_assert(subspan.empty(), ""); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + constexpr auto subspan = span.last<1>(); + static_assert(span.data() + 2 == subspan.data(), ""); + static_assert(1u == subspan.size(), ""); + static_assert(1u == decltype(subspan)::extent, ""); + static_assert(3 == subspan[0], ""); + } + + { + constexpr auto subspan = span.last<2>(); + static_assert(span.data() + 1 == subspan.data(), ""); + static_assert(2u == subspan.size(), ""); + static_assert(2u == decltype(subspan)::extent, ""); + static_assert(2 == subspan[0], ""); + static_assert(3 == subspan[1], ""); + } + + { + constexpr auto subspan = span.last<3>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(3u == subspan.size(), ""); + static_assert(3u == decltype(subspan)::extent, ""); + static_assert(1 == subspan[0], ""); + static_assert(2 == subspan[1], ""); + static_assert(3 == subspan[2], ""); + } +} + +TEST(SpanTest, TemplatedSubspan) { + static constexpr int array[] = {1, 2, 3}; + constexpr span<const int, 3> span(array); + + { + constexpr auto subspan = span.subspan<0>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(3u == subspan.size(), ""); + static_assert(3u == decltype(subspan)::extent, ""); + static_assert(1 == subspan[0], ""); + static_assert(2 == subspan[1], ""); + static_assert(3 == subspan[2], ""); + } + + { + constexpr auto subspan = span.subspan<1>(); + static_assert(span.data() + 1 == subspan.data(), ""); + static_assert(2u == subspan.size(), ""); + static_assert(2u == decltype(subspan)::extent, ""); + static_assert(2 == subspan[0], ""); + static_assert(3 == subspan[1], ""); + } + + { + constexpr auto subspan = span.subspan<2>(); + static_assert(span.data() + 2 == subspan.data(), ""); + static_assert(1u == subspan.size(), ""); + static_assert(1u == decltype(subspan)::extent, ""); + static_assert(3 == subspan[0], ""); + } + + { + constexpr auto subspan = span.subspan<3>(); + static_assert(span.data() + 3 == subspan.data(), ""); + static_assert(subspan.empty(), ""); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + constexpr auto subspan = span.subspan<0, 0>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(subspan.empty(), ""); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + constexpr auto subspan = span.subspan<1, 0>(); + static_assert(span.data() + 1 == subspan.data(), ""); + static_assert(subspan.empty(), ""); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + constexpr auto subspan = span.subspan<2, 0>(); + static_assert(span.data() + 2 == subspan.data(), ""); + static_assert(subspan.empty(), ""); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + constexpr auto subspan = span.subspan<0, 1>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(1u == subspan.size(), ""); + static_assert(1u == decltype(subspan)::extent, ""); + static_assert(1 == subspan[0], ""); + } + + { + constexpr auto subspan = span.subspan<1, 1>(); + static_assert(span.data() + 1 == subspan.data(), ""); + static_assert(1u == subspan.size(), ""); + static_assert(1u == decltype(subspan)::extent, ""); + static_assert(2 == subspan[0], ""); + } + + { + constexpr auto subspan = span.subspan<2, 1>(); + static_assert(span.data() + 2 == subspan.data(), ""); + static_assert(1u == subspan.size(), ""); + static_assert(1u == decltype(subspan)::extent, ""); + static_assert(3 == subspan[0], ""); + } + + { + constexpr auto subspan = span.subspan<0, 2>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(2u == subspan.size(), ""); + static_assert(2u == decltype(subspan)::extent, ""); + static_assert(1 == subspan[0], ""); + static_assert(2 == subspan[1], ""); + } + + { + constexpr auto subspan = span.subspan<1, 2>(); + static_assert(span.data() + 1 == subspan.data(), ""); + static_assert(2u == subspan.size(), ""); + static_assert(2u == decltype(subspan)::extent, ""); + static_assert(2 == subspan[0], ""); + static_assert(3 == subspan[1], ""); + } + + { + constexpr auto subspan = span.subspan<0, 3>(); + static_assert(span.data() == subspan.data(), ""); + static_assert(3u == subspan.size(), ""); + static_assert(3u == decltype(subspan)::extent, ""); + static_assert(1 == subspan[0], ""); + static_assert(2 == subspan[1], ""); + static_assert(3 == subspan[2], ""); + } +} + +TEST(SpanTest, SubscriptedBeginIterator) { + int array[] = {1, 2, 3}; + span<const int> const_span(array); + for (size_t i = 0; i < const_span.size(); ++i) + EXPECT_EQ(array[i], const_span.begin()[i]); + + span<int> mutable_span(array); + for (size_t i = 0; i < mutable_span.size(); ++i) + EXPECT_EQ(array[i], mutable_span.begin()[i]); +} + +TEST(SpanTest, TemplatedFirstOnDynamicSpan) { + int array[] = {1, 2, 3}; + span<const int> span(array); + + { + auto subspan = span.first<0>(); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(0u, subspan.size()); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + auto subspan = span.first<1>(); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(1u, subspan.size()); + static_assert(1u == decltype(subspan)::extent, ""); + EXPECT_EQ(1, subspan[0]); + } + + { + auto subspan = span.first<2>(); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(2u, subspan.size()); + static_assert(2u == decltype(subspan)::extent, ""); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + } + + { + auto subspan = span.first<3>(); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(3u, subspan.size()); + static_assert(3u == decltype(subspan)::extent, ""); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + EXPECT_EQ(3, subspan[2]); + } +} + +TEST(SpanTest, TemplatedLastOnDynamicSpan) { + int array[] = {1, 2, 3}; + span<int> span(array); + + { + auto subspan = span.last<0>(); + EXPECT_EQ(span.data() + 3, subspan.data()); + EXPECT_EQ(0u, subspan.size()); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + auto subspan = span.last<1>(); + EXPECT_EQ(span.data() + 2, subspan.data()); + EXPECT_EQ(1u, subspan.size()); + static_assert(1u == decltype(subspan)::extent, ""); + EXPECT_EQ(3, subspan[0]); + } + + { + auto subspan = span.last<2>(); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(2u, subspan.size()); + static_assert(2u == decltype(subspan)::extent, ""); + EXPECT_EQ(2, subspan[0]); + EXPECT_EQ(3, subspan[1]); + } + + { + auto subspan = span.last<3>(); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(3u, subspan.size()); + static_assert(3u == decltype(subspan)::extent, ""); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + EXPECT_EQ(3, subspan[2]); + } +} + +TEST(SpanTest, TemplatedSubspanFromDynamicSpan) { + int array[] = {1, 2, 3}; + span<int, 3> span(array); + + { + auto subspan = span.subspan<0>(); + EXPECT_EQ(span.data(), subspan.data()); + static_assert(3u == decltype(subspan)::extent, ""); + EXPECT_EQ(3u, subspan.size()); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + EXPECT_EQ(3, subspan[2]); + } + + { + auto subspan = span.subspan<1>(); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(2u, subspan.size()); + static_assert(2u == decltype(subspan)::extent, ""); + EXPECT_EQ(2, subspan[0]); + EXPECT_EQ(3, subspan[1]); + } + + { + auto subspan = span.subspan<2>(); + EXPECT_EQ(span.data() + 2, subspan.data()); + EXPECT_EQ(1u, subspan.size()); + static_assert(1u == decltype(subspan)::extent, ""); + EXPECT_EQ(3, subspan[0]); + } + + { + auto subspan = span.subspan<3>(); + EXPECT_EQ(span.data() + 3, subspan.data()); + EXPECT_EQ(0u, subspan.size()); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + auto subspan = span.subspan<0, 0>(); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(0u, subspan.size()); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + auto subspan = span.subspan<1, 0>(); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(0u, subspan.size()); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + auto subspan = span.subspan<2, 0>(); + EXPECT_EQ(span.data() + 2, subspan.data()); + EXPECT_EQ(0u, subspan.size()); + static_assert(0u == decltype(subspan)::extent, ""); + } + + { + auto subspan = span.subspan<0, 1>(); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(1u, subspan.size()); + static_assert(1u == decltype(subspan)::extent, ""); + EXPECT_EQ(1, subspan[0]); + } + + { + auto subspan = span.subspan<1, 1>(); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(1u, subspan.size()); + static_assert(1u == decltype(subspan)::extent, ""); + EXPECT_EQ(2, subspan[0]); + } + + { + auto subspan = span.subspan<2, 1>(); + EXPECT_EQ(span.data() + 2, subspan.data()); + EXPECT_EQ(1u, subspan.size()); + static_assert(1u == decltype(subspan)::extent, ""); + EXPECT_EQ(3, subspan[0]); + } + + { + auto subspan = span.subspan<0, 2>(); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(2u, subspan.size()); + static_assert(2u == decltype(subspan)::extent, ""); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + } + + { + auto subspan = span.subspan<1, 2>(); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(2u, subspan.size()); + static_assert(2u == decltype(subspan)::extent, ""); + EXPECT_EQ(2, subspan[0]); + EXPECT_EQ(3, subspan[1]); + } + + { + auto subspan = span.subspan<0, 3>(); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(3u, subspan.size()); + static_assert(3u == decltype(subspan)::extent, ""); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + EXPECT_EQ(3, subspan[2]); + } +} + +TEST(SpanTest, First) { + int array[] = {1, 2, 3}; + span<int> span(array); + + { + auto subspan = span.first(0); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(0u, subspan.size()); + } + + { + auto subspan = span.first(1); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(1u, subspan.size()); + EXPECT_EQ(1, subspan[0]); + } + + { + auto subspan = span.first(2); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(2u, subspan.size()); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + } + + { + auto subspan = span.first(3); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(3u, subspan.size()); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + EXPECT_EQ(3, subspan[2]); + } +} + +TEST(SpanTest, Last) { + int array[] = {1, 2, 3}; + span<int> span(array); + + { + auto subspan = span.last(0); + EXPECT_EQ(span.data() + 3, subspan.data()); + EXPECT_EQ(0u, subspan.size()); + } + + { + auto subspan = span.last(1); + EXPECT_EQ(span.data() + 2, subspan.data()); + EXPECT_EQ(1u, subspan.size()); + EXPECT_EQ(3, subspan[0]); + } + + { + auto subspan = span.last(2); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(2u, subspan.size()); + EXPECT_EQ(2, subspan[0]); + EXPECT_EQ(3, subspan[1]); + } + + { + auto subspan = span.last(3); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(3u, subspan.size()); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + EXPECT_EQ(3, subspan[2]); + } +} + +TEST(SpanTest, Subspan) { + int array[] = {1, 2, 3}; + span<int> span(array); + + { + auto subspan = span.subspan(0); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(3u, subspan.size()); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + EXPECT_EQ(3, subspan[2]); + } + + { + auto subspan = span.subspan(1); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(2u, subspan.size()); + EXPECT_EQ(2, subspan[0]); + EXPECT_EQ(3, subspan[1]); + } + + { + auto subspan = span.subspan(2); + EXPECT_EQ(span.data() + 2, subspan.data()); + EXPECT_EQ(1u, subspan.size()); + EXPECT_EQ(3, subspan[0]); + } + + { + auto subspan = span.subspan(3); + EXPECT_EQ(span.data() + 3, subspan.data()); + EXPECT_EQ(0u, subspan.size()); + } + + { + auto subspan = span.subspan(0, 0); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(0u, subspan.size()); + } + + { + auto subspan = span.subspan(1, 0); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(0u, subspan.size()); + } + + { + auto subspan = span.subspan(2, 0); + EXPECT_EQ(span.data() + 2, subspan.data()); + EXPECT_EQ(0u, subspan.size()); + } + + { + auto subspan = span.subspan(0, 1); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(1u, subspan.size()); + EXPECT_EQ(1, subspan[0]); + } + + { + auto subspan = span.subspan(1, 1); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(1u, subspan.size()); + EXPECT_EQ(2, subspan[0]); + } + + { + auto subspan = span.subspan(2, 1); + EXPECT_EQ(span.data() + 2, subspan.data()); + EXPECT_EQ(1u, subspan.size()); + EXPECT_EQ(3, subspan[0]); + } + + { + auto subspan = span.subspan(0, 2); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(2u, subspan.size()); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + } + + { + auto subspan = span.subspan(1, 2); + EXPECT_EQ(span.data() + 1, subspan.data()); + EXPECT_EQ(2u, subspan.size()); + EXPECT_EQ(2, subspan[0]); + EXPECT_EQ(3, subspan[1]); + } + + { + auto subspan = span.subspan(0, 3); + EXPECT_EQ(span.data(), subspan.data()); + EXPECT_EQ(span.size(), subspan.size()); + EXPECT_EQ(1, subspan[0]); + EXPECT_EQ(2, subspan[1]); + EXPECT_EQ(3, subspan[2]); + } +} + +TEST(SpanTest, Size) { + { + span<int> span; + EXPECT_EQ(0u, span.size()); + } + + { + int array[] = {1, 2, 3}; + span<int> span(array); + EXPECT_EQ(3u, span.size()); + } +} + +TEST(SpanTest, SizeBytes) { + { + span<int> span; + EXPECT_EQ(0u, span.size_bytes()); + } + + { + int array[] = {1, 2, 3}; + span<int> span(array); + EXPECT_EQ(3u * sizeof(int), span.size_bytes()); + } +} + +TEST(SpanTest, Empty) { + { + span<int> span; + EXPECT_TRUE(span.empty()); + } + + { + int array[] = {1, 2, 3}; + span<int> span(array); + EXPECT_FALSE(span.empty()); + } +} + +TEST(SpanTest, OperatorAt) { + static constexpr int kArray[] = {1, 6, 1, 8, 0}; + constexpr span<const int> span(kArray); + + static_assert(&kArray[0] == &span[0], + "span[0] does not refer to the same element as kArray[0]"); + static_assert(&kArray[1] == &span[1], + "span[1] does not refer to the same element as kArray[1]"); + static_assert(&kArray[2] == &span[2], + "span[2] does not refer to the same element as kArray[2]"); + static_assert(&kArray[3] == &span[3], + "span[3] does not refer to the same element as kArray[3]"); + static_assert(&kArray[4] == &span[4], + "span[4] does not refer to the same element as kArray[4]"); +} + +TEST(SpanTest, Front) { + static constexpr int kArray[] = {1, 6, 1, 8, 0}; + constexpr span<const int> span(kArray); + static_assert(&kArray[0] == &span.front(), + "span.front() does not refer to the same element as kArray[0]"); +} + +TEST(SpanTest, Back) { + static constexpr int kArray[] = {1, 6, 1, 8, 0}; + constexpr span<const int> span(kArray); + static_assert(&kArray[4] == &span.back(), + "span.back() does not refer to the same element as kArray[4]"); +} + +// Pigweed: This test uses gMock features not yet supported in Pigweed. +#if 0 +TEST(SpanTest, Iterator) { + static constexpr int kArray[] = {1, 6, 1, 8, 0}; + constexpr span<const int> span(kArray); + + std::vector<int> results; + for (int i : span) + results.emplace_back(i); + EXPECT_THAT(results, ElementsAre(1, 6, 1, 8, 0)); +} +#endif // 0 + +TEST(SpanTest, ConstexprIterator) { + static constexpr int kArray[] = {1, 6, 1, 8, 0}; + constexpr span<const int> span(kArray); + + static_assert( + constexpr_equal( + std::begin(kArray), std::end(kArray), span.begin(), span.end()), + ""); + static_assert(1 == span.begin()[0], ""); + // Pigweed: These tests assume an iterator object, but Pigweed's span uses a + // simple pointer. +#if 0 + static_assert(1 == *(span.begin() += 0), ""); + static_assert(6 == *(span.begin() += 1), ""); + + static_assert(1 == *((span.begin() + 1) -= 1), ""); + static_assert(6 == *((span.begin() + 1) -= 0), ""); +#endif // 0 +} + +TEST(SpanTest, ReverseIterator) { + static constexpr int kArray[] = {1, 6, 1, 8, 0}; + constexpr span<const int> span(kArray); + + EXPECT_TRUE(std::equal( + std::rbegin(kArray), std::rend(kArray), span.rbegin(), span.rend())); + EXPECT_TRUE(std::equal(std::crbegin(kArray), + std::crend(kArray), + std::crbegin(span), + std::crend(span))); +} + +// Pigweed: These are tests for make_span, which is not included in Pigweed's +// implementation, since class template deduction is available. +#if 0 +TEST(SpanTest, AsBytes) { + { + constexpr int kArray[] = {2, 3, 5, 7, 11, 13}; + span<const uint8_t, sizeof(kArray)> bytes_span = + as_bytes(make_span(kArray)); + EXPECT_EQ(reinterpret_cast<const uint8_t*>(kArray), bytes_span.data()); + EXPECT_EQ(sizeof(kArray), bytes_span.size()); + EXPECT_EQ(bytes_span.size(), bytes_span.size_bytes()); + } + + { + std::vector<int> vec = {1, 1, 2, 3, 5, 8}; + span<int> mutable_span(vec); + span<const uint8_t> bytes_span = as_bytes(mutable_span); + EXPECT_EQ(reinterpret_cast<const uint8_t*>(vec.data()), bytes_span.data()); + EXPECT_EQ(sizeof(int) * vec.size(), bytes_span.size()); + EXPECT_EQ(bytes_span.size(), bytes_span.size_bytes()); + } +} + +TEST(SpanTest, AsWritableBytes) { + std::vector<int> vec = {1, 1, 2, 3, 5, 8}; + span<int> mutable_span(vec); + span<uint8_t> writable_bytes_span = as_writable_bytes(mutable_span); + EXPECT_EQ(reinterpret_cast<uint8_t*>(vec.data()), writable_bytes_span.data()); + EXPECT_EQ(sizeof(int) * vec.size(), writable_bytes_span.size()); + EXPECT_EQ(writable_bytes_span.size(), writable_bytes_span.size_bytes()); + + // Set the first entry of vec to zero while writing through the span. + std::fill(writable_bytes_span.data(), + writable_bytes_span.data() + sizeof(int), 0); + EXPECT_EQ(0, vec[0]); +} + +TEST(SpanTest, MakeSpanFromDataAndSize) { + int* nullint = nullptr; + auto empty_span = make_span(nullint, 0); + EXPECT_TRUE(empty_span.empty()); + EXPECT_EQ(nullptr, empty_span.data()); + + std::vector<int> vector = {1, 1, 2, 3, 5, 8}; + span<int> expected_span(vector.data(), vector.size()); + auto made_span = make_span(vector.data(), vector.size()); + EXPECT_EQ(expected_span.data(), made_span.data()); + EXPECT_EQ(expected_span.size(), made_span.size()); + static_assert(decltype(made_span)::extent == dynamic_extent, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeSpanFromPointerPair) { + int* nullint = nullptr; + auto empty_span = make_span(nullint, nullint); + EXPECT_TRUE(empty_span.empty()); + EXPECT_EQ(nullptr, empty_span.data()); + + std::vector<int> vector = {1, 1, 2, 3, 5, 8}; + span<int> expected_span(vector.data(), vector.size()); + auto made_span = make_span(vector.data(), vector.data() + vector.size()); + EXPECT_EQ(expected_span.data(), made_span.data()); + EXPECT_EQ(expected_span.size(), made_span.size()); + static_assert(decltype(made_span)::extent == dynamic_extent, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeSpanFromConstexprArray) { + static constexpr int kArray[] = {1, 2, 3, 4, 5}; + constexpr span<const int, 5> expected_span(kArray); + constexpr auto made_span = make_span(kArray); + EXPECT_EQ(expected_span.data(), made_span.data()); + EXPECT_EQ(expected_span.size(), made_span.size()); + static_assert(decltype(made_span)::extent == 5, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeSpanFromStdArray) { + const std::array<int, 5> kArray = {{1, 2, 3, 4, 5}}; + span<const int, 5> expected_span(kArray); + auto made_span = make_span(kArray); + EXPECT_EQ(expected_span.data(), made_span.data()); + EXPECT_EQ(expected_span.size(), made_span.size()); + static_assert(decltype(made_span)::extent == 5, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeSpanFromConstContainer) { + const std::vector<int> vector = {-1, -2, -3, -4, -5}; + span<const int> expected_span(vector); + auto made_span = make_span(vector); + EXPECT_EQ(expected_span.data(), made_span.data()); + EXPECT_EQ(expected_span.size(), made_span.size()); + static_assert(decltype(made_span)::extent == dynamic_extent, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeStaticSpanFromConstContainer) { + const std::vector<int> vector = {-1, -2, -3, -4, -5}; + span<const int, 5> expected_span(vector.data(), vector.size()); + auto made_span = make_span<5>(vector); + EXPECT_EQ(expected_span.data(), made_span.data()); + EXPECT_EQ(expected_span.size(), made_span.size()); + static_assert(decltype(made_span)::extent == 5, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeSpanFromContainer) { + std::vector<int> vector = {-1, -2, -3, -4, -5}; + span<int> expected_span(vector); + auto made_span = make_span(vector); + EXPECT_EQ(expected_span.data(), made_span.data()); + EXPECT_EQ(expected_span.size(), made_span.size()); + static_assert(decltype(made_span)::extent == dynamic_extent, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeStaticSpanFromContainer) { + std::vector<int> vector = {-1, -2, -3, -4, -5}; + span<int, 5> expected_span(vector.data(), vector.size()); + auto made_span = make_span<5>(vector); + EXPECT_EQ(expected_span.data(), make_span<5>(vector).data()); + EXPECT_EQ(expected_span.size(), make_span<5>(vector).size()); + static_assert(decltype(make_span<5>(vector))::extent == 5, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeStaticSpanFromConstexprContainer) { + constexpr StringPiece str = "Hello, World"; + constexpr auto made_span = make_span<12>(str); + static_assert(str.data() == made_span.data(), "Error: data() does not match"); + static_assert(str.size() == made_span.size(), "Error: size() does not match"); + static_assert(std::is_same<decltype(str)::value_type, + decltype(made_span)::value_type>::value, + "Error: value_type does not match"); + static_assert(str.size() == decltype(made_span)::extent, + "Error: extent does not match"); +} + +TEST(SpanTest, MakeSpanFromRValueContainer) { + std::vector<int> vector = {-1, -2, -3, -4, -5}; + span<const int> expected_span(vector); + // Note: While static_cast<T&&>(foo) is effectively just a fancy spelling of + // std::move(foo), make_span does not actually take ownership of the passed in + // container. Writing it this way makes it more obvious that we simply care + // about the right behavour when passing rvalues. + auto made_span = make_span(static_cast<std::vector<int>&&>(vector)); + EXPECT_EQ(expected_span.data(), made_span.data()); + EXPECT_EQ(expected_span.size(), made_span.size()); + static_assert(decltype(made_span)::extent == dynamic_extent, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeStaticSpanFromRValueContainer) { + std::vector<int> vector = {-1, -2, -3, -4, -5}; + span<const int, 5> expected_span(vector.data(), vector.size()); + // Note: While static_cast<T&&>(foo) is effectively just a fancy spelling of + // std::move(foo), make_span does not actually take ownership of the passed in + // container. Writing it this way makes it more obvious that we simply care + // about the right behavour when passing rvalues. + auto made_span = make_span<5>(static_cast<std::vector<int>&&>(vector)); + EXPECT_EQ(expected_span.data(), made_span.data()); + EXPECT_EQ(expected_span.size(), made_span.size()); + static_assert(decltype(made_span)::extent == 5, ""); + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeSpanFromDynamicSpan) { + static constexpr int kArray[] = {1, 2, 3, 4, 5}; + constexpr span<const int> expected_span(kArray); + constexpr auto made_span = make_span(expected_span); + static_assert(std::is_same<decltype(expected_span)::element_type, + decltype(made_span)::element_type>::value, + "make_span(span) should have the same element_type as span"); + + static_assert(expected_span.data() == made_span.data(), + "make_span(span) should have the same data() as span"); + + static_assert(expected_span.size() == made_span.size(), + "make_span(span) should have the same size() as span"); + + static_assert(decltype(made_span)::extent == decltype(expected_span)::extent, + "make_span(span) should have the same extent as span"); + + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} + +TEST(SpanTest, MakeSpanFromStaticSpan) { + static constexpr int kArray[] = {1, 2, 3, 4, 5}; + constexpr span<const int, 5> expected_span(kArray); + constexpr auto made_span = make_span(expected_span); + static_assert(std::is_same<decltype(expected_span)::element_type, + decltype(made_span)::element_type>::value, + "make_span(span) should have the same element_type as span"); + + static_assert(expected_span.data() == made_span.data(), + "make_span(span) should have the same data() as span"); + + static_assert(expected_span.size() == made_span.size(), + "make_span(span) should have the same size() as span"); + + static_assert(decltype(made_span)::extent == decltype(expected_span)::extent, + "make_span(span) should have the same extent as span"); + + static_assert( + std::is_same<decltype(expected_span), decltype(made_span)>::value, + "the type of made_span differs from expected_span!"); +} +#endif // 0 + +TEST(SpanTest, EnsureConstexprGoodness) { + static constexpr int kArray[] = {5, 4, 3, 2, 1}; + constexpr span<const int> constexpr_span(kArray); + const size_t size = 2; + + const size_t start = 1; + constexpr span<const int> subspan = + constexpr_span.subspan(start, start + size); + for (size_t i = 0; i < subspan.size(); ++i) + EXPECT_EQ(kArray[start + i], subspan[i]); + + constexpr span<const int> firsts = constexpr_span.first(size); + for (size_t i = 0; i < firsts.size(); ++i) + EXPECT_EQ(kArray[i], firsts[i]); + + constexpr span<const int> lasts = constexpr_span.last(size); + for (size_t i = 0; i < lasts.size(); ++i) { + const size_t j = (std::size(kArray) - size) + i; + EXPECT_EQ(kArray[j], lasts[i]); + } + + constexpr int item = constexpr_span[size]; + EXPECT_EQ(kArray[size], item); +} + +#if 0 + +// Pigweed: Death tests are not yet supported. +TEST(SpanTest, OutOfBoundsDeath) { + constexpr span<int, 0> kEmptySpan; + ASSERT_DEATH_IF_SUPPORTED(kEmptySpan[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.front(), ""); + ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.first(1), ""); + ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.last(1), ""); + ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.back(), ""); + ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(1), ""); + + static constexpr int kArray[] = {0, 1, 2}; + constexpr span<const int> kNonEmptyDynamicSpan(kArray); + EXPECT_EQ(3U, kNonEmptyDynamicSpan.size()); + ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan[4], ""); + ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.subspan(10), ""); + ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.subspan(1, 7), ""); +} + +// Pigweed: These tests use CheckedContiguousConstIterator, which isn't used in +// Pigweed's version. +TEST(SpanTest, IteratorIsRangeMoveSafe) { + static constexpr int kArray[] = {1, 6, 1, 8, 0}; + const size_t kNumElements = 5; + constexpr span<const int> span(kArray); + + static constexpr int kOverlappingStartIndexes[] = {-4, 0, 3, 4}; + static constexpr int kNonOverlappingStartIndexes[] = {-7, -5, 5, 7}; + + // Overlapping ranges. + for (const int dest_start_index : kOverlappingStartIndexes) { + EXPECT_FALSE(CheckedContiguousIterator<const int>::IsRangeMoveSafe( + span.begin(), span.end(), + CheckedContiguousIterator<const int>( + span.data() + dest_start_index, + span.data() + dest_start_index + kNumElements))); + EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe( + std::cbegin(span), std::cend(span), + CheckedContiguousConstIterator<const int>( + span.data() + dest_start_index, + span.data() + dest_start_index + kNumElements))); + } + + // Non-overlapping ranges. + for (const int dest_start_index : kNonOverlappingStartIndexes) { + EXPECT_TRUE(CheckedContiguousIterator<const int>::IsRangeMoveSafe( + span.begin(), span.end(), + CheckedContiguousIterator<const int>( + span.data() + dest_start_index, + span.data() + dest_start_index + kNumElements))); + EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe( + std::cbegin(span), std::cend(span), + CheckedContiguousConstIterator<const int>( + span.data() + dest_start_index, + span.data() + dest_start_index + kNumElements))); + } + + // IsRangeMoveSafe is true if the length to be moved is 0. + EXPECT_TRUE(CheckedContiguousIterator<const int>::IsRangeMoveSafe( + span.begin(), span.begin(), + CheckedContiguousIterator<const int>(span.data(), span.data()))); + EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe( + std::cbegin(span), std::cbegin(span), + CheckedContiguousConstIterator<const int>(span.data(), span.data()))); + + // IsRangeMoveSafe is false if end < begin. + EXPECT_FALSE(CheckedContiguousIterator<const int>::IsRangeMoveSafe( + span.end(), span.begin(), + CheckedContiguousIterator<const int>(span.data(), span.data()))); + EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe( + std::cend(span), std::cbegin(span), + CheckedContiguousConstIterator<const int>(span.data(), span.data()))); +} + +// Pigweed: gMock matchers are not yet supported. +TEST(SpanTest, Sort) { + int array[] = {5, 4, 3, 2, 1}; + + span<int> dynamic_span = array; + std::sort(dynamic_span.begin(), dynamic_span.end()); + EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5)); + std::sort(dynamic_span.rbegin(), dynamic_span.rend()); + EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1)); + + span<int, 5> static_span = array; + std::sort(static_span.rbegin(), static_span.rend(), std::greater<>()); + EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5)); + std::sort(static_span.begin(), static_span.end(), std::greater<>()); + EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1)); +} +#endif // 0 + +TEST(SpanTest, SpanExtentConversions) { + // Statically checks that various conversions between spans of dynamic and + // static extent are possible or not. + static_assert( + !std::is_constructible<span<int, 0>, span<int>>::value, + "Error: static span should not be constructible from dynamic span"); + + static_assert(!std::is_constructible<span<int, 2>, span<int, 1>>::value, + "Error: static span should not be constructible from static " + "span with different extent"); + + static_assert(std::is_convertible<span<int, 0>, span<int>>::value, + "Error: static span should be convertible to dynamic span"); + + static_assert(std::is_convertible<span<int>, span<int>>::value, + "Error: dynamic span should be convertible to dynamic span"); + + static_assert(std::is_convertible<span<int, 2>, span<int, 2>>::value, + "Error: static span should be convertible to static span"); +} + +TEST(SpanTest, IteratorConversions) { + static_assert(std::is_convertible<span<int>::iterator, + span<const int>::iterator>::value, + "Error: iterator should be convertible to const iterator"); + + static_assert(!std::is_convertible<span<const int>::iterator, + span<int>::iterator>::value, + "Error: const iterator should not be convertible to iterator"); +} + +} // namespace pw
diff --git a/pw_span/span_test.cc b/pw_span/span_test.cc index 37d5ee4..e41a006 100644 --- a/pw_span/span_test.cc +++ b/pw_span/span_test.cc
@@ -25,6 +25,7 @@ #include <algorithm> #include <cstdint> #include <memory> +#include <span> #include <string> #include <type_traits> #include <vector> @@ -38,7 +39,7 @@ using ::testing::Pointwise; #endif // 0 -namespace pw { +namespace std { namespace { @@ -58,6 +59,59 @@ } // namespace +// TODO(hepler): Remove these tests after eliminating pw::span. +TEST(StdSpanCompatibility, ConstructFromPwSpanToStdSpan) { + char array[] = {'a', 'b', 'c', 'd', '\0'}; + + ::pw::span pw_span(array); + std::span std_span(pw_span); + + std_span[0] = '!'; + EXPECT_STREQ(std_span.data(), "!bcd"); + + EXPECT_EQ(pw_span.data(), std_span.data()); + EXPECT_EQ(pw_span.size(), std_span.size()); +} + +TEST(StdSpanCompatibility, ConstructFromStdSpanToPwSpan) { + char array[] = {'a', 'b', 'c', 'd', '\0'}; + + std::span std_span(array); + ::pw::span pw_span(std_span); + + pw_span[0] = '!'; + EXPECT_STREQ(pw_span.data(), "!bcd"); + + EXPECT_EQ(pw_span.data(), std_span.data()); + EXPECT_EQ(pw_span.size(), std_span.size()); +} + +TEST(StdSpanCompatibility, AssignFromPwSpanToStdSpan) { + char array[] = {'a', 'b', 'c', 'd', '\0'}; + + auto pw_span = ::pw::span(array); + std::span std_span = pw_span; + + std_span[0] = '!'; + EXPECT_STREQ(std_span.data(), "!bcd"); + + EXPECT_EQ(pw_span.data(), std_span.data()); + EXPECT_EQ(pw_span.size(), std_span.size()); +} + +TEST(StdSpanCompatibility, AssignFromStdSpanToPwSpan) { + char array[] = {'a', 'b', 'c', 'd', '\0'}; + + auto std_span = std::span(array); + ::pw::span pw_span = std_span; + + pw_span[0] = '!'; + EXPECT_STREQ(pw_span.data(), "!bcd"); + + EXPECT_EQ(pw_span.data(), std_span.data()); + EXPECT_EQ(pw_span.size(), std_span.size()); +} + TEST(SpanTest, DeductionGuides_MutableArray) { char array[] = {'a', 'b', 'c', 'd', '\0'}; @@ -1629,4 +1683,4 @@ "Error: const iterator should not be convertible to iterator"); } -} // namespace pw +} // namespace std