1 // Licensed to the Apache Software Foundation (ASF) under one 2 // or more contributor license agreements. See the NOTICE file 3 // distributed with this work for additional information 4 // regarding copyright ownership. The ASF licenses this file 5 // to you under the Apache License, Version 2.0 (the 6 // "License"); you may not use this file except in compliance 7 // with the License. You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 #pragma once 19 20 #include <cstdint> 21 #include <memory> 22 #include <string> 23 #include <utility> 24 #include <vector> 25 26 #include "arrow/array/builder_binary.h" 27 #include "arrow/array/data.h" 28 #include "arrow/buffer.h" 29 #include "arrow/buffer_builder.h" 30 #include "arrow/compute/exec.h" 31 #include "arrow/compute/kernel.h" 32 #include "arrow/datum.h" 33 #include "arrow/result.h" 34 #include "arrow/scalar.h" 35 #include "arrow/status.h" 36 #include "arrow/type.h" 37 #include "arrow/type_traits.h" 38 #include "arrow/util/bit_block_counter.h" 39 #include "arrow/util/bit_util.h" 40 #include "arrow/util/bitmap_generate.h" 41 #include "arrow/util/bitmap_reader.h" 42 #include "arrow/util/bitmap_writer.h" 43 #include "arrow/util/checked_cast.h" 44 #include "arrow/util/decimal.h" 45 #include "arrow/util/logging.h" 46 #include "arrow/util/macros.h" 47 #include "arrow/util/make_unique.h" 48 #include "arrow/util/optional.h" 49 #include "arrow/util/string_view.h" 50 #include "arrow/visitor_inline.h" 51 52 namespace arrow { 53 54 using internal::BinaryBitBlockCounter; 55 using internal::BitBlockCount; 56 using internal::BitmapReader; 57 using internal::checked_cast; 58 using internal::FirstTimeBitmapWriter; 59 using internal::GenerateBitsUnrolled; 60 using internal::VisitBitBlocksVoid; 61 using internal::VisitTwoBitBlocksVoid; 62 63 namespace compute { 64 namespace internal { 65 66 #ifdef ARROW_EXTRA_ERROR_CONTEXT 67 68 #define KERNEL_RETURN_IF_ERROR(ctx, expr) \ 69 do { \ 70 Status _st = (expr); \ 71 if (ARROW_PREDICT_FALSE(!_st.ok())) { \ 72 _st.AddContextLine(__FILE__, __LINE__, #expr); \ 73 ctx->SetStatus(_st); \ 74 return; \ 75 } \ 76 } while (0) 77 78 #else 79 80 #define KERNEL_RETURN_IF_ERROR(ctx, expr) \ 81 do { \ 82 Status _st = (expr); \ 83 if (ARROW_PREDICT_FALSE(!_st.ok())) { \ 84 ctx->SetStatus(_st); \ 85 return; \ 86 } \ 87 } while (0) 88 89 #endif // ARROW_EXTRA_ERROR_CONTEXT 90 91 #define KERNEL_ASSIGN_OR_RAISE_IMPL(result_name, lhs, ctx, rexpr) \ 92 auto result_name = (rexpr); \ 93 KERNEL_RETURN_IF_ERROR(ctx, (result_name).status()); \ 94 lhs = std::move(result_name).MoveValueUnsafe(); 95 96 #define KERNEL_ASSIGN_OR_RAISE_NAME(x, y) ARROW_CONCAT(x, y) 97 98 #define KERNEL_ASSIGN_OR_RAISE(lhs, ctx, rexpr) \ 99 KERNEL_ASSIGN_OR_RAISE_IMPL(KERNEL_ASSIGN_OR_RAISE_NAME(_error_or_value, __COUNTER__), \ 100 lhs, ctx, rexpr); 101 102 /// KernelState adapter for the common case of kernels whose only 103 /// state is an instance of a subclass of FunctionOptions. 104 /// Default FunctionOptions are *not* handled here. 105 template <typename OptionsType> 106 struct OptionsWrapper : public KernelState { OptionsWrapperOptionsWrapper107 explicit OptionsWrapper(OptionsType options) : options(std::move(options)) {} 108 InitOptionsWrapper109 static std::unique_ptr<KernelState> Init(KernelContext* ctx, 110 const KernelInitArgs& args) { 111 if (auto options = static_cast<const OptionsType*>(args.options)) { 112 return ::arrow::internal::make_unique<OptionsWrapper>(*options); 113 } 114 115 ctx->SetStatus( 116 Status::Invalid("Attempted to initialize KernelState from null FunctionOptions")); 117 return NULLPTR; 118 } 119 GetOptionsWrapper120 static const OptionsType& Get(const KernelState& state) { 121 return ::arrow::internal::checked_cast<const OptionsWrapper&>(state).options; 122 } 123 GetOptionsWrapper124 static const OptionsType& Get(KernelContext* ctx) { return Get(*ctx->state()); } 125 126 OptionsType options; 127 }; 128 129 /// KernelState adapter for when the state is an instance constructed with the 130 /// KernelContext and the FunctionOptions as argument 131 template <typename StateType, typename OptionsType> 132 struct KernelStateFromFunctionOptions : public KernelState { KernelStateFromFunctionOptionsKernelStateFromFunctionOptions133 explicit KernelStateFromFunctionOptions(KernelContext* ctx, OptionsType state) 134 : state(StateType(ctx, std::move(state))) {} 135 InitKernelStateFromFunctionOptions136 static std::unique_ptr<KernelState> Init(KernelContext* ctx, 137 const KernelInitArgs& args) { 138 if (auto options = static_cast<const OptionsType*>(args.options)) { 139 return ::arrow::internal::make_unique<KernelStateFromFunctionOptions>(ctx, 140 *options); 141 } 142 143 ctx->SetStatus( 144 Status::Invalid("Attempted to initialize KernelState from null FunctionOptions")); 145 return NULLPTR; 146 } 147 GetKernelStateFromFunctionOptions148 static const StateType& Get(const KernelState& state) { 149 return ::arrow::internal::checked_cast<const KernelStateFromFunctionOptions&>(state) 150 .state; 151 } 152 GetKernelStateFromFunctionOptions153 static const StateType& Get(KernelContext* ctx) { return Get(*ctx->state()); } 154 155 StateType state; 156 }; 157 158 // ---------------------------------------------------------------------- 159 // Input and output value type definitions 160 161 template <typename Type, typename Enable = void> 162 struct GetViewType; 163 164 template <typename Type> 165 struct GetViewType<Type, enable_if_has_c_type<Type>> { 166 using T = typename Type::c_type; 167 using PhysicalType = T; 168 169 static T LogicalValue(PhysicalType value) { return value; } 170 }; 171 172 template <typename Type> 173 struct GetViewType<Type, enable_if_t<is_base_binary_type<Type>::value || 174 is_fixed_size_binary_type<Type>::value>> { 175 using T = util::string_view; 176 using PhysicalType = T; 177 178 static T LogicalValue(PhysicalType value) { return value; } 179 }; 180 181 template <> 182 struct GetViewType<Decimal128Type> { 183 using T = Decimal128; 184 using PhysicalType = util::string_view; 185 186 static T LogicalValue(PhysicalType value) { 187 return Decimal128(reinterpret_cast<const uint8_t*>(value.data())); 188 } 189 }; 190 191 template <typename Type, typename Enable = void> 192 struct GetOutputType; 193 194 template <typename Type> 195 struct GetOutputType<Type, enable_if_has_c_type<Type>> { 196 using T = typename Type::c_type; 197 }; 198 199 template <typename Type> 200 struct GetOutputType<Type, enable_if_t<is_string_like_type<Type>::value>> { 201 using T = std::string; 202 }; 203 204 template <> 205 struct GetOutputType<Decimal128Type> { 206 using T = Decimal128; 207 }; 208 209 // ---------------------------------------------------------------------- 210 // Iteration / value access utilities 211 212 template <typename T, typename R = void> 213 using enable_if_has_c_type_not_boolean = 214 enable_if_t<has_c_type<T>::value && !is_boolean_type<T>::value, R>; 215 216 // Iterator over various input array types, yielding a GetViewType<Type> 217 218 template <typename Type, typename Enable = void> 219 struct ArrayIterator; 220 221 template <typename Type> 222 struct ArrayIterator<Type, enable_if_has_c_type_not_boolean<Type>> { 223 using T = typename Type::c_type; 224 const T* values; 225 226 explicit ArrayIterator(const ArrayData& data) : values(data.GetValues<T>(1)) {} 227 T operator()() { return *values++; } 228 }; 229 230 template <typename Type> 231 struct ArrayIterator<Type, enable_if_boolean<Type>> { 232 BitmapReader reader; 233 234 explicit ArrayIterator(const ArrayData& data) 235 : reader(data.buffers[1]->data(), data.offset, data.length) {} 236 bool operator()() { 237 bool out = reader.IsSet(); 238 reader.Next(); 239 return out; 240 } 241 }; 242 243 template <typename Type> 244 struct ArrayIterator<Type, enable_if_base_binary<Type>> { 245 using offset_type = typename Type::offset_type; 246 const ArrayData& arr; 247 const offset_type* offsets; 248 offset_type cur_offset; 249 const char* data; 250 int64_t position; 251 252 explicit ArrayIterator(const ArrayData& arr) 253 : arr(arr), 254 offsets(reinterpret_cast<const offset_type*>(arr.buffers[1]->data()) + 255 arr.offset), 256 cur_offset(offsets[0]), 257 data(reinterpret_cast<const char*>(arr.buffers[2]->data())), 258 position(0) {} 259 260 util::string_view operator()() { 261 offset_type next_offset = offsets[++position]; 262 auto result = util::string_view(data + cur_offset, next_offset - cur_offset); 263 cur_offset = next_offset; 264 return result; 265 } 266 }; 267 268 // Iterator over various output array types, taking a GetOutputType<Type> 269 270 template <typename Type, typename Enable = void> 271 struct OutputArrayWriter; 272 273 template <typename Type> 274 struct OutputArrayWriter<Type, enable_if_has_c_type_not_boolean<Type>> { 275 using T = typename Type::c_type; 276 T* values; 277 278 explicit OutputArrayWriter(ArrayData* data) : values(data->GetMutableValues<T>(1)) {} 279 280 void Write(T value) { *values++ = value; } 281 282 // Note that this doesn't write the null bitmap, which should be consistent 283 // with Write / WriteNull calls 284 void WriteNull() { *values++ = T{}; } 285 }; 286 287 // (Un)box Scalar to / from C++ value 288 289 template <typename Type, typename Enable = void> 290 struct UnboxScalar; 291 292 template <typename Type> 293 struct UnboxScalar<Type, enable_if_has_c_type<Type>> { 294 using T = typename Type::c_type; 295 static T Unbox(const Scalar& val) { 296 return *reinterpret_cast<const T*>( 297 checked_cast<const ::arrow::internal::PrimitiveScalarBase&>(val).data()); 298 } 299 }; 300 301 template <typename Type> 302 struct UnboxScalar<Type, enable_if_base_binary<Type>> { 303 static util::string_view Unbox(const Scalar& val) { 304 if (!val.is_valid) return util::string_view(); 305 return util::string_view(*checked_cast<const BaseBinaryScalar&>(val).value); 306 } 307 }; 308 309 template <> 310 struct UnboxScalar<Decimal128Type> { 311 static Decimal128 Unbox(const Scalar& val) { 312 return checked_cast<const Decimal128Scalar&>(val).value; 313 } 314 }; 315 316 template <typename Type, typename Enable = void> 317 struct BoxScalar; 318 319 template <typename Type> 320 struct BoxScalar<Type, enable_if_has_c_type<Type>> { 321 using T = typename GetOutputType<Type>::T; 322 using ScalarType = typename TypeTraits<Type>::ScalarType; 323 static void Box(T val, Scalar* out) { checked_cast<ScalarType*>(out)->value = val; } 324 }; 325 326 template <typename Type> 327 struct BoxScalar<Type, enable_if_base_binary<Type>> { 328 using T = typename GetOutputType<Type>::T; 329 using ScalarType = typename TypeTraits<Type>::ScalarType; 330 static void Box(T val, Scalar* out) { 331 checked_cast<ScalarType*>(out)->value = std::make_shared<Buffer>(val); 332 } 333 }; 334 335 template <> 336 struct BoxScalar<Decimal128Type> { 337 using T = Decimal128; 338 using ScalarType = Decimal128Scalar; 339 static void Box(T val, Scalar* out) { checked_cast<ScalarType*>(out)->value = val; } 340 }; 341 342 // A VisitArrayDataInline variant that calls its visitor function with logical 343 // values, such as Decimal128 rather than util::string_view. 344 345 template <typename T, typename VisitFunc, typename NullFunc> 346 static void VisitArrayValuesInline(const ArrayData& arr, VisitFunc&& valid_func, 347 NullFunc&& null_func) { 348 VisitArrayDataInline<T>( 349 arr, 350 [&](typename GetViewType<T>::PhysicalType v) { 351 valid_func(GetViewType<T>::LogicalValue(std::move(v))); 352 }, 353 std::forward<NullFunc>(null_func)); 354 } 355 356 // Like VisitArrayValuesInline, but for binary functions. 357 358 template <typename Arg0Type, typename Arg1Type, typename VisitFunc, typename NullFunc> 359 static void VisitTwoArrayValuesInline(const ArrayData& arr0, const ArrayData& arr1, 360 VisitFunc&& valid_func, NullFunc&& null_func) { 361 ArrayIterator<Arg0Type> arr0_it(arr0); 362 ArrayIterator<Arg1Type> arr1_it(arr1); 363 364 auto visit_valid = [&](int64_t i) { 365 valid_func(GetViewType<Arg0Type>::LogicalValue(arr0_it()), 366 GetViewType<Arg1Type>::LogicalValue(arr1_it())); 367 }; 368 auto visit_null = [&]() { 369 arr0_it(); 370 arr1_it(); 371 null_func(); 372 }; 373 VisitTwoBitBlocksVoid(arr0.buffers[0], arr0.offset, arr1.buffers[0], arr1.offset, 374 arr0.length, std::move(visit_valid), std::move(visit_null)); 375 } 376 377 // ---------------------------------------------------------------------- 378 // Reusable type resolvers 379 380 Result<ValueDescr> FirstType(KernelContext*, const std::vector<ValueDescr>& descrs); 381 382 // ---------------------------------------------------------------------- 383 // Generate an array kernel given template classes 384 385 void ExecFail(KernelContext* ctx, const ExecBatch& batch, Datum* out); 386 387 ArrayKernelExec MakeFlippedBinaryExec(ArrayKernelExec exec); 388 389 // ---------------------------------------------------------------------- 390 // Helpers for iterating over common DataType instances for adding kernels to 391 // functions 392 393 const std::vector<std::shared_ptr<DataType>>& BaseBinaryTypes(); 394 const std::vector<std::shared_ptr<DataType>>& StringTypes(); 395 const std::vector<std::shared_ptr<DataType>>& SignedIntTypes(); 396 const std::vector<std::shared_ptr<DataType>>& UnsignedIntTypes(); 397 const std::vector<std::shared_ptr<DataType>>& IntTypes(); 398 const std::vector<std::shared_ptr<DataType>>& FloatingPointTypes(); 399 400 ARROW_EXPORT 401 const std::vector<TimeUnit::type>& AllTimeUnits(); 402 403 // Returns a vector of example instances of parametric types such as 404 // 405 // * Decimal 406 // * Timestamp (requiring unit) 407 // * Time32 (requiring unit) 408 // * Time64 (requiring unit) 409 // * Duration (requiring unit) 410 // * List, LargeList, FixedSizeList 411 // * Struct 412 // * Union 413 // * Dictionary 414 // * Map 415 // 416 // Generally kernels will use the "FirstType" OutputType::Resolver above for 417 // the OutputType of the kernel's signature and match::SameTypeId for the 418 // corresponding InputType 419 const std::vector<std::shared_ptr<DataType>>& ExampleParametricTypes(); 420 421 // Number types without boolean 422 const std::vector<std::shared_ptr<DataType>>& NumericTypes(); 423 424 // Temporal types including time and timestamps for each unit 425 const std::vector<std::shared_ptr<DataType>>& TemporalTypes(); 426 427 // Integer, floating point, base binary, and temporal 428 const std::vector<std::shared_ptr<DataType>>& PrimitiveTypes(); 429 430 // ---------------------------------------------------------------------- 431 // "Applicators" take an operator definition (which may be scalar-valued or 432 // array-valued) and creates an ArrayKernelExec which can be used to add an 433 // ArrayKernel to a Function. 434 435 namespace applicator { 436 437 // Generate an ArrayKernelExec given a functor that handles all of its own 438 // iteration, etc. 439 // 440 // Operator must implement 441 // 442 // static void Call(KernelContext*, const ArrayData& in, ArrayData* out) 443 // static void Call(KernelContext*, const Scalar& in, Scalar* out) 444 template <typename Operator> 445 static void SimpleUnary(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 446 if (batch[0].kind() == Datum::SCALAR) { 447 Operator::Call(ctx, *batch[0].scalar(), out->scalar().get()); 448 } else if (batch.length > 0) { 449 Operator::Call(ctx, *batch[0].array(), out->mutable_array()); 450 } 451 } 452 453 // Generate an ArrayKernelExec given a functor that handles all of its own 454 // iteration, etc. 455 // 456 // Operator must implement 457 // 458 // static void Call(KernelContext*, const ArrayData& arg0, const ArrayData& arg1, 459 // ArrayData* out) 460 // static void Call(KernelContext*, const ArrayData& arg0, const Scalar& arg1, 461 // ArrayData* out) 462 // static void Call(KernelContext*, const Scalar& arg0, const ArrayData& arg1, 463 // ArrayData* out) 464 // static void Call(KernelContext*, const Scalar& arg0, const Scalar& arg1, 465 // Scalar* out) 466 template <typename Operator> 467 static void SimpleBinary(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 468 if (batch.length == 0) return; 469 470 if (batch[0].kind() == Datum::ARRAY) { 471 if (batch[1].kind() == Datum::ARRAY) { 472 Operator::Call(ctx, *batch[0].array(), *batch[1].array(), out->mutable_array()); 473 } else { 474 Operator::Call(ctx, *batch[0].array(), *batch[1].scalar(), out->mutable_array()); 475 } 476 } else { 477 if (batch[1].kind() == Datum::ARRAY) { 478 Operator::Call(ctx, *batch[0].scalar(), *batch[1].array(), out->mutable_array()); 479 } else { 480 Operator::Call(ctx, *batch[0].scalar(), *batch[1].scalar(), out->scalar().get()); 481 } 482 } 483 } 484 485 // OutputAdapter allows passing an inlineable lambda that provides a sequence 486 // of output values to write into output memory. Boolean and primitive outputs 487 // are currently implemented, and the validity bitmap is presumed to be handled 488 // at a higher level, so this writes into every output slot, null or not. 489 template <typename Type, typename Enable = void> 490 struct OutputAdapter; 491 492 template <typename Type> 493 struct OutputAdapter<Type, enable_if_boolean<Type>> { 494 template <typename Generator> 495 static void Write(KernelContext*, Datum* out, Generator&& generator) { 496 ArrayData* out_arr = out->mutable_array(); 497 auto out_bitmap = out_arr->buffers[1]->mutable_data(); 498 GenerateBitsUnrolled(out_bitmap, out_arr->offset, out_arr->length, 499 std::forward<Generator>(generator)); 500 } 501 }; 502 503 template <typename Type> 504 struct OutputAdapter<Type, enable_if_has_c_type_not_boolean<Type>> { 505 template <typename Generator> 506 static void Write(KernelContext*, Datum* out, Generator&& generator) { 507 ArrayData* out_arr = out->mutable_array(); 508 auto out_data = out_arr->GetMutableValues<typename Type::c_type>(1); 509 // TODO: Is this as fast as a more explicitly inlined function? 510 for (int64_t i = 0; i < out_arr->length; ++i) { 511 *out_data++ = generator(); 512 } 513 } 514 }; 515 516 template <typename Type> 517 struct OutputAdapter<Type, enable_if_base_binary<Type>> { 518 template <typename Generator> 519 static void Write(KernelContext* ctx, Datum* out, Generator&& generator) { 520 ctx->SetStatus(Status::NotImplemented("NYI")); 521 } 522 }; 523 524 // A kernel exec generator for unary functions that addresses both array and 525 // scalar inputs and dispatches input iteration and output writing to other 526 // templates 527 // 528 // This template executes the operator even on the data behind null values, 529 // therefore it is generally only suitable for operators that are safe to apply 530 // even on the null slot values. 531 // 532 // The "Op" functor should have the form 533 // 534 // struct Op { 535 // template <typename OutValue, typename Arg0Value> 536 // static OutValue Call(KernelContext* ctx, Arg0Value val) { 537 // // implementation 538 // } 539 // }; 540 template <typename OutType, typename Arg0Type, typename Op> 541 struct ScalarUnary { 542 using OutValue = typename GetOutputType<OutType>::T; 543 using Arg0Value = typename GetViewType<Arg0Type>::T; 544 545 static void ExecArray(KernelContext* ctx, const ArrayData& arg0, Datum* out) { 546 ArrayIterator<Arg0Type> arg0_it(arg0); 547 OutputAdapter<OutType>::Write(ctx, out, [&]() -> OutValue { 548 return Op::template Call<OutValue, Arg0Value>(ctx, arg0_it()); 549 }); 550 } 551 552 static void ExecScalar(KernelContext* ctx, const Scalar& arg0, Datum* out) { 553 Scalar* out_scalar = out->scalar().get(); 554 if (arg0.is_valid) { 555 Arg0Value arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); 556 out_scalar->is_valid = true; 557 BoxScalar<OutType>::Box(Op::template Call<OutValue, Arg0Value>(ctx, arg0_val), 558 out_scalar); 559 } else { 560 out_scalar->is_valid = false; 561 } 562 } 563 564 static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 565 if (batch[0].kind() == Datum::ARRAY) { 566 return ExecArray(ctx, *batch[0].array(), out); 567 } else { 568 return ExecScalar(ctx, *batch[0].scalar(), out); 569 } 570 } 571 }; 572 573 // An alternative to ScalarUnary that Applies a scalar operation with state on 574 // only the not-null values of a single array 575 template <typename OutType, typename Arg0Type, typename Op> 576 struct ScalarUnaryNotNullStateful { 577 using ThisType = ScalarUnaryNotNullStateful<OutType, Arg0Type, Op>; 578 using OutValue = typename GetOutputType<OutType>::T; 579 using Arg0Value = typename GetViewType<Arg0Type>::T; 580 581 Op op; 582 explicit ScalarUnaryNotNullStateful(Op op) : op(std::move(op)) {} 583 584 // NOTE: In ArrayExec<Type>, Type is really OutputType 585 586 template <typename Type, typename Enable = void> 587 struct ArrayExec { 588 static void Exec(const ThisType& functor, KernelContext* ctx, const ExecBatch& batch, 589 Datum* out) { 590 ARROW_LOG(FATAL) << "Missing ArrayExec specialization for output type " 591 << out->type(); 592 } 593 }; 594 595 template <typename Type> 596 struct ArrayExec< 597 Type, enable_if_t<has_c_type<Type>::value && !is_boolean_type<Type>::value>> { 598 static void Exec(const ThisType& functor, KernelContext* ctx, const ArrayData& arg0, 599 Datum* out) { 600 ArrayData* out_arr = out->mutable_array(); 601 auto out_data = out_arr->GetMutableValues<OutValue>(1); 602 VisitArrayValuesInline<Arg0Type>( 603 arg0, 604 [&](Arg0Value v) { 605 *out_data++ = functor.op.template Call<OutValue, Arg0Value>(ctx, v); 606 }, 607 [&]() { 608 // null 609 ++out_data; 610 }); 611 } 612 }; 613 614 template <typename Type> 615 struct ArrayExec<Type, enable_if_base_binary<Type>> { 616 static void Exec(const ThisType& functor, KernelContext* ctx, const ArrayData& arg0, 617 Datum* out) { 618 // NOTE: This code is not currently used by any kernels and has 619 // suboptimal performance because it's recomputing the validity bitmap 620 // that is already computed by the kernel execution layer. Consider 621 // writing a lower-level "output adapter" for base binary types. 622 typename TypeTraits<Type>::BuilderType builder; 623 VisitArrayValuesInline<Arg0Type>( 624 arg0, 625 [&](Arg0Value v) { 626 KERNEL_RETURN_IF_ERROR(ctx, builder.Append(functor.op.Call(ctx, v))); 627 }, 628 [&]() { KERNEL_RETURN_IF_ERROR(ctx, builder.AppendNull()); }); 629 if (!ctx->HasError()) { 630 std::shared_ptr<ArrayData> result; 631 ctx->SetStatus(builder.FinishInternal(&result)); 632 out->value = std::move(result); 633 } 634 } 635 }; 636 637 template <typename Type> 638 struct ArrayExec<Type, enable_if_t<is_boolean_type<Type>::value>> { 639 static void Exec(const ThisType& functor, KernelContext* ctx, const ArrayData& arg0, 640 Datum* out) { 641 ArrayData* out_arr = out->mutable_array(); 642 FirstTimeBitmapWriter out_writer(out_arr->buffers[1]->mutable_data(), 643 out_arr->offset, out_arr->length); 644 VisitArrayValuesInline<Arg0Type>( 645 arg0, 646 [&](Arg0Value v) { 647 if (functor.op.template Call<OutValue, Arg0Value>(ctx, v)) { 648 out_writer.Set(); 649 } 650 out_writer.Next(); 651 }, 652 [&]() { 653 // null 654 out_writer.Clear(); 655 out_writer.Next(); 656 }); 657 out_writer.Finish(); 658 } 659 }; 660 661 template <typename Type> 662 struct ArrayExec<Type, enable_if_t<std::is_same<Type, Decimal128Type>::value>> { 663 static void Exec(const ThisType& functor, KernelContext* ctx, const ArrayData& arg0, 664 Datum* out) { 665 ArrayData* out_arr = out->mutable_array(); 666 auto out_data = out_arr->GetMutableValues<Decimal128>(1); 667 VisitArrayValuesInline<Arg0Type>( 668 arg0, 669 [&](Arg0Value v) { 670 *out_data++ = functor.op.template Call<OutValue, Arg0Value>(ctx, v); 671 }, 672 [&]() { ++out_data; }); 673 } 674 }; 675 676 void Scalar(KernelContext* ctx, const Scalar& arg0, Datum* out) { 677 if (arg0.is_valid) { 678 Arg0Value arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); 679 BoxScalar<OutType>::Box(this->op.template Call<OutValue, Arg0Value>(ctx, arg0_val), 680 out->scalar().get()); 681 } 682 } 683 684 void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 685 if (batch[0].kind() == Datum::ARRAY) { 686 ArrayExec<OutType>::Exec(*this, ctx, *batch[0].array(), out); 687 } else { 688 return Scalar(ctx, *batch[0].scalar(), out); 689 } 690 } 691 }; 692 693 // An alternative to ScalarUnary that Applies a scalar operation on only the 694 // not-null values of a single array. The operator is not stateful; if the 695 // operator requires some initialization use ScalarUnaryNotNullStateful 696 template <typename OutType, typename Arg0Type, typename Op> 697 struct ScalarUnaryNotNull { 698 using OutValue = typename GetOutputType<OutType>::T; 699 using Arg0Value = typename GetViewType<Arg0Type>::T; 700 701 static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 702 // Seed kernel with dummy state 703 ScalarUnaryNotNullStateful<OutType, Arg0Type, Op> kernel({}); 704 return kernel.Exec(ctx, batch, out); 705 } 706 }; 707 708 // A kernel exec generator for binary functions that addresses both array and 709 // scalar inputs and dispatches input iteration and output writing to other 710 // templates 711 // 712 // This template executes the operator even on the data behind null values, 713 // therefore it is generally only suitable for operators that are safe to apply 714 // even on the null slot values. 715 // 716 // The "Op" functor should have the form 717 // 718 // struct Op { 719 // template <typename OutValue, typename Arg0Value, typename Arg1Value> 720 // static OutValue Call(KernelContext* ctx, Arg0Value arg0, Arg1Value arg1) { 721 // // implementation 722 // } 723 // }; 724 template <typename OutType, typename Arg0Type, typename Arg1Type, typename Op> 725 struct ScalarBinary { 726 using OutValue = typename GetOutputType<OutType>::T; 727 using Arg0Value = typename GetViewType<Arg0Type>::T; 728 using Arg1Value = typename GetViewType<Arg1Type>::T; 729 730 static void ArrayArray(KernelContext* ctx, const ArrayData& arg0, const ArrayData& arg1, 731 Datum* out) { 732 ArrayIterator<Arg0Type> arg0_it(arg0); 733 ArrayIterator<Arg1Type> arg1_it(arg1); 734 OutputAdapter<OutType>::Write(ctx, out, [&]() -> OutValue { 735 return Op::template Call(ctx, arg0_it(), arg1_it()); 736 }); 737 } 738 739 static void ArrayScalar(KernelContext* ctx, const ArrayData& arg0, const Scalar& arg1, 740 Datum* out) { 741 ArrayIterator<Arg0Type> arg0_it(arg0); 742 auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); 743 OutputAdapter<OutType>::Write(ctx, out, [&]() -> OutValue { 744 return Op::template Call(ctx, arg0_it(), arg1_val); 745 }); 746 } 747 748 static void ScalarArray(KernelContext* ctx, const Scalar& arg0, const ArrayData& arg1, 749 Datum* out) { 750 auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); 751 ArrayIterator<Arg1Type> arg1_it(arg1); 752 OutputAdapter<OutType>::Write(ctx, out, [&]() -> OutValue { 753 return Op::template Call(ctx, arg0_val, arg1_it()); 754 }); 755 } 756 757 static void ScalarScalar(KernelContext* ctx, const Scalar& arg0, const Scalar& arg1, 758 Datum* out) { 759 if (out->scalar()->is_valid) { 760 auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); 761 auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); 762 BoxScalar<OutType>::Box(Op::template Call(ctx, arg0_val, arg1_val), 763 out->scalar().get()); 764 } 765 } 766 767 static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 768 if (batch[0].kind() == Datum::ARRAY) { 769 if (batch[1].kind() == Datum::ARRAY) { 770 return ArrayArray(ctx, *batch[0].array(), *batch[1].array(), out); 771 } else { 772 return ArrayScalar(ctx, *batch[0].array(), *batch[1].scalar(), out); 773 } 774 } else { 775 if (batch[1].kind() == Datum::ARRAY) { 776 return ScalarArray(ctx, *batch[0].scalar(), *batch[1].array(), out); 777 } else { 778 return ScalarScalar(ctx, *batch[0].scalar(), *batch[1].scalar(), out); 779 } 780 } 781 } 782 }; 783 784 // An alternative to ScalarBinary that Applies a scalar operation with state on 785 // only the value pairs which are not-null in both arrays 786 template <typename OutType, typename Arg0Type, typename Arg1Type, typename Op> 787 struct ScalarBinaryNotNullStateful { 788 using ThisType = ScalarBinaryNotNullStateful<OutType, Arg0Type, Arg1Type, Op>; 789 using OutValue = typename GetOutputType<OutType>::T; 790 using Arg0Value = typename GetViewType<Arg0Type>::T; 791 using Arg1Value = typename GetViewType<Arg1Type>::T; 792 793 Op op; 794 explicit ScalarBinaryNotNullStateful(Op op) : op(std::move(op)) {} 795 796 // NOTE: In ArrayExec<Type>, Type is really OutputType 797 798 void ArrayArray(KernelContext* ctx, const ArrayData& arg0, const ArrayData& arg1, 799 Datum* out) { 800 OutputArrayWriter<OutType> writer(out->mutable_array()); 801 VisitTwoArrayValuesInline<Arg0Type, Arg1Type>( 802 arg0, arg1, 803 [&](Arg0Value u, Arg1Value v) { 804 writer.Write(op.template Call<OutValue, Arg0Value, Arg1Value>(ctx, u, v)); 805 }, 806 [&]() { writer.WriteNull(); }); 807 } 808 809 void ArrayScalar(KernelContext* ctx, const ArrayData& arg0, const Scalar& arg1, 810 Datum* out) { 811 OutputArrayWriter<OutType> writer(out->mutable_array()); 812 if (arg1.is_valid) { 813 const auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); 814 VisitArrayValuesInline<Arg0Type>( 815 arg0, 816 [&](Arg0Value u) { 817 writer.Write( 818 op.template Call<OutValue, Arg0Value, Arg1Value>(ctx, u, arg1_val)); 819 }, 820 [&]() { writer.WriteNull(); }); 821 } 822 } 823 824 void ScalarArray(KernelContext* ctx, const Scalar& arg0, const ArrayData& arg1, 825 Datum* out) { 826 OutputArrayWriter<OutType> writer(out->mutable_array()); 827 if (arg0.is_valid) { 828 const auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); 829 VisitArrayValuesInline<Arg1Type>( 830 arg1, 831 [&](Arg1Value v) { 832 writer.Write( 833 op.template Call<OutValue, Arg0Value, Arg1Value>(ctx, arg0_val, v)); 834 }, 835 [&]() { writer.WriteNull(); }); 836 } 837 } 838 839 void ScalarScalar(KernelContext* ctx, const Scalar& arg0, const Scalar& arg1, 840 Datum* out) { 841 if (arg0.is_valid && arg1.is_valid) { 842 const auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); 843 const auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); 844 BoxScalar<OutType>::Box( 845 op.template Call<OutValue, Arg0Value, Arg1Value>(ctx, arg0_val, arg1_val), 846 out->scalar().get()); 847 } 848 } 849 850 void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 851 if (batch[0].kind() == Datum::ARRAY) { 852 if (batch[1].kind() == Datum::ARRAY) { 853 return ArrayArray(ctx, *batch[0].array(), *batch[1].array(), out); 854 } else { 855 return ArrayScalar(ctx, *batch[0].array(), *batch[1].scalar(), out); 856 } 857 } else { 858 if (batch[1].kind() == Datum::ARRAY) { 859 return ScalarArray(ctx, *batch[0].scalar(), *batch[1].array(), out); 860 } else { 861 return ScalarScalar(ctx, *batch[0].scalar(), *batch[1].scalar(), out); 862 } 863 } 864 } 865 }; 866 867 // An alternative to ScalarBinary that Applies a scalar operation on only 868 // the value pairs which are not-null in both arrays. 869 // The operator is not stateful; if the operator requires some initialization 870 // use ScalarBinaryNotNullStateful. 871 template <typename OutType, typename Arg0Type, typename Arg1Type, typename Op> 872 struct ScalarBinaryNotNull { 873 using OutValue = typename GetOutputType<OutType>::T; 874 using Arg0Value = typename GetViewType<Arg0Type>::T; 875 using Arg1Value = typename GetViewType<Arg1Type>::T; 876 877 static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 878 // Seed kernel with dummy state 879 ScalarBinaryNotNullStateful<OutType, Arg0Type, Arg1Type, Op> kernel({}); 880 return kernel.Exec(ctx, batch, out); 881 } 882 }; 883 884 // A kernel exec generator for binary kernels where both input types are the 885 // same 886 template <typename OutType, typename ArgType, typename Op> 887 using ScalarBinaryEqualTypes = ScalarBinary<OutType, ArgType, ArgType, Op>; 888 889 // A kernel exec generator for non-null binary kernels where both input types are the 890 // same 891 template <typename OutType, typename ArgType, typename Op> 892 using ScalarBinaryNotNullEqualTypes = ScalarBinaryNotNull<OutType, ArgType, ArgType, Op>; 893 894 } // namespace applicator 895 896 // ---------------------------------------------------------------------- 897 // BEGIN of kernel generator-dispatchers ("GD") 898 // 899 // These GD functions instantiate kernel functor templates and select one of 900 // the instantiated kernels dynamically based on the data type or Type::type id 901 // that is passed. This enables functions to be populated with kernels by 902 // looping over vectors of data types rather than using macros or other 903 // approaches. 904 // 905 // The kernel functor must be of the form: 906 // 907 // template <typename Type0, typename Type1, Args...> 908 // struct FUNCTOR { 909 // static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 910 // // IMPLEMENTATION 911 // } 912 // }; 913 // 914 // When you pass FUNCTOR to a GD function, you must pass at least one static 915 // type along with the functor -- this is often the fixed return type of the 916 // functor. This Type0 argument is passed as the first argument to the functor 917 // during instantiation. The 2nd type passed to the functor is the DataType 918 // subclass corresponding to the type passed as argument (not template type) to 919 // the function. 920 // 921 // For example, GenerateNumeric<FUNCTOR, Type0>(int32()) will select a kernel 922 // instantiated like FUNCTOR<Type0, Int32Type>. Any additional variadic 923 // template arguments will be passed as additional template arguments to the 924 // kernel template. 925 926 namespace detail { 927 928 // Convenience so we can pass DataType or Type::type for the GD's 929 struct GetTypeId { 930 Type::type id; 931 GetTypeId(const std::shared_ptr<DataType>& type) // NOLINT implicit construction 932 : id(type->id()) {} 933 GetTypeId(const DataType& type) // NOLINT implicit construction 934 : id(type.id()) {} 935 GetTypeId(Type::type id) // NOLINT implicit construction 936 : id(id) {} 937 }; 938 939 } // namespace detail 940 941 // GD for numeric types (integer and floating point) 942 template <template <typename...> class Generator, typename Type0, typename... Args> 943 ArrayKernelExec GenerateNumeric(detail::GetTypeId get_id) { 944 switch (get_id.id) { 945 case Type::INT8: 946 return Generator<Type0, Int8Type, Args...>::Exec; 947 case Type::UINT8: 948 return Generator<Type0, UInt8Type, Args...>::Exec; 949 case Type::INT16: 950 return Generator<Type0, Int16Type, Args...>::Exec; 951 case Type::UINT16: 952 return Generator<Type0, UInt16Type, Args...>::Exec; 953 case Type::INT32: 954 return Generator<Type0, Int32Type, Args...>::Exec; 955 case Type::UINT32: 956 return Generator<Type0, UInt32Type, Args...>::Exec; 957 case Type::INT64: 958 return Generator<Type0, Int64Type, Args...>::Exec; 959 case Type::UINT64: 960 return Generator<Type0, UInt64Type, Args...>::Exec; 961 case Type::FLOAT: 962 return Generator<Type0, FloatType, Args...>::Exec; 963 case Type::DOUBLE: 964 return Generator<Type0, DoubleType, Args...>::Exec; 965 default: 966 DCHECK(false); 967 return ExecFail; 968 } 969 } 970 971 // Generate a kernel given a templated functor for floating point types 972 // 973 // See "Numeric" above for description of the generator functor 974 template <template <typename...> class Generator, typename Type0, typename... Args> 975 ArrayKernelExec GenerateFloatingPoint(detail::GetTypeId get_id) { 976 switch (get_id.id) { 977 case Type::FLOAT: 978 return Generator<Type0, FloatType, Args...>::Exec; 979 case Type::DOUBLE: 980 return Generator<Type0, DoubleType, Args...>::Exec; 981 default: 982 DCHECK(false); 983 return ExecFail; 984 } 985 } 986 987 // Generate a kernel given a templated functor for integer types 988 // 989 // See "Numeric" above for description of the generator functor 990 template <template <typename...> class Generator, typename Type0, typename... Args> 991 ArrayKernelExec GenerateInteger(detail::GetTypeId get_id) { 992 switch (get_id.id) { 993 case Type::INT8: 994 return Generator<Type0, Int8Type, Args...>::Exec; 995 case Type::INT16: 996 return Generator<Type0, Int16Type, Args...>::Exec; 997 case Type::INT32: 998 return Generator<Type0, Int32Type, Args...>::Exec; 999 case Type::INT64: 1000 return Generator<Type0, Int64Type, Args...>::Exec; 1001 case Type::UINT8: 1002 return Generator<Type0, UInt8Type, Args...>::Exec; 1003 case Type::UINT16: 1004 return Generator<Type0, UInt16Type, Args...>::Exec; 1005 case Type::UINT32: 1006 return Generator<Type0, UInt32Type, Args...>::Exec; 1007 case Type::UINT64: 1008 return Generator<Type0, UInt64Type, Args...>::Exec; 1009 default: 1010 DCHECK(false); 1011 return ExecFail; 1012 } 1013 } 1014 1015 template <template <typename...> class Generator, typename Type0, typename... Args> 1016 ArrayKernelExec GeneratePhysicalInteger(detail::GetTypeId get_id) { 1017 switch (get_id.id) { 1018 case Type::INT8: 1019 return Generator<Type0, Int8Type, Args...>::Exec; 1020 case Type::INT16: 1021 return Generator<Type0, Int16Type, Args...>::Exec; 1022 case Type::INT32: 1023 case Type::DATE32: 1024 case Type::TIME32: 1025 return Generator<Type0, Int32Type, Args...>::Exec; 1026 case Type::INT64: 1027 case Type::DATE64: 1028 case Type::TIMESTAMP: 1029 case Type::TIME64: 1030 case Type::DURATION: 1031 return Generator<Type0, Int64Type, Args...>::Exec; 1032 case Type::UINT8: 1033 return Generator<Type0, UInt8Type, Args...>::Exec; 1034 case Type::UINT16: 1035 return Generator<Type0, UInt16Type, Args...>::Exec; 1036 case Type::UINT32: 1037 return Generator<Type0, UInt32Type, Args...>::Exec; 1038 case Type::UINT64: 1039 return Generator<Type0, UInt64Type, Args...>::Exec; 1040 default: 1041 DCHECK(false); 1042 return ExecFail; 1043 } 1044 } 1045 1046 // Generate a kernel given a templated functor for integer types 1047 // 1048 // See "Numeric" above for description of the generator functor 1049 template <template <typename...> class Generator, typename Type0, typename... Args> 1050 ArrayKernelExec GenerateSignedInteger(detail::GetTypeId get_id) { 1051 switch (get_id.id) { 1052 case Type::INT8: 1053 return Generator<Type0, Int8Type, Args...>::Exec; 1054 case Type::INT16: 1055 return Generator<Type0, Int16Type, Args...>::Exec; 1056 case Type::INT32: 1057 return Generator<Type0, Int32Type, Args...>::Exec; 1058 case Type::INT64: 1059 return Generator<Type0, Int64Type, Args...>::Exec; 1060 default: 1061 DCHECK(false); 1062 return ExecFail; 1063 } 1064 } 1065 1066 // Generate a kernel given a templated functor. Only a single template is 1067 // instantiated for each bit width, and the functor is expected to treat types 1068 // of the same bit width the same without utilizing any type-specific behavior 1069 // (e.g. int64 should be handled equivalent to uint64 or double -- all 64 1070 // bits). 1071 // 1072 // See "Numeric" above for description of the generator functor 1073 template <template <typename...> class Generator> 1074 ArrayKernelExec GenerateTypeAgnosticPrimitive(detail::GetTypeId get_id) { 1075 switch (get_id.id) { 1076 case Type::NA: 1077 return Generator<NullType>::Exec; 1078 case Type::BOOL: 1079 return Generator<BooleanType>::Exec; 1080 case Type::UINT8: 1081 case Type::INT8: 1082 return Generator<UInt8Type>::Exec; 1083 case Type::UINT16: 1084 case Type::INT16: 1085 return Generator<UInt16Type>::Exec; 1086 case Type::UINT32: 1087 case Type::INT32: 1088 case Type::FLOAT: 1089 case Type::DATE32: 1090 case Type::TIME32: 1091 return Generator<UInt32Type>::Exec; 1092 case Type::UINT64: 1093 case Type::INT64: 1094 case Type::DOUBLE: 1095 case Type::DATE64: 1096 case Type::TIMESTAMP: 1097 case Type::TIME64: 1098 case Type::DURATION: 1099 return Generator<UInt64Type>::Exec; 1100 default: 1101 DCHECK(false); 1102 return ExecFail; 1103 } 1104 } 1105 1106 // similar to GenerateTypeAgnosticPrimitive, but for variable types 1107 template <template <typename...> class Generator> 1108 ArrayKernelExec GenerateTypeAgnosticVarBinaryBase(detail::GetTypeId get_id) { 1109 switch (get_id.id) { 1110 case Type::BINARY: 1111 case Type::STRING: 1112 return Generator<BinaryType>::Exec; 1113 case Type::LARGE_BINARY: 1114 case Type::LARGE_STRING: 1115 return Generator<LargeBinaryType>::Exec; 1116 default: 1117 DCHECK(false); 1118 return ExecFail; 1119 } 1120 } 1121 1122 // Generate a kernel given a templated functor for base binary types. Generates 1123 // a single kernel for binary/string and large binary / large string. If your 1124 // kernel implementation needs access to the specific type at compile time, 1125 // please use BaseBinarySpecific. 1126 // 1127 // See "Numeric" above for description of the generator functor 1128 template <template <typename...> class Generator, typename Type0, typename... Args> 1129 ArrayKernelExec GenerateVarBinaryBase(detail::GetTypeId get_id) { 1130 switch (get_id.id) { 1131 case Type::BINARY: 1132 case Type::STRING: 1133 return Generator<Type0, BinaryType, Args...>::Exec; 1134 case Type::LARGE_BINARY: 1135 case Type::LARGE_STRING: 1136 return Generator<Type0, LargeBinaryType, Args...>::Exec; 1137 default: 1138 DCHECK(false); 1139 return ExecFail; 1140 } 1141 } 1142 1143 // See BaseBinary documentation 1144 template <template <typename...> class Generator, typename Type0, typename... Args> 1145 ArrayKernelExec GenerateVarBinary(detail::GetTypeId get_id) { 1146 switch (get_id.id) { 1147 case Type::BINARY: 1148 return Generator<Type0, BinaryType, Args...>::Exec; 1149 case Type::STRING: 1150 return Generator<Type0, StringType, Args...>::Exec; 1151 case Type::LARGE_BINARY: 1152 return Generator<Type0, LargeBinaryType, Args...>::Exec; 1153 case Type::LARGE_STRING: 1154 return Generator<Type0, LargeStringType, Args...>::Exec; 1155 default: 1156 DCHECK(false); 1157 return ExecFail; 1158 } 1159 } 1160 1161 // Generate a kernel given a templated functor for temporal types 1162 // 1163 // See "Numeric" above for description of the generator functor 1164 template <template <typename...> class Generator, typename Type0, typename... Args> 1165 ArrayKernelExec GenerateTemporal(detail::GetTypeId get_id) { 1166 switch (get_id.id) { 1167 case Type::DATE32: 1168 return Generator<Type0, Date32Type, Args...>::Exec; 1169 case Type::DATE64: 1170 return Generator<Type0, Date64Type, Args...>::Exec; 1171 case Type::DURATION: 1172 return Generator<Type0, DurationType, Args...>::Exec; 1173 case Type::TIME32: 1174 return Generator<Type0, Time32Type, Args...>::Exec; 1175 case Type::TIME64: 1176 return Generator<Type0, Time64Type, Args...>::Exec; 1177 case Type::TIMESTAMP: 1178 return Generator<Type0, TimestampType, Args...>::Exec; 1179 default: 1180 DCHECK(false); 1181 return ExecFail; 1182 } 1183 } 1184 1185 // END of kernel generator-dispatchers 1186 // ---------------------------------------------------------------------- 1187 1188 ARROW_EXPORT 1189 void EnsureDictionaryDecoded(std::vector<ValueDescr>* descrs); 1190 1191 ARROW_EXPORT 1192 void ReplaceNullWithOtherType(std::vector<ValueDescr>* descrs); 1193 1194 ARROW_EXPORT 1195 void ReplaceTypes(const std::shared_ptr<DataType>&, std::vector<ValueDescr>* descrs); 1196 1197 ARROW_EXPORT 1198 std::shared_ptr<DataType> CommonNumeric(const std::vector<ValueDescr>& descrs); 1199 1200 ARROW_EXPORT 1201 std::shared_ptr<DataType> CommonTimestamp(const std::vector<ValueDescr>& descrs); 1202 1203 ARROW_EXPORT 1204 std::shared_ptr<DataType> CommonBinary(const std::vector<ValueDescr>& descrs); 1205 1206 } // namespace internal 1207 } // namespace compute 1208 } // namespace arrow 1209