folly-clib-20250713.1537: folly/folly/algorithm/simd/Contains.h
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/CPortability.h>
#include <folly/algorithm/simd/detail/Traits.h>
#include <iterator>
namespace folly::simd {
namespace detail {
// no overloading for easier profiling.
bool containsU8(
folly::span<const std::uint8_t> haystack, std::uint8_t needle) noexcept;
bool containsU16(
folly::span<const std::uint16_t> haystack, std::uint16_t needle) noexcept;
bool containsU32(
folly::span<const std::uint32_t> haystack, std::uint32_t needle) noexcept;
bool containsU64(
folly::span<const std::uint64_t> haystack, std::uint64_t needle) noexcept;
template <typename R>
using std_range_value_t = typename std::iterator_traits<decltype(std::begin(
std::declval<R&>()))>::value_type;
// Constexpr check that we can always safely cast from From to To.
// If we don't require this, we might silently get different semantics from
// standard algorithms.
template <typename From, typename To>
constexpr bool convertible_with_no_loss() {
if (sizeof(From) > sizeof(To)) {
return false;
}
if (std::is_signed_v<From>) {
return std::is_signed_v<To>;
}
return std::is_unsigned_v<To> || sizeof(From) < sizeof(To);
}
// All the requirements to call contains(haystack, needle);
// * both are simd friendly (contigious range, primitive types)
// * integrals only
// * needle can be converted to the value_type of haystack and
// the result of equality comparison will be the same.
template <typename R, typename T>
constexpr bool contains_haystack_needle_test() {
if constexpr (!std::is_invocable_v<AsSimdFriendlyUintFn, R>) {
return false;
} else if constexpr (!has_integral_simd_friendly_equivalent_scalar_v<T>) {
return false;
} else {
using simd_haystack_value =
simd_friendly_equivalent_scalar_t<std_range_value_t<R>>;
using simd_needle = simd_friendly_equivalent_scalar_t<T>;
return convertible_with_no_loss<simd_needle, simd_haystack_value>();
}
}
} // namespace detail
/**
* folly::simd::contains
* folly::simd::contains_fn
*
* A vectorized version of `std::ranges::find(r, x) != r.end()`.
* Only works for "simd friendly cases" -
* specifically the ones where the type can be reasonably cast
* to `uint8/16/32/64_t`.
*
**/
struct contains_fn {
template <
typename R,
typename T,
typename =
std::enable_if_t<detail::contains_haystack_needle_test<R, T>()>>
FOLLY_ERASE bool operator()(R&& r, T x) const {
auto castR = detail::asSimdFriendlyUint(folly::span(r));
using value_type = detail::std_range_value_t<decltype(castR)>;
auto castX = static_cast<value_type>(detail::asSimdFriendlyUint(x));
if constexpr (std::is_same_v<value_type, std::uint8_t>) {
return detail::containsU8(castR, castX);
} else if constexpr (std::is_same_v<value_type, std::uint16_t>) {
return detail::containsU16(castR, castX);
} else if constexpr (std::is_same_v<value_type, std::uint32_t>) {
return detail::containsU32(castR, castX);
} else {
static_assert(
std::is_same_v<value_type, std::uint64_t>,
"internal error, unknown type");
return detail::containsU64(castR, castX);
}
}
};
inline constexpr contains_fn contains;
} // namespace folly::simd