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