• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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