From c9c2b90b20a301b8e09eaf195bcfd64675a816b3 Mon Sep 17 00:00:00 2001 From: Yedidya Feldblum Date: Thu, 12 Sep 2024 18:25:29 -0700 Subject: [PATCH] span Reviewed By: DenisYaroshevskiy Differential Revision: D62460848 fbshipit-source-id: ba7aa75d812b142eb6ddd5431c22c4f9a8cf7162 --- folly/container/BUCK | 5 + folly/container/span.h | 292 ++++++++++++++++++++++++- folly/container/test/BUCK | 1 + folly/container/test/span_test.cpp | 339 +++++++++++++++++++++++++++++ 4 files changed, 631 insertions(+), 6 deletions(-) diff --git a/folly/container/BUCK b/folly/container/BUCK index c4ef301b11e..528f8b81fad 100644 --- a/folly/container/BUCK +++ b/folly/container/BUCK @@ -172,7 +172,12 @@ cpp_library( name = "span", headers = ["span.h"], exported_deps = [ + "//folly:cpp_attributes", "//folly:portability", + "//folly:traits", + "//folly:utility", + "//folly/container:access", + "//folly/functional:invoke", "//folly/portability:constexpr", ], ) diff --git a/folly/container/span.h b/folly/container/span.h index d28d0caee92..656c6c76d02 100644 --- a/folly/container/span.h +++ b/folly/container/span.h @@ -16,10 +16,18 @@ #pragma once +#include #include #include +#include +#include +#include #include +#include +#include +#include +#include #include #if __cpp_lib_span >= 202002L @@ -28,13 +36,241 @@ namespace folly { +namespace detail { + +namespace fallback_span { + +inline constexpr auto dynamic_extent = std::size_t(-1); + +template +struct span_extent { + constexpr span_extent() = default; + explicit constexpr span_extent( + [[maybe_unused]] std::size_t const e) noexcept { + assert(e == N); + } + constexpr span_extent(span_extent const&) = default; + constexpr span_extent& operator=(span_extent const&) = default; + + /* implicit */ operator std::size_t() const noexcept { return N; } +}; + +template <> +struct span_extent { + std::size_t extent{}; + + constexpr span_extent() = default; + explicit constexpr span_extent(std::size_t const e) noexcept : extent{e} {} + constexpr span_extent(span_extent const&) = default; + constexpr span_extent& operator=(span_extent const&) = default; + + /* implicit */ operator std::size_t() const noexcept { return extent; } +}; + +/// span +/// +/// mimic: std::span, C++20 +template +class span { + public: + static_assert(!std::is_reference_v); + static_assert(!std::is_void_v); + static_assert(!std::is_function_v); + static_assert(sizeof(T) < size_t(-1)); + static_assert(!std::is_abstract_v); + + using element_type = T; + using value_type = std::remove_cv_t; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = element_type*; + using const_pointer = element_type const*; + using reference = element_type&; + using const_reference = element_type const&; + using iterator = pointer; + using reverse_iterator = std::reverse_iterator; + + static inline constexpr std::size_t extent = Extent; + + private: + template + using if_ = std::enable_if_t; + + template > + static inline constexpr bool array_element_match_v = + std::is_same_v && std::is_convertible_v; + + template < + typename Rng, + typename Size = remove_cvref_t>, + typename Data = invoke_result_t, + typename U = std::remove_pointer_t> + static constexpr bool is_range_v = + !std::is_same_v && std::is_unsigned_v && + std::is_pointer_v && array_element_match_v; + + static constexpr size_type subspan_extent( + size_type const offset, size_type const count) { + // clang-format off + return + count != dynamic_extent ? count : + extent != dynamic_extent ? extent - offset : + dynamic_extent; + // clang-format on + } + + pointer data_; + [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] span_extent extent_; + + public: + template = 0> + constexpr span() noexcept : data_{}, extent_{} {} + + constexpr span(pointer const first, size_type const count) + : data_{first}, extent_{count} {} + + constexpr span(pointer const first, pointer const last) + : data_{first}, extent_{to_unsigned(last - first)} { + assert(!(last < first)); + } + + template < + std::size_t N, + std::size_t E = extent, + if_ = 0> + /* implicit */ constexpr span(type_t (&arr)[N]) noexcept + : data_{arr}, extent_{N} {} + + template < + typename U, + std::size_t N, + std::size_t E = extent, + if_ = 0, + if_> = 0> + /* implicit */ constexpr span(std::array& arr) noexcept + : data_{arr.data()}, extent_{N} {} + + template < + typename U, + std::size_t N, + std::size_t E = extent, + if_ = 0, + if_> = 0> + /* implicit */ constexpr span(std::array const& arr) noexcept + : data_{arr.data()}, extent_{N} {} + + template > = 0> + /* implicit */ constexpr span(Rng&& range) + : data_{range.data()}, extent_{range.size()} {} + + constexpr span(span const&) = default; + + constexpr span& operator=(span const&) = default; + + constexpr pointer data() const noexcept { return data_; } + constexpr size_type size() const noexcept { return extent_; } + constexpr size_type size_bytes() const noexcept { + return size() * sizeof(element_type); + } + constexpr bool empty() const noexcept { return size() == 0; } + + constexpr iterator begin() const noexcept { return data_; } + constexpr iterator end() const noexcept { return data_ + size(); } + constexpr reverse_iterator rbegin() const noexcept { + return std::make_reverse_iterator(begin()); + } + constexpr reverse_iterator rend() const noexcept { + return std::make_reverse_iterator(end()); + } + + constexpr reference front() const { + assert(!empty()); + return data_[0]; + } + constexpr reference back() const { + assert(!empty()); + return data_[size() - 1]; + } + constexpr reference operator[](size_type const idx) const { + assert(idx < size()); + return data_[idx]; + } + + template < + size_type Offset, + size_type Count = dynamic_extent, + typename..., + size_type E = subspan_extent(Offset, Count)> + constexpr span subspan() const { + static_assert(!(Extent < Offset)); + static_assert(Count == dynamic_extent || !(extent - Offset < Count)); + assert(!(size() < Offset)); + assert(Count == dynamic_extent || !(size() - Offset < Count)); + return {data_ + Offset, Count == dynamic_extent ? size() - Offset : Count}; + } + + constexpr span subspan( + size_type const offset, size_type const count = dynamic_extent) const { + assert(!(extent < offset)); + assert(count == dynamic_extent || !(extent - offset < count)); + assert(!(size() < offset)); + assert(count == dynamic_extent || !(size() - offset < count)); + return {data_ + offset, count == dynamic_extent ? size() - offset : count}; + } + + template + constexpr span first() const { + static_assert(!(extent < Count)); + assert(!(size() < Count)); + return {data_, Count}; + } + constexpr span first( + size_type const count) const { + assert(!(extent < count)); + assert(!(size() < count)); + return {data_, count}; + } + + template + constexpr span last() const { + static_assert(!(extent < Count)); + assert(!(size() < Count)); + return {data_ + size() - Count, Count}; + } + constexpr span last( + size_type const count) const { + assert(!(extent < count)); + assert(!(size() < count)); + return {data_ + size() - count, count}; + } +}; + +} // namespace fallback_span + +} // namespace detail + #if __cpp_lib_span >= 202002L +using std::dynamic_extent; +using std::span; + +#else + +using detail::fallback_span::dynamic_extent; +using detail::fallback_span::span; + +#endif + namespace detail { struct span_cast_impl_fn { - template - constexpr auto operator()(std::span in, U* castData) const { + template < + template + class Span, + typename U, + typename T, + std::size_t Extent> + constexpr auto operator()(Span in, U* castData) const { assert( static_cast(in.data()) == static_cast(castData)); @@ -44,13 +280,14 @@ struct span_cast_impl_fn { assert(reinterpret_cast(in.data()) % sizeof(U) == 0); } - if constexpr (Extent == std::dynamic_extent) { + if constexpr (Extent == dynamic_extent) { assert(in.size() * sizeof(T) % sizeof(U) == 0); - return std::span(castData, in.size() * sizeof(T) / sizeof(U)); + return Span( + castData, in.size() * sizeof(T) / sizeof(U)); } else { static_assert(Extent * sizeof(T) % sizeof(U) == 0); constexpr std::size_t kResSize = Extent * sizeof(T) / sizeof(U); - return std::span(castData, kResSize); + return Span(castData, kResSize); } } }; @@ -76,34 +313,77 @@ inline constexpr span_cast_impl_fn span_cast_impl; template struct static_span_cast_fn { + template + constexpr auto operator()(detail::fallback_span::span in) const { + return detail::span_cast_impl(in, static_cast(in.data())); + } +#if __cpp_lib_span >= 202002L template constexpr auto operator()(std::span in) const { return detail::span_cast_impl(in, static_cast(in.data())); } +#endif }; template inline constexpr static_span_cast_fn static_span_cast; template struct reinterpret_span_cast_fn { + template + constexpr auto operator()(detail::fallback_span::span in) const { + return detail::span_cast_impl(in, reinterpret_cast(in.data())); + } +#if __cpp_lib_span >= 202002L template constexpr auto operator()(std::span in) const { return detail::span_cast_impl(in, reinterpret_cast(in.data())); } +#endif }; template inline constexpr reinterpret_span_cast_fn reinterpret_span_cast; template struct const_span_cast_fn { + template + constexpr auto operator()(detail::fallback_span::span in) const { + return detail::span_cast_impl(in, const_cast(in.data())); + } +#if __cpp_lib_span >= 202002L template constexpr auto operator()(std::span in) const { return detail::span_cast_impl(in, const_cast(in.data())); } +#endif }; template inline constexpr const_span_cast_fn const_span_cast; -#endif +namespace detail { + +namespace fallback_span { + +/// as_bytes +/// +/// mimic: std::as_bytes, C++20 +template +auto as_bytes(span s) noexcept { + return reinterpret_span_cast(s); +} + +/// as_writable_bytes +/// +/// mimic: std::as_writable_bytes, C++20 +template < + typename T, + std::size_t Extent, + std::enable_if_t, int> = 0> +auto as_writable_bytes(span s) noexcept { + return reinterpret_span_cast(s); +} + +} // namespace fallback_span + +} // namespace detail } // namespace folly diff --git a/folly/container/test/BUCK b/folly/container/test/BUCK index e7d559f7c16..8937ef8a79a 100644 --- a/folly/container/test/BUCK +++ b/folly/container/test/BUCK @@ -338,6 +338,7 @@ cpp_unittest( supports_static_listing = False, deps = [ "//folly/container:span", + "//folly/portability:gmock", "//folly/portability:gtest", ], ) diff --git a/folly/container/test/span_test.cpp b/folly/container/test/span_test.cpp index 6cc6ebb082c..226c7d05cee 100644 --- a/folly/container/test/span_test.cpp +++ b/folly/container/test/span_test.cpp @@ -16,12 +16,351 @@ #include +#include +#include #include #include +#include +#include #include #if __cpp_lib_span >= 202002L +#include +#endif + +#if __cpp_lib_span >= 202002L + +static_assert( + std::dynamic_extent == folly::detail::fallback_span::dynamic_extent); + +#endif + +template +struct SpanTest : public testing::TestWithParam {}; +TYPED_TEST_SUITE_P(SpanTest); + +template