C/C++ API Reference
Loading...
Searching...
No Matches
span_impl.h
1// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15// This span implementation is a stand-in for C++20's std::span. Do NOT include
16// this header directly; instead, include it as "pw_span/span.h".
17//
18// A span is a non-owning array view class. It refers to an external array by
19// storing a pointer and length. Unlike std::array, the size does not have to be
20// a template parameter, so this class can be used to without stating its size.
21//
22// This file a modified version of base::span from Chromium:
23// https://chromium.googlesource.com/chromium/src/+/d93ae920e4309682deb9352a4637cfc2941c1d1f/base/containers/span.h
24//
25// In order to minimize changes from the original, this file does NOT fully
26// adhere to Pigweed's style guide.
27//
28// A few changes were made to the Chromium version of span. These include:
29// - Use std::data and std::size instead of base::* versions.
30// - Rename base namespace to pw.
31// - Rename internal namespace to pw_span_internal.
32// - Remove uses of checked_iterators.h and CHECK.
33// - Replace make_span functions with C++17 class template deduction guides.
34// - Use std::byte instead of uint8_t for compatibility with std::span.
35#pragma once
36
37#include <algorithm>
38#include <array>
39#include <cstddef>
40#include <iterator>
41#include <limits>
42#include <type_traits>
43#include <utility>
44
45#include "pw_span/internal/config.h"
46
47// IWYU pragma: private, include "pw_span/span.h"
48
49namespace pw {
50
51// [views.constants]
52inline constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
53
54template <typename T, size_t Extent = dynamic_extent>
55class span;
56
57namespace pw_span_internal {
58
59template <typename T>
60struct ExtentImpl : std::integral_constant<size_t, dynamic_extent> {};
61
62template <typename T, size_t N>
63struct ExtentImpl<T[N]> : std::integral_constant<size_t, N> {};
64
65template <typename T, size_t N>
66struct ExtentImpl<std::array<T, N>> : std::integral_constant<size_t, N> {};
67
68template <typename T, size_t N>
69struct ExtentImpl<span<T, N>> : std::integral_constant<size_t, N> {};
70
71template <typename T>
73
74template <typename T>
75struct IsSpanImpl : std::false_type {};
76
77template <typename T, size_t Extent>
78struct IsSpanImpl<span<T, Extent>> : std::true_type {};
79
80template <typename T>
82
83template <typename T>
84struct IsStdArrayImpl : std::false_type {};
85
86template <typename T, size_t N>
87struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
88
89template <typename T>
91
92template <typename T>
93using IsCArray = std::is_array<std::remove_reference_t<T>>;
94
95template <typename From, typename To>
96using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
97
98template <typename Container, typename T>
99using ContainerHasConvertibleData = IsLegalDataConversion<
100 std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>,
101 T>;
102
103template <typename Container>
104using ContainerHasIntegralSize =
105 std::is_integral<decltype(std::size(std::declval<Container>()))>;
106
107template <typename From, size_t FromExtent, typename To, size_t ToExtent>
108using EnableIfLegalSpanConversion =
109 std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
110 IsLegalDataConversion<From, To>::value>;
111
112// SFINAE check if Array can be converted to a span<T>.
113template <typename Array, typename T, size_t Extent>
114using EnableIfSpanCompatibleArray =
115 std::enable_if_t<(Extent == dynamic_extent ||
117 ContainerHasConvertibleData<Array, T>::value>;
118
119// SFINAE check if Container can be converted to a span<T>.
120template <typename Container, typename T>
121using IsSpanCompatibleContainer =
122 std::conditional_t<!IsSpan<Container>::value &&
124 !IsCArray<Container>::value &&
125 ContainerHasConvertibleData<Container, T>::value &&
126 ContainerHasIntegralSize<Container>::value,
127 std::true_type,
128 std::false_type>;
129
130template <typename Container, typename T>
131using EnableIfSpanCompatibleContainer =
132 std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value>;
133
134template <typename Container, typename T, size_t Extent>
135using EnableIfSpanCompatibleContainerAndSpanIsDynamic =
136 std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value &&
137 Extent == dynamic_extent>;
138
139// A helper template for storing the size of a span. Spans with static extents
140// don't require additional storage, since the extent itself is specified in the
141// template parameter.
142template <size_t Extent>
144 public:
145 constexpr explicit ExtentStorage(size_t /* size */) noexcept {}
146 constexpr size_t size() const noexcept { return Extent; }
147};
148
149// Specialization of ExtentStorage for dynamic extents, which do require
150// explicit storage for the size.
151template <>
152struct ExtentStorage<dynamic_extent> {
153 constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {}
154 constexpr size_t size() const noexcept { return size_; }
155
156 private:
157 size_t size_;
158};
159
160} // namespace pw_span_internal
161
163
233// [span], class template span
234template <typename T, size_t Extent>
235class span : public pw_span_internal::ExtentStorage<Extent> {
236 private:
238
239 public:
240 using element_type = T;
241 using value_type = std::remove_cv_t<T>;
242 using size_type = size_t;
243 using difference_type = ptrdiff_t;
244 using pointer = T*;
245 using reference = T&;
246 using iterator = T*;
247 using reverse_iterator = std::reverse_iterator<iterator>;
248 static constexpr size_t extent = Extent;
249
250 // [span.cons], span constructors, copy, assignment, and destructor
251 constexpr span() noexcept : ExtentStorage(0), data_(nullptr) {
252 static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
253 }
254
255 constexpr span(T* data, size_t size) noexcept
256 : ExtentStorage(size), data_(data) {
257 _PW_SPAN_ASSERT(Extent == dynamic_extent || Extent == size);
258 }
259
260 // Prevent construction from nullptr, which is disallowed by C++20's std::span
261 constexpr span(std::nullptr_t data, size_t size) = delete;
262
263 // Artificially templatized to break ambiguity for span(ptr, 0).
264 template <typename = void>
265 constexpr span(T* begin, T* end) noexcept
266 : span(begin, static_cast<size_t>(end - begin)) {
267 // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
268 _PW_SPAN_ASSERT(begin <= end);
269 }
270
271 template <
272 size_t N,
273 typename =
274 pw_span_internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>>
275 constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {}
276
277 template <typename U,
278 size_t N,
279 typename = pw_span_internal::
280 EnableIfSpanCompatibleArray<std::array<U, N>&, T, Extent>>
281 constexpr span(std::array<U, N>& array) noexcept
282 : span(std::data(array), N) {}
283
284 template <typename U,
285 size_t N,
286 typename = pw_span_internal::
287 EnableIfSpanCompatibleArray<const std::array<U, N>&, T, Extent>>
288 constexpr span(const std::array<U, N>& array) noexcept
289 : span(std::data(array), N) {}
290
291 // Conversion from a container that has compatible std::data() and integral
292 // std::size().
293 template <typename Container,
294 typename = pw_span_internal::
295 EnableIfSpanCompatibleContainerAndSpanIsDynamic<Container&,
296 T,
297 Extent>>
298 constexpr span(Container& container) noexcept
299 : span(std::data(container), std::size(container)) {}
300
301 template <
302 typename Container,
303 typename = pw_span_internal::
304 EnableIfSpanCompatibleContainerAndSpanIsDynamic<const Container&,
305 T,
306 Extent>>
307 constexpr span(const Container& container) noexcept
308 : span(std::data(container), std::size(container)) {}
309
310 constexpr span(const span& other) noexcept = default;
311
312 // Conversions from spans of compatible types and extents: this allows a
313 // span<T> to be seamlessly used as a span<const T>, but not the other way
314 // around. If extent is not dynamic, OtherExtent has to be equal to Extent.
315 template <typename U,
316 size_t OtherExtent,
317 typename = pw_span_internal::
318 EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
319 constexpr span(const span<U, OtherExtent>& other)
320 : span(other.data(), other.size()) {}
321
322 constexpr span& operator=(const span& other) noexcept = default;
323 ~span() noexcept = default;
324
325 // [span.sub], span subviews
326 template <size_t Count>
327 constexpr span<T, Count> first() const noexcept {
328 static_assert(Count <= Extent, "Count must not exceed Extent");
329 _PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size());
330 return {data(), Count};
331 }
332
333 template <size_t Count>
334 constexpr span<T, Count> last() const noexcept {
335 static_assert(Count <= Extent, "Count must not exceed Extent");
336 _PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size());
337 return {data() + (size() - Count), Count};
338 }
339
340 template <size_t Offset, size_t Count = dynamic_extent>
341 constexpr span<T,
342 (Count != dynamic_extent
343 ? Count
344 : (Extent != dynamic_extent ? Extent - Offset
345 : dynamic_extent))>
346 subspan() const noexcept {
347 static_assert(Offset <= Extent, "Offset must not exceed Extent");
348 static_assert(Count == dynamic_extent || Count <= Extent - Offset,
349 "Count must not exceed Extent - Offset");
350 _PW_SPAN_ASSERT(Extent != dynamic_extent || Offset <= size());
351 _PW_SPAN_ASSERT(Extent != dynamic_extent || Count == dynamic_extent ||
352 Count <= size() - Offset);
353 return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
354 }
355
356 constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
357 // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
358 _PW_SPAN_ASSERT(count <= size());
359 return {data(), count};
360 }
361
362 constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
363 // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
364 _PW_SPAN_ASSERT(count <= size());
365 return {data() + (size() - count), count};
366 }
367
368 constexpr span<T, dynamic_extent> subspan(
369 size_t offset, size_t count = dynamic_extent) const noexcept {
370 // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
371 _PW_SPAN_ASSERT(offset <= size());
372 _PW_SPAN_ASSERT(count == dynamic_extent || count <= size() - offset);
373 return {data() + offset, count != dynamic_extent ? count : size() - offset};
374 }
375
376 // [span.obs], span observers
377 constexpr size_t size() const noexcept { return ExtentStorage::size(); }
378 constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
379 [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
380
381 // [span.elem], span element access
382 constexpr T& operator[](size_t idx) const noexcept {
383 // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
384 _PW_SPAN_ASSERT(idx < size());
385 return *(data() + idx);
386 }
387
388 constexpr T& front() const noexcept {
389 static_assert(Extent == dynamic_extent || Extent > 0,
390 "Extent must not be 0");
391 _PW_SPAN_ASSERT(Extent != dynamic_extent || !empty());
392 return *data();
393 }
394
395 constexpr T& back() const noexcept {
396 static_assert(Extent == dynamic_extent || Extent > 0,
397 "Extent must not be 0");
398 _PW_SPAN_ASSERT(Extent != dynamic_extent || !empty());
399 return *(data() + size() - 1);
400 }
401
402 constexpr T* data() const noexcept { return data_; }
403
404 // [span.iter], span iterator support
405 constexpr iterator begin() const noexcept { return data_; }
406 constexpr iterator end() const noexcept { return data_ + size(); }
407
408 constexpr reverse_iterator rbegin() const noexcept {
409 return reverse_iterator(end());
410 }
411 constexpr reverse_iterator rend() const noexcept {
412 return reverse_iterator(begin());
413 }
414
415 private:
416 T* data_;
417};
418
420
421// span<T, Extent>::extent can not be declared inline prior to C++17, hence this
422// definition is required.
423// template <class T, size_t Extent>
424// constexpr size_t span<T, Extent>::extent;
425
426// [span.objectrep], views of object representation
427template <typename T, size_t X>
428span<const std::byte, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
429as_bytes(span<T, X> s) noexcept {
430 return {reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()};
431}
432
433template <typename T,
434 size_t X,
435 typename = std::enable_if_t<!std::is_const<T>::value>>
436span<std::byte, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
437as_writable_bytes(span<T, X> s) noexcept {
438 return {reinterpret_cast<std::byte*>(s.data()), s.size_bytes()};
439}
440
441// Type-deducing helpers for constructing a span.
442// Pigweed: Instead of a make_span function, provide the deduction guides
443// specified in the C++20 standard.
444#ifdef __cpp_deduction_guides
445
446template <class T, std::size_t N>
447span(T (&)[N]) -> span<T, N>;
448
449template <class T, std::size_t N>
450span(std::array<T, N>&) -> span<T, N>;
451
452template <class T, std::size_t N>
453span(const std::array<T, N>&) -> span<const T, N>;
454
455namespace pw_span_internal {
456
457// Containers can be mutable or const and have mutable or const members. Check
458// the type of the accessed elements to determine which type of span should be
459// created (e.g. span<char> or span<const char>).
460template <typename T>
461using ValueType = std::remove_reference_t<decltype(std::declval<T>()[0])>;
462
463} // namespace pw_span_internal
464
465// This diverges a little from the standard, which uses std::ranges.
466template <class Container>
467span(Container&) -> span<pw_span_internal::ValueType<Container>>;
468
469template <class Container>
470span(const Container&) -> span<pw_span_internal::ValueType<const Container>>;
471
472#endif // __cpp_deduction_guides
473
474} // namespace pw
Definition: span_impl.h:143
Definition: span_impl.h:235
The Pigweed namespace.
Definition: alignment.h:27
Definition: span_impl.h:60
Definition: span_impl.h:75
Definition: span_impl.h:84