Don't assume the total length of certain arrays
[openal-soft.git] / common / alspan.h
blob046b7d769b389aa861eda65027a656d820daeac4
1 #ifndef AL_SPAN_H
2 #define AL_SPAN_H
4 #include <array>
5 #include <cstddef>
6 #include <initializer_list>
7 #include <iterator>
8 #include <type_traits>
10 namespace al {
12 template<typename T>
13 constexpr auto size(T &cont) -> decltype(cont.size())
14 { return cont.size(); }
16 template<typename T>
17 constexpr auto size(const T &cont) -> decltype(cont.size())
18 { return cont.size(); }
20 template<typename T, size_t N>
21 constexpr size_t size(T (&)[N]) noexcept
22 { return N; }
24 template<typename T>
25 constexpr size_t size(std::initializer_list<T> list) noexcept
26 { return list.size(); }
29 template<typename T>
30 constexpr auto data(T &cont) -> decltype(cont.data())
31 { return cont.data(); }
33 template<typename T>
34 constexpr auto data(const T &cont) -> decltype(cont.data())
35 { return cont.data(); }
37 template<typename T, size_t N>
38 constexpr T* data(T (&arr)[N]) noexcept
39 { return arr; }
41 template<typename T>
42 constexpr const T* data(std::initializer_list<T> list) noexcept
43 { return list.begin(); }
46 template<typename T, size_t E=static_cast<size_t>(-1)>
47 class span;
49 namespace detail_ {
50 template<typename... Ts>
51 struct make_void { using type = void; };
52 template<typename... Ts>
53 using void_t = typename make_void<Ts...>::type;
55 template<typename T>
56 struct is_span_ : std::false_type { };
57 template<typename T, size_t E>
58 struct is_span_<span<T,E>> : std::true_type { };
59 template<typename T>
60 using is_span = is_span_<typename std::remove_cv<T>::type>;
62 template<typename T>
63 struct is_std_array_ : std::false_type { };
64 template<typename T, size_t N>
65 struct is_std_array_<std::array<T,N>> : std::true_type { };
66 template<typename T>
67 using is_std_array = is_std_array_<typename std::remove_cv<T>::type>;
69 template<typename T, typename = void>
70 struct has_size_and_data : std::false_type { };
71 template<typename T>
72 struct has_size_and_data<T,
73 void_t<decltype(al::size(std::declval<T>())), decltype(al::data(std::declval<T>()))>>
74 : std::true_type { };
76 template<typename T>
77 using remove_pointer_t = typename std::remove_pointer<T>::type;
78 } // namespace detail_
80 #define REQUIRES(...) bool rt_=true, typename std::enable_if<rt_ && (__VA_ARGS__),bool>::type = true
81 #define IS_VALID_CONTAINER(C) \
82 !detail_::is_span<C>::value && !detail_::is_std_array<C>::value && \
83 !std::is_array<C>::value && detail_::has_size_and_data<C>::value && \
84 std::is_convertible<detail_::remove_pointer_t<decltype(al::data(std::declval<C&>()))>(*)[],element_type(*)[]>::value
86 template<typename T, size_t E>
87 class span {
88 static constexpr size_t dynamic_extent{static_cast<size_t>(-1)};
90 public:
91 using element_type = T;
92 using value_type = typename std::remove_cv<T>::type;
93 using index_type = size_t;
94 using difference_type = ptrdiff_t;
96 using pointer = T*;
97 using const_pointer = const T*;
98 using reference = T&;
99 using const_reference = const T&;
101 using iterator = pointer;
102 using const_iterator = const_pointer;
103 using reverse_iterator = std::reverse_iterator<iterator>;
104 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
106 static constexpr size_t extent{E};
108 template<REQUIRES(extent==0)>
109 constexpr span() noexcept { }
110 constexpr span(pointer ptr, index_type /*count*/) : mData{ptr} { }
111 constexpr span(pointer first, pointer /*last*/) : mData{first} { }
112 constexpr span(element_type (&arr)[E]) noexcept : span{al::data(arr), al::size(arr)} { }
113 constexpr span(std::array<value_type,E> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
114 template<REQUIRES(std::is_const<element_type>::value)>
115 constexpr span(const std::array<value_type,E> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
116 template<typename U, REQUIRES(IS_VALID_CONTAINER(U))>
117 constexpr span(U &cont) : span{al::data(cont), al::size(cont)} { }
118 template<typename U, REQUIRES(IS_VALID_CONTAINER(const U))>
119 constexpr span(const U &cont) : span{al::data(cont), al::size(cont)} { }
120 template<typename U, REQUIRES(!std::is_same<element_type,U>::value && std::is_convertible<U(*)[],element_type(*)[]>::value)>
121 constexpr span(const span<U,E> &span_) noexcept : span{al::data(span_), al::size(span_)} { }
122 constexpr span(const span&) noexcept = default;
124 span& operator=(const span &rhs) noexcept = default;
126 constexpr reference front() const { return *mData; }
127 constexpr reference back() const { return *(mData+E-1); }
128 constexpr reference operator[](index_type idx) const { return mData[idx]; }
129 constexpr pointer data() const noexcept { return mData; }
131 constexpr index_type size() const noexcept { return E; }
132 constexpr index_type size_bytes() const noexcept { return E * sizeof(value_type); }
133 constexpr bool empty() const noexcept { return E == 0; }
135 constexpr iterator begin() const noexcept { return mData; }
136 constexpr iterator end() const noexcept { return mData+E; }
137 constexpr const_iterator cbegin() const noexcept { return mData; }
138 constexpr const_iterator cend() const noexcept { return mData+E; }
140 constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
141 constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
142 constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; }
143 constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; }
145 template<size_t C>
146 constexpr span<element_type,C> first() const
148 static_assert(E >= C, "New size exceeds original capacity");
149 return span<element_type,C>{mData, C};
152 template<size_t C>
153 constexpr span<element_type,C> last() const
155 static_assert(E >= C, "New size exceeds original capacity");
156 return span<element_type,C>{mData+(E-C), C};
159 template<size_t O, size_t C, size_t RealC=((C==dynamic_extent) ? E-O : C)>
160 constexpr span<element_type,RealC> subspan() const
162 static_assert(E >= O, "Offset exceeds extent");
163 static_assert(E-O >= RealC, "New size exceeds original capacity");
164 return span<element_type,RealC>{mData+O, RealC};
167 /* NOTE: Can't declare objects of a specialized template class prior to
168 * defining the specialization. As a result, these methods need to be
169 * defined later.
171 constexpr span<element_type,dynamic_extent> first(size_t count) const;
172 constexpr span<element_type,dynamic_extent> last(size_t count) const;
173 constexpr span<element_type,dynamic_extent> subspan(size_t offset, size_t count=dynamic_extent) const;
175 private:
176 pointer mData{nullptr};
179 template<typename T>
180 class span<T,static_cast<size_t>(-1)> {
181 static constexpr size_t dynamic_extent{static_cast<size_t>(-1)};
183 public:
184 using element_type = T;
185 using value_type = typename std::remove_cv<T>::type;
186 using index_type = size_t;
187 using difference_type = ptrdiff_t;
189 using pointer = T*;
190 using const_pointer = const T*;
191 using reference = T&;
192 using const_reference = const T&;
194 using iterator = pointer;
195 using const_iterator = const_pointer;
196 using reverse_iterator = std::reverse_iterator<iterator>;
197 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
199 static constexpr size_t extent{static_cast<size_t>(-1)};
201 constexpr span() noexcept = default;
202 constexpr span(pointer ptr, index_type count) : mData{ptr}, mDataEnd{ptr+count} { }
203 constexpr span(pointer first, pointer last) : mData{first}, mDataEnd{last} { }
204 template<size_t N>
205 constexpr span(element_type (&arr)[N]) noexcept : span{al::data(arr), al::size(arr)} { }
206 template<size_t N>
207 constexpr span(std::array<value_type,N> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
208 template<size_t N, REQUIRES(std::is_const<element_type>::value)>
209 constexpr span(const std::array<value_type,N> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
210 template<typename U, REQUIRES(IS_VALID_CONTAINER(U))>
211 constexpr span(U &cont) : span{al::data(cont), al::size(cont)} { }
212 template<typename U, REQUIRES(IS_VALID_CONTAINER(const U))>
213 constexpr span(const U &cont) : span{al::data(cont), al::size(cont)} { }
214 template<typename U, size_t N, REQUIRES((!std::is_same<element_type,U>::value || extent != N) && std::is_convertible<U(*)[],element_type(*)[]>::value)>
215 constexpr span(const span<U,N> &span_) noexcept : span{al::data(span_), al::size(span_)} { }
216 constexpr span(const span&) noexcept = default;
218 span& operator=(const span &rhs) noexcept = default;
220 constexpr reference front() const { return *mData; }
221 constexpr reference back() const { return *(mDataEnd-1); }
222 constexpr reference operator[](index_type idx) const { return mData[idx]; }
223 constexpr pointer data() const noexcept { return mData; }
225 constexpr index_type size() const noexcept { return static_cast<index_type>(mDataEnd-mData); }
226 constexpr index_type size_bytes() const noexcept
227 { return static_cast<index_type>(mDataEnd-mData) * sizeof(value_type); }
228 constexpr bool empty() const noexcept { return mData == mDataEnd; }
230 constexpr iterator begin() const noexcept { return mData; }
231 constexpr iterator end() const noexcept { return mDataEnd; }
232 constexpr const_iterator cbegin() const noexcept { return mData; }
233 constexpr const_iterator cend() const noexcept { return mDataEnd; }
235 constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
236 constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
237 constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; }
238 constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; }
240 template<size_t C>
241 constexpr span<element_type,C> first() const
242 { return span<element_type,C>{mData, C}; }
244 constexpr span first(size_t count) const
245 { return (count >= size()) ? *this : span{mData, mData+count}; }
247 template<size_t C>
248 constexpr span<element_type,C> last() const
249 { return span<element_type,C>{mDataEnd-C, C}; }
251 constexpr span last(size_t count) const
252 { return (count >= size()) ? *this : span{mDataEnd-count, mDataEnd}; }
254 template<size_t O, size_t C=dynamic_extent>
255 constexpr span<element_type,C> subspan() const
256 { return span<element_type,C>{mData+O, (C!=dynamic_extent) ? mData+C : mDataEnd}; }
258 constexpr span subspan(size_t offset, size_t count=dynamic_extent) const
260 return (offset > size()) ? span{} :
261 (count >= size()-offset) ? span{mData+offset, mDataEnd} :
262 span{mData+offset, mData+offset+count};
265 private:
266 pointer mData{nullptr};
267 pointer mDataEnd{nullptr};
270 template<typename T, size_t E>
271 constexpr inline auto span<T,E>::first(size_t count) const -> span<element_type,dynamic_extent>
273 return (count >= size()) ? span<element_type>{mData, extent} :
274 span<element_type>{mData, count};
277 template<typename T, size_t E>
278 constexpr inline auto span<T,E>::last(size_t count) const -> span<element_type,dynamic_extent>
280 return (count >= size()) ? span<element_type>{mData, extent} :
281 span<element_type>{mData+extent-count, count};
284 template<typename T, size_t E>
285 constexpr inline auto span<T,E>::subspan(size_t offset, size_t count) const -> span<element_type,dynamic_extent>
287 return (offset > size()) ? span<element_type>{} :
288 (count >= size()-offset) ? span<element_type>{mData+offset, mData+extent} :
289 span<element_type>{mData+offset, mData+offset+count};
292 #undef IS_VALID_CONTAINER
293 #undef REQUIRES
295 } // namespace al
297 #endif /* AL_SPAN_H */