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