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 <vector> 23 24 #include "arrow/array.h" 25 #include "arrow/compute/kernel.h" 26 #include "arrow/scalar.h" 27 #include "arrow/type_traits.h" 28 #include "arrow/util/bit_util.h" 29 #include "arrow/util/logging.h" 30 #include "arrow/util/optional.h" 31 #include "arrow/util/string_view.h" 32 #include "arrow/visitor_inline.h" 33 34 namespace arrow { 35 36 using internal::BitmapReader; 37 using internal::FirstTimeBitmapWriter; 38 using internal::GenerateBitsUnrolled; 39 40 namespace compute { 41 42 #ifdef ARROW_EXTRA_ERROR_CONTEXT 43 44 #define KERNEL_RETURN_IF_ERROR(ctx, expr) \ 45 do { \ 46 Status _st = (expr); \ 47 if (ARROW_PREDICT_FALSE(!_st.ok())) { \ 48 _st.AddContextLine(__FILE__, __LINE__, #expr); \ 49 ctx->SetStatus(_st); \ 50 return; \ 51 } \ 52 } while (0) 53 54 #else 55 56 #define KERNEL_RETURN_IF_ERROR(ctx, expr) \ 57 do { \ 58 Status _st = (expr); \ 59 if (ARROW_PREDICT_FALSE(!_st.ok())) { \ 60 ctx->SetStatus(_st); \ 61 return; \ 62 } \ 63 } while (0) 64 65 #endif // ARROW_EXTRA_ERROR_CONTEXT 66 67 // ---------------------------------------------------------------------- 68 // Iteration / value access utilities 69 70 template <typename T, typename R = void> 71 using enable_if_has_c_type_not_boolean = enable_if_t<has_c_type<T>::value && 72 !is_boolean_type<T>::value, R>; 73 74 template <typename Type, typename Enable = void> 75 struct ArrayIterator; 76 77 template <typename Type> 78 struct ArrayIterator<Type, enable_if_has_c_type_not_boolean<Type>> { 79 using T = typename Type::c_type; 80 const T* values; 81 ArrayIterator(const ArrayData& data) : values(data.GetValues<T>(1)) {} 82 T operator()() { return *values++; } 83 }; 84 85 template <typename Type> 86 struct ArrayIterator<Type, enable_if_boolean<Type>> { 87 BitmapReader reader; 88 ArrayIterator(const ArrayData& data) 89 : reader(data.buffers[1]->data(), data.offset, data.length) {} 90 bool operator()() { 91 bool out = reader.IsSet(); 92 reader.Next(); 93 return out; 94 } 95 }; 96 97 template <typename Type> 98 struct ArrayIterator<Type, enable_if_base_binary<Type>> { 99 int64_t position = 0; 100 typename TypeTraits<Type>::ArrayType arr; 101 ArrayIterator(const ArrayData& data) 102 : arr(data.Copy()) {} 103 util::string_view operator()() { return arr.GetView(position++); } 104 }; 105 106 template <typename Type, typename Enable = void> 107 struct UnboxScalar; 108 109 template <typename Type> 110 struct UnboxScalar<Type, enable_if_has_c_type<Type>> { 111 using ScalarType = typename TypeTraits<Type>::ScalarType; 112 static typename Type::c_type Unbox(const Datum& datum) { 113 return datum.scalar_as<ScalarType>().value; 114 } 115 }; 116 117 template <typename Type> 118 struct UnboxScalar<Type, enable_if_base_binary<Type>> { 119 static util::string_view Unbox(const Datum& datum) { 120 return util::string_view(*datum.scalar_as<BaseBinaryScalar>().value); 121 } 122 }; 123 124 template <typename Type, typename Enable = void> 125 struct GetViewType; 126 127 template <typename Type> 128 struct GetViewType<Type, enable_if_has_c_type<Type>> { 129 using T = typename Type::c_type; 130 }; 131 132 template <typename Type> 133 struct GetViewType< 134 Type, enable_if_t<is_base_binary_type<Type>::value || is_decimal_type<Type>::value || 135 is_fixed_size_binary_type<Type>::value>> { 136 using T = util::string_view; 137 }; 138 139 template <typename Type, typename Enable = void> 140 struct GetOutputType; 141 142 template <typename Type> 143 struct GetOutputType<Type, enable_if_has_c_type<Type>> { 144 using T = typename Type::c_type; 145 }; 146 147 template <typename Type> 148 struct GetOutputType< 149 Type, enable_if_t<is_string_like_type<Type>::value>> { 150 using T = std::string; 151 }; 152 153 template <typename Type, typename Enable = void> 154 struct BoxScalar; 155 156 template <typename Type> 157 struct BoxScalar<Type, enable_if_has_c_type<Type>> { 158 using T = typename GetOutputType<Type>::T; 159 using ScalarType = typename TypeTraits<Type>::ScalarType; 160 static std::shared_ptr<Scalar> Box(T val, const std::shared_ptr<DataType>& type) { 161 return std::make_shared<ScalarType>(val, type); 162 } 163 }; 164 165 template <typename Type> 166 struct BoxScalar<Type, enable_if_base_binary<Type>> { 167 using T = typename GetOutputType<Type>::T; 168 using ScalarType = typename TypeTraits<Type>::ScalarType; 169 static std::shared_ptr<Scalar> Box(T val, const std::shared_ptr<DataType>&) { 170 return std::make_shared<ScalarType>(val); 171 } 172 }; 173 174 // ---------------------------------------------------------------------- 175 // Reusable type resolvers 176 177 Result<ValueDescr> FirstType(KernelContext*, const std::vector<ValueDescr>& descrs); 178 179 // ---------------------------------------------------------------------- 180 // Generate an array kernel given template classes 181 182 void ExecFail(KernelContext* ctx, const ExecBatch& batch, Datum* out); 183 184 void BinaryExecFlipped(KernelContext* ctx, ArrayKernelExec exec, 185 const ExecBatch& batch, Datum* out); 186 187 // ---------------------------------------------------------------------- 188 // Helpers for iterating over common DataType instances for adding kernels to 189 // functions 190 191 const std::vector<std::shared_ptr<DataType>>& BaseBinaryTypes(); 192 const std::vector<std::shared_ptr<DataType>>& StringTypes(); 193 const std::vector<std::shared_ptr<DataType>>& SignedIntTypes(); 194 const std::vector<std::shared_ptr<DataType>>& UnsignedIntTypes(); 195 const std::vector<std::shared_ptr<DataType>>& IntTypes(); 196 const std::vector<std::shared_ptr<DataType>>& FloatingPointTypes(); 197 198 // Returns a vector of example instances of parametric types such as 199 // 200 // * Decimal 201 // * Timestamp (requiring unit) 202 // * Time32 (requiring unit) 203 // * Time64 (requiring unit) 204 // * Duration (requiring unit) 205 // * List, LargeList, FixedSizeList 206 // * Struct 207 // * Union 208 // * Dictionary 209 // * Map 210 // 211 // Generally kernels will use the "FirstType" OutputType::Resolver above for 212 // the OutputType of the kernel's signature and match::SameTypeId for the 213 // corresponding InputType 214 const std::vector<std::shared_ptr<DataType>>& ExampleParametricTypes(); 215 216 // Number types without boolean 217 const std::vector<std::shared_ptr<DataType>>& NumericTypes(); 218 219 // Temporal types including time and timestamps for each unit 220 const std::vector<std::shared_ptr<DataType>>& TemporalTypes(); 221 222 // Integer, floating point, base binary, and temporal 223 const std::vector<std::shared_ptr<DataType>>& PrimitiveTypes(); 224 225 // ---------------------------------------------------------------------- 226 // Template functions and utilities for generating ArrayKernelExec functions 227 // for kernels given functors providing the right kind of template / prototype 228 229 namespace codegen { 230 231 // Generate an ArrayKernelExec given a functor that handles all of its own 232 // iteration, etc. 233 // 234 // Operator must implement 235 // 236 // static void Call(KernelContext*, const ArrayData& in, ArrayData* out) 237 template <typename Operator> 238 void SimpleUnary(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 239 if (batch[0].kind() == Datum::SCALAR) { 240 ctx->SetStatus(Status::NotImplemented("NYI")); 241 } else if (batch.length > 0) { 242 Operator::Call(ctx, *batch[0].array(), out->mutable_array()); 243 } 244 } 245 246 // Generate an ArrayKernelExec given a functor that handles all of its own 247 // iteration, etc. 248 // 249 // Operator must implement 250 // 251 // static void Call(KernelContext*, const ArrayData& arg0, const ArrayData& arg1, 252 // ArrayData* out) 253 template <typename Operator> 254 void SimpleBinary(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 255 if (batch[0].kind() == Datum::SCALAR || batch[1].kind() == Datum::SCALAR) { 256 ctx->SetStatus(Status::NotImplemented("NYI")); 257 } else if (batch.length > 0) { 258 Operator::Call(ctx, *batch[0].array(), *batch[1].array(), out->mutable_array()); 259 } 260 } 261 262 // A ArrayKernelExec-creation template that iterates over primitive non-boolean 263 // inputs and writes into non-boolean primitive outputs. 264 // 265 // It may be possible to create a more generic template that can deal with any 266 // input writing to any output, but we will need to write benchmarks to 267 // investigate that on all compiler targets to ensure that the additional 268 // template abstractions do not incur performance overhead. This template 269 // provides a reference point for performance when there are no templates 270 // dealing with value iteration. 271 // 272 // TODO: Run benchmarks to determine if OutputAdapter is a zero-cost abstraction 273 template <typename Op, typename OutType, typename Arg0Type> 274 void ScalarPrimitiveExecUnary(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 275 using OUT = typename OutType::c_type; 276 using ARG0 = typename Arg0Type::c_type; 277 278 if (batch[0].kind() == Datum::SCALAR) { 279 ctx->SetStatus(Status::NotImplemented("NYI")); 280 } else { 281 ArrayData* out_arr = out->mutable_array(); 282 auto out_data = out_arr->GetMutableValues<OUT>(1); 283 auto arg0_data = batch[0].array()->GetValues<ARG0>(1); 284 for (int64_t i = 0; i < batch.length; ++i) { 285 *out_data++ = Op::template Call<OUT, ARG0>(ctx, *arg0_data++); 286 } 287 } 288 } 289 290 template <typename Op, typename OutType, typename Arg0Type, typename Arg1Type> 291 void ScalarPrimitiveExecBinary(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 292 using OUT = typename OutType::c_type; 293 using ARG0 = typename Arg0Type::c_type; 294 using ARG1 = typename Arg1Type::c_type; 295 296 if (batch[0].kind() == Datum::SCALAR || batch[1].kind() == Datum::SCALAR) { 297 ctx->SetStatus(Status::NotImplemented("NYI")); 298 } else { 299 ArrayData* out_arr = out->mutable_array(); 300 auto out_data = out_arr->GetMutableValues<OUT>(1); 301 auto arg0_data = batch[0].array()->GetValues<ARG0>(1); 302 auto arg1_data = batch[1].array()->GetValues<ARG1>(1); 303 for (int64_t i = 0; i < batch.length; ++i) { 304 *out_data++ = Op::template Call<OUT, ARG0, ARG1>(ctx, *arg0_data++, *arg1_data++); 305 } 306 } 307 } 308 309 // OutputAdapter allows passing an inlineable lambda that provides a sequence 310 // of output values to write into output memory. Boolean and primitive outputs 311 // are currently implemented, and the validity bitmap is presumed to be handled 312 // at a higher level, so this writes into every output slot, null or not. 313 template <typename Type, typename Enable = void> 314 struct OutputAdapter; 315 316 template <typename Type> 317 struct OutputAdapter<Type, enable_if_boolean<Type>> { 318 template <typename Generator> 319 static void Write(KernelContext*, Datum* out, Generator&& generator) { 320 ArrayData* out_arr = out->mutable_array(); 321 auto out_bitmap = out_arr->buffers[1]->mutable_data(); 322 GenerateBitsUnrolled(out_bitmap, out_arr->offset, out_arr->length, 323 std::forward<Generator>(generator)); 324 } 325 }; 326 327 template <typename Type> 328 struct OutputAdapter<Type, enable_if_has_c_type_not_boolean<Type>> { 329 template <typename Generator> 330 static void Write(KernelContext*, Datum* out, Generator&& generator) { 331 ArrayData* out_arr = out->mutable_array(); 332 auto out_data = out_arr->GetMutableValues<typename Type::c_type>(1); 333 // TODO: Is this as fast as a more explicitly inlined function? 334 for (int64_t i = 0 ; i < out_arr->length; ++i) { 335 *out_data++ = generator(); 336 } 337 } 338 }; 339 340 template <typename Type> 341 struct OutputAdapter<Type, enable_if_base_binary<Type>> { 342 template <typename Generator> 343 static void Write(KernelContext* ctx, Datum* out, Generator&& generator) { 344 ctx->SetStatus(Status::NotImplemented("NYI")); 345 } 346 }; 347 348 // A kernel exec generator for unary functions that addresses both array and 349 // scalar inputs and dispatches input iteration and output writing to other 350 // templates 351 // 352 // This template executes the operator even on the data behind null values, 353 // therefore it is generally only suitable for operators that are safe to apply 354 // even on the null slot values. 355 // 356 // The "Op" functor should have the form 357 // 358 // struct Op { 359 // template <typename OUT, typename ARG0> 360 // static OUT Call(KernelContext* ctx, ARG0 val) { 361 // // implementation 362 // } 363 // }; 364 template <typename OutType, typename Arg0Type, typename Op> 365 struct ScalarUnary { 366 using OUT = typename GetOutputType<OutType>::T; 367 using ARG0 = typename GetViewType<Arg0Type>::T; 368 369 static void Array(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 370 ArrayIterator<Arg0Type> arg0(*batch[0].array()); 371 OutputAdapter<OutType>::Write(ctx, out, [&]() -> OUT { 372 return Op::template Call<OUT, ARG0>(ctx, arg0()); 373 }); 374 } 375 376 static void Scalar(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 377 if (batch[0].scalar()->is_valid) { 378 ARG0 arg0 = UnboxScalar<Arg0Type>::Unbox(batch[0]); 379 out->value = BoxScalar<OutType>::Box( 380 Op::template Call<OUT, ARG0>(ctx, arg0), 381 out->type()); 382 } else { 383 out->value = MakeNullScalar(batch[0].type()); 384 } 385 } 386 387 static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 388 if (batch[0].kind() == Datum::ARRAY) { 389 return Array(ctx, batch, out); 390 } else { 391 return Scalar(ctx, batch, out); 392 } 393 } 394 }; 395 396 // An alternative to ScalarUnary that Applies a scalar operation with state on 397 // only the not-null values of a single array 398 template <typename OutType, typename Arg0Type, typename Op> 399 struct ScalarUnaryNotNullStateful { 400 using ThisType = ScalarUnaryNotNullStateful<OutType, Arg0Type, Op>; 401 using OUT = typename GetOutputType<OutType>::T; 402 using ARG0 = typename GetViewType<Arg0Type>::T; 403 404 Op op; 405 ScalarUnaryNotNullStateful(Op op) : op(std::move(op)) {} 406 407 template <typename Type, typename Enable = void> 408 struct ArrayExec { 409 static void Exec(const ThisType& functor, KernelContext* ctx, const ExecBatch& batch, 410 Datum* out) { 411 DCHECK(false); 412 } 413 }; 414 415 template <typename Type> 416 struct ArrayExec<Type, enable_if_t<has_c_type<Type>::value && 417 !is_boolean_type<Type>::value>> { 418 static void Exec(const ThisType& functor, KernelContext* ctx, const ExecBatch& batch, 419 Datum* out) { 420 ArrayData* out_arr = out->mutable_array(); 421 auto out_data = out_arr->GetMutableValues<OUT>(1); 422 VisitArrayDataInline<Arg0Type>(*batch[0].array(), [&](util::optional<ARG0> v) { 423 if (v.has_value()) { 424 *out_data = functor.op.template Call<OUT, ARG0>(ctx, *v); 425 } 426 ++out_data; 427 }); 428 } 429 }; 430 431 template <typename Type> 432 struct ArrayExec<Type, enable_if_string_like<Type>> { 433 static void Exec(const ThisType& functor, KernelContext* ctx, const ExecBatch& batch, 434 Datum* out) { 435 typename TypeTraits<Type>::BuilderType builder; 436 Status s = VisitArrayDataInline<Arg0Type>( 437 *batch[0].array(), [&](util::optional<ARG0> v) -> Status { 438 if (v.has_value()) { 439 return builder.Append(functor.op.Call(ctx, *v)); 440 } else { 441 return builder.AppendNull(); 442 } 443 }); 444 if (!s.ok()) { 445 ctx->SetStatus(s); 446 return; 447 } else { 448 std::shared_ptr<ArrayData> result; 449 ctx->SetStatus(builder.FinishInternal(&result)); 450 out->value = std::move(result); 451 } 452 } 453 }; 454 455 template <typename Type> 456 struct ArrayExec<Type, enable_if_t<is_boolean_type<Type>::value>> { 457 static void Exec(const ThisType& functor, KernelContext* ctx, const ExecBatch& batch, 458 Datum* out) { 459 ArrayData* out_arr = out->mutable_array(); 460 FirstTimeBitmapWriter out_writer(out_arr->buffers[1]->mutable_data(), 461 out_arr->offset, out_arr->length); 462 VisitArrayDataInline<Arg0Type>(*batch[0].array(), [&](util::optional<ARG0> v) { 463 if (v.has_value()) { 464 if (functor.op.template Call<OUT, ARG0>(ctx, *v)) { 465 out_writer.Set(); 466 } 467 } 468 out_writer.Next(); 469 }); 470 out_writer.Finish(); 471 } 472 }; 473 474 void Scalar(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 475 if (batch[0].scalar()->is_valid) { 476 ARG0 arg0 = UnboxScalar<Arg0Type>::Unbox(batch[0]); 477 out->value = BoxScalar<OutType>::Box( 478 this->op.template Call<OUT, ARG0>(ctx, arg0), 479 out->type()); 480 } else { 481 out->value = MakeNullScalar(batch[0].type()); 482 } 483 } 484 485 void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 486 if (batch[0].kind() == Datum::ARRAY) { 487 ArrayExec<OutType>::Exec(*this, ctx, batch, out); 488 } else { 489 return Scalar(ctx, batch, out); 490 } 491 } 492 }; 493 494 // An alternative to ScalarUnary that Applies a scalar operation on only the 495 // not-null values of a single array. The operator is not stateful; if the 496 // operator requires some initialization use ScalarUnaryNotNullStateful 497 template <typename OutType, typename Arg0Type, typename Op> 498 struct ScalarUnaryNotNull { 499 using OUT = typename GetOutputType<OutType>::T; 500 using ARG0 = typename GetViewType<Arg0Type>::T; 501 502 static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 503 // Seed kernel with dummy state 504 ScalarUnaryNotNullStateful<OutType, Arg0Type, Op> kernel({}); 505 return kernel.Exec(ctx, batch, out); 506 } 507 }; 508 509 // A kernel exec generator for binary functions that addresses both array and 510 // scalar inputs and dispatches input iteration and output writing to other 511 // templates 512 // 513 // This template executes the operator even on the data behind null values, 514 // therefore it is generally only suitable for operators that are safe to apply 515 // even on the null slot values. 516 // 517 // The "Op" functor should have the form 518 // 519 // struct Op { 520 // template <typename OUT, typename ARG0, typename ARG1> 521 // static OUT Call(KernelContext* ctx, ARG0 arg0, ARG1 arg1) { 522 // // implementation 523 // } 524 // }; 525 template <typename OutType, typename Arg0Type, typename Arg1Type, typename Op, 526 typename FlippedOp = Op> 527 struct ScalarBinary { 528 using OUT = typename GetOutputType<OutType>::T; 529 using ARG0 = typename GetViewType<Arg0Type>::T; 530 using ARG1 = typename GetViewType<Arg1Type>::T; 531 532 template <typename ChosenOp> 533 static void ArrayArray(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 534 ArrayIterator<Arg0Type> arg0(*batch[0].array()); 535 ArrayIterator<Arg1Type> arg1(*batch[1].array()); 536 OutputAdapter<OutType>::Write(ctx, out, [&]() -> OUT { 537 return ChosenOp::template Call(ctx, arg0(), arg1()); 538 }); 539 } 540 541 template <typename ChosenOp> 542 static void ArrayScalar(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 543 ArrayIterator<Arg0Type> arg0(*batch[0].array()); 544 auto arg1 = UnboxScalar<Arg1Type>::Unbox(batch[1]); 545 OutputAdapter<OutType>::Write(ctx, out, [&]() -> OUT { 546 return ChosenOp::template Call(ctx, arg0(), arg1); 547 }); 548 } 549 550 template <typename ChosenOp> 551 static void ScalarScalar(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 552 auto arg0 = UnboxScalar<Arg0Type>::Unbox(batch[0]); 553 auto arg1 = UnboxScalar<Arg1Type>::Unbox(batch[1]); 554 out->value = BoxScalar<OutType>::Box(ChosenOp::template Call(ctx, arg0, arg1), 555 out->type()); 556 } 557 558 static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 559 560 if (batch[0].kind() == Datum::ARRAY) { 561 if (batch[1].kind() == Datum::ARRAY) { 562 return ArrayArray<Op>(ctx, batch, out); 563 } else { 564 return ArrayScalar<Op>(ctx, batch, out); 565 } 566 } else { 567 if (batch[1].kind() == Datum::ARRAY) { 568 // e.g. if we were doing scalar < array, we flip and do array >= scalar 569 return BinaryExecFlipped(ctx, ArrayScalar<FlippedOp>, batch, out); 570 } else { 571 return ScalarScalar<Op>(ctx, batch, out); 572 } 573 } 574 } 575 }; 576 577 // A kernel exec generator for binary kernels where both input types are the 578 // same 579 template <typename OutType, typename ArgType, typename Op, 580 typename FlippedOp = Op> 581 using ScalarBinaryEqualTypes = ScalarBinary<OutType, ArgType, ArgType, Op, FlippedOp>; 582 583 // ---------------------------------------------------------------------- 584 // Dynamic kernel selectors. These functors allow a kernel implementation to be 585 // selected given a arrow::DataType instance. Using these functors triggers the 586 // corresponding template that generate's the kernel's Exec function to be 587 // instantiated 588 589 namespace detail { 590 591 // Convenience so we can pass DataType or Type::type into these kernel selectors 592 struct GetTypeId { 593 Type::type id; 594 GetTypeId(const std::shared_ptr<DataType>& type) // NOLINT implicit construction 595 : id(type->id()) {} 596 GetTypeId(const DataType& type) // NOLINT implicit construction 597 : id(type.id()) {} 598 GetTypeId(Type::type id) // NOLINT implicit construction 599 : id(id) {} 600 }; 601 602 } // namespace detail 603 604 // Generate a kernel given a functor of type 605 // 606 // struct OPERATOR_NAME { 607 // template <typename OUT, typename ARG0> 608 // static OUT Call(KernelContext*, ARG0 val) { 609 // // IMPLEMENTATION 610 // } 611 // }; 612 template <typename Op> 613 ArrayKernelExec NumericEqualTypesUnary(detail::GetTypeId get_id) { 614 switch (get_id.id) { 615 case Type::INT8: 616 return ScalarPrimitiveExecUnary<Op, Int8Type, Int8Type>; 617 case Type::UINT8: 618 return ScalarPrimitiveExecUnary<Op, UInt8Type, UInt8Type>; 619 case Type::INT16: 620 return ScalarPrimitiveExecUnary<Op, Int16Type, Int16Type>; 621 case Type::UINT16: 622 return ScalarPrimitiveExecUnary<Op, UInt16Type, UInt16Type>; 623 case Type::INT32: 624 return ScalarPrimitiveExecUnary<Op, Int32Type, Int32Type>; 625 case Type::UINT32: 626 return ScalarPrimitiveExecUnary<Op, UInt32Type, UInt32Type>; 627 case Type::INT64: 628 return ScalarPrimitiveExecUnary<Op, Int64Type, Int64Type>; 629 case Type::UINT64: 630 return ScalarPrimitiveExecUnary<Op, UInt64Type, UInt64Type>; 631 case Type::FLOAT: 632 return ScalarPrimitiveExecUnary<Op, FloatType, FloatType>; 633 case Type::DOUBLE: 634 return ScalarPrimitiveExecUnary<Op, DoubleType, DoubleType>; 635 default: 636 DCHECK(false); 637 return ExecFail; 638 } 639 } 640 641 // Generate a kernel given a functor of type 642 // 643 // struct OPERATOR_NAME { 644 // template <typename OUT, typename ARG0, typename ARG1> 645 // static OUT Call(KernelContext*, ARG0 left, ARG1 right) { 646 // // IMPLEMENTATION 647 // } 648 // }; 649 template <typename Op> 650 ArrayKernelExec NumericEqualTypesBinary(detail::GetTypeId get_id) { 651 switch (get_id.id) { 652 case Type::INT8: 653 return ScalarPrimitiveExecBinary<Op, Int8Type, Int8Type, Int8Type>; 654 case Type::UINT8: 655 return ScalarPrimitiveExecBinary<Op, UInt8Type, UInt8Type, UInt8Type>; 656 case Type::INT16: 657 return ScalarPrimitiveExecBinary<Op, Int16Type, Int16Type, Int16Type>; 658 case Type::UINT16: 659 return ScalarPrimitiveExecBinary<Op, UInt16Type, UInt16Type, UInt16Type>; 660 case Type::INT32: 661 return ScalarPrimitiveExecBinary<Op, Int32Type, Int32Type, Int32Type>; 662 case Type::UINT32: 663 return ScalarPrimitiveExecBinary<Op, UInt32Type, UInt32Type, UInt32Type>; 664 case Type::INT64: 665 return ScalarPrimitiveExecBinary<Op, Int64Type, Int64Type, Int64Type>; 666 case Type::UINT64: 667 return ScalarPrimitiveExecBinary<Op, UInt64Type, UInt64Type, UInt64Type>; 668 case Type::FLOAT: 669 return ScalarPrimitiveExecBinary<Op, FloatType, FloatType, FloatType>; 670 case Type::DOUBLE: 671 return ScalarPrimitiveExecBinary<Op, DoubleType, DoubleType, DoubleType>; 672 default: 673 DCHECK(false); 674 return ExecFail; 675 } 676 } 677 678 // Generate a kernel given a templated functor. This template effectively 679 // "curries" the first type argument. The functor must be of the form: 680 // 681 // template <typename Type0, typename Type1, Args...> 682 // struct FUNCTOR { 683 // static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { 684 // // IMPLEMENTATION 685 // } 686 // }; 687 // 688 // This function will generate exec functions where Type1 is one of the numeric 689 // types 690 template <template <typename...> class Generator, 691 typename Type0, typename... Args> 692 ArrayKernelExec Numeric(detail::GetTypeId get_id) { 693 switch (get_id.id) { 694 case Type::INT8: 695 return Generator<Type0, Int8Type, Args...>::Exec; 696 case Type::UINT8: 697 return Generator<Type0, UInt8Type, Args...>::Exec; 698 case Type::INT16: 699 return Generator<Type0, Int16Type, Args...>::Exec; 700 case Type::UINT16: 701 return Generator<Type0, UInt16Type, Args...>::Exec; 702 case Type::INT32: 703 return Generator<Type0, Int32Type, Args...>::Exec; 704 case Type::UINT32: 705 return Generator<Type0, UInt32Type, Args...>::Exec; 706 case Type::INT64: 707 return Generator<Type0, Int64Type, Args...>::Exec; 708 case Type::UINT64: 709 return Generator<Type0, UInt64Type, Args...>::Exec; 710 case Type::FLOAT: 711 return Generator<Type0, FloatType, Args...>::Exec; 712 case Type::DOUBLE: 713 return Generator<Type0, DoubleType, Args...>::Exec; 714 default: 715 DCHECK(false); 716 return ExecFail; 717 } 718 } 719 720 // Generate a kernel given a templated functor for floating point types 721 // 722 // See "Numeric" above for description of the generator functor 723 template <template <typename...> class Generator, 724 typename Type0, typename... Args> 725 ArrayKernelExec FloatingPoint(detail::GetTypeId get_id) { 726 switch (get_id.id) { 727 case Type::FLOAT: 728 return Generator<Type0, FloatType, Args...>::Exec; 729 case Type::DOUBLE: 730 return Generator<Type0, DoubleType, Args...>::Exec; 731 default: 732 DCHECK(false); 733 return ExecFail; 734 } 735 } 736 737 // Generate a kernel given a templated functor for integer types 738 // 739 // See "Numeric" above for description of the generator functor 740 template <template <typename...> class Generator, 741 typename Type0, typename... Args> 742 ArrayKernelExec Integer(detail::GetTypeId get_id) { 743 switch (get_id.id) { 744 case Type::INT8: 745 return Generator<Type0, Int8Type, Args...>::Exec; 746 case Type::INT16: 747 return Generator<Type0, Int16Type, Args...>::Exec; 748 case Type::INT32: 749 return Generator<Type0, Int32Type, Args...>::Exec; 750 case Type::INT64: 751 return Generator<Type0, Int64Type, Args...>::Exec; 752 case Type::UINT8: 753 return Generator<Type0, UInt8Type, Args...>::Exec; 754 case Type::UINT16: 755 return Generator<Type0, UInt16Type, Args...>::Exec; 756 case Type::UINT32: 757 return Generator<Type0, UInt32Type, Args...>::Exec; 758 case Type::UINT64: 759 return Generator<Type0, UInt64Type, Args...>::Exec; 760 default: 761 DCHECK(false); 762 return ExecFail; 763 } 764 } 765 766 767 // Generate a kernel given a templated functor for integer types 768 // 769 // See "Numeric" above for description of the generator functor 770 template <template <typename...> class Generator, 771 typename Type0, typename... Args> 772 ArrayKernelExec SignedInteger(detail::GetTypeId get_id) { 773 switch (get_id.id) { 774 case Type::INT8: 775 return Generator<Type0, Int8Type, Args...>::Exec; 776 case Type::INT16: 777 return Generator<Type0, Int16Type, Args...>::Exec; 778 case Type::INT32: 779 return Generator<Type0, Int32Type, Args...>::Exec; 780 case Type::INT64: 781 return Generator<Type0, Int64Type, Args...>::Exec; 782 default: 783 DCHECK(false); 784 return ExecFail; 785 } 786 } 787 788 // Generate a kernel given a templated functor for base binary types 789 // 790 // See "Numeric" above for description of the generator functor 791 template <template <typename...> class Generator, 792 typename Type0, typename... Args> 793 ArrayKernelExec BaseBinary(detail::GetTypeId get_id) { 794 switch (get_id.id) { 795 case Type::BINARY: 796 return Generator<Type0, BinaryType, Args...>::Exec; 797 case Type::STRING: 798 return Generator<Type0, StringType, Args...>::Exec; 799 case Type::LARGE_BINARY: 800 return Generator<Type0, LargeBinaryType, Args...>::Exec; 801 case Type::LARGE_STRING: 802 return Generator<Type0, LargeStringType, Args...>::Exec; 803 default: 804 DCHECK(false); 805 return ExecFail; 806 } 807 } 808 809 // Generate a kernel given a templated functor for temporal types 810 // 811 // See "Numeric" above for description of the generator functor 812 template <template <typename...> class Generator, 813 typename Type0, typename... Args> 814 ArrayKernelExec Temporal(detail::GetTypeId get_id) { 815 switch (get_id.id) { 816 case Type::DATE32: 817 return Generator<Type0, Date32Type, Args...>::Exec; 818 case Type::DATE64: 819 return Generator<Type0, Date64Type, Args...>::Exec; 820 case Type::DURATION: 821 return Generator<Type0, DurationType, Args...>::Exec; 822 case Type::TIME32: 823 return Generator<Type0, Time32Type, Args...>::Exec; 824 case Type::TIME64: 825 return Generator<Type0, Time64Type, Args...>::Exec; 826 case Type::TIMESTAMP: 827 return Generator<Type0, TimestampType, Args...>::Exec; 828 default: 829 DCHECK(false); 830 return ExecFail; 831 } 832 } 833 834 } // namespace codegen 835 } // namespace compute 836 } // namespace arrow 837