• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_CONTAINERS_SPAN_H_
6 #define BASE_CONTAINERS_SPAN_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <algorithm>
12 #include <array>
13 #include <iterator>
14 #include <string_view>
15 #include <type_traits>
16 #include <utility>
17 
18 #include "base/logging.h"
19 #include "base/stl_util.h"
20 
21 namespace base {
22 
23 // [views.constants]
24 constexpr size_t dynamic_extent = static_cast<size_t>(-1);
25 
26 template <typename T, size_t Extent = dynamic_extent>
27 class span;
28 
29 namespace internal {
30 
31 template <typename T>
32 struct IsSpanImpl : std::false_type {};
33 
34 template <typename T, size_t Extent>
35 struct IsSpanImpl<span<T, Extent>> : std::true_type {};
36 
37 template <typename T>
38 using IsSpan = IsSpanImpl<std::decay_t<T>>;
39 
40 template <typename T>
41 struct IsStdArrayImpl : std::false_type {};
42 
43 template <typename T, size_t N>
44 struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
45 
46 template <typename T>
47 using IsStdArray = IsStdArrayImpl<std::decay_t<T>>;
48 
49 template <typename T>
50 using IsCArray = std::is_array<std::remove_reference_t<T>>;
51 
52 template <typename From, typename To>
53 using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
54 
55 template <typename Container, typename T>
56 using ContainerHasConvertibleData = IsLegalDataConversion<
57     std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>,
58     T>;
59 
60 template <typename Container>
61 using ContainerHasIntegralSize =
62     std::is_integral<decltype(std::size(std::declval<Container>()))>;
63 
64 template <typename From, size_t FromExtent, typename To, size_t ToExtent>
65 using EnableIfLegalSpanConversion =
66     std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
67                      IsLegalDataConversion<From, To>::value>;
68 
69 // SFINAE check if Array can be converted to a span<T>.
70 template <typename Array, size_t N, typename T, size_t Extent>
71 using EnableIfSpanCompatibleArray =
72     std::enable_if_t<(Extent == dynamic_extent || Extent == N) &&
73                      ContainerHasConvertibleData<Array, T>::value>;
74 
75 // SFINAE check if Container can be converted to a span<T>.
76 template <typename Container, typename T>
77 using EnableIfSpanCompatibleContainer =
78     std::enable_if_t<!internal::IsSpan<Container>::value &&
79                      !internal::IsStdArray<Container>::value &&
80                      !internal::IsCArray<Container>::value &&
81                      ContainerHasConvertibleData<Container, T>::value &&
82                      ContainerHasIntegralSize<Container>::value>;
83 
84 }  // namespace internal
85 
86 // A span is a value type that represents an array of elements of type T. Since
87 // it only consists of a pointer to memory with an associated size, it is very
88 // light-weight. It is cheap to construct, copy, move and use spans, so that
89 // users are encouraged to use it as a pass-by-value parameter. A span does not
90 // own the underlying memory, so care must be taken to ensure that a span does
91 // not outlive the backing store.
92 //
93 // span is somewhat analogous to std::string_view, but with arbitrary element
94 // types, allowing mutation if T is non-const.
95 //
96 // span is implicitly convertible from C++ arrays, as well as most [1]
97 // container-like types that provide a data() and size() method (such as
98 // std::vector<T>). A mutable span<T> can also be implicitly converted to an
99 // immutable span<const T>.
100 //
101 // Consider using a span for functions that take a data pointer and size
102 // parameter: it allows the function to still act on an array-like type, while
103 // allowing the caller code to be a bit more concise.
104 //
105 // For read-only data access pass a span<const T>: the caller can supply either
106 // a span<const T> or a span<T>, while the callee will have a read-only view.
107 // For read-write access a mutable span<T> is required.
108 //
109 // Without span:
110 //   Read-Only:
111 //     // std::string HexEncode(const uint8_t* data, size_t size);
112 //     std::vector<uint8_t> data_buffer = GenerateData();
113 //     std::string r = HexEncode(data_buffer.data(), data_buffer.size());
114 //
115 //  Mutable:
116 //     // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
117 //     char str_buffer[100];
118 //     SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
119 //
120 // With span:
121 //   Read-Only:
122 //     // std::string HexEncode(base::span<const uint8_t> data);
123 //     std::vector<uint8_t> data_buffer = GenerateData();
124 //     std::string r = HexEncode(data_buffer);
125 //
126 //  Mutable:
127 //     // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
128 //     char str_buffer[100];
129 //     SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
130 //
131 // Spans with "const" and pointers
132 // -------------------------------
133 //
134 // Const and pointers can get confusing. Here are vectors of pointers and their
135 // corresponding spans:
136 //
137 //   const std::vector<int*>        =>  base::span<int* const>
138 //   std::vector<const int*>        =>  base::span<const int*>
139 //   const std::vector<const int*>  =>  base::span<const int* const>
140 //
141 // Differences from the working group proposal
142 // -------------------------------------------
143 //
144 // https://wg21.link/P0122 is the latest working group proposal, Chromium
145 // currently implements R7. Differences between the proposal and the
146 // implementation are documented in subsections below.
147 //
148 // Differences from [span.objectrep]:
149 // - as_bytes() and as_writable_bytes() return spans of uint8_t instead of
150 //   std::byte
151 //
152 // Differences in constants and types:
153 // - index_type is aliased to size_t
154 //
155 // Differences from [span.sub]:
156 // - using size_t instead of ptrdiff_t for indexing
157 //
158 // Differences from [span.obs]:
159 // - using size_t instead of ptrdiff_t to represent size()
160 //
161 // Differences from [span.elem]:
162 // - using size_t instead of ptrdiff_t for indexing
163 //
164 // Furthermore, all constructors and methods are marked noexcept due to the lack
165 // of exceptions in Chromium.
166 //
167 // Due to the lack of class template argument deduction guides in C++14
168 // appropriate make_span() utility functions are provided.
169 
170 // [span], class template span
171 template <typename T, size_t Extent>
172 class span {
173  public:
174   using element_type = T;
175   using value_type = std::remove_cv_t<T>;
176   using index_type = size_t;
177   using difference_type = ptrdiff_t;
178   using pointer = T*;
179   using reference = T&;
180   using iterator = T*;
181   using const_iterator = const T*;
182   using reverse_iterator = std::reverse_iterator<iterator>;
183   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
184   static constexpr index_type extent = Extent;
185 
186   // [span.cons], span constructors, copy, assignment, and destructor
187   constexpr span() noexcept : data_(nullptr), size_(0) {
188     static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
189   }
190 
191   constexpr span(T* data, size_t size) noexcept : data_(data), size_(size) {
192     CHECK(Extent == dynamic_extent || Extent == size);
193   }
194 
195   // Artificially templatized to break ambiguity for span(ptr, 0).
196   template <typename = void>
197   constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
198     // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
199     CHECK(begin <= end);
200   }
201 
202   template <
203       size_t N,
204       typename = internal::EnableIfSpanCompatibleArray<T (&)[N], N, T, Extent>>
205   constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {}
206 
207   template <
208       size_t N,
209       typename = internal::
210           EnableIfSpanCompatibleArray<std::array<value_type, N>&, N, T, Extent>>
211   constexpr span(std::array<value_type, N>& array) noexcept
212       : span(std::data(array), N) {}
213 
214   template <size_t N,
215             typename = internal::EnableIfSpanCompatibleArray<
216                 const std::array<value_type, N>&,
217                 N,
218                 T,
219                 Extent>>
220   constexpr span(const std::array<value_type, N>& array) noexcept
221       : span(std::data(array), N) {}
222 
223   // Conversion from a container that has compatible std::data() and integral
224   // std::size().
225   template <typename Container,
226             typename = internal::EnableIfSpanCompatibleContainer<Container&, T>>
227   constexpr span(Container& container) noexcept
228       : span(std::data(container), std::size(container)) {}
229 
230   template <
231       typename Container,
232       typename = internal::EnableIfSpanCompatibleContainer<const Container&, T>>
233   span(const Container& container) noexcept
234       : span(std::data(container), std::size(container)) {}
235 
236   constexpr span(const span& other) noexcept = default;
237 
238   // Conversions from spans of compatible types and extents: this allows a
239   // span<T> to be seamlessly used as a span<const T>, but not the other way
240   // around. If extent is not dynamic, OtherExtent has to be equal to Extent.
241   template <
242       typename U,
243       size_t OtherExtent,
244       typename =
245           internal::EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
246   constexpr span(const span<U, OtherExtent>& other)
247       : span(other.data(), other.size()) {}
248 
249   constexpr span& operator=(const span& other) noexcept = default;
250   ~span() noexcept = default;
251 
252   // [span.sub], span subviews
253   template <size_t Count>
254   constexpr span<T, Count> first() const noexcept {
255     static_assert(Extent == dynamic_extent || Count <= Extent,
256                   "Count must not exceed Extent");
257     CHECK(Extent != dynamic_extent || Count <= size());
258     return {data(), Count};
259   }
260 
261   template <size_t Count>
262   constexpr span<T, Count> last() const noexcept {
263     static_assert(Extent == dynamic_extent || Count <= Extent,
264                   "Count must not exceed Extent");
265     CHECK(Extent != dynamic_extent || Count <= size());
266     return {data() + (size() - Count), Count};
267   }
268 
269   template <size_t Offset, size_t Count = dynamic_extent>
270   constexpr span<T,
271                  (Count != dynamic_extent
272                       ? Count
273                       : (Extent != dynamic_extent ? Extent - Offset
274                                                   : dynamic_extent))>
275   subspan() const noexcept {
276     static_assert(Extent == dynamic_extent || Offset <= Extent,
277                   "Offset must not exceed Extent");
278     static_assert(Extent == dynamic_extent || Count == dynamic_extent ||
279                       Count <= Extent - Offset,
280                   "Count must not exceed Extent - Offset");
281     CHECK(Extent != dynamic_extent || Offset <= size());
282     CHECK(Extent != dynamic_extent || Count == dynamic_extent ||
283           Count <= size() - Offset);
284     return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
285   }
286 
287   constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
288     // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
289     CHECK(count <= size());
290     return {data(), count};
291   }
292 
293   constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
294     // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
295     CHECK(count <= size());
296     return {data() + (size() - count), count};
297   }
298 
299   constexpr span<T, dynamic_extent> subspan(
300       size_t offset,
301       size_t count = dynamic_extent) const noexcept {
302     // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
303     CHECK(offset <= size());
304     CHECK(count == dynamic_extent || count <= size() - offset);
305     return {data() + offset, count != dynamic_extent ? count : size() - offset};
306   }
307 
308   // [span.obs], span observers
309   constexpr size_t size() const noexcept { return size_; }
310   constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
311   constexpr bool empty() const noexcept { return size() == 0; }
312 
313   // [span.elem], span element access
314   constexpr T& operator[](size_t idx) const noexcept {
315     // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
316     CHECK(idx < size());
317     return *(data() + idx);
318   }
319 
320   constexpr T& operator()(size_t idx) const noexcept {
321     // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
322     CHECK(idx < size());
323     return *(data() + idx);
324   }
325 
326   constexpr T* data() const noexcept { return data_; }
327 
328   // [span.iter], span iterator support
329   constexpr iterator begin() const noexcept { return data(); }
330   constexpr iterator end() const noexcept { return data() + size(); }
331 
332   constexpr const_iterator cbegin() const noexcept { return begin(); }
333   constexpr const_iterator cend() const noexcept { return end(); }
334 
335   constexpr reverse_iterator rbegin() const noexcept {
336     return reverse_iterator(end());
337   }
338   constexpr reverse_iterator rend() const noexcept {
339     return reverse_iterator(begin());
340   }
341 
342   constexpr const_reverse_iterator crbegin() const noexcept {
343     return const_reverse_iterator(cend());
344   }
345   constexpr const_reverse_iterator crend() const noexcept {
346     return const_reverse_iterator(cbegin());
347   }
348 
349  private:
350   T* data_;
351   size_t size_;
352 };
353 
354 // span<T, Extent>::extent can not be declared inline prior to C++17, hence this
355 // definition is required.
356 template <class T, size_t Extent>
357 constexpr size_t span<T, Extent>::extent;
358 
359 // [span.comparison], span comparison operators
360 // Relational operators. Equality is a element-wise comparison.
361 template <typename T, size_t X, typename U, size_t Y>
362 constexpr bool operator==(span<T, X> lhs, span<U, Y> rhs) noexcept {
363   return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend());
364 }
365 
366 template <typename T, size_t X, typename U, size_t Y>
367 constexpr bool operator!=(span<T, X> lhs, span<U, Y> rhs) noexcept {
368   return !(lhs == rhs);
369 }
370 
371 template <typename T, size_t X, typename U, size_t Y>
372 constexpr bool operator<(span<T, X> lhs, span<U, Y> rhs) noexcept {
373   return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
374                                       rhs.cend());
375 }
376 
377 template <typename T, size_t X, typename U, size_t Y>
378 constexpr bool operator<=(span<T, X> lhs, span<U, Y> rhs) noexcept {
379   return !(rhs < lhs);
380 }
381 
382 template <typename T, size_t X, typename U, size_t Y>
383 constexpr bool operator>(span<T, X> lhs, span<U, Y> rhs) noexcept {
384   return rhs < lhs;
385 }
386 
387 template <typename T, size_t X, typename U, size_t Y>
388 constexpr bool operator>=(span<T, X> lhs, span<U, Y> rhs) noexcept {
389   return !(lhs < rhs);
390 }
391 
392 // [span.objectrep], views of object representation
393 template <typename T, size_t X>
394 span<const uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
395 as_bytes(span<T, X> s) noexcept {
396   return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
397 }
398 
399 template <typename T,
400           size_t X,
401           typename = std::enable_if_t<!std::is_const<T>::value>>
402 span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
403 as_writable_bytes(span<T, X> s) noexcept {
404   return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
405 }
406 
407 // Type-deducing helpers for constructing a span.
408 template <typename T>
409 constexpr span<T> make_span(T* data, size_t size) noexcept {
410   return {data, size};
411 }
412 
413 template <typename T>
414 constexpr span<T> make_span(T* begin, T* end) noexcept {
415   return {begin, end};
416 }
417 
418 template <typename T, size_t N>
419 constexpr span<T, N> make_span(T (&array)[N]) noexcept {
420   return array;
421 }
422 
423 template <typename T, size_t N>
424 constexpr span<T, N> make_span(std::array<T, N>& array) noexcept {
425   return array;
426 }
427 
428 template <typename T, size_t N>
429 constexpr span<const T, N> make_span(const std::array<T, N>& array) noexcept {
430   return array;
431 }
432 
433 template <typename Container,
434           typename T = typename Container::value_type,
435           typename = internal::EnableIfSpanCompatibleContainer<Container&, T>>
436 constexpr span<T> make_span(Container& container) noexcept {
437   return container;
438 }
439 
440 template <
441     typename Container,
442     typename T = const typename Container::value_type,
443     typename = internal::EnableIfSpanCompatibleContainer<const Container&, T>>
444 constexpr span<T> make_span(const Container& container) noexcept {
445   return container;
446 }
447 
448 template <typename T, size_t X>
449 constexpr span<T, X> make_span(const span<T, X>& span) noexcept {
450   return span;
451 }
452 
453 }  // namespace base
454 
455 #endif  // BASE_CONTAINERS_SPAN_H_
456