Skip to content

Commit f65f9ed

Browse files
authored
[wpiutil] Add rotated_span (#7111)
1 parent 8f57e4c commit f65f9ed

File tree

2 files changed

+458
-0
lines changed

2 files changed

+458
-0
lines changed
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
// Copyright (c) FIRST and other WPILib contributors.
2+
// Open Source Software; you can modify and/or share it under the terms of
3+
// the WPILib BSD license file in the root directory of this project.
4+
5+
#pragma once
6+
7+
#include <array>
8+
#include <cstddef>
9+
#include <iterator>
10+
#include <span>
11+
#include <type_traits>
12+
13+
namespace wpi {
14+
15+
/**
16+
* This is a simple rotated span view. Indexed/iterated access provides a
17+
* continuous view of the underlying span that wraps at the span size. An
18+
* internal offset determines the starting location.
19+
*
20+
* Constructors take a "rotation" value--if positive, the offset is the same as
21+
* the rotation; if negative, the offset is set relative to the end of the
22+
* array.
23+
*
24+
* For example, given an array of 5 values, providing a rotation value of 2
25+
* will result in an index of 0 accessing underlying span index 2, index 2
26+
* accessing underlying span index 4, and index 4 accessing underlying span
27+
* index 1.
28+
*
29+
* Similarly, providing a rotation value of -2 will result in index 0 accessing
30+
* underlying span index 3 (5-2), index 2 accessing underlying span index 0,
31+
* and index 4 accessing underlying span index 2.
32+
*
33+
* @tparam T element type
34+
* @tparam Extent static sized extent, or std::dynamic_extent
35+
*/
36+
template <typename T, size_t Extent = std::dynamic_extent>
37+
class rotated_span {
38+
public:
39+
using element_type = T;
40+
using value_type = std::remove_cv_t<T>;
41+
using size_type = size_t;
42+
using difference_type = std::ptrdiff_t;
43+
using pointer = T*;
44+
using const_pointer = const T*;
45+
using reference = element_type&;
46+
using const_reference = const element_type&;
47+
48+
// member constants
49+
static constexpr size_t extent = Extent;
50+
51+
// constructors, copy and assignment
52+
53+
constexpr rotated_span() noexcept
54+
requires(Extent == std::dynamic_extent || Extent == 0)
55+
= default;
56+
57+
constexpr /*implicit*/ rotated_span(std::span<T, Extent> data, // NOLINT
58+
int rotation = 0)
59+
: m_data{data}, m_offset{MakeOffset(data.size(), rotation)} {}
60+
61+
template <std::contiguous_iterator It>
62+
constexpr explicit(extent != std::dynamic_extent)
63+
rotated_span(It first, size_type count, int rotation = 0) noexcept
64+
: rotated_span{std::span<T, Extent>{first, count}, rotation} {}
65+
66+
template <std::contiguous_iterator It, std::sized_sentinel_for<It> End>
67+
requires(!std::is_convertible_v<End, size_type>)
68+
constexpr explicit(extent != std::dynamic_extent)
69+
rotated_span(It first, End last,
70+
int rotation = 0) noexcept(noexcept(std::span<T, Extent>{
71+
first, last}))
72+
: rotated_span{std::span<T, Extent>{first, last}, rotation} {}
73+
74+
template <size_t ArrayExtent>
75+
requires(Extent == std::dynamic_extent || ArrayExtent == Extent)
76+
constexpr rotated_span( // NOLINT
77+
std::type_identity_t<element_type> (&arr)[ArrayExtent],
78+
int rotation = 0) noexcept
79+
: rotated_span{std::span<T, Extent>{arr}, rotation} {}
80+
81+
template <typename Tp, size_t ArrayExtent>
82+
constexpr rotated_span(std::array<Tp, ArrayExtent>& arr, // NOLINT
83+
int rotation = 0) noexcept
84+
: rotated_span{std::span<T, Extent>{arr}, rotation} {}
85+
86+
template <typename Tp, size_t ArrayExtent>
87+
constexpr rotated_span(const std::array<Tp, ArrayExtent>& arr, // NOLINT
88+
int rotation = 0) noexcept
89+
: rotated_span{std::span<T, Extent>{arr}, rotation} {}
90+
91+
template <typename OType, size_t OExtent>
92+
requires(Extent == std::dynamic_extent || OExtent == std::dynamic_extent ||
93+
Extent == OExtent)
94+
constexpr explicit(extent != std::dynamic_extent &&
95+
OExtent == std::dynamic_extent)
96+
rotated_span(const rotated_span<OType, OExtent>& other,
97+
int rotation) noexcept
98+
: m_data{other.m_data},
99+
m_offset{MakeOffset(other.m_data.size(), other.m_offset + rotation)} {}
100+
101+
template <typename OType, size_t OExtent>
102+
requires(Extent == std::dynamic_extent || OExtent == std::dynamic_extent ||
103+
Extent == OExtent)
104+
constexpr explicit(extent != std::dynamic_extent &&
105+
OExtent == std::dynamic_extent) rotated_span( // NOLINT
106+
const rotated_span<OType, OExtent>& other) noexcept
107+
: m_data{other.m_data}, m_offset{other.m_offset} {}
108+
constexpr rotated_span& operator=(const rotated_span&) noexcept = default;
109+
constexpr rotated_span(rotated_span&&) noexcept = default;
110+
constexpr rotated_span& operator=(rotated_span&&) noexcept = default;
111+
112+
~rotated_span() noexcept = default;
113+
114+
// observers
115+
116+
constexpr std::span<T, Extent> data() const noexcept { return m_data; }
117+
118+
constexpr size_type offset() const noexcept { return m_offset; }
119+
120+
constexpr size_type size() const noexcept { return m_data.size(); }
121+
122+
constexpr size_type size_bytes() const noexcept {
123+
return m_data.size_bytes();
124+
}
125+
126+
constexpr bool empty() const noexcept { return m_data.empty(); }
127+
128+
// element access
129+
130+
[[nodiscard]]
131+
constexpr reference front() const noexcept {
132+
return m_data[m_offset];
133+
}
134+
135+
[[nodiscard]]
136+
constexpr reference back() const noexcept {
137+
return m_data[(m_offset + m_data.size() - 1) % m_data.size()];
138+
}
139+
140+
[[nodiscard]]
141+
constexpr reference operator[](size_type idx) const noexcept {
142+
return m_data[(m_offset + idx) % m_data.size()];
143+
}
144+
145+
// iterator support
146+
147+
class iterator {
148+
public:
149+
using iterator_category = std::forward_iterator_tag;
150+
using value_type = rotated_span::value_type;
151+
using difference_type = rotated_span::difference_type;
152+
using pointer = rotated_span::pointer;
153+
using reference = rotated_span::reference;
154+
155+
constexpr iterator(const rotated_span* obj, size_type idx) noexcept
156+
: m_obj{obj}, m_idx{idx} {}
157+
158+
constexpr iterator& operator++() noexcept {
159+
++m_idx;
160+
return *this;
161+
}
162+
constexpr iterator operator++(int) noexcept {
163+
iterator retval = *this;
164+
++(*this);
165+
return retval;
166+
}
167+
constexpr bool operator==(const iterator&) const = default;
168+
constexpr reference operator*() { return (*m_obj)[m_idx]; }
169+
170+
private:
171+
const rotated_span* m_obj;
172+
size_type m_idx;
173+
};
174+
175+
using reverse_iterator = std::reverse_iterator<iterator>;
176+
177+
[[nodiscard]]
178+
constexpr iterator begin() const noexcept {
179+
return iterator(this, 0);
180+
}
181+
182+
[[nodiscard]]
183+
constexpr iterator end() const noexcept {
184+
return iterator(this, size());
185+
}
186+
187+
[[nodiscard]]
188+
constexpr reverse_iterator rbegin() const noexcept {
189+
return reverse_iterator(end());
190+
}
191+
192+
[[nodiscard]]
193+
constexpr reverse_iterator rend() const noexcept {
194+
return reverse_iterator(begin());
195+
}
196+
197+
// subviews
198+
[[nodiscard]]
199+
constexpr rotated_span rotate(int amt) const noexcept {
200+
return rotated_span{*this, amt};
201+
}
202+
203+
private:
204+
std::span<T, Extent> m_data;
205+
size_type m_offset;
206+
207+
static constexpr size_type MakeOffset(size_t size, int rotation) {
208+
if (size == 0) {
209+
return 0;
210+
}
211+
if (rotation >= 0) {
212+
return rotation % size;
213+
} else {
214+
// The usual arithmetic conversions mean that rotation is converted to
215+
// size_t, which is unsigned. Converting negative values to unsigned
216+
// integer types produces large numbers with useless remainders, so
217+
// instead make rotation positive before doing the modulo arithmetic.
218+
return size - (-rotation % size);
219+
}
220+
}
221+
};
222+
223+
// deduction guides
224+
225+
template <typename Type, size_t ArrayExtent>
226+
rotated_span(Type (&)[ArrayExtent]) -> rotated_span<Type, ArrayExtent>;
227+
228+
template <typename Type, size_t ArrayExtent>
229+
rotated_span(std::array<Type, ArrayExtent>&) -> rotated_span<Type, ArrayExtent>;
230+
231+
template <std::contiguous_iterator It, typename End>
232+
rotated_span(It, End)
233+
-> rotated_span<std::remove_reference_t<std::iter_reference_t<It>>>;
234+
235+
template <typename Type, size_t Extent>
236+
[[nodiscard]]
237+
inline rotated_span<const std::byte, Extent == std::dynamic_extent
238+
? std::dynamic_extent
239+
: Extent * sizeof(Type)>
240+
as_bytes(rotated_span<Type, Extent> sp) noexcept {
241+
return {std::as_bytes(sp.data()), sp.offset() * sizeof(Type)};
242+
}
243+
244+
template <typename Type, size_t Extent>
245+
requires(!std::is_const_v<Type>)
246+
[[nodiscard]]
247+
inline rotated_span<std::byte, Extent == std::dynamic_extent
248+
? std::dynamic_extent
249+
: Extent * sizeof(Type)>
250+
as_writable_bytes(rotated_span<Type, Extent> sp) noexcept {
251+
return {std::as_writable_bytes(sp.data()), sp.offset() * sizeof(Type)};
252+
}
253+
254+
} // namespace wpi

0 commit comments

Comments
 (0)