From 19583cceba52995d3c4981ddb2c9dd73b8b5fa12 Mon Sep 17 00:00:00 2001 From: Fangchen Li Date: Sat, 11 Jul 2026 11:48:55 -0700 Subject: [PATCH 1/2] GH-50478: [C++][Compute] Support string_view/binary_view in scalar string predicate kernels Add STRING_VIEW/BINARY_VIEW support to the scalar string predicate and measurement kernels that read a string and emit a fixed-width type: match_substring(+regex), match_like, starts_with, ends_with, find_substring(+regex), count_substring(+regex), binary_length, utf8_length, string_is_ascii, and the ascii_is_*/utf8_is_* classifiers. The matcher/predicate logic already operates on std::string_view, so the change feeds view elements into the existing logic: MatchSubstringImpl gets a per-element ArrayIterator branch for the view layout (the packed-offset fast path stays for base binary/string), and the applicator-based kernels only need registration entries plus a StringOffsetType helper mapping views to Int32 output. Kernels that emit strings/lists are left for a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../compute/kernels/scalar_string_ascii.cc | 120 +++++++++++++--- .../kernels/scalar_string_benchmark.cc | 46 ++++++- .../compute/kernels/scalar_string_internal.h | 35 ++++- .../compute/kernels/scalar_string_test.cc | 129 ++++++++++++++++++ .../compute/kernels/scalar_string_utf8.cc | 5 + docs/source/cpp/compute.rst | 11 ++ 6 files changed, 313 insertions(+), 33 deletions(-) diff --git a/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc b/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc index d4dda5556b3b..f82ff6749c26 100644 --- a/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc +++ b/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc @@ -889,6 +889,12 @@ void AddAsciiStringLength(FunctionRegistry* registry) { ty); DCHECK_OK(func->AddKernel({ty}, int64(), std::move(exec))); } + // View element length is int32-sized, so both view types emit int32. + for (const auto& ty : BinaryViewTypes()) { + auto exec = GenerateVarBinaryViewBase(*ty); + DCHECK_OK(func->AddKernel({ty}, int32(), std::move(exec))); + } DCHECK_OK(func->AddKernel({InputType(Type::FIXED_SIZE_BINARY)}, int32(), BinaryLength::FixedSizeExec)); DCHECK_OK(registry->AddFunction(std::move(func))); @@ -1337,27 +1343,47 @@ struct RegexSubstringMatcher { template struct MatchSubstringImpl { - using offset_type = typename Type::offset_type; - static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out, const Matcher* matcher) { - StringBoolTransform( - ctx, batch, - [&matcher](const void* raw_offsets, const uint8_t* data, int64_t length, - int64_t output_offset, uint8_t* output) { - const offset_type* offsets = reinterpret_cast(raw_offsets); - FirstTimeBitmapWriter bitmap_writer(output, output_offset, length); - for (int64_t i = 0; i < length; ++i) { - const char* current_data = reinterpret_cast(data + offsets[i]); - int64_t current_length = offsets[i + 1] - offsets[i]; - if (matcher->Match(std::string_view(current_data, current_length))) { + if constexpr (is_binary_view_like_type::value) { + // Views have no packed offset buffer, so evaluate per element. Null slots are + // skipped: a view's null header is not validated and may carry a bogus + // buffer_index/offset that decoding would dereference. + const ArraySpan& input = batch[0].array; + ArraySpan* out_arr = out->array_span_mutable(); + FirstTimeBitmapWriter bitmap_writer(out_arr->buffers[1].data, out_arr->offset, + input.length); + VisitArrayValuesInline( + input, + [&](std::string_view val) { + if (matcher->Match(val)) { bitmap_writer.Set(); } bitmap_writer.Next(); - } - bitmap_writer.Finish(); - }, - out); + }, + [&]() { bitmap_writer.Next(); }); + bitmap_writer.Finish(); + } else { + using offset_type = typename Type::offset_type; + StringBoolTransform( + ctx, batch, + [&matcher](const void* raw_offsets, const uint8_t* data, int64_t length, + int64_t output_offset, uint8_t* output) { + const offset_type* offsets = + reinterpret_cast(raw_offsets); + FirstTimeBitmapWriter bitmap_writer(output, output_offset, length); + for (int64_t i = 0; i < length; ++i) { + const char* current_data = reinterpret_cast(data + offsets[i]); + int64_t current_length = offsets[i + 1] - offsets[i]; + if (matcher->Match(std::string_view(current_data, current_length))) { + bitmap_writer.Set(); + } + bitmap_writer.Next(); + } + bitmap_writer.Finish(); + }, + out); + } return Status::OK(); } }; @@ -1611,6 +1637,19 @@ const FunctionDoc match_like_doc( {"strings"}, "MatchSubstringOptions", /*options_required=*/true); #endif +// Register the view kernels for a match-substring-style predicate. Registered per +// view type (not via GenerateVarBinaryViewBase) so utf8_view -> StringViewType keeps +// the is_utf8 distinction used by ignore_case/regex folding. +template