• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // std::span is a stand-in for C++20's std::span. Do NOT include this header
16 // directly; instead, include it as <span>.
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 std.
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 //
36 #pragma once
37 
38 #ifndef __cpp_lib_span
39 #define __cpp_lib_span 202002L
40 
41 #include <algorithm>
42 #include <array>
43 #include <cstddef>
44 #include <iterator>
45 #include <limits>
46 #include <type_traits>
47 #include <utility>
48 
49 #include "pw_polyfill/language_feature_macros.h"
50 #include "pw_polyfill/standard_library/namespace.h"
51 
52 // Pigweed: Disable the asserts from Chromium for now.
53 #define _PW_SPAN_ASSERT(arg)
54 
55 _PW_POLYFILL_BEGIN_NAMESPACE_STD
56 
57 // [views.constants]
58 constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
59 
60 template <typename T, size_t Extent = dynamic_extent>
61 class span;
62 
63 namespace pw_span_internal {
64 
65 template <typename T>
66 struct ExtentImpl : std::integral_constant<size_t, dynamic_extent> {};
67 
68 template <typename T, size_t N>
69 struct ExtentImpl<T[N]> : std::integral_constant<size_t, N> {};
70 
71 template <typename T, size_t N>
72 struct ExtentImpl<std::array<T, N>> : std::integral_constant<size_t, N> {};
73 
74 template <typename T, size_t N>
75 struct ExtentImpl<std::span<T, N>> : std::integral_constant<size_t, N> {};
76 
77 template <typename T>
78 using Extent = ExtentImpl<std::remove_cv_t<std::remove_reference_t<T>>>;
79 
80 template <typename T>
81 struct IsSpanImpl : std::false_type {};
82 
83 template <typename T, size_t Extent>
84 struct IsSpanImpl<span<T, Extent>> : std::true_type {};
85 
86 template <typename T>
87 using IsSpan = IsSpanImpl<std::decay_t<T>>;
88 
89 template <typename T>
90 struct IsStdArrayImpl : std::false_type {};
91 
92 template <typename T, size_t N>
93 struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
94 
95 template <typename T>
96 using IsStdArray = IsStdArrayImpl<std::decay_t<T>>;
97 
98 template <typename T>
99 using IsCArray = std::is_array<std::remove_reference_t<T>>;
100 
101 template <typename From, typename To>
102 using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
103 
104 template <typename Container, typename T>
105 using ContainerHasConvertibleData = IsLegalDataConversion<
106     std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>,
107     T>;
108 
109 template <typename Container>
110 using ContainerHasIntegralSize =
111     std::is_integral<decltype(std::size(std::declval<Container>()))>;
112 
113 template <typename From, size_t FromExtent, typename To, size_t ToExtent>
114 using EnableIfLegalSpanConversion =
115     std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
116                      IsLegalDataConversion<From, To>::value>;
117 
118 // SFINAE check if Array can be converted to a span<T>.
119 template <typename Array, typename T, size_t Extent>
120 using EnableIfSpanCompatibleArray =
121     std::enable_if_t<(Extent == dynamic_extent ||
122                       Extent == pw_span_internal::Extent<Array>::value) &&
123                      ContainerHasConvertibleData<Array, T>::value>;
124 
125 // SFINAE check if Container can be converted to a span<T>.
126 template <typename Container, typename T>
127 using IsSpanCompatibleContainer =
128     std::conditional_t<!IsSpan<Container>::value &&
129                            !IsStdArray<Container>::value &&
130                            !IsCArray<Container>::value &&
131                            ContainerHasConvertibleData<Container, T>::value &&
132                            ContainerHasIntegralSize<Container>::value,
133                        std::true_type,
134                        std::false_type>;
135 
136 template <typename Container, typename T>
137 using EnableIfSpanCompatibleContainer =
138     std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value>;
139 
140 template <typename Container, typename T, size_t Extent>
141 using EnableIfSpanCompatibleContainerAndSpanIsDynamic =
142     std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value &&
143                      Extent == dynamic_extent>;
144 
145 // A helper template for storing the size of a span. Spans with static extents
146 // don't require additional storage, since the extent itself is specified in the
147 // template parameter.
148 template <size_t Extent>
149 class ExtentStorage {
150  public:
151   constexpr explicit ExtentStorage(size_t /* size */) noexcept {}
152   constexpr size_t size() const noexcept { return Extent; }
153 };
154 
155 // Specialization of ExtentStorage for dynamic extents, which do require
156 // explicit storage for the size.
157 template <>
158 struct ExtentStorage<dynamic_extent> {
159   constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {}
160   constexpr size_t size() const noexcept { return size_; }
161 
162  private:
163   size_t size_;
164 };
165 
166 }  // namespace pw_span_internal
167 
168 // A span is a value type that represents an array of elements of type T. Since
169 // it only consists of a pointer to memory with an associated size, it is very
170 // light-weight. It is cheap to construct, copy, move and use spans, so that
171 // users are encouraged to use it as a pass-by-value parameter. A span does not
172 // own the underlying memory, so care must be taken to ensure that a span does
173 // not outlive the backing store.
174 //
175 // span is somewhat analogous to StringPiece, but with arbitrary element types,
176 // allowing mutation if T is non-const.
177 //
178 // span is implicitly convertible from C++ arrays, as well as most [1]
179 // container-like types that provide a data() and size() method (such as
180 // std::vector<T>). A mutable span<T> can also be implicitly converted to an
181 // immutable span<const T>.
182 //
183 // Consider using a span for functions that take a data pointer and size
184 // parameter: it allows the function to still act on an array-like type, while
185 // allowing the caller code to be a bit more concise.
186 //
187 // For read-only data access pass a span<const T>: the caller can supply either
188 // a span<const T> or a span<T>, while the callee will have a read-only view.
189 // For read-write access a mutable span<T> is required.
190 //
191 // Without span:
192 //   Read-Only:
193 //     // std::string HexEncode(const uint8_t* data, size_t size);
194 //     std::vector<uint8_t> data_buffer = GenerateData();
195 //     std::string r = HexEncode(data_buffer.data(), data_buffer.size());
196 //
197 //  Mutable:
198 //     // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
199 //     char str_buffer[100];
200 //     SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
201 //
202 // With span:
203 //   Read-Only:
204 //     // std::string HexEncode(std::span<const uint8_t> data);
205 //     std::vector<uint8_t> data_buffer = GenerateData();
206 //     std::string r = HexEncode(data_buffer);
207 //
208 //  Mutable:
209 //     // ssize_t SafeSNPrintf(std::span<char>, const char* fmt, Args...);
210 //     char str_buffer[100];
211 //     SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
212 //
213 // Spans with "const" and pointers
214 // -------------------------------
215 //
216 // Const and pointers can get confusing. Here are vectors of pointers and their
217 // corresponding spans:
218 //
219 //   const std::vector<int*>        =>  std::span<int* const>
220 //   std::vector<const int*>        =>  std::span<const int*>
221 //   const std::vector<const int*>  =>  std::span<const int* const>
222 //
223 // Differences from the C++20 draft
224 // --------------------------------
225 //
226 // http://eel.is/c++draft/views contains the latest C++20 draft of std::span.
227 // Chromium tries to follow the draft as close as possible. Differences between
228 // the draft and the implementation are documented in subsections below.
229 //
230 // Differences from [span.cons]:
231 // - Constructing a static span (i.e. Extent != dynamic_extent) from a dynamic
232 //   sized container (e.g. std::vector) requires an explicit conversion (in the
233 //   C++20 draft this is simply UB)
234 //
235 // Furthermore, all constructors and methods are marked noexcept due to the lack
236 // of exceptions in Chromium.
237 
238 // [span], class template span
239 template <typename T, size_t Extent>
240 class span : public pw_span_internal::ExtentStorage<Extent> {
241  private:
242   using ExtentStorage = pw_span_internal::ExtentStorage<Extent>;
243 
244  public:
245   using element_type = T;
246   using value_type = std::remove_cv_t<T>;
247   using size_type = size_t;
248   using difference_type = ptrdiff_t;
249   using pointer = T*;
250   using reference = T&;
251   using iterator = T*;
252   using reverse_iterator = std::reverse_iterator<iterator>;
253   static constexpr size_t extent = Extent;
254 
255   // [span.cons], span constructors, copy, assignment, and destructor
256   constexpr span() noexcept : ExtentStorage(0), data_(nullptr) {
257     static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
258   }
259 
260   constexpr span(T* data, size_t size) noexcept
261       : ExtentStorage(size), data_(data) {
262     _PW_SPAN_ASSERT(Extent == dynamic_extent || Extent == size);
263   }
264 
265   // Artificially templatized to break ambiguity for span(ptr, 0).
266   template <typename = void>
267   constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
268     // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
269     _PW_SPAN_ASSERT(begin <= end);
270   }
271 
272   template <
273       size_t N,
274       typename =
275           pw_span_internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>>
276   constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {}
277 
278   template <typename U,
279             size_t N,
280             typename = pw_span_internal::
281                 EnableIfSpanCompatibleArray<std::array<U, N>&, T, Extent>>
282   constexpr span(std::array<U, N>& array) noexcept
283       : span(std::data(array), N) {}
284 
285   template <typename U,
286             size_t N,
287             typename = pw_span_internal::
288                 EnableIfSpanCompatibleArray<const std::array<U, N>&, T, Extent>>
289   constexpr span(const std::array<U, N>& array) noexcept
290       : span(std::data(array), N) {}
291 
292   // Conversion from a container that has compatible std::data() and integral
293   // std::size().
294   template <typename Container,
295             typename = pw_span_internal::
296                 EnableIfSpanCompatibleContainerAndSpanIsDynamic<Container&,
297                                                                 T,
298                                                                 Extent>>
299   constexpr span(Container& container) noexcept
300       : span(std::data(container), std::size(container)) {}
301 
302   template <
303       typename Container,
304       typename = pw_span_internal::
305           EnableIfSpanCompatibleContainerAndSpanIsDynamic<const Container&,
306                                                           T,
307                                                           Extent>>
308   constexpr span(const Container& container) noexcept
309       : span(std::data(container), std::size(container)) {}
310 
311   constexpr span(const span& other) noexcept = default;
312 
313   // Conversions from spans of compatible types and extents: this allows a
314   // span<T> to be seamlessly used as a span<const T>, but not the other way
315   // around. If extent is not dynamic, OtherExtent has to be equal to Extent.
316   template <typename U,
317             size_t OtherExtent,
318             typename = pw_span_internal::
319                 EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
320   constexpr span(const span<U, OtherExtent>& other)
321       : span(other.data(), other.size()) {}
322 
323   constexpr span& operator=(const span& other) noexcept = default;
324   ~span() noexcept = default;
325 
326   // [span.sub], span subviews
327   template <size_t Count>
328   constexpr span<T, Count> first() const noexcept {
329     static_assert(Count <= Extent, "Count must not exceed Extent");
330     _PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size());
331     return {data(), Count};
332   }
333 
334   template <size_t Count>
335   constexpr span<T, Count> last() const noexcept {
336     static_assert(Count <= Extent, "Count must not exceed Extent");
337     _PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size());
338     return {data() + (size() - Count), Count};
339   }
340 
341   template <size_t Offset, size_t Count = dynamic_extent>
342   constexpr span<T,
343                  (Count != dynamic_extent
344                       ? Count
345                       : (Extent != dynamic_extent ? Extent - Offset
346                                                   : dynamic_extent))>
347   subspan() const noexcept {
348     static_assert(Offset <= Extent, "Offset must not exceed Extent");
349     static_assert(Count == dynamic_extent || Count <= Extent - Offset,
350                   "Count must not exceed Extent - Offset");
351     _PW_SPAN_ASSERT(Extent != dynamic_extent || Offset <= size());
352     _PW_SPAN_ASSERT(Extent != dynamic_extent || Count == dynamic_extent ||
353                     Count <= size() - Offset);
354     return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
355   }
356 
357   constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
358     // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
359     _PW_SPAN_ASSERT(count <= size());
360     return {data(), count};
361   }
362 
363   constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
364     // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
365     _PW_SPAN_ASSERT(count <= size());
366     return {data() + (size() - count), count};
367   }
368 
369   constexpr span<T, dynamic_extent> subspan(
370       size_t offset, size_t count = dynamic_extent) const noexcept {
371     // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
372     _PW_SPAN_ASSERT(offset <= size());
373     _PW_SPAN_ASSERT(count == dynamic_extent || count <= size() - offset);
374     return {data() + offset, count != dynamic_extent ? count : size() - offset};
375   }
376 
377   // [span.obs], span observers
378   constexpr size_t size() const noexcept { return ExtentStorage::size(); }
379   constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
380   [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
381 
382   // [span.elem], span element access
383   constexpr T& operator[](size_t idx) const noexcept {
384     // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
385     _PW_SPAN_ASSERT(idx < size());
386     return *(data() + idx);
387   }
388 
389   constexpr T& front() const noexcept {
390     static_assert(Extent == dynamic_extent || Extent > 0,
391                   "Extent must not be 0");
392     _PW_SPAN_ASSERT(Extent != dynamic_extent || !empty());
393     return *data();
394   }
395 
396   constexpr T& back() const noexcept {
397     static_assert(Extent == dynamic_extent || Extent > 0,
398                   "Extent must not be 0");
399     _PW_SPAN_ASSERT(Extent != dynamic_extent || !empty());
400     return *(data() + size() - 1);
401   }
402 
403   constexpr T* data() const noexcept { return data_; }
404 
405   // [span.iter], span iterator support
406   constexpr iterator begin() const noexcept { return data_; }
407   constexpr iterator end() const noexcept { return data_ + size(); }
408 
409   constexpr reverse_iterator rbegin() const noexcept {
410     return reverse_iterator(end());
411   }
412   constexpr reverse_iterator rend() const noexcept {
413     return reverse_iterator(begin());
414   }
415 
416  private:
417   T* data_;
418 };
419 
420 // span<T, Extent>::extent can not be declared inline prior to C++17, hence this
421 // definition is required.
422 // template <class T, size_t Extent>
423 // constexpr size_t span<T, Extent>::extent;
424 
425 // [span.objectrep], views of object representation
426 template <typename T, size_t X>
427 span<const std::byte, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
428 as_bytes(span<T, X> s) noexcept {
429   return {reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()};
430 }
431 
432 template <typename T,
433           size_t X,
434           typename = std::enable_if_t<!std::is_const<T>::value>>
435 span<std::byte, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
436 as_writable_bytes(span<T, X> s) noexcept {
437   return {reinterpret_cast<std::byte*>(s.data()), s.size_bytes()};
438 }
439 
440 // Type-deducing helpers for constructing a span.
441 // Pigweed: Instead of a make_span function, provide the deduction guides
442 //     specified in the C++20 standard.
443 #ifdef __cpp_deduction_guides
444 
445 template <class T, std::size_t N>
446 span(T (&)[N]) -> span<T, N>;
447 
448 template <class T, std::size_t N>
449 span(std::array<T, N>&) -> span<T, N>;
450 
451 template <class T, std::size_t N>
452 span(const std::array<T, N>&) -> span<const T, N>;
453 
454 namespace pw_span_internal {
455 
456 // Containers can be mutable or const and have mutable or const members. Check
457 // the type of the accessed elements to determine which type of span should be
458 // created (e.g. span<char> or span<const char>).
459 template <typename T>
460 using ValueType = std::remove_reference_t<decltype(std::declval<T>()[0])>;
461 
462 }  // namespace pw_span_internal
463 
464 // This diverges a little from the standard, which uses std::ranges.
465 template <class Container>
466 span(Container&) -> span<pw_span_internal::ValueType<Container>>;
467 
468 template <class Container>
469 span(const Container&) -> span<pw_span_internal::ValueType<const Container>>;
470 
471 #endif  // __cpp_deduction_guides
472 
473 _PW_POLYFILL_END_NAMESPACE_STD
474 
475 #undef _PW_SPAN_ASSERT
476 #endif  // __cpp_lib_span
477