|  | // Copyright 2021 The PDFium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef CORE_FXCRT_SPAN_UTIL_H_ | 
|  | #define CORE_FXCRT_SPAN_UTIL_H_ | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <optional> | 
|  | #include <type_traits> | 
|  |  | 
|  | #include "core/fxcrt/check_op.h" | 
|  | #include "core/fxcrt/fx_memcpy_wrappers.h" | 
|  | #include "core/fxcrt/span.h" | 
|  |  | 
|  | namespace fxcrt { | 
|  |  | 
|  | // Bounds-checked byte-for-byte copies from spans into spans. Returns a | 
|  | // span describing the remaining portion of the destination span. | 
|  | template <typename T1, | 
|  | typename T2, | 
|  | size_t N1, | 
|  | size_t N2, | 
|  | typename P1, | 
|  | typename P2, | 
|  | typename = std::enable_if_t<sizeof(T1) == sizeof(T2) && | 
|  | std::is_trivially_copyable_v<T1> && | 
|  | std::is_trivially_copyable_v<T2>>> | 
|  | inline pdfium::span<T1> spancpy(pdfium::span<T1, N1, P1> dst, | 
|  | pdfium::span<T2, N2, P2> src) { | 
|  | CHECK_GE(dst.size(), src.size()); | 
|  | // SAFETY: SFINAE ensures `sizeof(T1)` equals `sizeof(T2)`, so comparing | 
|  | // `size()` for equality ensures `size_bytes()` are equal, and `size_bytes()` | 
|  | // accurately describes `data()`. | 
|  | UNSAFE_BUFFERS(FXSYS_memcpy(dst.data(), src.data(), src.size_bytes())); | 
|  | return dst.subspan(src.size()); | 
|  | } | 
|  |  | 
|  | // Bounds-checked byte-for-byte moves from spans into spans. Returns a | 
|  | // span describing the remaining portion of the destination span. | 
|  | template <typename T1, | 
|  | typename T2, | 
|  | size_t N1, | 
|  | size_t N2, | 
|  | typename P1, | 
|  | typename P2, | 
|  | typename = std::enable_if_t<sizeof(T1) == sizeof(T2) && | 
|  | std::is_trivially_copyable_v<T1> && | 
|  | std::is_trivially_copyable_v<T2>>> | 
|  | inline pdfium::span<T1> spanmove(pdfium::span<T1, N1, P1> dst, | 
|  | pdfium::span<T2, N2, P2> src) { | 
|  | CHECK_GE(dst.size(), src.size()); | 
|  | // SAFETY: SFINAE ensures `sizeof(T1)` equals `sizeof(T2)`, so comparing | 
|  | // `size()` for equality ensures `size_bytes()` are equal, and `size_bytes()` | 
|  | // accurately describes `data()`. | 
|  | UNSAFE_BUFFERS(FXSYS_memmove(dst.data(), src.data(), src.size_bytes())); | 
|  | return dst.subspan(src.size()); | 
|  | } | 
|  |  | 
|  | // Bounds-checked byte-for-byte copies from spans into spans. Performs the | 
|  | // copy if there is room, and returns true. Otherwise does not copy anything | 
|  | // and returns false. | 
|  | template <typename T1, | 
|  | typename T2, | 
|  | size_t N1, | 
|  | size_t N2, | 
|  | typename P1, | 
|  | typename P2, | 
|  | typename = std::enable_if_t<sizeof(T1) == sizeof(T2) && | 
|  | std::is_trivially_copyable_v<T1> && | 
|  | std::is_trivially_copyable_v<T2>>> | 
|  | inline bool try_spancpy(pdfium::span<T1, N1, P1> dst, | 
|  | pdfium::span<T2, N2, P2> src) { | 
|  | if (dst.size() < src.size()) { | 
|  | return false; | 
|  | } | 
|  | // SAFETY: SFINAE ensures `sizeof(T1)` equals `sizeof(T2)`, the test above | 
|  | // ensures `src.size()` <= `dst.size()` which implies `src.size_bytes()` | 
|  | // <= `dst.size_bytes()`, and `dst.size_bytes()` describes `dst.data()`. | 
|  | UNSAFE_BUFFERS(FXSYS_memcpy(dst.data(), src.data(), src.size_bytes())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Bounds-checked byte-for-byte moves from spans into spans. Peforms the | 
|  | // move if there is room, and returns true. Otherwise does not move anything | 
|  | // and returns false. | 
|  | template <typename T1, | 
|  | typename T2, | 
|  | size_t N1, | 
|  | size_t N2, | 
|  | typename P1, | 
|  | typename P2, | 
|  | typename = std::enable_if_t<sizeof(T1) == sizeof(T2) && | 
|  | std::is_trivially_copyable_v<T1> && | 
|  | std::is_trivially_copyable_v<T2>>> | 
|  | inline bool try_spanmove(pdfium::span<T1, N1, P1> dst, | 
|  | pdfium::span<T2, N2, P2> src) { | 
|  | if (dst.size() < src.size()) { | 
|  | return false; | 
|  | } | 
|  | // SAFETY: SFINAE ensures `sizeof(T1)` equals `sizeof(T2)`, the test above | 
|  | // ensures `src.size()` <= `dst.size()` which implies `src.size_bytes()` | 
|  | // <= `dst.size_bytes()`, and `dst.size_bytes()` describes `dst.data()`. | 
|  | UNSAFE_BUFFERS(FXSYS_memmove(dst.data(), src.data(), src.size_bytes())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Bounds-checked sets into spans. | 
|  | template <typename T, | 
|  | size_t N, | 
|  | typename P, | 
|  | typename = std::enable_if_t<std::is_trivially_constructible_v<T> && | 
|  | std::is_trivially_destructible_v<T>>> | 
|  | void spanset(pdfium::span<T, N, P> dst, uint8_t val) { | 
|  | // SAFETY: `dst.size_bytes()` accurately describes `dst.data()`. | 
|  | UNSAFE_BUFFERS(FXSYS_memset(dst.data(), val, dst.size_bytes())); | 
|  | } | 
|  |  | 
|  | // Bounds-checked zeroing of spans. | 
|  | template <typename T, | 
|  | size_t N, | 
|  | typename P, | 
|  | typename = std::enable_if_t<std::is_trivially_constructible_v<T> && | 
|  | std::is_trivially_destructible_v<T>>> | 
|  | void spanclr(pdfium::span<T, N, P> dst) { | 
|  | // SAFETY: `dst.size_bytes()` accurately describes `dst.data()`. | 
|  | UNSAFE_BUFFERS(FXSYS_memset(dst.data(), 0, dst.size_bytes())); | 
|  | } | 
|  |  | 
|  | // Bounds-checked byte-for-byte equality of same-sized spans. This is | 
|  | // helpful because span does not (yet) have an operator==(). | 
|  | template <typename T1, | 
|  | typename T2, | 
|  | size_t N1, | 
|  | size_t N2, | 
|  | typename P1, | 
|  | typename P2, | 
|  | typename = std::enable_if_t<sizeof(T1) == sizeof(T2) && | 
|  | std::is_trivially_copyable_v<T1> && | 
|  | std::is_trivially_copyable_v<T2>>> | 
|  | bool span_equals(pdfium::span<T1, N1, P1> s1, pdfium::span<T2, N2, P2> s2) { | 
|  | // SAFETY: For both `s1` and `s2`, there are `size_bytes()` valid bytes at | 
|  | // the corresponding `data()`, and the sizes are the same. | 
|  | return s1.size_bytes() == s2.size_bytes() && | 
|  | UNSAFE_BUFFERS(FXSYS_memcmp(s1.data(), s2.data(), s1.size_bytes())) == | 
|  | 0; | 
|  | } | 
|  |  | 
|  | // Returns the first position where `needle` occurs in `haystack`. | 
|  | template <typename T, | 
|  | typename U, | 
|  | typename = std::enable_if_t<sizeof(T) == sizeof(U) && | 
|  | std::is_trivially_copyable_v<T> && | 
|  | std::is_trivially_copyable_v<U>>> | 
|  | std::optional<size_t> spanpos(pdfium::span<T> haystack, | 
|  | pdfium::span<U> needle) { | 
|  | if (needle.empty() || needle.size() > haystack.size()) { | 
|  | return std::nullopt; | 
|  | } | 
|  | // After this `end_pos`, not enough characters remain in `haystack` for | 
|  | // a full match to occur. | 
|  | size_t end_pos = haystack.size() - needle.size(); | 
|  | for (size_t haystack_pos = 0; haystack_pos <= end_pos; ++haystack_pos) { | 
|  | auto candidate = haystack.subspan(haystack_pos, needle.size()); | 
|  | if (fxcrt::span_equals(candidate, needle)) { | 
|  | return haystack_pos; | 
|  | } | 
|  | } | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename U, | 
|  | typename = typename std::enable_if_t<std::is_const_v<T> || | 
|  | !std::is_const_v<U>>> | 
|  | inline pdfium::span<T> truncating_reinterpret_span(pdfium::span<U> s) noexcept { | 
|  | CHECK_EQ(reinterpret_cast<uintptr_t>(s.data()) % alignof(T), 0u); | 
|  | // SAFETY: relies on correct conversion of size_bytes() result. | 
|  | return UNSAFE_BUFFERS(pdfium::make_span(reinterpret_cast<T*>(s.data()), | 
|  | s.size_bytes() / sizeof(T))); | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename U, | 
|  | typename = typename std::enable_if_t<std::is_const_v<T> || | 
|  | !std::is_const_v<U>>> | 
|  | inline pdfium::span<T> reinterpret_span(pdfium::span<U> s) noexcept { | 
|  | CHECK_EQ(s.size_bytes() % sizeof(T), 0u); | 
|  | return truncating_reinterpret_span<T, U>(s); | 
|  | } | 
|  |  | 
|  | }  // namespace fxcrt | 
|  |  | 
|  | #endif  // CORE_FXCRT_SPAN_UTIL_H_ |