Skip to content

Commit

Permalink
span
Browse files Browse the repository at this point in the history
Reviewed By: DenisYaroshevskiy

Differential Revision: D62460848

fbshipit-source-id: ba7aa75d812b142eb6ddd5431c22c4f9a8cf7162
  • Loading branch information
yfeldblum authored and facebook-github-bot committed Sep 13, 2024
1 parent 2f3cbdc commit c9c2b90
Show file tree
Hide file tree
Showing 4 changed files with 631 additions and 6 deletions.
5 changes: 5 additions & 0 deletions folly/container/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
)
Expand Down
292 changes: 286 additions & 6 deletions folly/container/span.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@

#pragma once

#include <array>
#include <cassert>
#include <cstddef>
#include <limits>
#include <type_traits>

#include <folly/CppAttributes.h>
#include <folly/Portability.h>
#include <folly/Traits.h>
#include <folly/Utility.h>
#include <folly/container/Access.h>
#include <folly/functional/Invoke.h>
#include <folly/portability/Constexpr.h>

#if __cpp_lib_span >= 202002L
Expand All @@ -28,13 +36,241 @@

namespace folly {

namespace detail {

namespace fallback_span {

inline constexpr auto dynamic_extent = std::size_t(-1);

template <std::size_t N>
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<dynamic_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 <typename T, std::size_t Extent = dynamic_extent>
class span {
public:
static_assert(!std::is_reference_v<T>);
static_assert(!std::is_void_v<T>);
static_assert(!std::is_function_v<T>);
static_assert(sizeof(T) < size_t(-1));
static_assert(!std::is_abstract_v<T>);

using element_type = T;
using value_type = std::remove_cv_t<element_type>;
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<iterator>;

static inline constexpr std::size_t extent = Extent;

private:
template <bool C>
using if_ = std::enable_if_t<C, int>;

template <typename U, typename V = std::remove_cv_t<U>>
static inline constexpr bool array_element_match_v =
std::is_same_v<V, value_type> && std::is_convertible_v<U*, pointer>;

template <
typename Rng,
typename Size = remove_cvref_t<invoke_result_t<access::size_fn, Rng>>,
typename Data = invoke_result_t<access::data_fn, Rng>,
typename U = std::remove_pointer_t<Data>>
static constexpr bool is_range_v =
!std::is_same_v<bool, Size> && std::is_unsigned_v<Size> &&
std::is_pointer_v<Data> && array_element_match_v<U>;

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> extent_;

public:
template <size_type E = extent, if_<E == dynamic_extent || E == 0> = 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_<E == dynamic_extent || E == N> = 0>
/* implicit */ constexpr span(type_t<element_type> (&arr)[N]) noexcept
: data_{arr}, extent_{N} {}

template <
typename U,
std::size_t N,
std::size_t E = extent,
if_<E == dynamic_extent || E == N> = 0,
if_<array_element_match_v<U>> = 0>
/* implicit */ constexpr span(std::array<U, N>& arr) noexcept
: data_{arr.data()}, extent_{N} {}

template <
typename U,
std::size_t N,
std::size_t E = extent,
if_<E == dynamic_extent || E == N> = 0,
if_<array_element_match_v<U const>> = 0>
/* implicit */ constexpr span(std::array<U, N> const& arr) noexcept
: data_{arr.data()}, extent_{N} {}

template <typename Rng, if_<is_range_v<Rng&>> = 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<element_type, E> 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<element_type, dynamic_extent> 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 <size_type Count>
constexpr span<element_type, Count> first() const {
static_assert(!(extent < Count));
assert(!(size() < Count));
return {data_, Count};
}
constexpr span<element_type, dynamic_extent> first(
size_type const count) const {
assert(!(extent < count));
assert(!(size() < count));
return {data_, count};
}

template <size_type Count>
constexpr span<element_type, Count> last() const {
static_assert(!(extent < Count));
assert(!(size() < Count));
return {data_ + size() - Count, Count};
}
constexpr span<element_type, dynamic_extent> 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 <typename U, typename T, std::size_t Extent>
constexpr auto operator()(std::span<T, Extent> in, U* castData) const {
template <
template <typename, std::size_t>
class Span,
typename U,
typename T,
std::size_t Extent>
constexpr auto operator()(Span<T, Extent> in, U* castData) const {
assert(
static_cast<void const*>(in.data()) ==
static_cast<void const*>(castData));
Expand All @@ -44,13 +280,14 @@ struct span_cast_impl_fn {
assert(reinterpret_cast<std::uintptr_t>(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<U>(castData, in.size() * sizeof(T) / sizeof(U));
return Span<U, dynamic_extent>(
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<U, kResSize>(castData, kResSize);
return Span<U, kResSize>(castData, kResSize);
}
}
};
Expand All @@ -76,34 +313,77 @@ inline constexpr span_cast_impl_fn span_cast_impl;

template <typename U>
struct static_span_cast_fn {
template <typename T, std::size_t Extent>
constexpr auto operator()(detail::fallback_span::span<T, Extent> in) const {
return detail::span_cast_impl(in, static_cast<U*>(in.data()));
}
#if __cpp_lib_span >= 202002L
template <typename T, std::size_t Extent>
constexpr auto operator()(std::span<T, Extent> in) const {
return detail::span_cast_impl(in, static_cast<U*>(in.data()));
}
#endif
};
template <typename U>
inline constexpr static_span_cast_fn<U> static_span_cast;

template <typename U>
struct reinterpret_span_cast_fn {
template <typename T, std::size_t Extent>
constexpr auto operator()(detail::fallback_span::span<T, Extent> in) const {
return detail::span_cast_impl(in, reinterpret_cast<U*>(in.data()));
}
#if __cpp_lib_span >= 202002L
template <typename T, std::size_t Extent>
constexpr auto operator()(std::span<T, Extent> in) const {
return detail::span_cast_impl(in, reinterpret_cast<U*>(in.data()));
}
#endif
};
template <typename U>
inline constexpr reinterpret_span_cast_fn<U> reinterpret_span_cast;

template <typename U>
struct const_span_cast_fn {
template <typename T, std::size_t Extent>
constexpr auto operator()(detail::fallback_span::span<T, Extent> in) const {
return detail::span_cast_impl(in, const_cast<U*>(in.data()));
}
#if __cpp_lib_span >= 202002L
template <typename T, std::size_t Extent>
constexpr auto operator()(std::span<T, Extent> in) const {
return detail::span_cast_impl(in, const_cast<U*>(in.data()));
}
#endif
};
template <typename U>
inline constexpr const_span_cast_fn<U> const_span_cast;

#endif
namespace detail {

namespace fallback_span {

/// as_bytes
///
/// mimic: std::as_bytes, C++20
template <typename T, std::size_t Extent>
auto as_bytes(span<T, Extent> s) noexcept {
return reinterpret_span_cast<std::byte const>(s);
}

/// as_writable_bytes
///
/// mimic: std::as_writable_bytes, C++20
template <
typename T,
std::size_t Extent,
std::enable_if_t<!std::is_const_v<T>, int> = 0>
auto as_writable_bytes(span<T, Extent> s) noexcept {
return reinterpret_span_cast<std::byte>(s);
}

} // namespace fallback_span

} // namespace detail

} // namespace folly
1 change: 1 addition & 0 deletions folly/container/test/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ cpp_unittest(
supports_static_listing = False,
deps = [
"//folly/container:span",
"//folly/portability:gmock",
"//folly/portability:gtest",
],
)
Expand Down
Loading

0 comments on commit c9c2b90

Please sign in to comment.