2 //===----------------------------------------------------------------------===//
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
9 // Copyright (2022) National Technology & Engineering
10 // Solutions of Sandia, LLC (NTESS).
12 // Under the terms of Contract DE-NA0003525 with NTESS,
13 // the U.S. Government retains certain rights in this software.
15 //===---------------------------------------------------------------------===//
17 #ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H
18 #define TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H
28 #include <span> // dynamic_extent
29 #include <type_traits>
32 // Layout that wraps indices to test some idiosyncratic behavior
33 // - basically it is a layout_left where indices are first wrapped i.e. i%Wrap
34 // - only accepts integers as indices
35 // - is_always_strided and is_always_unique are false
36 // - is_strided and is_unique are true if all extents are smaller than Wrap
37 // - not default constructible
38 // - not extents constructible
39 // - not trivially copyable
40 // - does not check dynamic to static extent conversion in converting ctor
41 // - check via side-effects that mdspan::swap calls mappings swap via ADL
43 struct not_extents_constructible_tag
{};
45 template <size_t Wrap
>
46 class layout_wrapping_integral
{
48 template <class Extents
>
52 template <size_t WrapArg
>
53 template <class Extents
>
54 class layout_wrapping_integral
<WrapArg
>::mapping
{
55 static constexpr typename
Extents::index_type Wrap
= static_cast<typename
Extents::index_type
>(WrapArg
);
58 using extents_type
= Extents
;
59 using index_type
= typename
extents_type::index_type
;
60 using size_type
= typename
extents_type::size_type
;
61 using rank_type
= typename
extents_type::rank_type
;
62 using layout_type
= layout_wrapping_integral
<Wrap
>;
65 static constexpr bool required_span_size_is_representable(const extents_type
& ext
) {
66 if constexpr (extents_type::rank() == 0)
69 index_type prod
= ext
.extent(0);
70 for (rank_type r
= 1; r
< extents_type::rank(); r
++) {
71 bool overflowed
= __builtin_mul_overflow(prod
, std::min(ext
.extent(r
), Wrap
), &prod
);
79 constexpr mapping() noexcept
= delete;
80 constexpr mapping(const mapping
& other
) noexcept
: extents_(other
.extents()) {}
81 constexpr mapping(extents_type
&& ext
) noexcept
84 constexpr mapping(const extents_type
& ext
, not_extents_constructible_tag
) noexcept
: extents_(ext
) {}
86 template <class OtherExtents
>
87 requires(std::is_constructible_v
<extents_type
, OtherExtents
> && (Wrap
!= 8))
88 constexpr explicit(!std::is_convertible_v
<OtherExtents
, extents_type
>)
89 mapping(const mapping
<OtherExtents
>& other
) noexcept
{
90 std::array
<index_type
, extents_type::rank_dynamic()> dyn_extents
;
92 for (rank_type r
= 0; r
< extents_type::rank(); r
++) {
93 if (extents_type::static_extent(r
) == std::dynamic_extent
) {
94 dyn_extents
[count
++] = other
.extents().extent(r
);
97 extents_
= extents_type(dyn_extents
);
99 template <class OtherExtents
>
100 requires(std::is_constructible_v
<extents_type
, OtherExtents
> && (Wrap
== 8))
101 constexpr explicit(!std::is_convertible_v
<OtherExtents
, extents_type
>)
102 mapping(mapping
<OtherExtents
>&& other
) noexcept
{
103 std::array
<index_type
, extents_type::rank_dynamic()> dyn_extents
;
105 for (rank_type r
= 0; r
< extents_type::rank(); r
++) {
106 if (extents_type::static_extent(r
) == std::dynamic_extent
) {
107 dyn_extents
[count
++] = other
.extents().extent(r
);
110 extents_
= extents_type(dyn_extents
);
113 constexpr mapping
& operator=(const mapping
& other
) noexcept
{
114 extents_
= other
.extents_
;
118 constexpr const extents_type
& extents() const noexcept
{ return extents_
; }
120 constexpr index_type
required_span_size() const noexcept
{
122 for (size_t r
= 0; r
< extents_type::rank(); r
++)
123 size
*= extents_
.extent(r
) < Wrap
? extents_
.extent(r
) : Wrap
;
127 template <std::integral
... Indices
>
128 requires((sizeof...(Indices
) == extents_type::rank()) && (std::is_convertible_v
<Indices
, index_type
> && ...) &&
129 (std::is_nothrow_constructible_v
<index_type
, Indices
> && ...))
130 constexpr index_type
operator()(Indices
... idx
) const noexcept
{
131 std::array
<index_type
, extents_type::rank()> idx_a
{static_cast<index_type
>(static_cast<index_type
>(idx
) % Wrap
)...};
132 return [&]<size_t... Pos
>(std::index_sequence
<Pos
...>) {
134 ((res
= idx_a
[extents_type::rank() - 1 - Pos
] +
135 (extents_
.extent(extents_type::rank() - 1 - Pos
) < Wrap
? extents_
.extent(extents_type::rank() - 1 - Pos
)
140 }(std::make_index_sequence
<sizeof...(Indices
)>());
143 static constexpr bool is_always_unique() noexcept
{ return false; }
144 static constexpr bool is_always_exhaustive() noexcept
{ return true; }
145 static constexpr bool is_always_strided() noexcept
{ return false; }
147 constexpr bool is_unique() const noexcept
{
148 for (rank_type r
= 0; r
< extents_type::rank(); r
++) {
149 if (extents_
.extent(r
) > Wrap
)
154 static constexpr bool is_exhaustive() noexcept
{ return true; }
155 constexpr bool is_strided() const noexcept
{
156 for (rank_type r
= 0; r
< extents_type::rank(); r
++) {
157 if (extents_
.extent(r
) > Wrap
)
163 constexpr index_type
stride(rank_type r
) const noexcept
164 requires(extents_type::rank() > 0)
167 for (rank_type i
= extents_type::rank() - 1; i
> r
; i
--)
168 s
*= extents_
.extent(i
);
172 template <class OtherExtents
>
173 requires(OtherExtents::rank() == extents_type::rank())
174 friend constexpr bool operator==(const mapping
& lhs
, const mapping
<OtherExtents
>& rhs
) noexcept
{
175 return lhs
.extents() == rhs
.extents();
178 friend constexpr void swap(mapping
& x
, mapping
& y
) noexcept
{
179 swap(x
.extents_
, y
.extents_
);
180 if (!std::is_constant_evaluated()) {
185 static int& swap_counter() {
186 static int value
= 0;
191 extents_type extents_
{};
194 template <class Extents
>
195 constexpr auto construct_mapping(std::layout_left
, Extents exts
) {
196 return std::layout_left::mapping
<Extents
>(exts
);
199 template <class Extents
>
200 constexpr auto construct_mapping(std::layout_right
, Extents exts
) {
201 return std::layout_right::mapping
<Extents
>(exts
);
204 template <size_t Wraps
, class Extents
>
205 constexpr auto construct_mapping(layout_wrapping_integral
<Wraps
>, Extents exts
) {
206 return typename layout_wrapping_integral
<Wraps
>::template mapping
<Extents
>(exts
, not_extents_constructible_tag
{});
209 // This layout does not check convertibility of extents for its conversion ctor
210 // Allows triggering mdspan's ctor static assertion on convertibility of extents
211 // It also allows for negative strides and offsets via runtime arguments
212 class always_convertible_layout
{
214 template <class Extents
>
218 template <class Extents
>
219 class always_convertible_layout::mapping
{
221 using extents_type
= Extents
;
222 using index_type
= typename
extents_type::index_type
;
223 using size_type
= typename
extents_type::size_type
;
224 using rank_type
= typename
extents_type::rank_type
;
225 using layout_type
= always_convertible_layout
;
228 static constexpr bool required_span_size_is_representable(const extents_type
& ext
) {
229 if constexpr (extents_type::rank() == 0)
232 index_type prod
= ext
.extent(0);
233 for (rank_type r
= 1; r
< extents_type::rank(); r
++) {
234 bool overflowed
= __builtin_mul_overflow(prod
, ext
.extent(r
), &prod
);
242 constexpr mapping() noexcept
= delete;
243 constexpr mapping(const mapping
& other
) noexcept
244 : extents_(other
.extents_
), offset_(other
.offset_
), scaling_(other
.scaling_
) {}
245 constexpr mapping(const extents_type
& ext
, index_type offset
= 0, index_type scaling
= 1) noexcept
246 : extents_(ext
), offset_(offset
), scaling_(scaling
) {}
248 template <class OtherExtents
>
249 constexpr mapping(const mapping
<OtherExtents
>& other
) noexcept
{
250 if constexpr (extents_type::rank() == OtherExtents::rank()) {
251 std::array
<index_type
, extents_type::rank_dynamic()> dyn_extents
;
253 for (rank_type r
= 0; r
< extents_type::rank(); r
++) {
254 if (extents_type::static_extent(r
) == std::dynamic_extent
) {
255 dyn_extents
[count
++] = other
.extents().extent(r
);
258 extents_
= extents_type(dyn_extents
);
260 extents_
= extents_type();
262 offset_
= other
.offset_
;
263 scaling_
= other
.scaling_
;
266 constexpr mapping
& operator=(const mapping
& other
) noexcept
{
267 extents_
= other
.extents_
;
268 offset_
= other
.offset_
;
269 scaling_
= other
.scaling_
;
273 constexpr const extents_type
& extents() const noexcept
{ return extents_
; }
275 constexpr index_type
required_span_size() const noexcept
{
277 for (size_t r
= 0; r
< extents_type::rank(); r
++)
278 size
*= extents_
.extent(r
);
279 return std::max(size
* scaling_
+ offset_
, offset_
);
282 template <std::integral
... Indices
>
283 requires((sizeof...(Indices
) == extents_type::rank()) && (std::is_convertible_v
<Indices
, index_type
> && ...) &&
284 (std::is_nothrow_constructible_v
<index_type
, Indices
> && ...))
285 constexpr index_type
operator()(Indices
... idx
) const noexcept
{
286 std::array
<index_type
, extents_type::rank()> idx_a
{static_cast<index_type
>(static_cast<index_type
>(idx
))...};
288 scaling_
* ([&]<size_t... Pos
>(std::index_sequence
<Pos
...>) {
290 ((res
= idx_a
[extents_type::rank() - 1 - Pos
] + extents_
.extent(extents_type::rank() - 1 - Pos
) * res
),
293 }(std::make_index_sequence
<sizeof...(Indices
)>()));
296 static constexpr bool is_always_unique() noexcept
{ return true; }
297 static constexpr bool is_always_exhaustive() noexcept
{ return true; }
298 static constexpr bool is_always_strided() noexcept
{ return true; }
300 static constexpr bool is_unique() noexcept
{ return true; }
301 static constexpr bool is_exhaustive() noexcept
{ return true; }
302 static constexpr bool is_strided() noexcept
{ return true; }
304 constexpr index_type
stride(rank_type r
) const noexcept
305 requires(extents_type::rank() > 0)
308 for (rank_type i
= 0; i
< r
; i
++)
309 s
*= extents_
.extent(i
);
313 template <class OtherExtents
>
314 requires(OtherExtents::rank() == extents_type::rank())
315 friend constexpr bool operator==(const mapping
& lhs
, const mapping
<OtherExtents
>& rhs
) noexcept
{
316 return lhs
.extents() == rhs
.extents() && lhs
.offset_
== rhs
.offset
&& lhs
.scaling_
== rhs
.scaling_
;
319 friend constexpr void swap(mapping
& x
, mapping
& y
) noexcept
{
320 swap(x
.extents_
, y
.extents_
);
321 if (!std::is_constant_evaluated()) {
326 static int& swap_counter() {
327 static int value
= 0;
333 friend class mapping
;
335 extents_type extents_
{};
336 index_type offset_
{};
337 index_type scaling_
{};
339 #endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H