.. _program_listing_file_src_rdf4cpp_util_CharMatcher.cpp: Program Listing for File CharMatcher.cpp ======================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/rdf4cpp/util/CharMatcher.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "CharMatcher.hpp" #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "rdf4cpp/util/CharMatcher.cpp" #include #include #include #include #include HWY_BEFORE_NAMESPACE(); // at file scope namespace rdf4cpp::util::char_matcher_detail::HWY_NAMESPACE { // Calls `func(d, v)` for each input vector; out of bound lanes with index i >= // `count` are instead taken from `no[i % Lanes(d)]`. // if func returns false, exits early // from hwy/contrib/algo/transform-inl.h, added early return template> HWY_INLINE void Foreach(D d, T const *HWY_RESTRICT in, size_t const count, hwy::HWY_NAMESPACE::Vec const no, Func const &func) { size_t const N = Lanes(d); size_t idx = 0; if (count >= N) { for (; idx <= count - N; idx += N) { hwy::HWY_NAMESPACE::Vec const v = LoadU(d, in + idx); if (!func(d, v)) { return; } } } // `count` was a multiple of the vector length `N`: already done. if (HWY_UNLIKELY(idx == count)) return; size_t const remaining = count - idx; HWY_DASSERT(0 != remaining && remaining < N); hwy::HWY_NAMESPACE::Vec const v = LoadNOr(no, d, in + idx, remaining); func(d, v); } template requires(rn > 0) HWY_INLINE std::optional try_match_simd_impl(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString const &single) { using namespace hwy::HWY_NAMESPACE; bool found_unicode = false; bool r = true; using D = ScalableTag; //NOLINT tag type, selects the used vector type using V = Vec; //NOLINT vector type D const d; // elements from this vector are used, if data is not a multiple of Lanes(d) // should not influence final result // => comparisons need to evaluate as true for the logic to work V const zero = Set(d, static_cast(ranges[0].first)); #if !(HWY_TARGET & (HWY_ALL_NEON | HWY_ALL_SVE)) && !HWY_HAVE_SCALABLE // highway doc: on x86 < and > are 1 instruction for signed ints (3 for unsigned) // and <= and >= are 2 instructions regardless of signed/unsigned // Set is 2 instructions, while potential load/store is 1 // // storing a vector directly is possible on x86 // // => load vector once and store it inside an array using single_storage = V; #define GET_SINGLE(x) x #define LOAD_SINGLE(x) Set(d, x) #else // highway doc: on arm neon <, >, <= and >= are 1 instruction for any int // Set is 1 instruction, while potential load/store is 1 // // storing a vector directly is not possible on arm // // => store raw chars inside an array, load it when needed using single_storage = int8_t; #define GET_SINGLE(x) Set(d, x) #define LOAD_SINGLE(x) x #endif // set up ranges std::array, rn> range_vectors; for (size_t i = 0; i < rn; ++i) { RDF4CPP_ASSERT(ranges[i].first != '\0'); RDF4CPP_ASSERT(ranges[i].first < ranges[i].last); range_vectors[i].first = LOAD_SINGLE(static_cast(ranges[i].first - 1)); range_vectors[i].second = LOAD_SINGLE(static_cast(ranges[i].last + 1)); } single_storage const unicode_bit_index = LOAD_SINGLE(7); // set up single compares std::array single_vectors{}; auto view = static_cast(single); for (size_t i = 0; i < view.size(); ++i) { single_vectors[i] = LOAD_SINGLE(static_cast(view[i])); } Foreach(d, reinterpret_cast(data.data()), data.size(), zero, [&](auto d, auto in_vec) HWY_ATTR { // if bit 7 is set, the char belongs to a unicode sequence // => std::nullopt if (!AllTrue(d, Lt(HighestSetBitIndex(in_vec), GET_SINGLE(unicode_bit_index)))) { found_unicode = true; return false; } // check if target is in one of the ranges auto m = And(Gt(in_vec, GET_SINGLE(range_vectors[0].first)), Lt(in_vec, GET_SINGLE(range_vectors[0].second))); for (size_t i = 1; i < rn; ++i) { m = Or(m, And(Gt(in_vec, GET_SINGLE(range_vectors[i].first)), Lt(in_vec, GET_SINGLE(range_vectors[i].second)))); } // check the single compares for (size_t i = 0; i < sn - 1; ++i) { m = Or(m, Eq(in_vec, GET_SINGLE(single_vectors[i]))); } r = r && AllTrue(d, m); return r; // possible early return }); #undef GET_SINGLE #undef LOAD_SINGLE if (found_unicode) return std::nullopt; return r; } std::optional try_match_simd_impl_3_1(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<1> const &single) { return try_match_simd_impl(data, ranges, single); } std::optional try_match_simd_impl_3_4(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<4> const &single) { return try_match_simd_impl(data, ranges, single); } std::optional try_match_simd_impl_3_18(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<18> const &single) { return try_match_simd_impl(data, ranges, single); } std::optional try_match_simd_impl_3_20(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<20> const &single) { return try_match_simd_impl(data, ranges, single); } std::optional try_match_simd_impl_3_21(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<21> const &single) { return try_match_simd_impl(data, ranges, single); } std::optional try_match_simd_impl_1_1(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<1> const &single) { return try_match_simd_impl(data, ranges, single); } template HWY_INLINE bool contains_any_impl(std::string_view data, datatypes::registry::util::ConstexprString const &match) { using namespace hwy::HWY_NAMESPACE; bool r = false; using D = ScalableTag; //NOLINT tag type, selects the used vector type using V = Vec; //NOLINT vector type D const d; #if !(HWY_TARGET & (HWY_ALL_NEON | HWY_ALL_SVE)) && !HWY_HAVE_SCALABLE // highway doc: on x86 < and > are 1 instruction for signed ints (3 for unsigned) // and <= and >= are 2 instructions regardless of signed/unsigned // Set is 2 instructions, while potential load/store is 1 // // storing a vector directly is possible on x86 // // => load vector once and store it inside an array using single_storage = V; #define GET_SINGLE(x) x #define LOAD_SINGLE(x) Set(d, x) #else // highway doc: on arm neon <, >, <= and >= are 1 instruction for any int // Set is 1 instruction, while potential load/store is 1 // // storing a vector directly is not possible on arm // // => store raw chars inside an array, load it when needed using single_storage = int8_t; #define GET_SINGLE(x) Set(d, x) #define LOAD_SINGLE(x) x #endif // elements from this vector are used, if data is not a multiple of Lanes(d) // should not influence final result // => comparisons need to evaluate as false for the logic to work V const zero = Set(d, static_cast(0)); // load comparison vectors std::array match_vectors; auto view = static_cast(match); for (size_t i = 0; i < n-1; ++i) { RDF4CPP_ASSERT(view[i] != '\0'); match_vectors[i] = LOAD_SINGLE(static_cast(view[i])); } Foreach(d, reinterpret_cast(data.data()), data.size(), zero, [&](auto d, V in_vec) HWY_ATTR { // compare auto m = Eq(in_vec, GET_SINGLE(match_vectors[0])); for (size_t i = 1; i < n-1; ++i) { m = Or(m, Eq(in_vec, GET_SINGLE(match_vectors[i]))); } r = r || !AllFalse(d, m); return !r; // potential early return }); #undef GET_SINGLE #undef LOAD_SINGLE return r; } bool contains_any_impl_5(std::string_view data, datatypes::registry::util::ConstexprString<5> const &match) { return contains_any_impl(data, match); } // NOLINTNEXTLINE(google-readability-namespace-comments) } // namespace rdf4cpp::util::char_matcher_detail::HWY_NAMESPACE HWY_AFTER_NAMESPACE(); #if HWY_ONCE namespace rdf4cpp::util::char_matcher_detail { HWY_EXPORT(try_match_simd_impl_3_1); HWY_EXPORT(try_match_simd_impl_3_4); HWY_EXPORT(try_match_simd_impl_3_18); HWY_EXPORT(try_match_simd_impl_3_20); HWY_EXPORT(try_match_simd_impl_3_21); HWY_EXPORT(try_match_simd_impl_1_1); HWY_EXPORT(contains_any_impl_5); template<> std::optional try_match_simd(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<1> const &single) { return HWY_DYNAMIC_DISPATCH(try_match_simd_impl_3_1)(data, ranges, single); } template<> std::optional try_match_simd(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<4> const &single) { return HWY_DYNAMIC_DISPATCH(try_match_simd_impl_3_4)(data, ranges, single); } template<> std::optional try_match_simd(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<18> const &single) { return HWY_DYNAMIC_DISPATCH(try_match_simd_impl_3_18)(data, ranges, single); } template<> std::optional try_match_simd(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<20> const &single) { return HWY_DYNAMIC_DISPATCH(try_match_simd_impl_3_20)(data, ranges, single); } template<> std::optional try_match_simd(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<21> const &single) { return HWY_DYNAMIC_DISPATCH(try_match_simd_impl_3_21)(data, ranges, single); } template<> std::optional try_match_simd(std::string_view data, std::array const &ranges, datatypes::registry::util::ConstexprString<1> const &single) { return HWY_DYNAMIC_DISPATCH(try_match_simd_impl_1_1)(data, ranges, single); } template<> bool contains_any(std::string_view data, datatypes::registry::util::ConstexprString<5> const &match) { return HWY_DYNAMIC_DISPATCH(contains_any_impl_5)(data, match); } } // namespace rdf4cpp::util::char_matcher_detail #endif