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