blob: 4dedfe8c9aabe08f0c4092ceea769ace2e13d0e8 [file] [log] [blame]
// 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> reinterpret_span(pdfium::span<U> s) noexcept {
CHECK_EQ(s.size_bytes() % sizeof(T), 0u);
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)));
}
} // namespace fxcrt
#endif // CORE_FXCRT_SPAN_UTIL_H_