• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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_UTIL_H_
6 #define CORE_FXCRT_SPAN_UTIL_H_
7 
8 #include <stdint.h>
9 
10 #include <optional>
11 #include <type_traits>
12 
13 #include "core/fxcrt/check_op.h"
14 #include "core/fxcrt/fx_memcpy_wrappers.h"
15 #include "core/fxcrt/span.h"
16 
17 namespace fxcrt {
18 
19 // Bounds-checked byte-for-byte copies from spans into spans. Returns a
20 // span describing the remaining portion of the destination span.
21 template <typename T1,
22           typename T2,
23           size_t N1,
24           size_t N2,
25           typename P1,
26           typename P2,
27           typename = std::enable_if_t<sizeof(T1) == sizeof(T2) &&
28                                       std::is_trivially_copyable_v<T1> &&
29                                       std::is_trivially_copyable_v<T2>>>
spancpy(pdfium::span<T1,N1,P1> dst,pdfium::span<T2,N2,P2> src)30 inline pdfium::span<T1> spancpy(pdfium::span<T1, N1, P1> dst,
31                                 pdfium::span<T2, N2, P2> src) {
32   CHECK_GE(dst.size(), src.size());
33   // SAFETY: SFINAE ensures `sizeof(T1)` equals `sizeof(T2)`, so comparing
34   // `size()` for equality ensures `size_bytes()` are equal, and `size_bytes()`
35   // accurately describes `data()`.
36   UNSAFE_BUFFERS(FXSYS_memcpy(dst.data(), src.data(), src.size_bytes()));
37   return dst.subspan(src.size());
38 }
39 
40 // Bounds-checked byte-for-byte moves from spans into spans. Returns a
41 // span describing the remaining portion of the destination span.
42 template <typename T1,
43           typename T2,
44           size_t N1,
45           size_t N2,
46           typename P1,
47           typename P2,
48           typename = std::enable_if_t<sizeof(T1) == sizeof(T2) &&
49                                       std::is_trivially_copyable_v<T1> &&
50                                       std::is_trivially_copyable_v<T2>>>
spanmove(pdfium::span<T1,N1,P1> dst,pdfium::span<T2,N2,P2> src)51 inline pdfium::span<T1> spanmove(pdfium::span<T1, N1, P1> dst,
52                                  pdfium::span<T2, N2, P2> src) {
53   CHECK_GE(dst.size(), src.size());
54   // SAFETY: SFINAE ensures `sizeof(T1)` equals `sizeof(T2)`, so comparing
55   // `size()` for equality ensures `size_bytes()` are equal, and `size_bytes()`
56   // accurately describes `data()`.
57   UNSAFE_BUFFERS(FXSYS_memmove(dst.data(), src.data(), src.size_bytes()));
58   return dst.subspan(src.size());
59 }
60 
61 // Bounds-checked byte-for-byte copies from spans into spans. Performs the
62 // copy if there is room, and returns true. Otherwise does not copy anything
63 // and returns false.
64 template <typename T1,
65           typename T2,
66           size_t N1,
67           size_t N2,
68           typename P1,
69           typename P2,
70           typename = std::enable_if_t<sizeof(T1) == sizeof(T2) &&
71                                       std::is_trivially_copyable_v<T1> &&
72                                       std::is_trivially_copyable_v<T2>>>
try_spancpy(pdfium::span<T1,N1,P1> dst,pdfium::span<T2,N2,P2> src)73 inline bool try_spancpy(pdfium::span<T1, N1, P1> dst,
74                         pdfium::span<T2, N2, P2> src) {
75   if (dst.size() < src.size()) {
76     return false;
77   }
78   // SAFETY: SFINAE ensures `sizeof(T1)` equals `sizeof(T2)`, the test above
79   // ensures `src.size()` <= `dst.size()` which implies `src.size_bytes()`
80   // <= `dst.size_bytes()`, and `dst.size_bytes()` describes `dst.data()`.
81   UNSAFE_BUFFERS(FXSYS_memcpy(dst.data(), src.data(), src.size_bytes()));
82   return true;
83 }
84 
85 // Bounds-checked byte-for-byte moves from spans into spans. Peforms the
86 // move if there is room, and returns true. Otherwise does not move anything
87 // and returns false.
88 template <typename T1,
89           typename T2,
90           size_t N1,
91           size_t N2,
92           typename P1,
93           typename P2,
94           typename = std::enable_if_t<sizeof(T1) == sizeof(T2) &&
95                                       std::is_trivially_copyable_v<T1> &&
96                                       std::is_trivially_copyable_v<T2>>>
try_spanmove(pdfium::span<T1,N1,P1> dst,pdfium::span<T2,N2,P2> src)97 inline bool try_spanmove(pdfium::span<T1, N1, P1> dst,
98                          pdfium::span<T2, N2, P2> src) {
99   if (dst.size() < src.size()) {
100     return false;
101   }
102   // SAFETY: SFINAE ensures `sizeof(T1)` equals `sizeof(T2)`, the test above
103   // ensures `src.size()` <= `dst.size()` which implies `src.size_bytes()`
104   // <= `dst.size_bytes()`, and `dst.size_bytes()` describes `dst.data()`.
105   UNSAFE_BUFFERS(FXSYS_memmove(dst.data(), src.data(), src.size_bytes()));
106   return true;
107 }
108 
109 // Bounds-checked byte-for-byte equality of same-sized spans. This is
110 // helpful because span does not (yet) have an operator==().
111 template <typename T1,
112           typename T2,
113           size_t N1,
114           size_t N2,
115           typename P1,
116           typename P2,
117           typename = std::enable_if_t<sizeof(T1) == sizeof(T2) &&
118                                       std::is_trivially_copyable_v<T1> &&
119                                       std::is_trivially_copyable_v<T2>>>
span_equals(pdfium::span<T1,N1,P1> s1,pdfium::span<T2,N2,P2> s2)120 bool span_equals(pdfium::span<T1, N1, P1> s1, pdfium::span<T2, N2, P2> s2) {
121   // SAFETY: For both `s1` and `s2`, there are `size_bytes()` valid bytes at
122   // the corresponding `data()`, and the sizes are the same.
123   return s1.size_bytes() == s2.size_bytes() &&
124          UNSAFE_BUFFERS(FXSYS_memcmp(s1.data(), s2.data(), s1.size_bytes())) ==
125              0;
126 }
127 
128 // Returns the first position where `needle` occurs in `haystack`.
129 template <typename T,
130           typename U,
131           typename = std::enable_if_t<sizeof(T) == sizeof(U) &&
132                                       std::is_trivially_copyable_v<T> &&
133                                       std::is_trivially_copyable_v<U>>>
spanpos(pdfium::span<T> haystack,pdfium::span<U> needle)134 std::optional<size_t> spanpos(pdfium::span<T> haystack,
135                               pdfium::span<U> needle) {
136   if (needle.empty() || needle.size() > haystack.size()) {
137     return std::nullopt;
138   }
139   // After this `end_pos`, not enough characters remain in `haystack` for
140   // a full match to occur.
141   size_t end_pos = haystack.size() - needle.size();
142   for (size_t haystack_pos = 0; haystack_pos <= end_pos; ++haystack_pos) {
143     auto candidate = haystack.subspan(haystack_pos, needle.size());
144     if (fxcrt::span_equals(candidate, needle)) {
145       return haystack_pos;
146     }
147   }
148   return std::nullopt;
149 }
150 
151 template <typename T,
152           typename U,
153           typename = typename std::enable_if_t<std::is_const_v<T> ||
154                                                !std::is_const_v<U>>>
reinterpret_span(pdfium::span<U> s)155 inline pdfium::span<T> reinterpret_span(pdfium::span<U> s) noexcept {
156   CHECK(alignof(T) == alignof(U) ||
157         reinterpret_cast<uintptr_t>(s.data()) % alignof(T) == 0u);
158   // SAFETY: relies on correct conversion of size_bytes() result.
159   return UNSAFE_BUFFERS(pdfium::make_span(reinterpret_cast<T*>(s.data()),
160                                           s.size_bytes() / sizeof(T)));
161 }
162 
163 }  // namespace fxcrt
164 
165 #endif  // CORE_FXCRT_SPAN_UTIL_H_
166