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