1 // Copyright 2024 The PDFium Authors 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 CORE_FXCRT_SPAN_H_ 6 #define CORE_FXCRT_SPAN_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <array> 12 #include <iterator> 13 #include <type_traits> 14 15 #include "core/fxcrt/check.h" 16 #include "core/fxcrt/compiler_specific.h" 17 #include "core/fxcrt/unowned_ptr_exclusion.h" 18 19 // SAFETY: TODO(crbug.com/pdfium/2085): this entire file is to be replaced 20 // with the fully annotated one that is being prepared in base/. 21 22 namespace pdfium { 23 24 constexpr size_t dynamic_extent = static_cast<size_t>(-1); 25 26 template <typename T> 27 using DefaultSpanInternalPtr = UNOWNED_PTR_EXCLUSION T*; 28 29 template <typename T, 30 size_t Extent = dynamic_extent, 31 typename InternalPtr = DefaultSpanInternalPtr<T>> 32 class span; 33 34 namespace internal { 35 36 template <typename T> 37 struct IsSpanImpl : std::false_type {}; 38 39 template <typename T> 40 struct IsSpanImpl<span<T>> : std::true_type {}; 41 42 template <typename T> 43 using IsSpan = IsSpanImpl<typename std::decay<T>::type>; 44 45 template <typename T> 46 struct IsStdArrayImpl : std::false_type {}; 47 48 template <typename T, size_t N> 49 struct IsStdArrayImpl<std::array<T, N>> : std::true_type {}; 50 51 template <typename T> 52 using IsStdArray = IsStdArrayImpl<typename std::decay<T>::type>; 53 54 template <typename From, typename To> 55 using IsLegalSpanConversion = std::is_convertible<From*, To*>; 56 57 template <typename Container, typename T> 58 using ContainerHasConvertibleData = 59 IsLegalSpanConversion<typename std::remove_pointer<decltype( 60 std::declval<Container>().data())>::type, 61 T>; 62 template <typename Container> 63 using ContainerHasIntegralSize = 64 std::is_integral<decltype(std::declval<Container>().size())>; 65 66 template <typename From, typename To> 67 using EnableIfLegalSpanConversion = 68 typename std::enable_if<IsLegalSpanConversion<From, To>::value>::type; 69 70 // SFINAE check if Container can be converted to a span<T>. Note that the 71 // implementation details of this check differ slightly from the requirements in 72 // the working group proposal: in particular, the proposal also requires that 73 // the container conversion constructor participate in overload resolution only 74 // if two additional conditions are true: 75 // 76 // 1. Container implements operator[]. 77 // 2. Container::value_type matches remove_const_t<element_type>. 78 // 79 // The requirements are relaxed slightly here: in particular, not requiring (2) 80 // means that an immutable span can be easily constructed from a mutable 81 // container. 82 template <typename Container, typename T> 83 using EnableIfSpanCompatibleContainer = 84 typename std::enable_if<!internal::IsSpan<Container>::value && 85 !internal::IsStdArray<Container>::value && 86 ContainerHasConvertibleData<Container, T>::value && 87 ContainerHasIntegralSize<Container>::value>::type; 88 89 template <typename Container, typename T> 90 using EnableIfConstSpanCompatibleContainer = 91 typename std::enable_if<std::is_const<T>::value && 92 !internal::IsSpan<Container>::value && 93 !internal::IsStdArray<Container>::value && 94 ContainerHasConvertibleData<Container, T>::value && 95 ContainerHasIntegralSize<Container>::value>::type; 96 97 } // namespace internal 98 99 // A span is a value type that represents an array of elements of type T. Since 100 // it only consists of a pointer to memory with an associated size, it is very 101 // light-weight. It is cheap to construct, copy, move and use spans, so that 102 // users are encouraged to use it as a pass-by-value parameter. A span does not 103 // own the underlying memory, so care must be taken to ensure that a span does 104 // not outlive the backing store. 105 // 106 // span is somewhat analogous to StringPiece, but with arbitrary element types, 107 // allowing mutation if T is non-const. 108 // 109 // span is implicitly convertible from C++ arrays, as well as most [1] 110 // container-like types that provide a data() and size() method (such as 111 // std::vector<T>). A mutable span<T> can also be implicitly converted to an 112 // immutable span<const T>. 113 // 114 // Consider using a span for functions that take a data pointer and size 115 // parameter: it allows the function to still act on an array-like type, while 116 // allowing the caller code to be a bit more concise. 117 // 118 // For read-only data access pass a span<const T>: the caller can supply either 119 // a span<const T> or a span<T>, while the callee will have a read-only view. 120 // For read-write access a mutable span<T> is required. 121 // 122 // Without span: 123 // Read-Only: 124 // // std::string HexEncode(const uint8_t* data, size_t size); 125 // std::vector<uint8_t> data_buffer = GenerateData(); 126 // std::string r = HexEncode(data_buffer.data(), data_buffer.size()); 127 // 128 // Mutable: 129 // // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...); 130 // char str_buffer[100]; 131 // SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14); 132 // 133 // With span: 134 // Read-Only: 135 // // std::string HexEncode(base::span<const uint8_t> data); 136 // std::vector<uint8_t> data_buffer = GenerateData(); 137 // std::string r = HexEncode(data_buffer); 138 // 139 // Mutable: 140 // // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...); 141 // char str_buffer[100]; 142 // SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14); 143 // 144 // Spans with "const" and pointers 145 // ------------------------------- 146 // 147 // Const and pointers can get confusing. Here are vectors of pointers and their 148 // corresponding spans (you can always make the span "more const" too): 149 // 150 // const std::vector<int*> => base::span<int* const> 151 // std::vector<const int*> => base::span<const int*> 152 // const std::vector<const int*> => base::span<const int* const> 153 // 154 // Differences from the working group proposal 155 // ------------------------------------------- 156 // 157 // https://wg21.link/P0122 is the latest working group proposal, Chromium 158 // currently implements R6. The biggest difference is span does not support a 159 // static extent template parameter. Other differences are documented in 160 // subsections below. 161 // 162 // Differences in constants and types: 163 // - no element_type type alias 164 // - no index_type type alias 165 // - no different_type type alias 166 // - no extent constant 167 // 168 // Differences from [span.cons]: 169 // - no constructor from a pointer range 170 // 171 // Differences from [span.sub]: 172 // - using size_t instead of ptrdiff_t for indexing 173 // 174 // Differences from [span.obs]: 175 // - using size_t instead of ptrdiff_t to represent size() 176 // 177 // Differences from [span.elem]: 178 // - no operator ()() 179 // - using size_t instead of ptrdiff_t for indexing 180 // 181 // Additions beyond the C++ standard draft 182 // - as_chars() function. 183 // - as_writable_chars() function. 184 // - as_byte_span() function. 185 // - as_writable_byte_span() function. 186 // - span_from_ref() function. 187 // - byte_span_from_ref() function. 188 189 // [span], class template span 190 template <typename T, size_t Extent, typename InternalPtr> 191 class TRIVIAL_ABI GSL_POINTER span { 192 public: 193 using value_type = typename std::remove_cv<T>::type; 194 using pointer = T*; 195 using reference = T&; 196 using iterator = T*; 197 using const_iterator = const T*; 198 using reverse_iterator = std::reverse_iterator<iterator>; 199 using const_reverse_iterator = std::reverse_iterator<const_iterator>; 200 201 // [span.cons], span constructors, copy, assignment, and destructor 202 constexpr span() noexcept = default; 203 204 UNSAFE_BUFFER_USAGE constexpr span(T* data, size_t size) noexcept 205 : data_(data), size_(size) { 206 DCHECK(data_ || size_ == 0); 207 } 208 209 // TODO(dcheng): Implement construction from a |begin| and |end| pointer. 210 template <size_t N> 211 constexpr span(T (&array)[N]) noexcept : span(array, N) { 212 static_assert(Extent == dynamic_extent || Extent == N); 213 } 214 215 template <size_t N> 216 constexpr span(std::array<T, N>& array) noexcept : span(array.data(), N) { 217 static_assert(Extent == dynamic_extent || Extent == N); 218 } 219 220 template <size_t N> 221 constexpr span(const std::array<std::remove_cv_t<T>, N>& array) noexcept 222 : span(array.data(), N) { 223 static_assert(Extent == dynamic_extent || Extent == N); 224 } 225 226 // Conversion from a container that provides |T* data()| and |integral_type 227 // size()|. Note that |data()| may not return nullptr for some empty 228 // containers, which can lead to container overflow errors when probing 229 // raw ptrs. 230 #if defined(ADDRESS_SANITIZER) && defined(PDF_USE_PARTITION_ALLOC) 231 template <typename Container, 232 typename = internal::EnableIfSpanCompatibleContainer<Container, T>> 233 constexpr span(Container& container) 234 : span(container.size() ? container.data() : nullptr, container.size()) {} 235 #else 236 template <typename Container, 237 typename = internal::EnableIfSpanCompatibleContainer<Container, T>> 238 constexpr span(Container& container) 239 : span(container.data(), container.size()) {} 240 #endif 241 242 template < 243 typename Container, 244 typename = internal::EnableIfConstSpanCompatibleContainer<Container, T>> 245 span(const Container& container) : span(container.data(), container.size()) {} 246 247 constexpr span(const span& other) noexcept = default; 248 249 // Conversions from spans of compatible types: this allows a span<T> to be 250 // seamlessly used as a span<const T>, but not the other way around. 251 template <typename U, 252 size_t M, 253 typename R, 254 typename = internal::EnableIfLegalSpanConversion<U, T>> 255 constexpr span(const span<U, M, R>& other) 256 : span(other.data(), other.size()) {} 257 258 span& operator=(const span& other) noexcept { 259 if (this != &other) { 260 data_ = other.data_; 261 size_ = other.size_; 262 } 263 return *this; 264 } 265 ~span() noexcept = default; 266 267 // [span.sub], span subviews 268 template <size_t Count> 269 span first() const { 270 // TODO(tsepez): The following check isn't yet good enough to replace 271 // the runtime check since we are still allowing unchecked conversions 272 // to arbitrary non-dynamic_extent spans. 273 static_assert(Extent == dynamic_extent || Count <= Extent); 274 return first(Count); 275 } 276 const span first(size_t count) const { 277 CHECK(count <= size_); 278 // SAFETY: CHECK() on line above. 279 return UNSAFE_BUFFERS(span(static_cast<T*>(data_), count)); 280 } 281 282 template <size_t Count> 283 span last() const { 284 // TODO(tsepez): The following check isn't yet good enough to replace 285 // the runtime check since we are still allowing unchecked conversions 286 // to arbitrary non-dynamic_extent spans. 287 static_assert(Extent == dynamic_extent || Count <= Extent); 288 return last(Count); 289 } 290 const span last(size_t count) const { 291 CHECK(count <= size_); 292 return UNSAFE_BUFFERS( 293 span(static_cast<T*>(data_) + (size_ - count), count)); 294 } 295 296 template <size_t Offset, size_t Count = dynamic_extent> 297 span subspan() const { 298 // TODO(tsepez): The following check isn't yet good enough to replace 299 // the runtime check since we are still allowing unchecked conversions 300 // to arbitrary non-dynamic_extent spans. 301 static_assert(Extent == dynamic_extent || Count == dynamic_extent || 302 Offset + Count <= Extent); 303 return subspan(Offset, Count); 304 } 305 const span subspan(size_t pos, size_t count = dynamic_extent) const { 306 CHECK(pos <= size_); 307 CHECK(count == dynamic_extent || count <= size_ - pos); 308 // SAFETY: CHECK()s on lines above. 309 return UNSAFE_BUFFERS(span(static_cast<T*>(data_) + pos, 310 count == dynamic_extent ? size_ - pos : count)); 311 } 312 313 // [span.obs], span observers 314 constexpr size_t size() const noexcept { return size_; } 315 constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); } 316 constexpr bool empty() const noexcept { return size_ == 0; } 317 318 // [span.elem], span element access 319 T& operator[](size_t index) const noexcept { 320 CHECK(index < size_); 321 return UNSAFE_BUFFERS(static_cast<T*>(data_)[index]); 322 } 323 324 constexpr T& front() const noexcept { 325 CHECK(!empty()); 326 return *data(); 327 } 328 329 constexpr T& back() const noexcept { 330 CHECK(!empty()); 331 return UNSAFE_BUFFERS(*(data() + size() - 1)); 332 } 333 334 constexpr T* data() const noexcept { return static_cast<T*>(data_); } 335 336 // [span.iter], span iterator support 337 constexpr iterator begin() const noexcept { return static_cast<T*>(data_); } 338 constexpr iterator end() const noexcept { 339 return UNSAFE_BUFFERS(begin() + size_); 340 } 341 342 constexpr const_iterator cbegin() const noexcept { return begin(); } 343 constexpr const_iterator cend() const noexcept { return end(); } 344 345 constexpr reverse_iterator rbegin() const noexcept { 346 return reverse_iterator(end()); 347 } 348 constexpr reverse_iterator rend() const noexcept { 349 return reverse_iterator(begin()); 350 } 351 352 constexpr const_reverse_iterator crbegin() const noexcept { 353 return const_reverse_iterator(cend()); 354 } 355 constexpr const_reverse_iterator crend() const noexcept { 356 return const_reverse_iterator(cbegin()); 357 } 358 359 private: 360 template <typename U> 361 friend constexpr span<U> make_span(U* data, size_t size) noexcept; 362 363 InternalPtr data_ = nullptr; 364 size_t size_ = 0; 365 }; 366 367 // Type-deducing helpers for constructing a span. 368 template <typename T> 369 UNSAFE_BUFFER_USAGE constexpr span<T> make_span(T* data, size_t size) noexcept { 370 return UNSAFE_BUFFERS(span<T>(data, size)); 371 } 372 373 template <typename T, size_t N> 374 constexpr span<T> make_span(T (&array)[N]) noexcept { 375 return span<T>(array); 376 } 377 378 template <typename T, size_t N> 379 constexpr span<T> make_span(std::array<T, N>& array) noexcept { 380 return span<T>(array); 381 } 382 383 template <typename Container, 384 typename T = typename Container::value_type, 385 typename = internal::EnableIfSpanCompatibleContainer<Container, T>> 386 constexpr span<T> make_span(Container& container) { 387 return span<T>(container); 388 } 389 390 template < 391 typename Container, 392 typename T = typename std::add_const<typename Container::value_type>::type, 393 typename = internal::EnableIfConstSpanCompatibleContainer<Container, T>> 394 constexpr span<T> make_span(const Container& container) { 395 return span<T>(container); 396 } 397 398 // [span.objectrep], views of object representation 399 template <typename T, size_t N, typename P> 400 span<const uint8_t> as_bytes(span<T, N, P> s) noexcept { 401 // SAFETY: from size_bytes() method. 402 return UNSAFE_BUFFERS( 403 make_span(reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes())); 404 } 405 406 template <typename T, 407 size_t N, 408 typename P, 409 typename U = typename std::enable_if<!std::is_const<T>::value>::type> 410 span<uint8_t> as_writable_bytes(span<T, N, P> s) noexcept { 411 // SAFETY: from size_bytes() method. 412 return UNSAFE_BUFFERS( 413 make_span(reinterpret_cast<uint8_t*>(s.data()), s.size_bytes())); 414 } 415 416 template <typename T, size_t N, typename P> 417 span<const char> as_chars(span<T, N, P> s) noexcept { 418 // SAFETY: from size_bytes() method. 419 return UNSAFE_BUFFERS( 420 make_span(reinterpret_cast<const char*>(s.data()), s.size_bytes())); 421 } 422 423 template <typename T, 424 size_t N, 425 typename P, 426 typename U = typename std::enable_if<!std::is_const<T>::value>::type> 427 span<char> as_writable_chars(span<T, N, P> s) noexcept { 428 // SAFETY: from size_bytes() method. 429 return UNSAFE_BUFFERS( 430 make_span(reinterpret_cast<char*>(s.data()), s.size_bytes())); 431 } 432 433 // `span_from_ref` converts a reference to T into a span of length 1. This is a 434 // non-std helper that is inspired by the `std::slice::from_ref()` function from 435 // Rust. 436 template <typename T> 437 static constexpr span<T> span_from_ref(T& single_object) noexcept { 438 // SAFETY: single object passed by reference. 439 return UNSAFE_BUFFERS(make_span<T>(&single_object, 1u)); 440 } 441 442 // `byte_span_from_ref` converts a reference to T into a span of uint8_t of 443 // length sizeof(T). This is a non-std helper that is a sugar for 444 // `as_writable_bytes(span_from_ref(x))`. 445 template <typename T> 446 static constexpr span<const uint8_t> byte_span_from_ref( 447 const T& single_object) noexcept { 448 return as_bytes(span_from_ref(single_object)); 449 } 450 template <typename T> 451 static constexpr span<uint8_t> byte_span_from_ref(T& single_object) noexcept { 452 return as_writable_bytes(span_from_ref(single_object)); 453 } 454 455 // Convenience function for converting an object which is itself convertible 456 // to span into a span of bytes (i.e. span of const uint8_t). Typically used 457 // to convert std::string or string-objects holding chars, or std::vector 458 // or vector-like objects holding other scalar types, prior to passing them 459 // into an API that requires byte spans. 460 template <typename T> 461 span<const uint8_t> as_byte_span(const T& arg) { 462 return as_bytes(make_span(arg)); 463 } 464 template <typename T> 465 span<const uint8_t> as_byte_span(T&& arg) { 466 return as_bytes(make_span(arg)); 467 } 468 469 // Convenience function for converting an object which is itself convertible 470 // to span into a span of mutable bytes (i.e. span of uint8_t). Typically used 471 // to convert std::string or string-objects holding chars, or std::vector 472 // or vector-like objects holding other scalar types, prior to passing them 473 // into an API that requires mutable byte spans. 474 template <typename T> 475 constexpr span<uint8_t> as_writable_byte_span(T&& arg) { 476 return as_writable_bytes(make_span(arg)); 477 } 478 479 } // namespace pdfium 480 481 #endif // CORE_FXCRT_SPAN_H_ 482