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