From 53ffd56d87a2590182e1b8c2535c5824ad77462c Mon Sep 17 00:00:00 2001 From: Denis Yaroshevskiy Date: Wed, 14 Aug 2024 06:42:43 -0700 Subject: [PATCH] folly::span_cast Summary: a simple utility to convert span types to a different span. Differential Revision: D61202580 --- folly/BUCK | 1 + folly/Memory.h | 57 +++++++++++++++++++ folly/Traits.h | 1 + folly/test/MemoryTest.cpp | 116 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) diff --git a/folly/BUCK b/folly/BUCK index de765471a05..4eef0773a45 100644 --- a/folly/BUCK +++ b/folly/BUCK @@ -915,6 +915,7 @@ cpp_library( "//folly/lang:thunk", "//folly/memory:malloc", "//folly/portability:config", + "//folly/portability:constexpr", "//folly/portability:malloc", ], ) diff --git a/folly/Memory.h b/folly/Memory.h index 91a7533afb5..260131b3ce4 100644 --- a/folly/Memory.h +++ b/folly/Memory.h @@ -38,8 +38,13 @@ #include #include #include +#include #include +#if __cpp_lib_span +#include +#endif + namespace folly { #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \ @@ -845,4 +850,56 @@ template struct AllocatorHasDefaultObjectDestroy, T> : std::true_type {}; +#if __cpp_lib_span + +namespace detail { + +template +constexpr auto span_cast_impl(std::span in, U* castData) { + // check alignment + if (!folly::is_constant_evaluated_or(true)) { + assert(reinterpret_cast(in.data()) % sizeof(U) == 0); + } + + if constexpr (Extend == std::dynamic_extent) { + assert(in.size() * sizeof(T) % sizeof(U) == 0); + return std::span(castData, in.size() * sizeof(T) / sizeof(U)); + } else { + static_assert(in.size() * sizeof(T) % sizeof(U) == 0); + constexpr std::size_t kResSize = Extend * sizeof(T) / sizeof(U); + return std::span(castData, kResSize); + } +} + +} // namespace detail + +/** + * converting a span to a different span. + * (you get a span to the same bytes but treated as different type) + * + * Example: + * + * enum class SomeEnum : int { ... }; + * + * std::span s = ... + * std::span as_ints = folly::reinterpret_span_cast(s); + */ + +template +constexpr auto static_span_cast(std::span in) { + return detail::span_cast_impl(in, static_cast(in.data())); +} + +template +constexpr auto reinterpret_span_cast(std::span in) { + return detail::span_cast_impl(in, reinterpret_cast(in.data())); +} + +template +constexpr auto const_span_cast(std::span in) { + return detail::span_cast_impl(in, const_cast(in.data())); +} + +#endif + } // namespace folly diff --git a/folly/Traits.h b/folly/Traits.h index 6ae5c064c49..3474b827353 100644 --- a/folly/Traits.h +++ b/folly/Traits.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include diff --git a/folly/test/MemoryTest.cpp b/folly/test/MemoryTest.cpp index e23ed677604..7c660521a15 100644 --- a/folly/test/MemoryTest.cpp +++ b/folly/test/MemoryTest.cpp @@ -585,3 +585,119 @@ TEST(allocateOverAligned, defaultOverCustomAlloc) { folly::deallocateOverAligned(a, p, 1); EXPECT_EQ(folly::allocationBytesForOverAligned(1), 128); } + +#if __cpp_lib_span + +template +using reinterpret_span_cast_result_type = + decltype(folly::reinterpret_span_cast( + std::declval>())); + +template +using static_span_cast_result_type = decltype(folly::const_span_cast( + std::declval>())); + +template +using const_span_cast_result_type = decltype(folly::const_span_cast( + std::declval>())); + +TEST(Traits, SpanCast) { + auto tstSpanCast = [](auto to, auto from) { + ASSERT_EQ( + static_cast(from.data()), + static_cast(to.data())); + + ASSERT_EQ( + static_cast(from.data() + from.size()), + static_cast(to.data() + to.size())); + }; + + { + std::array a; + tstSpanCast( + folly::reinterpret_span_cast(std::span(a)), std::span(a)); + tstSpanCast( + folly::reinterpret_span_cast(std::span(a)), std::span(a)); + } + + { + std::vector a(4u, 1); + tstSpanCast( + folly::reinterpret_span_cast(std::span(a)), std::span(a)); + tstSpanCast( + folly::reinterpret_span_cast(std::span(a)), std::span(a)); + } + + { + const std::vector a(4u, 1); + tstSpanCast(folly::const_span_cast(std::span(a)), std::span(a)); + + std::vector b(4u, 1); + tstSpanCast(folly::static_span_cast(std::span(b)), std::span(b)); + tstSpanCast(folly::const_span_cast(std::span(b)), std::span(b)); + tstSpanCast( + folly::reinterpret_span_cast(std::span(b)), std::span(b)); + } + + // types + { + static_assert( + std::is_same_v< + std::span, + reinterpret_span_cast_result_type>); + static_assert(std::is_same_v< + std::span, + reinterpret_span_cast_result_type< + const char, + const int, + std::dynamic_extent>>); + static_assert(std::is_same_v< + std::span, + reinterpret_span_cast_result_type>); + + static_assert(std::is_same_v< + std::span, + const_span_cast_result_type>); + static_assert(std::is_same_v< + std::span, + const_span_cast_result_type>); + static_assert(std::is_same_v< + std::span, + const_span_cast_result_type< + const char, + char, + std::dynamic_extent>>); + static_assert(std::is_same_v< + std::span, + const_span_cast_result_type< + char, + const char, + std::dynamic_extent>>); + + static_assert(std::is_same_v< + std::span, + static_span_cast_result_type>); + static_assert(std::is_same_v< + std::span, + static_span_cast_result_type< + const char, + char, + std::dynamic_extent>>); + } + + // constexpr + { + [[maybe_unused]] constexpr auto _ = [] { + std::array a{0, 1, 2, 3}; + std::span mutableAFixed(a); + std::span mutableADynamic(a); + auto resFixed = folly::static_span_cast(mutableAFixed); + (void)resFixed; + auto resDynamic = folly::static_span_cast(mutableADynamic); + (void)resDynamic; + return 0; + }(); + } +} + +#endif