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