#pragma clang system_header // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #pragma once #include #include #include #include #include #include #include #include #include "contrib/libs/apache/arrow_next/cpp/src/arrow/array/builder_binary.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/array/data.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/buffer.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/buffer_builder.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/compute/kernel.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/datum.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/result.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/scalar.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/status.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/type.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/type_traits.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/bit_block_counter.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/bit_util.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/bitmap_generate.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/bitmap_reader.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/bitmap_writer.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/checked_cast.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/decimal.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/logging.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/macros.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/visibility.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/visit_data_inline.h" namespace arrow20 { using internal::BinaryBitBlockCounter; using internal::BitBlockCount; using internal::BitmapReader; using internal::checked_cast; using internal::FirstTimeBitmapWriter; using internal::GenerateBitsUnrolled; using internal::VisitBitBlocks; using internal::VisitBitBlocksVoid; using internal::VisitTwoBitBlocksVoid; namespace compute { namespace internal { /// KernelState adapter for the common case of kernels whose only /// state is an instance of a subclass of FunctionOptions. /// Default FunctionOptions are *not* handled here. template struct OptionsWrapper : public KernelState { explicit OptionsWrapper(OptionsType options) : options(std::move(options)) {} static Result> Init(KernelContext* ctx, const KernelInitArgs& args) { if (auto options = static_cast(args.options)) { return std::make_unique(*options); } return Status::Invalid( "Attempted to initialize KernelState from null FunctionOptions"); } static const OptionsType& Get(const KernelState& state) { return ::arrow20::internal::checked_cast(state).options; } static const OptionsType& Get(KernelContext* ctx) { return Get(*ctx->state()); } OptionsType options; }; /// KernelState adapter for when the state is an instance constructed with the /// KernelContext and the FunctionOptions as argument template struct KernelStateFromFunctionOptions : public KernelState { explicit KernelStateFromFunctionOptions(KernelContext* ctx, OptionsType options) : state(StateType(ctx, std::move(options))) {} static Result> Init(KernelContext* ctx, const KernelInitArgs& args) { if (auto options = static_cast(args.options)) { return std::make_unique(ctx, *options); } return Status::Invalid( "Attempted to initialize KernelState from null FunctionOptions"); } static const StateType& Get(const KernelState& state) { return ::arrow20::internal::checked_cast(state) .state; } static const StateType& Get(KernelContext* ctx) { return Get(*ctx->state()); } StateType state; }; // ---------------------------------------------------------------------- // Input and output value type definitions template struct GetViewType; template struct GetViewType> { using T = typename Type::c_type; using PhysicalType = T; static T LogicalValue(PhysicalType value) { return value; } }; template struct GetViewType::value || is_fixed_size_binary_type::value || is_binary_view_like_type::value>> { using T = std::string_view; using PhysicalType = T; static T LogicalValue(PhysicalType value) { return value; } }; template <> struct GetViewType { using T = Decimal32; using PhysicalType = std::string_view; static T LogicalValue(PhysicalType value) { return Decimal32(reinterpret_cast(value.data())); } static T LogicalValue(T value) { return value; } }; template <> struct GetViewType { using T = Decimal64; using PhysicalType = std::string_view; static T LogicalValue(PhysicalType value) { return Decimal64(reinterpret_cast(value.data())); } static T LogicalValue(T value) { return value; } }; template <> struct GetViewType { using T = Decimal128; using PhysicalType = std::string_view; static T LogicalValue(PhysicalType value) { return Decimal128(reinterpret_cast(value.data())); } static T LogicalValue(T value) { return value; } }; template <> struct GetViewType { using T = Decimal256; using PhysicalType = std::string_view; static T LogicalValue(PhysicalType value) { return Decimal256(reinterpret_cast(value.data())); } static T LogicalValue(T value) { return value; } }; template struct GetOutputType; template struct GetOutputType> { using T = typename Type::c_type; }; template struct GetOutputType::value>> { using T = std::string; }; template <> struct GetOutputType { using T = Decimal32; }; template <> struct GetOutputType { using T = Decimal64; }; template <> struct GetOutputType { using T = Decimal128; }; template <> struct GetOutputType { using T = Decimal256; }; // ---------------------------------------------------------------------- // enable_if helpers for C types template using is_unsigned_integer_value = std::integral_constant::value && std::is_unsigned::value>; template using is_signed_integer_value = std::integral_constant::value && std::is_signed::value>; template using is_integer_value = std::integral_constant::value || is_unsigned_integer_value::value>; template using enable_if_signed_integer_value = enable_if_t::value, R>; template using enable_if_unsigned_integer_value = enable_if_t::value, R>; template using enable_if_integer_value = enable_if_t::value || is_unsigned_integer_value::value, R>; template using enable_if_floating_value = enable_if_t::value, R>; template using enable_if_not_floating_value = enable_if_t::value, R>; template using enable_if_decimal_value = enable_if_t::value || std::is_same::value || std::is_same::value || std::is_same::value, R>; // ---------------------------------------------------------------------- // Iteration / value access utilities template using enable_if_c_number_or_decimal = enable_if_t< (has_c_type::value && !is_boolean_type::value) || is_decimal_type::value, R>; // Iterator over various input array types, yielding a GetViewType template struct ArrayIterator; template struct ArrayIterator> { using T = typename TypeTraits::ScalarType::ValueType; const T* values; explicit ArrayIterator(const ArraySpan& arr) : values(arr.GetValues(1)) {} T operator()() { return *values++; } }; template struct ArrayIterator> { BitmapReader reader; explicit ArrayIterator(const ArraySpan& arr) : reader(arr.buffers[1].data, arr.offset, arr.length) {} bool operator()() { bool out = reader.IsSet(); reader.Next(); return out; } }; template struct ArrayIterator> { using offset_type = typename Type::offset_type; const ArraySpan& arr; const offset_type* offsets; offset_type cur_offset; const char* data; int64_t position; explicit ArrayIterator(const ArraySpan& arr) : arr(arr), offsets(reinterpret_cast(arr.buffers[1].data) + arr.offset), cur_offset(offsets[0]), data(reinterpret_cast(arr.buffers[2].data)), position(0) {} std::string_view operator()() { offset_type next_offset = offsets[++position]; auto result = std::string_view(data + cur_offset, next_offset - cur_offset); cur_offset = next_offset; return result; } }; template <> struct ArrayIterator { const ArraySpan& arr; const char* data; const int32_t width; int64_t position; explicit ArrayIterator(const ArraySpan& arr) : arr(arr), data(reinterpret_cast(arr.buffers[1].data)), width(arr.type->byte_width()), position(arr.offset) {} std::string_view operator()() { auto result = std::string_view(data + position * width, width); position++; return result; } }; // Iterator over various output array types, taking a GetOutputType template struct OutputArrayWriter; template struct OutputArrayWriter> { using T = typename TypeTraits::ScalarType::ValueType; T* values; explicit OutputArrayWriter(ArraySpan* data) : values(data->GetValues(1)) {} void Write(T value) { *values++ = value; } // Note that this doesn't write the null bitmap, which should be consistent // with Write / WriteNull calls void WriteNull() { *values++ = T{}; } void WriteAllNull(int64_t length) { std::memset(static_cast(values), 0, sizeof(T) * length); } }; // (Un)box Scalar to / from C++ value template struct UnboxScalar; template struct UnboxScalar> { using T = typename Type::c_type; static T Unbox(const Scalar& val) { std::string_view view = checked_cast(val).view(); DCHECK_EQ(view.size(), sizeof(T)); return *reinterpret_cast(view.data()); } }; template struct UnboxScalar> { using T = std::string_view; static T Unbox(const Scalar& val) { if (!val.is_valid) return std::string_view(); return checked_cast(val).view(); } }; template <> struct UnboxScalar { using T = Decimal32; static const T& Unbox(const Scalar& val) { return checked_cast(val).value; } }; template <> struct UnboxScalar { using T = Decimal64; static const T& Unbox(const Scalar& val) { return checked_cast(val).value; } }; template <> struct UnboxScalar { using T = Decimal128; static const T& Unbox(const Scalar& val) { return checked_cast(val).value; } }; template <> struct UnboxScalar { using T = Decimal256; static const T& Unbox(const Scalar& val) { return checked_cast(val).value; } }; // A VisitArraySpanInline variant that calls its visitor function with logical // values, such as Decimal128 rather than std::string_view. template static typename ::arrow20::internal::call_traits::enable_if_return::type VisitArrayValuesInline(const ArraySpan& arr, VisitFunc&& valid_func, NullFunc&& null_func) { VisitArraySpanInline( arr, [&](typename GetViewType::PhysicalType v) { valid_func(GetViewType::LogicalValue(std::move(v))); }, std::forward(null_func)); } template static typename ::arrow20::internal::call_traits::enable_if_return::type VisitArrayValuesInline(const ArraySpan& arr, VisitFunc&& valid_func, NullFunc&& null_func) { return VisitArraySpanInline( arr, [&](typename GetViewType::PhysicalType v) { return valid_func(GetViewType::LogicalValue(std::move(v))); }, std::forward(null_func)); } // Like VisitArrayValuesInline, but for binary functions. template static void VisitTwoArrayValuesInline(const ArraySpan& arr0, const ArraySpan& arr1, VisitFunc&& valid_func, NullFunc&& null_func) { ArrayIterator arr0_it(arr0); ArrayIterator arr1_it(arr1); auto visit_valid = [&](int64_t i) { valid_func(GetViewType::LogicalValue(arr0_it()), GetViewType::LogicalValue(arr1_it())); }; auto visit_null = [&]() { arr0_it(); arr1_it(); null_func(); }; VisitTwoBitBlocksVoid(arr0.buffers[0].data, arr0.offset, arr1.buffers[0].data, arr1.offset, arr0.length, std::move(visit_valid), std::move(visit_null)); } // ---------------------------------------------------------------------- // Reusable type resolvers Result FirstType(KernelContext*, const std::vector& types); Result LastType(KernelContext*, const std::vector& types); Result ListValuesType(KernelContext* ctx, const std::vector& types); // ---------------------------------------------------------------------- // Helpers for iterating over common DataType instances for adding kernels to // functions // Returns a vector of example instances of parametric types such as // // * Decimal // * Timestamp (requiring unit) // * Time32 (requiring unit) // * Time64 (requiring unit) // * Duration (requiring unit) // * List, LargeList, FixedSizeList // * Struct // * Union // * Dictionary // * Map // // Generally kernels will use the "FirstType" OutputType::Resolver above for // the OutputType of the kernel's signature and match::SameTypeId for the // corresponding InputType const std::vector>& ExampleParametricTypes(); // ---------------------------------------------------------------------- // "Applicators" take an operator definition and creates an ArrayKernelExec // which can be used to add a kernel to a Function. namespace applicator { // Generate an ArrayKernelExec given a functor that handles all of its own // iteration, etc. // // Operator must implement // // static Status Call(KernelContext*, const ArraySpan& arg0, const ArraySpan& arg1, // * out) // static Status Call(KernelContext*, const ArraySpan& arg0, const Scalar& arg1, // ExecResult* out) // static Status Call(KernelContext*, const Scalar& arg0, const ArraySpan& arg1, // ExecResult* out) template static Status SimpleBinary(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { if (batch.length == 0) return Status::OK(); if (batch[0].is_array()) { if (batch[1].is_array()) { return Operator::Call(ctx, batch[0].array, batch[1].array, out); } else { return Operator::Call(ctx, batch[0].array, *batch[1].scalar, out); } } else { if (batch[1].is_array()) { return Operator::Call(ctx, *batch[0].scalar, batch[1].array, out); } else { DCHECK(false); return Status::Invalid("Should be unreachable"); } } } // OutputAdapter allows passing an inlineable lambda that provides a sequence // of output values to write into output memory. Boolean and primitive outputs // are currently implemented, and the validity bitmap is presumed to be handled // at a higher level, so this writes into every output slot, null or not. template struct OutputAdapter; template struct OutputAdapter> { template static Status Write(KernelContext*, ArraySpan* out, Generator&& generator) { GenerateBitsUnrolled(out->buffers[1].data, out->offset, out->length, std::forward(generator)); return Status::OK(); } }; template struct OutputAdapter> { using T = typename TypeTraits::ScalarType::ValueType; template static Status Write(KernelContext*, ArraySpan* out, Generator&& generator) { T* out_data = out->GetValues(1); // TODO: Is this as fast as a more explicitly inlined function? for (int64_t i = 0; i < out->length; ++i) { *out_data++ = generator(); } return Status::OK(); } }; template struct OutputAdapter> { template static Status Write(KernelContext* ctx, ArraySpan* out, Generator&& generator) { return Status::NotImplemented("NYI"); } }; // A kernel exec generator for unary functions that addresses both array and // scalar inputs and dispatches input iteration and output writing to other // templates // // This template executes the operator even on the data behind null values, // therefore it is generally only suitable for operators that are safe to apply // even on the null slot values. // // The "Op" functor should have the form // // struct Op { // template // static OutValue Call(KernelContext* ctx, Arg0Value val, Status* st) { // // implementation // // NOTE: "status" should only populated with errors, // // leave it unmodified to indicate Status::OK() // } // }; template struct ScalarUnary { using OutValue = typename GetOutputType::T; using Arg0Value = typename GetViewType::T; static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { DCHECK(batch[0].is_array()); const ArraySpan& arg0 = batch[0].array; Status st = Status::OK(); ArrayIterator arg0_it(arg0); RETURN_NOT_OK( OutputAdapter::Write(ctx, out->array_span_mutable(), [&]() -> OutValue { return Op::template Call(ctx, arg0_it(), &st); })); return st; } }; // An alternative to ScalarUnary that Applies a scalar operation with state on // only the not-null values of a single array template struct ScalarUnaryNotNullStateful { using ThisType = ScalarUnaryNotNullStateful; using OutValue = typename GetOutputType::T; using Arg0Value = typename GetViewType::T; Op op; explicit ScalarUnaryNotNullStateful(Op op) : op(std::move(op)) {} // NOTE: In ArrayExec, Type is really OutputType template struct ArrayExec { static Status Exec(const ThisType& functor, KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { ARROW_LOG(FATAL) << "Missing ArrayExec specialization for output type " << out->type(); return Status::NotImplemented("NYI"); } }; template struct ArrayExec> { static Status Exec(const ThisType& functor, KernelContext* ctx, const ArraySpan& arg0, ExecResult* out) { Status st = Status::OK(); auto out_data = out->array_span_mutable()->GetValues(1); VisitArrayValuesInline( arg0, [&](Arg0Value v) { *out_data++ = functor.op.template Call(ctx, v, &st); }, [&]() { // null *out_data++ = OutValue{}; }); return st; } }; template struct ArrayExec> { static Status Exec(const ThisType& functor, KernelContext* ctx, const ArraySpan& arg0, ExecResult* out) { // NOTE: This code is not currently used by any kernels and has // suboptimal performance because it's recomputing the validity bitmap // that is already computed by the kernel execution layer. Consider // writing a lower-level "output adapter" for base binary types. typename TypeTraits::BuilderType builder; Status st = Status::OK(); RETURN_NOT_OK(VisitArrayValuesInline( arg0, [&](Arg0Value v) { return builder.Append(functor.op.Call(ctx, v, &st)); }, [&]() { return builder.AppendNull(); })); if (st.ok()) { std::shared_ptr result; RETURN_NOT_OK(builder.FinishInternal(&result)); out->value = std::move(result); } return st; } }; template struct ArrayExec::value>> { static Status Exec(const ThisType& functor, KernelContext* ctx, const ArraySpan& arg0, ExecResult* out) { Status st = Status::OK(); ArraySpan* out_arr = out->array_span_mutable(); FirstTimeBitmapWriter out_writer(out_arr->buffers[1].data, out_arr->offset, out_arr->length); VisitArrayValuesInline( arg0, [&](Arg0Value v) { if (functor.op.template Call(ctx, v, &st)) { out_writer.Set(); } out_writer.Next(); }, [&]() { // null out_writer.Clear(); out_writer.Next(); }); out_writer.Finish(); return st; } }; Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { DCHECK(batch[0].is_array()); return ArrayExec::Exec(*this, ctx, batch[0].array, out); } }; // An alternative to ScalarUnary that Applies a scalar operation on only the // not-null values of a single array. The operator is not stateful; if the // operator requires some initialization use ScalarUnaryNotNullStateful template struct ScalarUnaryNotNull { using OutValue = typename GetOutputType::T; using Arg0Value = typename GetViewType::T; static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { // Seed kernel with dummy state ScalarUnaryNotNullStateful kernel({}); return kernel.Exec(ctx, batch, out); } }; // A kernel exec generator for binary functions that addresses both array and // scalar inputs and dispatches input iteration and output writing to other // templates // // This template executes the operator even on the data behind null values, // therefore it is generally only suitable for operators that are safe to apply // even on the null slot values. // // The "Op" functor should have the form // // struct Op { // template // static OutValue Call(KernelContext* ctx, Arg0Value arg0, Arg1Value arg1, Status* st) // { // // implementation // // NOTE: "status" should only populated with errors, // // leave it unmodified to indicate Status::OK() // } // }; template struct ScalarBinary { using OutValue = typename GetOutputType::T; using Arg0Value = typename GetViewType::T; using Arg1Value = typename GetViewType::T; static Status ArrayArray(KernelContext* ctx, const ArraySpan& arg0, const ArraySpan& arg1, ExecResult* out) { Status st = Status::OK(); ArrayIterator arg0_it(arg0); ArrayIterator arg1_it(arg1); RETURN_NOT_OK( OutputAdapter::Write(ctx, out->array_span_mutable(), [&]() -> OutValue { return Op::template Call(ctx, arg0_it(), arg1_it(), &st); })); return st; } static Status ArrayScalar(KernelContext* ctx, const ArraySpan& arg0, const Scalar& arg1, ExecResult* out) { Status st = Status::OK(); ArrayIterator arg0_it(arg0); auto arg1_val = UnboxScalar::Unbox(arg1); RETURN_NOT_OK( OutputAdapter::Write(ctx, out->array_span_mutable(), [&]() -> OutValue { return Op::template Call(ctx, arg0_it(), arg1_val, &st); })); return st; } static Status ScalarArray(KernelContext* ctx, const Scalar& arg0, const ArraySpan& arg1, ExecResult* out) { Status st = Status::OK(); auto arg0_val = UnboxScalar::Unbox(arg0); ArrayIterator arg1_it(arg1); RETURN_NOT_OK( OutputAdapter::Write(ctx, out->array_span_mutable(), [&]() -> OutValue { return Op::template Call(ctx, arg0_val, arg1_it(), &st); })); return st; } static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { if (batch[0].is_array()) { if (batch[1].is_array()) { return ArrayArray(ctx, batch[0].array, batch[1].array, out); } else { return ArrayScalar(ctx, batch[0].array, *batch[1].scalar, out); } } else { if (batch[1].is_array()) { return ScalarArray(ctx, *batch[0].scalar, batch[1].array, out); } else { DCHECK(false); return Status::Invalid("Should be unreachable"); } } } }; // An alternative to ScalarBinary that Applies a scalar operation with state on // only the value pairs which are not-null in both arrays template struct ScalarBinaryNotNullStateful { using ThisType = ScalarBinaryNotNullStateful; using OutValue = typename GetOutputType::T; using Arg0Value = typename GetViewType::T; using Arg1Value = typename GetViewType::T; Op op; explicit ScalarBinaryNotNullStateful(Op op) : op(std::move(op)) {} // NOTE: In ArrayExec, Type is really OutputType Status ArrayArray(KernelContext* ctx, const ArraySpan& arg0, const ArraySpan& arg1, ExecResult* out) { Status st = Status::OK(); OutputArrayWriter writer(out->array_span_mutable()); VisitTwoArrayValuesInline( arg0, arg1, [&](Arg0Value u, Arg1Value v) { writer.Write(op.template Call(ctx, u, v, &st)); }, [&]() { writer.WriteNull(); }); return st; } Status ArrayScalar(KernelContext* ctx, const ArraySpan& arg0, const Scalar& arg1, ExecResult* out) { Status st = Status::OK(); ArraySpan* out_span = out->array_span_mutable(); OutputArrayWriter writer(out_span); if (arg1.is_valid) { const auto arg1_val = UnboxScalar::Unbox(arg1); VisitArrayValuesInline( arg0, [&](Arg0Value u) { writer.Write( op.template Call(ctx, u, arg1_val, &st)); }, [&]() { writer.WriteNull(); }); } else { writer.WriteAllNull(out_span->length); } return st; } Status ScalarArray(KernelContext* ctx, const Scalar& arg0, const ArraySpan& arg1, ExecResult* out) { Status st = Status::OK(); ArraySpan* out_span = out->array_span_mutable(); OutputArrayWriter writer(out_span); if (arg0.is_valid) { const auto arg0_val = UnboxScalar::Unbox(arg0); VisitArrayValuesInline( arg1, [&](Arg1Value v) { writer.Write( op.template Call(ctx, arg0_val, v, &st)); }, [&]() { writer.WriteNull(); }); } else { writer.WriteAllNull(out_span->length); } return st; } Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { if (batch[0].is_array()) { if (batch[1].is_array()) { return ArrayArray(ctx, batch[0].array, batch[1].array, out); } else { return ArrayScalar(ctx, batch[0].array, *batch[1].scalar, out); } } else { if (batch[1].is_array()) { return ScalarArray(ctx, *batch[0].scalar, batch[1].array, out); } else { DCHECK(false); return Status::Invalid("Should be unreachable"); } } } }; // An alternative to ScalarBinary that Applies a scalar operation on only // the value pairs which are not-null in both arrays. // The operator is not stateful; if the operator requires some initialization // use ScalarBinaryNotNullStateful. template struct ScalarBinaryNotNull { using OutValue = typename GetOutputType::T; using Arg0Value = typename GetViewType::T; using Arg1Value = typename GetViewType::T; static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { // Seed kernel with dummy state ScalarBinaryNotNullStateful kernel({}); return kernel.Exec(ctx, batch, out); } }; // A kernel exec generator for binary kernels where both input types are the // same template using ScalarBinaryEqualTypes = ScalarBinary; // A kernel exec generator for non-null binary kernels where both input types are the // same template using ScalarBinaryNotNullEqualTypes = ScalarBinaryNotNull; template using ScalarBinaryNotNullStatefulEqualTypes = ScalarBinaryNotNullStateful; } // namespace applicator // ---------------------------------------------------------------------- // BEGIN of kernel generator-dispatchers ("GD") // // These GD functions instantiate kernel functor templates and select one of // the instantiated kernels dynamically based on the data type or Type::type id // that is passed. This enables functions to be populated with kernels by // looping over vectors of data types rather than using macros or other // approaches. // // The kernel functor must be of the form: // // template // struct FUNCTOR { // static void Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { // // IMPLEMENTATION // } // }; // // When you pass FUNCTOR to a GD function, you must pass at least one static // type along with the functor -- this is often the fixed return type of the // functor. This Type0 argument is passed as the first argument to the functor // during instantiation. The 2nd type passed to the functor is the DataType // subclass corresponding to the type passed as argument (not template type) to // the function. // // For example, GenerateNumeric(int32()) will select a kernel // instantiated like FUNCTOR. Any additional variadic // template arguments will be passed as additional template arguments to the // kernel template. namespace detail { // Convenience so we can pass DataType or Type::type for the GD's struct GetTypeId { Type::type id; GetTypeId(const std::shared_ptr& type) // NOLINT implicit construction : id(type->id()) {} GetTypeId(const DataType& type) // NOLINT implicit construction : id(type.id()) {} GetTypeId(Type::type id) // NOLINT implicit construction : id(id) {} }; } // namespace detail template struct FailFunctor {}; template <> struct FailFunctor { static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) { return Status::NotImplemented("This kernel is malformed"); } }; template <> struct FailFunctor { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { return Status::NotImplemented("This kernel is malformed"); } }; // GD for numeric types (integer and floating point) template